]> git.lyx.org Git - features.git/blob - development/autotests/keytest.py
keytests: Added some new controls for the *-in.txt files
[features.git] / development / autotests / keytest.py
1 #!/usr/bin/python
2 # -*- coding: utf-8 -*-
3 # This script generates hundreds of random keypresses per second,
4 #  and sends them to the lyx window
5 # It requires xvkbd and wmctrl
6 # It generates a log of the KEYCODES it sends as development/keystest/out/KEYCODES
7 #
8 # Adapted by Tommaso Cucinotta from the original MonKey Test by
9 # John McCabe-Dansted.
10
11 from __future__ import print_function
12 import random
13 import os
14 import re
15 import sys
16 import time
17 #from subprocess import call
18 import subprocess
19
20 print('Beginning keytest.py')
21
22 FNULL = open('/dev/null', 'w')
23
24 key_delay = ''
25
26 # Ignore status == "dead" if this is set. Used at the last commands after "\Cq"
27 dead_expected = False
28
29 class CommandSource:
30
31     def __init__(self):
32         keycode = [
33             "\[Left]",
34             '\[Right]',
35             '\[Down]',
36             '\[Up]',
37             '\[BackSpace]',
38             '\[Delete]',
39             '\[Escape]',
40             ]
41         keycode[:0] = keycode
42         keycode[:0] = keycode
43
44         keycode[:0] = ['\\']
45
46         for k in range(97, 123):
47             keycode[:0] = chr(k)
48
49         for k in range(97, 123):
50             keycode[:0] = ["\A" + chr(k)]
51
52         for k in range(97, 123):
53             keycode[:0] = ["\A" + chr(k)]
54
55         for k in range(97, 123):
56             keycode[:0] = ["\C" + chr(k)]
57
58         self.keycode = keycode
59         self.count = 0
60         self.count_max = 1999
61
62     def getCommand(self):
63         self.count = self.count + 1
64         if self.count % 200 == 0:
65             return 'RaiseLyx'
66         elif self.count > self.count_max:
67             os._exit(0)
68         else:
69             keystr = ''
70             for k in range(1, 2):
71                 keystr = keystr + self.keycode[random.randint(1,
72                         len(self.keycode)) - 1]
73             return 'KK: ' + keystr
74
75
76 class CommandSourceFromFile(CommandSource):
77
78     def __init__(self, filename, p):
79
80         self.infile = open(filename, 'r')
81         self.lines = self.infile.readlines()
82         self.infile.close()
83         linesbak = self.lines
84         self.p = p
85         print(p, self.p, 'self.p')
86         self.i = 0
87         self.count = 0
88         self.loops = 0
89
90         # Now we start randomly dropping lines, which we hope are redundant
91         # p is the probability that any given line will be removed
92
93         if p > 0.001:
94             if random.uniform(0, 1) < 0.5:
95                 print('randomdrop_independant\n')
96                 self.randomdrop_independant()
97             else:
98                 print('randomdrop_slice\n')
99                 self.randomdrop_slice()
100         if screenshot_out is None:
101             count_atleast = 100
102         else:
103             count_atleast = 1
104         self.max_count = max(len(self.lines) + 20, count_atleast)
105         if len(self.lines) < 1:
106             self.lines = linesbak
107
108     def randomdrop_independant(self):
109         p = self.p
110
111         # The next couple of lines are to ensure that at least one line is dropped
112
113         drop = random.randint(0, len(self.lines) - 1)
114         del self.lines[drop]
115         #p = p - 1 / len(self.lines)
116         origlines = self.lines
117         self.lines = []
118         for l in origlines:
119             if random.uniform(0, 1) < self.p:
120                 print('Randomly dropping line ' + l + '\n')
121             else:
122                 self.lines.append(l)
123         print('LINES\n')
124         print(self.lines)
125         sys.stdout.flush()
126
127     def randomdrop_slice(self):
128         lines = self.lines
129         if random.uniform(0, 1) < 0.4:
130             lines.append(lines[0])
131             del lines[0]
132         num_lines = len(lines)
133         max_drop = max(5, num_lines / 5)
134         num_drop = random.randint(1, 5)
135         drop_mid = random.randint(0, num_lines)
136         drop_start = max(drop_mid - num_drop / 2, 0)
137         drop_end = min(drop_start + num_drop, num_lines)
138         print(drop_start, drop_mid, drop_end)
139         print(lines)
140         del lines[drop_start:drop_end]
141         print(lines)
142         self.lines = lines
143
144     def getCommand(self):
145         if self.count >= self.max_count:
146             os._exit(0)
147         if self.i >= len(self.lines):
148             self.loops = self.loops + 1
149             if self.loops >= int(max_loops):
150                 return None
151             self.i = 0
152             return 'Loop'
153         line = self.lines[self.i].rstrip('\n')
154         self.count = self.count + 1
155         self.i = self.i + 1
156         return line
157
158 class ControlFile:
159
160     def __init__(self):
161         self.control = re.compile(r'^(C[ONPRC]):\s+(.*)$')
162         self.cntrname = None
163         self.cntrfile = None
164
165     def open(self, filename):
166         self.cntrname = filename
167         self.cntrfile = open(filename, 'w')
168
169     def close(self):
170         if not self.cntrfile is None:
171             self.cntrfile.close()
172             self.cntrfile = None
173             self.cntrname = None
174
175     def addline(self, pat):
176         self.cntrfile.writelines(pat + "\n")
177
178     def getfname(self):
179         return self.cntrname
180
181     def dispatch(self, c):
182         m = self.control.match(c)
183         if not m:
184             return False
185         command = m.group(1)
186         text = m.group(2)
187         if command == "CO":
188             self.open(text);
189         elif command == "CC":
190             self.close()
191         else:
192             if self.cntrfile is None:
193                 print("Controlfile not initialized")
194             else:
195                 if command == "CN":
196                     self.addline("Comment: " + text)
197                 elif command == "CP":
198                     self.addline("Simple: " + text)
199                 elif command == "CR":
200                     self.addline("Regex: " + text)
201                 else:
202                     print("Error")
203                     _exit(1)
204         return True
205
206
207 def get_proc_pid(proc_name):
208     pid=os.popen("pidof " + proc_name).read().rstrip()
209     return pid
210
211 wlistreg = re.compile(r'^(0x[0-9a-f]{5,9})\s+[^\s]+\s+([0-9]+)\s.*$')
212 def get_proc_win_id(pid, ignoreid):
213     nlist = os.popen("wmctrl -l -p").read()
214     wlist = nlist.split("\n")
215     for item in wlist:
216         m = wlistreg.match(item)
217         if m:
218             win_id = m.group(1)
219             win_pid = m.group(2)
220             if win_pid == pid:
221                 if win_id != ignoreid:
222                     return win_id
223     return None
224
225 def lyx_exists():
226     if lyx_pid is None:
227         return False
228     fname = '/proc/' + lyx_pid + '/status'
229     return os.path.exists(fname)
230
231
232 # Interruptible os.system()
233 def intr_system(cmd, ignore_err = False):
234     print("Executing " + cmd)
235     ret = os.system(cmd)
236     if os.WIFSIGNALED(ret):
237         raise KeyboardInterrupt
238     if ret != 0 and not ignore_err:
239         raise BaseException("command failed:" + cmd)
240     return ret
241
242 statreg = re.compile(r'^State:.*\(([a-z]+)\)')
243
244 resstatus = []
245 def printresstatus():
246     for line in resstatus:
247         line = line.rstrip()
248         print("    " + line.rstrip())
249     print('End of /proc-lines')
250
251 def lyx_status_retry(pid):
252     resstatus = []
253     if lyx_pid is None:
254         print('Pid is None')
255         return "dead"
256     fname = '/proc/' + pid + '/status'
257     status = "dead"
258     try:
259         f = open(fname)
260         found = False
261         for line in f:
262             resstatus.extend([line])
263             m = statreg.match(line)
264             if m:
265                 status = m.group(1)
266                 found = True
267         f.close()
268         if not found:
269             return "retry"
270         return status
271     except IOError as e:
272         print("I/O error({0}): {1}".format(e.errno, e.strerror))
273         return "dead"
274     except:
275         print("Unexpected error:", sys.exc_info()[0])
276         return "dead"
277     print('This should not happen')
278     return status
279
280 def lyx_status(pid):
281     count = 0
282     while 1:
283         status = lyx_status_retry(pid)
284         if status != "retry":
285             break
286         if count == 0:
287             print('Retrying check for status')
288         count += 1
289         time.sleep(0.01)
290     if count > 1:
291         print('Retried to read status ' + str(count) + ' times')
292     #print('lys_status() returning ' + status)
293     return status
294
295 # Return true if LyX (identified via lyx_pid) is sleeping
296 def lyx_sleeping(LYX_PID):
297     return lyx_status(LYX_PID) == "sleeping"
298
299 # Return true if LyX (identified via lyx_pid) is zombie
300 def lyx_zombie(LYX_PID):
301     return lyx_status(LYX_PID) == "zombie"
302
303 def lyx_dead(LYX_PID):
304     status = lyx_status(LYX_PID)
305     return (status == "dead") or (status == "zombie")
306
307 def wait_until_lyx_sleeping(LYX_PID):
308     before_secs = time.time()
309     while True:
310         status = lyx_status(LYX_PID)
311         if status == "sleeping":
312             return True
313         if (status == "dead") or (status == "zombie"):
314             printresstatus()
315             sys.stdout.flush()
316             if dead_expected:
317                 print('Lyx died while waiting for status == sleeping')
318                 return False
319             else:
320                 print('Lyx is dead, exiting')
321                 os._exit(1)
322         if time.time() - before_secs > 180:
323             print('Killing due to freeze (KILL_FREEZE)')
324
325             # Do profiling, but sysprof has no command line interface?
326             # intr_system("killall -KILL lyx")
327             printresstatus()
328             sys.stdout.flush()
329             os._exit(1)
330         time.sleep(0.02)
331     # Should be never reached
332     print('Wait for sleeping ends unexpectedly')
333     return False
334
335 def sendKeystringLocal(keystr, LYX_PID):
336     is_sleeping = wait_until_lyx_sleeping(LYX_PID)
337     if not is_sleeping:
338         print("Not sending \"" + keystr + "\"")
339         return
340     if not screenshot_out is None:
341         print('Making Screenshot: ' + screenshot_out + ' OF ' + infilename)
342         time.sleep(0.2)
343         intr_system('import -window root '+screenshot_out+str(x.count)+".png")
344         time.sleep(0.1)
345     actual_delay = key_delay
346     if actual_delay == '':
347         actual_delay = def_delay
348     xvpar = [xvkbd_exe]
349     if qt_frontend == 'QT5':
350         xvpar.extend(["-jump-pointer", "-no-back-pointer"])
351     else:
352         xvpar.extend(["-xsendevent"])
353     if lyx_other_window_name is None:
354         xvpar.extend(["-window", lyx_window_name])
355     else:
356         xvpar.extend(["-window", lyx_other_window_name])
357     xvpar.extend(["-delay", actual_delay, "-text", keystr])
358     print("Sending \"" + keystr + "\"")
359     subprocess.call(xvpar, stdout = FNULL, stderr = FNULL)
360     sys.stdout.flush()
361
362 Axreg = re.compile(r'^(.*)\\Ax([^\\]*)(.*)$')
363 returnreg = re.compile(r'(\\\[[A-Z][a-z]+\])(.*)$')
364
365 # recursive wrapper around sendKeystringLocal()
366 # handling \Ax-entries
367 def sendKeystringAx(line, LYX_PID):
368     global key_delay
369     saved_delay = key_delay
370     m = Axreg.match(line)
371     if m:
372         prefix = m.group(1)
373         content = m.group(2)
374         rest = m.group(3);
375         if prefix != "":
376             # since (.*) is greedy, check prefix for '\Ax' again
377             sendKeystringAx(prefix, LYX_PID)
378         sendKeystringLocal('\Ax', LYX_PID)
379         time.sleep(0.1)
380         m2 = returnreg.match(rest)
381         if m2:
382             line = m2.group(2)
383             ctrlk = m2.group(1)
384             key_delay = "1"
385             sendKeystringLocal(content + ctrlk, LYX_PID)
386             key_delay = saved_delay
387             time.sleep(controlkey_delay)
388             if line != "":
389                 sendKeystringLocal(line, LYX_PID)
390         else:
391             if content != "":
392                 sendKeystringLocal(content, LYX_PID)
393             if rest != "":
394                 sendKeystringLocal(rest, LYX_PID)
395     else:
396         if line != "":
397             sendKeystringLocal(line, LYX_PID)
398
399 specialkeyreg = re.compile(r'(.+)(\\[AC]([a-zA-Z]|\\\[[A-Z][a-z]+\]).*)$')
400 # Split line at start of each meta or controll char
401
402 def sendKeystringAC(line, LYX_PID):
403     m = specialkeyreg.match(line)
404     if m:
405         first = m.group(1)
406         second = m.group(2)
407         sendKeystringAC(first, LYX_PID)
408         sendKeystringAC(second, LYX_PID)
409     else:
410         sendKeystringAx(line, LYX_PID)
411
412 controlkeyreg = re.compile(r'^(.*\\\[[A-Z][a-z]+\])(.*\\\[[A-Z][a-z]+\])(.*)$')
413 # Make sure, only one of \[Return], \[Tab], \[Down], \[Home] etc are in one sent line
414 # e.g. split the input line on each keysym
415 def sendKeystringRT(line, LYX_PID):
416     m = controlkeyreg.match(line)
417     if m:
418         first = m.group(1)
419         second = m.group(2)
420         third = m.group(3)
421         sendKeystringRT(first, LYX_PID)
422         time.sleep(controlkey_delay)
423         sendKeystringRT(second, LYX_PID)
424         time.sleep(controlkey_delay)
425         if third != "":
426             sendKeystringRT(third, LYX_PID)
427     else:
428         sendKeystringAC(line, LYX_PID)
429
430 def system_retry(num_retry, cmd):
431     i = 0
432     rtn = intr_system(cmd)
433     while ( ( i < num_retry ) and ( rtn != 0) ):
434         i = i + 1
435         rtn = intr_system(cmd)
436         time.sleep(1)
437     if ( rtn != 0 ):
438         print("Command Failed: "+cmd)
439         print(" EXITING!\n")
440         os._exit(1)
441
442 def RaiseWindow():
443     #intr_system("echo x-session-manager PID: $X_PID.")
444     #intr_system("echo x-session-manager open files: `lsof -p $X_PID | grep ICE-unix | wc -l`")
445     ####intr_system("wmctrl -l | ( grep '"+lyx_window_name+"' || ( killall lyx ; sleep 1 ; killall -9 lyx ))")
446     print("lyx_window_name = " + lyx_window_name + "\n")
447     intr_system("wmctrl -R '"+lyx_window_name+"' ;sleep 0.1")
448     system_retry(30, "wmctrl -i -a '"+lyx_window_name+"'")
449
450
451 lyx_pid = os.environ.get('LYX_PID')
452 print('lyx_pid: ' + str(lyx_pid) + '\n')
453 infilename = os.environ.get('KEYTEST_INFILE')
454 outfilename = os.environ.get('KEYTEST_OUTFILE')
455 max_drop = os.environ.get('MAX_DROP')
456 lyx_window_name = os.environ.get('LYX_WINDOW_NAME')
457 lyx_other_window_name = None
458 screenshot_out = os.environ.get('SCREENSHOT_OUT')
459 lyx_userdir = os.environ.get('LYX_USERDIR')
460
461 max_loops = os.environ.get('MAX_LOOPS')
462 if max_loops is None:
463     max_loops = 3
464
465 extra_path = os.environ.get('EXTRA_PATH')
466 if not extra_path is None:
467   os.environ['PATH'] = extra_path + os.pathsep + os.environ['PATH']
468   print("Added " + extra_path + " to path")
469   print(os.environ['PATH'])
470
471 PACKAGE = os.environ.get('PACKAGE')
472 if not PACKAGE is None:
473   print("PACKAGE = " + PACKAGE + "\n")
474
475 PO_BUILD_DIR = os.environ.get('PO_BUILD_DIR')
476 if not PO_BUILD_DIR is None:
477   print("PO_BUILD_DIR = " + PO_BUILD_DIR + "\n")
478
479 lyx = os.environ.get('LYX')
480 if lyx is None:
481     lyx = "lyx"
482
483 lyx_exe = os.environ.get('LYX_EXE')
484 if lyx_exe is None:
485     lyx_exe = lyx
486
487 xvkbd_exe = os.environ.get('XVKBD_EXE')
488 if xvkbd_exe is None:
489     xvkbd_exe = "xvkbd"
490
491 qt_frontend = os.environ.get('QT_FRONTEND')
492 if qt_frontend is None:
493     qt_frontend = 'QT4'
494 if qt_frontend == 'QT5':
495     controlkey_delay = 0.01
496 else:
497     controlkey_delay = 0.4
498
499 locale_dir = os.environ.get('LOCALE_DIR')
500 if locale_dir is None:
501     locale_dir = '.'
502
503 def_delay = os.environ.get('XVKBD_DELAY')
504 if def_delay is None:
505     if qt_frontend == 'QT5':
506         def_delay = '1'
507     else:
508         def_delay = '1'
509
510 file_new_command = os.environ.get('FILE_NEW_COMMAND')
511 if file_new_command is None:
512     file_new_command = "\Afn"
513
514 ResetCommand = os.environ.get('RESET_COMMAND')
515 if ResetCommand is None:
516     ResetCommand = "\[Escape]\[Escape]\[Escape]\[Escape]" + file_new_command
517     #ResetCommand="\[Escape]\[Escape]\[Escape]\[Escape]\Cw\Cw\Cw\Cw\Cw\Afn"
518
519 if lyx_window_name is None:
520     lyx_window_name = 'LyX'
521
522 print('outfilename: ' + outfilename + '\n')
523 print('max_drop: ' + max_drop + '\n')
524
525 if infilename is None:
526     print('infilename is None\n')
527     x = CommandSource()
528     print('Using x=CommandSource\n')
529 else:
530     print('infilename: ' + infilename + '\n')
531     probability_we_drop_a_command = random.uniform(0, float(max_drop))
532     print('probability_we_drop_a_command: ')
533     print('%s' % probability_we_drop_a_command)
534     print('\n')
535     x = CommandSourceFromFile(infilename, probability_we_drop_a_command)
536     print('Using x=CommandSourceFromFile\n')
537
538 outfile = open(outfilename, 'w')
539
540 if not lyx_pid is None:
541     RaiseWindow()
542     # Next command is language dependent
543     #sendKeystringRT("\Afn", lyx_pid)
544
545 write_commands = True
546 failed = False
547 lineempty = re.compile(r'^\s*$')
548 marked = ControlFile()
549 while not failed:
550     #intr_system('echo -n LOADAVG:; cat /proc/loadavg')
551     c = x.getCommand()
552     if c is None:
553         break
554
555     # Do not strip trailing spaces, only check for 'empty' lines
556     if lineempty.match(c):
557         continue
558     outfile.writelines(c + '\n')
559     outfile.flush()
560     if marked.dispatch(c):
561         continue
562     if c[0] == '#':
563         print("Ignoring comment line: " + c)
564     elif c[0:9] == 'TestBegin':
565         print("\n")
566         lyx_pid=get_proc_pid(lyx)
567         if lyx_pid != "":
568             print("Found running instance(s) of LyX: " + lyx_pid + ": killing them all\n")
569             intr_system("killall " + lyx, True)
570             time.sleep(0.5)
571             intr_system("killall -KILL " + lyx, True)
572             time.sleep(0.2)
573         print("Starting LyX . . .")
574         if lyx_userdir is None:
575             intr_system(lyx_exe + c[9:] + "&")
576         else:
577             intr_system(lyx_exe + " -userdir " + lyx_userdir + " " + c[9:] + "&")
578         count = 10
579         old_lyx_pid = "-7"
580         old_lyx_window_name = None
581         print("Waiting for LyX to show up . . .")
582         while count > 0:
583             lyx_pid=get_proc_pid(lyx)
584             if lyx_pid != old_lyx_pid:
585                 print('lyx_pid=' + lyx_pid)
586                 old_lyx_pid = lyx_pid
587             if lyx_pid != "":
588                 lyx_window_name=get_proc_win_id(lyx_pid, "")
589                 if not lyx_window_name is None:
590                     if old_lyx_window_name != lyx_window_name:
591                         print('lyx_win=' + lyx_window_name, '\n')
592                         old_lyx_window_name = lyx_window_name
593                     break
594             else:
595                 count = count - 1
596             time.sleep(0.5)
597         if count <= 0:
598             print('Timeout: could not start ' + lyx_exe, '\n')
599             sys.stdout.flush()
600             failed = True
601         else:
602             print('lyx_pid: ' + lyx_pid)
603             print('lyx_win: ' + lyx_window_name)
604             dead_expected = False
605             sendKeystringLocal("\C\[Home]", lyx_pid)
606             time.sleep(controlkey_delay)
607     elif c[0:5] == 'Sleep':
608         print("Sleeping for " + c[6:] + " seconds")
609         time.sleep(float(c[6:]))
610     elif c[0:4] == 'Exec':
611         cmd = c[5:].rstrip()
612         intr_system(cmd)
613     elif c == 'Loop':
614         outfile.close()
615         outfile = open(outfilename + '+', 'w')
616         print('Now Looping')
617     elif c == 'RaiseLyx':
618         print('Raising Lyx')
619         RaiseWindow()
620     elif c[0:4] == 'KK: ':
621         if lyx_exists():
622             sendKeystringRT(c[4:], lyx_pid)
623         else:
624             ##intr_system('killall lyx; sleep 2 ; killall -9 lyx')
625             if lyx_pid is None:
626               print('No path /proc/xxxx/status, exiting')
627             else:
628               print('No path /proc/' + lyx_pid + '/status, exiting')
629             os._exit(1)
630     elif c[0:4] == 'KD: ':
631         key_delay = c[4:].rstrip('\n')
632         print('Setting DELAY to ' + key_delay)
633     elif c == 'Loop':
634         RaiseWindow()
635         sendKeystringRT(ResetCommand, lyx_pid)
636     elif c[0:6] == 'Assert':
637         cmd = c[7:].rstrip()
638         result = intr_system(cmd)
639         failed = failed or (result != 0)
640         print("result=" + str(result) + ", failed=" + str(failed))
641     elif c[0:15] == 'TestEndWithKill':
642         marked.close()
643         cmd = c[16:].rstrip()
644         if lyx_dead(lyx_pid):
645             print("LyX instance not found because of crash or assert !\n")
646             failed = True
647         else:
648             print("    ------------    Forcing kill of lyx instance: " + str(lyx_pid) + "    ------------")
649             # This line below is there only to allow lyx to update its log-file
650             sendKeystringLocal("\[Escape]", lyx_pid)
651             dead_expected = True
652             while not lyx_dead(lyx_pid):
653                 intr_system("kill -9 " + str(lyx_pid), True);
654                 time.sleep(0.5)
655             if cmd != "":
656                 print("Executing " + cmd)
657                 result = intr_system(cmd)
658                 failed = failed or (result != 0)
659                 print("result=" + str(result) + ", failed=" + str(failed))
660             else:
661                 print("failed=" + str(failed))
662     elif c[0:7] == 'TestEnd':
663         marked.close()
664         #lyx_other_window_name = None
665         if lyx_dead(lyx_pid):
666             print("LyX instance not found because of crash or assert !\n")
667             failed = True
668         else:
669             print("    ------------    Forcing quit of lyx instance: " + str(lyx_pid) + "    ------------")
670             # \Ax Enter command line is sometimes blocked
671             # \[Escape] works after this
672             sendKeystringAx("\Ax\[Escape]", lyx_pid)
673             time.sleep(controlkey_delay)
674             # now we should be outside any dialog
675             # and so the function lyx-quit should work
676             sendKeystringLocal("\Cq", lyx_pid)
677             time.sleep(0.5)
678             dead_expected = True
679             is_sleeping = wait_until_lyx_sleeping(lyx_pid)
680             if is_sleeping:
681                 print('wait_until_lyx_sleeping() indicated "sleeping"')
682                 # For a short time lyx-status is 'sleeping', even if it is nearly dead.
683                 # Without the wait below, the \[Tab]-char is sent to nirvana
684                 # causing a 'beep'
685                 time.sleep(0.5)
686                 # probably waiting for Save/Discard/Abort, we select 'Discard'
687                 sendKeystringRT("\[Tab]\[Return]", lyx_pid)
688                 lcount = 0
689             else:
690                 lcount = 1
691             while not lyx_dead(lyx_pid):
692                 lcount = lcount + 1
693                 if lcount > 20:
694                     print("LyX still up, killing process and waiting for it to die...\n")
695                     intr_system("kill -9 " + str(lyx_pid), True);
696                 time.sleep(0.5)
697         cmd = c[8:].rstrip()
698         if cmd != "":
699             print("Executing " + cmd)
700             result = intr_system(cmd)
701             failed = failed or (result != 0)
702             print("result=" + str(result) + ", failed=" + str(failed))
703         else:
704             print("failed=" + str(failed))
705     elif c[0:4] == 'Lang':
706         lang = c[5:].rstrip()
707         print("Setting LANG=" + lang)
708         os.environ['LANG'] = lang
709         os.environ['LC_ALL'] = lang
710 # If it doesn't exist, create a link <locale_dir>/<country-code>/LC_MESSAGES/lyx<version-suffix>.mo
711 # pointing to the corresponding .gmo file. Needed to let lyx find the right translation files.
712 # See http://www.mail-archive.com/lyx-devel@lists.lyx.org/msg165613.html
713         idx = lang.rfind(".")
714         if idx != -1:
715             ccode = lang[0:idx]
716         else:
717             ccode = lang
718
719         print("Setting LANGUAGE=" + ccode)
720         os.environ['LANGUAGE'] = ccode
721
722         idx = lang.find("_")
723         if idx != -1:
724             short_code = lang[0:idx]
725         else:
726             short_code = ccode
727         lyx_dir = os.popen("dirname \"" + lyx_exe + "\"").read().rstrip()
728         if PACKAGE is None:
729           # on cmake-build there is no Makefile in this directory
730           # so PACKAGE has to be provided
731           if os.path.exists(lyx_dir + "/Makefile"):
732             print("Executing: grep 'PACKAGE =' " + lyx_dir + "/Makefile | sed -e 's/PACKAGE = \(.*\)/\\1/'")
733             lyx_name = os.popen("grep 'PACKAGE =' " + lyx_dir + "/Makefile | sed -e 's/PACKAGE = \(.*\)/\\1/'").read().rstrip()
734           else:
735             print('Could not determine PACKAGE name needed for translations\n')
736             failed = True
737         else:
738           lyx_name = PACKAGE
739         intr_system("mkdir -p " + locale_dir + "/" + ccode + "/LC_MESSAGES")
740         intr_system("rm -f " + locale_dir + "/" + ccode + "/LC_MESSAGES/" + lyx_name + ".mo")
741         if PO_BUILD_DIR is None:
742             if lyx_dir[0:3] == "../":
743                 rel_dir = "../../" + lyx_dir
744             else:
745                 rel_dir = lyx_dir
746             intr_system("ln -s " + rel_dir + "/../po/" + short_code + ".gmo " + locale_dir + "/" + ccode + "/LC_MESSAGES/" + lyx_name + ".mo")
747         else:
748             intr_system("ln -s " + PO_BUILD_DIR + "/" + short_code + ".gmo " + locale_dir + "/" + ccode + "/LC_MESSAGES/" + lyx_name + ".mo")
749     else:
750         print("Unrecognised Command '" + c + "'\n")
751         failed = True
752
753 print("Test case terminated: ")
754 if failed:
755     print("FAIL\n")
756     os._exit(1)
757 else:
758     print("Ok\n")
759     os._exit(0)