X-Git-Url: https://git.lyx.org/gitweb/?a=blobdiff_plain;f=development%2Fautotests%2Fkeytest.py;h=dab92a8ca07ed184cf14e79807a8f5914098cde8;hb=cb3922093016875404aedc6d1fcf2e088944b5c9;hp=7dd86a6357777d9fa5f671f955abb396e726f15e;hpb=4436431ea53b44d95a33f8fc94353edd49c1f06b;p=lyx.git diff --git a/development/autotests/keytest.py b/development/autotests/keytest.py index 7dd86a6357..dab92a8ca0 100755 --- a/development/autotests/keytest.py +++ b/development/autotests/keytest.py @@ -14,6 +14,9 @@ import os import re import sys import time +import tempfile +import shutil + #from subprocess import call import subprocess @@ -23,6 +26,15 @@ FNULL = open('/dev/null', 'w') key_delay = '' +# Ignore status == "dead" if this is set. Used at the last commands after "\Cq" +dead_expected = False + +def die(excode, text): + if text != "": + print(text) + sys.stdout.flush() + os._exit(excode) + class CommandSource: def __init__(self): @@ -61,7 +73,7 @@ class CommandSource: if self.count % 200 == 0: return 'RaiseLyx' elif self.count > self.count_max: - os._exit(0) + die(0, "") else: keystr = '' for k in range(1, 2): @@ -140,7 +152,7 @@ class CommandSourceFromFile(CommandSource): def getCommand(self): if self.count >= self.max_count: - os._exit(0) + die(0, "") if self.i >= len(self.lines): self.loops = self.loops + 1 if self.loops >= int(max_loops): @@ -152,6 +164,93 @@ class CommandSourceFromFile(CommandSource): self.i = self.i + 1 return line +class ControlFile: + + def __init__(self): + self.control = re.compile(r'^(C[ONPpRrC])([A-Za-z0-9]*):\s*(.*)$') + self.fileformat = re.compile(r'^((\>\>?)[,\s]\s*)?([^\s]+)\s*$') + self.cntrfile = dict() + # Map keytest marker to pattern-file-marker for searchPatterns.pl + self.convertSearchMark = { 'CN': 'Comment: ', + 'CP': 'Simple: ', 'Cp': 'ErrSimple: ', + 'CR': 'Regex: ', 'Cr': 'ErrRegex: '} + + def __open(self, handle, filename): + if handle in self.cntrfile: + self.cntrfile[handle].close() + del self.cntrfile[handle] + m = self.fileformat.match(filename) + if m: + type = m.group(2) + filename = m.group(3) + if type == '>>': + append = True + else: + append = False + else: + append = False + if append: + self.cntrfile[handle] = open(filename, 'a') + else: + self.cntrfile[handle] = open(filename, 'w') + + def closeall(self): + handles = self.cntrfile.keys() + for handle in handles: + self.__close(handle) + + def __close(self, handle): + if handle in self.cntrfile: + name = self.cntrfile[handle].name + self.cntrfile[handle].close() + del self.cntrfile[handle] + print("Closed ctrl " + handle + " (" + name + ")") + + # make the method below 'private' + def __addline(self, handle, pat): + self.cntrfile[handle].writelines(pat + "\n") + + def dispatch(self, c): + m = self.control.match(c) + if not m: + return False + command = m.group(1) + handle = m.group(2) + if handle is None: + handle = "" + text = m.group(3) + if command == "CO": + self.__open(handle, text); + elif command == "CC": + self.__close(handle) + else: + if handle in self.cntrfile: + if command in self.convertSearchMark: + self.__addline(handle, self.convertSearchMark[command] + text) + else: + die(1,"Error, Unrecognised Command '" + command + "'") + elif handle != "": + die(1, "Ctrl-file " + handle + " not in use") + return True + + +def get_proc_pid(proc_name): + pid=os.popen("pidof " + proc_name).read().rstrip() + return pid + +wlistreg = re.compile(r'^(0x[0-9a-f]{5,9})\s+[^\s]+\s+([0-9]+)\s.*$') +def get_proc_win_id(pid, ignoreid): + nlist = os.popen("wmctrl -l -p").read() + wlist = nlist.split("\n") + for item in wlist: + m = wlistreg.match(item) + if m: + win_id = m.group(1) + win_pid = m.group(2) + if win_pid == pid: + if win_id != ignoreid: + return win_id + return None def lyx_exists(): if lyx_pid is None: @@ -162,7 +261,9 @@ def lyx_exists(): # Interruptible os.system() def intr_system(cmd, ignore_err = False): - print("Executing " + cmd + "\n") + print("Executing " + cmd) + # Assure the output of cmd does not overhaul + sys.stdout.flush() ret = os.system(cmd) if os.WIFSIGNALED(ret): raise KeyboardInterrupt @@ -172,124 +273,200 @@ def intr_system(cmd, ignore_err = False): statreg = re.compile(r'^State:.*\(([a-z]+)\)') -def lyx_status(pid): - if lyx_pid is None: - return "dead" - fname = '/proc/' + pid + '/status' - try: - f = open(fname) - for line in f: - m = statreg.match(line) - if m: - status = m.group(1) +resstatus = [] +def printresstatus(): + for line in resstatus: + line = line.rstrip() + print(" " + line.rstrip()) + print('End of /proc-lines') + +def lyx_status_retry(pid): + resstatus = [] + if pid is None: + print('Pid is None') + return "dead" + fname = '/proc/' + pid + '/status' + status = "dead" + try: + f = open(fname) + found = False + for line in f: + resstatus.extend([line]) + m = statreg.match(line) + if m: + status = m.group(1) + found = True f.close() + if not found: + return "retry" return status - f.close() - except IOError as e: - print("I/O error({0}): {1}".format(e.errno, e.strerror)) - return "dead" - except: - print("Unexpected error:", sys.exc_info()[0]) - return "dead" + except IOError as e: + print("I/O error({0}): {1}".format(e.errno, e.strerror)) + return "dead" + except: + print("Unexpected error:", sys.exc_info()[0]) + return "dead" + print('This should not happen') + return status + +def lyx_status(pid): + count = 0 + while 1: + status = lyx_status_retry(pid) + if status != "retry": + break + if count == 0: + print('Retrying check for status') + count += 1 + time.sleep(0.01) + if count > 1: + print('Retried to read status ' + str(count) + ' times') + #print('lys_status() returning ' + status) + return status # Return true if LyX (identified via lyx_pid) is sleeping -def lyx_sleeping(): - return lyx_status(lyx_pid) == "sleeping" +def lyx_sleeping(LYX_PID): + return lyx_status(LYX_PID) == "sleeping" # Return true if LyX (identified via lyx_pid) is zombie -def lyx_zombie(): - return lyx_status(lyx_pid) == "zombie" +def lyx_zombie(LYX_PID): + return lyx_status(LYX_PID) == "zombie" -def lyx_dead(): - status = lyx_status(lyx_pid) +def lyx_dead(LYX_PID): + status = lyx_status(LYX_PID) return (status == "dead") or (status == "zombie") -def sendKeystringLocal(keystr, LYX_PID): - - # print "sending keystring "+keystr+"\n" - if not re.match(".*\w.*", keystr): - print('print .' + keystr + '.\n') - keystr = 'a' +def wait_until_lyx_sleeping(LYX_PID): before_secs = time.time() - while lyx_exists() and not lyx_sleeping(): - time.sleep(0.02) - sys.stdout.flush() + while True: + status = lyx_status(LYX_PID) + if status == "sleeping": + return True + if (status == "dead") or (status == "zombie"): + printresstatus() + if dead_expected: + print('Lyx died while waiting for status == sleeping') + return False + else: + die(1,"Lyx is dead, exiting") if time.time() - before_secs > 180: - print('Killing due to freeze (KILL_FREEZE)') - # Do profiling, but sysprof has no command line interface? # intr_system("killall -KILL lyx") + printresstatus() + die(1,"Killing due to freeze (KILL_FREEZE)") + time.sleep(0.02) + # Should be never reached + print('Wait for sleeping ends unexpectedly') + return False - os._exit(1) +def sendKeystringLocal(keystr, LYX_PID): + is_sleeping = wait_until_lyx_sleeping(LYX_PID) + if not is_sleeping: + print("Not sending \"" + keystr + "\"") + return if not screenshot_out is None: - while lyx_exists() and not lyx_sleeping(): - time.sleep(0.02) - sys.stdout.flush() print('Making Screenshot: ' + screenshot_out + ' OF ' + infilename) time.sleep(0.2) intr_system('import -window root '+screenshot_out+str(x.count)+".png") time.sleep(0.1) - sys.stdout.flush() actual_delay = key_delay if actual_delay == '': actual_delay = def_delay xvpar = [xvkbd_exe] if qt_frontend == 'QT5': - xvpar.extend(["-no-jump-pointer"]) + xvpar.extend(["-jump-pointer", "-no-back-pointer"]) else: xvpar.extend(["-xsendevent"]) - xvpar.extend(["-window", lyx_window_name, "-delay", actual_delay, "-text", keystr]) - - print("Sending \"" + keystr + "\"\n") + if lyx_other_window_name is None: + xvpar.extend(["-window", lyx_window_name]) + else: + xvpar.extend(["-window", lyx_other_window_name]) + xvpar.extend(["-delay", actual_delay, "-text", keystr]) + print("Sending \"" + keystr + "\"") subprocess.call(xvpar, stdout = FNULL, stderr = FNULL) + sys.stdout.flush() -Axreg = re.compile(r'^(.*)\\Ax([^\\]*)(.*)$') -returnreg = re.compile(r'\\\[Return\](.*)$') - -# recursive wrapper around sendKeystringLocal() -def sendKeystring(line, LYX_PID): - global key_delay - saved_delay = key_delay - m = Axreg.match(line) +def extractmultiple(line, regex): + #print("extractmultiple " + line) + res = ["", ""] + m = regex.match(line) if m: - prefix = m.group(1) - content = m.group(2) - rest = m.group(3); - if prefix != "": - # since (.*) is greedy, check prefix for '\Ax' again - sendKeystring(prefix, LYX_PID) - sendKeystringLocal('\Ax', LYX_PID) - time.sleep(0.1) - m2 = returnreg.match(rest) - if m2: - line = m2.group(1) - key_delay = "1" - sendKeystringLocal(content + '\[Return]', LYX_PID) - key_delay = saved_delay - time.sleep(0.1) - if line != "": - sendKeystringLocal(line, LYX_PID) + chr = m.group(1) + if m.group(2) == "": + res[0] = chr + res[1] = "" else: - if content != "": - sendKeystringLocal(content, LYX_PID) - if rest != "": - sendKeystringLocal(rest, LYX_PID) + norm = extractmultiple(m.group(2), regex) + res[0] = chr + norm[0] + res[1] = norm[1] else: - if line != "": - sendKeystringLocal(line, LYX_PID) + res[0] = "" + res[1] = line + return res + +normal_re = re.compile(r'^([^\\]|\\\\)(.*)$') +def extractnormal(line): + # collect non-special chars from start of line + return extractmultiple(line, normal_re) + +modifier_re = re.compile(r'^(\\[CAS])(.+)$') +def extractmodifiers(line): + # collect modifiers like '\\A' at start of line + return extractmultiple(line, modifier_re) + +special_re = re.compile(r'^(\\\[[A-Z][a-z0-9]+\])(.*)$') +def extractsingle(line): + # check for single key following a modifier + # either ascii like 'a' + # or special like '\[Return]' + res = [False, "", ""] + m = normal_re.match(line) + if m: + res[0] = False + res[1] = m.group(1) + res[2] = m.group(2) + else: + m = special_re.match(line) + if m: + res[0] = True + res[1] = m.group(1) + res[2] = m.group(2) + else: + die(1, "Undecodable key for line \'" + line + "\"") + return res +def sendKeystring(line, LYX_PID): + if line == "": + return + normalchars = extractnormal(line) + line = normalchars[1] + if normalchars[0] != "": + sendKeystringLocal(normalchars[0], LYX_PID) + if line == "": + return + modchars = extractmodifiers(line) + line = modchars[1] + if line == "": + die(1, "Missing modified key") + modifiedchar = extractsingle(line) + line = modifiedchar[2] + special = modchars[0] != "" or modifiedchar[0] + sendKeystringLocal(modchars[0] + modifiedchar[1], LYX_PID) + if special: + # give the os time to update the status info (in /proc) + time.sleep(controlkey_delay) + sendKeystring(line, LYX_PID) def system_retry(num_retry, cmd): i = 0 - rtn = intr_system(cmd) + rtn = intr_system(cmd, True) while ( ( i < num_retry ) and ( rtn != 0) ): i = i + 1 - rtn = intr_system(cmd) + rtn = intr_system(cmd, True) time.sleep(1) if ( rtn != 0 ): print("Command Failed: "+cmd) - print(" EXITING!\n") - os._exit(1) + die(1," EXITING!") def RaiseWindow(): #intr_system("echo x-session-manager PID: $X_PID.") @@ -299,6 +476,74 @@ def RaiseWindow(): intr_system("wmctrl -R '"+lyx_window_name+"' ;sleep 0.1") system_retry(30, "wmctrl -i -a '"+lyx_window_name+"'") +class Shortcuts: + + def __init__(self): + self.shortcut_entry = re.compile(r'^\s*"([^"]+)"\s*\"([^"]+)\"') + self.bindings = {} + self.bind = re.compile(r'^\s*\\bind\s+"([^"]+)"') + if lyx_userdir_ver is None: + self.dir = lyx_userdir + else: + self.dir = lyx_userdir_ver + + def __UseShortcut(self, c): + m = self.shortcut_entry.match(c) + if m: + sh = m.group(1) + fkt = m.group(2) + self.bindings[sh] = fkt + else: + die(1, "cad shortcut spec(" + c + ")") + + def __PrepareShortcuts(self): + if not self.dir is None: + tmp = tempfile.NamedTemporaryFile(suffix='.bind', delete=False) + try: + old = open(self.dir + '/bind/user.bind', 'r') + except IOError as e: + old = None + if not old is None: + lines = old.read().split("\n") + old.close() + bindfound = False + for line in lines: + m = self.bind.match(line) + if m: + bindfound = True + val = m.group(1) + if val in self.bindings: + if self.bindings[val] != "": + tmp.write("\\bind \"" + val + "\" \"" + self.bindings[val] + "\"\n") + self.bindings[val] = "" + else: + tmp.write(line + '\n') + elif not bindfound: + tmp.write(line + '\n') + else: + tmp.writelines( + '## This file is used for keytests only\n\n' + + 'Format 4\n\n' + ) + for val in self.bindings: + if not self.bindings[val] is None: + if self.bindings[val] != "": + tmp.write("\\bind \"" + val + "\" \"" + self.bindings[val] + "\"\n") + self.bindings[val] = "" + tmp.close() + shutil.move(tmp.name, self.dir + '/bind/user.bind') + else: + print("User dir not specified") + + def dispatch(self, c): + if c[0:12] == 'UseShortcut ': + self.__UseShortcut(c[12:]) + elif c == 'PrepareShortcuts': + print('Preparing usefull sortcuts for tests') + self.__PrepareShortcuts() + else: + return False + return True lyx_pid = os.environ.get('LYX_PID') print('lyx_pid: ' + str(lyx_pid) + '\n') @@ -306,13 +551,23 @@ infilename = os.environ.get('KEYTEST_INFILE') outfilename = os.environ.get('KEYTEST_OUTFILE') max_drop = os.environ.get('MAX_DROP') lyx_window_name = os.environ.get('LYX_WINDOW_NAME') +lyx_other_window_name = None screenshot_out = os.environ.get('SCREENSHOT_OUT') lyx_userdir = os.environ.get('LYX_USERDIR') +lyx_userdir_ver = os.environ.get('LYX_USERDIR_24x') +if lyx_userdir is None: + lyx_userdir = lyx_userdir_ver max_loops = os.environ.get('MAX_LOOPS') if max_loops is None: max_loops = 3 +extra_path = os.environ.get('EXTRA_PATH') +if not extra_path is None: + os.environ['PATH'] = extra_path + os.pathsep + os.environ['PATH'] + print("Added " + extra_path + " to path") + print(os.environ['PATH']) + PACKAGE = os.environ.get('PACKAGE') if not PACKAGE is None: print("PACKAGE = " + PACKAGE + "\n") @@ -336,6 +591,11 @@ if xvkbd_exe is None: qt_frontend = os.environ.get('QT_FRONTEND') if qt_frontend is None: qt_frontend = 'QT4' +if qt_frontend == 'QT5': + # Some tests sometimes failed with value 0.01 on Qt5.8 + controlkey_delay = 0.4 +else: + controlkey_delay = 0.4 locale_dir = os.environ.get('LOCALE_DIR') if locale_dir is None: @@ -343,7 +603,10 @@ if locale_dir is None: def_delay = os.environ.get('XVKBD_DELAY') if def_delay is None: - def_delay = '100' + if qt_frontend == 'QT5': + def_delay = '1' + else: + def_delay = '1' file_new_command = os.environ.get('FILE_NEW_COMMAND') if file_new_command is None: @@ -382,55 +645,70 @@ if not lyx_pid is None: write_commands = True failed = False - +lineempty = re.compile(r'^\s*$') +marked = ControlFile() +shortcuts = Shortcuts() while not failed: #intr_system('echo -n LOADAVG:; cat /proc/loadavg') c = x.getCommand() if c is None: break - if c.strip() == "": + + # Do not strip trailing spaces, only check for 'empty' lines + if lineempty.match(c): continue outfile.writelines(c + '\n') outfile.flush() + if marked.dispatch(c): + continue + elif shortcuts.dispatch(c): + continue if c[0] == '#': print("Ignoring comment line: " + c) elif c[0:9] == 'TestBegin': print("\n") - lyx_pid=os.popen("pidof " + lyx).read() + lyx_pid=get_proc_pid(lyx) if lyx_pid != "": print("Found running instance(s) of LyX: " + lyx_pid + ": killing them all\n") intr_system("killall " + lyx, True) time.sleep(0.5) intr_system("killall -KILL " + lyx, True) - time.sleep(0.2) + time.sleep(0.2) print("Starting LyX . . .") if lyx_userdir is None: intr_system(lyx_exe + c[9:] + "&") else: intr_system(lyx_exe + " -userdir " + lyx_userdir + " " + c[9:] + "&") - count = 5 + count = 10 + old_lyx_pid = "-7" + old_lyx_window_name = None + print("Waiting for LyX to show up . . .") while count > 0: - lyx_pid=os.popen("pidof " + lyx).read().rstrip() - print('lyx_pid=' + lyx_pid, '\n') + lyx_pid=get_proc_pid(lyx) + if lyx_pid != old_lyx_pid: + print('lyx_pid=' + lyx_pid) + old_lyx_pid = lyx_pid if lyx_pid != "": - lyx_window_name=os.popen("wmctrl -l -p | grep ' " + str(lyx_pid) + " ' | cut -d ' ' -f 1").read().rstrip() - print('lyx_win=' + lyx_window_name, '\n') - if lyx_window_name != "": + lyx_window_name=get_proc_win_id(lyx_pid, "") + if not lyx_window_name is None: + if old_lyx_window_name != lyx_window_name: + print('lyx_win=' + lyx_window_name, '\n') + old_lyx_window_name = lyx_window_name break else: count = count - 1 - print('lyx_win: ' + lyx_window_name + '\n') - print("Waiting for LyX to show up . . .") - time.sleep(1) + time.sleep(0.5) if count <= 0: print('Timeout: could not start ' + lyx_exe, '\n') sys.stdout.flush() failed = True - print('lyx_pid: ' + lyx_pid + '\n') - print('lyx_win: ' + lyx_window_name + '\n') - sendKeystringLocal("\C\[Home]", lyx_pid) + else: + print('lyx_pid: ' + lyx_pid) + print('lyx_win: ' + lyx_window_name) + dead_expected = False + sendKeystring("\C\[Home]", lyx_pid) elif c[0:5] == 'Sleep': - print("Sleeping for " + c[6:] + " seconds\n") + print("Sleeping for " + c[6:] + " seconds") time.sleep(float(c[6:])) elif c[0:4] == 'Exec': cmd = c[5:].rstrip() @@ -448,55 +726,87 @@ while not failed: else: ##intr_system('killall lyx; sleep 2 ; killall -9 lyx') if lyx_pid is None: - print('No path /proc/xxxx/status, exiting') + die(1, 'No path /proc/xxxx/status, exiting') else: - print('No path /proc/' + lyx_pid + '/status, exiting') - os._exit(1) + die(1, 'No path /proc/' + lyx_pid + '/status, exiting') elif c[0:4] == 'KD: ': key_delay = c[4:].rstrip('\n') - print('Setting DELAY to ' + key_delay + '.\n') + print('Setting DELAY to ' + key_delay) elif c == 'Loop': RaiseWindow() sendKeystring(ResetCommand, lyx_pid) elif c[0:6] == 'Assert': cmd = c[7:].rstrip() - result = intr_system(cmd) + result = intr_system(cmd, True) failed = failed or (result != 0) print("result=" + str(result) + ", failed=" + str(failed)) + elif c[0:15] == 'TestEndWithKill': + marked.closeall() + cmd = c[16:].rstrip() + if lyx_dead(lyx_pid): + print("LyX instance not found because of crash or assert !\n") + failed = True + else: + print(" ------------ Forcing kill of lyx instance: " + str(lyx_pid) + " ------------") + # This line below is there only to allow lyx to update its log-file + sendKeystring("\[Escape]", lyx_pid) + dead_expected = True + while not lyx_dead(lyx_pid): + intr_system("kill -9 " + str(lyx_pid), True); + time.sleep(0.5) + if cmd != "": + print("Executing " + cmd) + result = intr_system(cmd, True) + failed = failed or (result != 0) + print("result=" + str(result) + ", failed=" + str(failed)) + else: + print("failed=" + str(failed)) elif c[0:7] == 'TestEnd': -# time.sleep(0.5) - if lyx_dead(): + #lyx_other_window_name = None + if lyx_dead(lyx_pid): print("LyX instance not found because of crash or assert !\n") + marked.closeall() failed = True else: - print("Forcing quit of lyx instance: " + str(lyx_pid) + "...\n") - # \Ax Enter command line is sometimes blocked - # \[Escape] works after this - sendKeystring("\Ax\[Escape]", lyx_pid) - # now we should be outside any dialog - # and so the function lyx-quit should work + print(" ------------ Forcing quit of lyx instance: " + str(lyx_pid) + " ------------") + # \[Escape]+ should work as RESET focus to main window + sendKeystring("\[Escape]\[Escape]\[Escape]\[Escape]", lyx_pid) + # now we should be outside any dialog + # and so the function lyx-quit should work sendKeystring("\Cq", lyx_pid) + marked.dispatch('CP: action=lyx-quit') + marked.dispatch('CC:') time.sleep(0.5) - if lyx_sleeping(): + dead_expected = True + is_sleeping = wait_until_lyx_sleeping(lyx_pid) + if is_sleeping: + print('wait_until_lyx_sleeping() indicated "sleeping"') + # For a short time lyx-status is 'sleeping', even if it is nearly dead. + # Without the wait below, the \[Tab]-char is sent to nirvana + # causing a 'beep' + time.sleep(0.5) # probably waiting for Save/Discard/Abort, we select 'Discard' sendKeystring("\[Tab]\[Return]", lyx_pid) lcount = 0 else: lcount = 1 - while not lyx_dead(): + while not lyx_dead(lyx_pid): lcount = lcount + 1 if lcount > 20: print("LyX still up, killing process and waiting for it to die...\n") intr_system("kill -9 " + str(lyx_pid), True); time.sleep(0.5) cmd = c[8:].rstrip() - print("Executing " + cmd) - result = intr_system(cmd) - failed = failed or (result != 0) - print("result=" + str(result) + ", failed=" + str(failed)) + if cmd != "": + print("Executing " + cmd) + result = intr_system(cmd, True) + failed = failed or (result != 0) + print("result=" + str(result) + ", failed=" + str(failed)) + else: + print("failed=" + str(failed)) elif c[0:4] == 'Lang': lang = c[5:].rstrip() - print("Setting LANG=" + lang + "\n") + print("Setting LANG=" + lang) os.environ['LANG'] = lang os.environ['LC_ALL'] = lang # If it doesn't exist, create a link //LC_MESSAGES/lyx.mo @@ -508,7 +818,7 @@ while not failed: else: ccode = lang - print("Setting LANGUAGE=" + ccode + "\n") + print("Setting LANGUAGE=" + ccode) os.environ['LANGUAGE'] = ccode idx = lang.find("_") @@ -527,7 +837,7 @@ while not failed: print('Could not determine PACKAGE name needed for translations\n') failed = True else: - lyx_name = PACKAGE + lyx_name = PACKAGE intr_system("mkdir -p " + locale_dir + "/" + ccode + "/LC_MESSAGES") intr_system("rm -f " + locale_dir + "/" + ccode + "/LC_MESSAGES/" + lyx_name + ".mo") if PO_BUILD_DIR is None: @@ -542,10 +852,8 @@ while not failed: print("Unrecognised Command '" + c + "'\n") failed = True -print("Test case terminated: ") +print("Test case terminated: ", end = '') if failed: - print("FAIL\n") - os._exit(1) + die(1,"FAIL") else: - print("Ok\n") - os._exit(0) + die(0, "Ok")