comparison en/examples/run-example @ 776:019040fbf5f5

merged to upstream: phase 1
author Yoshiki Yazawa <yaz@honeyplanet.jp>
date Tue, 21 Apr 2009 00:36:40 +0900
parents 73aa295a40da 3b640272a966
children
comparison
equal deleted inserted replaced
389:5981a0f7540a 776:019040fbf5f5
5 # files, for use in examples. 5 # files, for use in examples.
6 6
7 import cStringIO 7 import cStringIO
8 import errno 8 import errno
9 import getopt 9 import getopt
10 import glob
10 import os 11 import os
11 import pty 12 import pty
12 import re 13 import re
13 import select 14 import select
14 import shutil 15 import shutil
16 import stat 17 import stat
17 import sys 18 import sys
18 import tempfile 19 import tempfile
19 import time 20 import time
20 21
21 tex_subs = { 22 xml_subs = {
22 '\\': '\\textbackslash{}', 23 '<': '&lt;',
23 '{': '\\{', 24 '>': '&gt;',
24 '}': '\\}', 25 '&': '&amp;',
25 } 26 }
26 27
27 def gensubs(s): 28 def gensubs(s):
28 start = 0 29 start = 0
29 for i, c in enumerate(s): 30 for i, c in enumerate(s):
30 sub = tex_subs.get(c) 31 sub = xml_subs.get(c)
31 if sub: 32 if sub:
32 yield s[start:i] 33 yield s[start:i]
33 start = i + 1 34 start = i + 1
34 yield sub 35 yield sub
35 yield s[start:] 36 yield s[start:]
36 37
37 def tex_escape(s): 38 def xml_escape(s):
38 return ''.join(gensubs(s)) 39 return ''.join(gensubs(s))
39 40
40 def maybe_unlink(name): 41 def maybe_unlink(name):
41 try: 42 try:
42 os.unlink(name) 43 os.unlink(name)
51 name = os.path.join(p, program) 52 name = os.path.join(p, program)
52 if os.access(name, os.X_OK): 53 if os.access(name, os.X_OK):
53 return p 54 return p
54 return None 55 return None
55 56
57 def result_name(name):
58 return os.path.normpath(os.path.join('results', name.replace(os.sep, '-')))
59
60 def wopen(name):
61 path = os.path.dirname(name)
62 if path:
63 try:
64 os.makedirs(path)
65 except OSError, err:
66 if err.errno != errno.EEXIST:
67 raise
68 return open(name, 'w')
69
56 class example: 70 class example:
71 entities = dict.fromkeys(l.rstrip() for l in open('auto-snippets.xml'))
72
73 def __init__(self, name, verbose, keep_change):
74 self.name = os.path.normpath(name)
75 self.verbose = verbose
76 self.keep_change = keep_change
77
78 def status(self, s):
79 sys.stdout.write(s)
80 if not s.endswith('\n'):
81 sys.stdout.flush()
82
83 def rename_output(self, base, ignore=[]):
84 mangle_re = re.compile('(?:' + '|'.join(ignore) + ')')
85 def mangle(s):
86 return mangle_re.sub('', s)
87 def matchfp(fp1, fp2):
88 while True:
89 s1 = mangle(fp1.readline())
90 s2 = mangle(fp2.readline())
91 if cmp(s1, s2):
92 break
93 if not s1:
94 return True
95 return False
96
97 oldname = result_name(base + '.out')
98 tmpname = result_name(base + '.tmp')
99 errname = result_name(base + '.err')
100 errfp = open(errname, 'w+')
101 for line in open(tmpname):
102 errfp.write(mangle_re.sub('', line))
103 os.rename(tmpname, result_name(base + '.lxo'))
104 errfp.seek(0)
105 try:
106 oldfp = open(oldname)
107 except IOError, err:
108 if err.errno != errno.ENOENT:
109 raise
110 os.rename(errname, oldname)
111 return False
112 if matchfp(oldfp, errfp):
113 os.unlink(errname)
114 return False
115 else:
116 print >> sys.stderr, '\nOutput of %s has changed!' % base
117 if self.keep_change:
118 os.rename(errname, oldname)
119 return False
120 else:
121 os.system('diff -u %s %s 1>&2' % (oldname, errname))
122 return True
123
124 class static_example(example):
125 def run(self):
126 self.status('running %s\n' % self.name)
127 s = open(self.name).read().rstrip()
128 s = s.replace('&', '&amp;').replace('<', '&lt;').replace('>', '&gt;')
129 ofp = wopen(result_name(self.name + '.tmp'))
130 ofp.write('<!-- BEGIN %s -->\n' % self.name)
131 ofp.write('<programlisting>')
132 ofp.write(s)
133 ofp.write('</programlisting>\n')
134 ofp.write('<!-- END %s -->\n' % self.name)
135 ofp.close()
136 self.rename_output(self.name)
137 norm = self.name.replace(os.sep, '-')
138 example.entities[
139 '<!ENTITY %s SYSTEM "results/%s.lxo">' % (norm, norm)] = 1
140
141
142 class shell_example(example):
57 shell = '/usr/bin/env bash' 143 shell = '/usr/bin/env bash'
58 ps1 = '__run_example_ps1__ ' 144 ps1 = '__run_example_ps1__ '
59 ps2 = '__run_example_ps2__ ' 145 ps2 = '__run_example_ps2__ '
60 pi_re = re.compile(r'#\$\s*(name|ignore):\s*(.*)$') 146 pi_re = re.compile(r'#\$\s*(name|ignore):\s*(.*)$')
61 147
62 timeout = 10 148 timeout = 10
63 149
64 def __init__(self, name, verbose): 150 def __init__(self, name, verbose, keep_change):
65 self.name = name 151 example.__init__(self, name, verbose, keep_change)
66 self.verbose = verbose
67 self.poll = select.poll() 152 self.poll = select.poll()
68 153
69 def parse(self): 154 def parse(self):
70 '''yield each hunk of input from the file.''' 155 '''yield each hunk of input from the file.'''
71 fp = open(self.name) 156 fp = open(self.name)
74 cfp.write(line) 159 cfp.write(line)
75 if not line.rstrip().endswith('\\'): 160 if not line.rstrip().endswith('\\'):
76 yield cfp.getvalue() 161 yield cfp.getvalue()
77 cfp.seek(0) 162 cfp.seek(0)
78 cfp.truncate() 163 cfp.truncate()
79
80 def status(self, s):
81 sys.stdout.write(s)
82 if not s.endswith('\n'):
83 sys.stdout.flush()
84 164
85 def send(self, s): 165 def send(self, s):
86 if self.verbose: 166 if self.verbose:
87 print >> sys.stderr, '>', self.debugrepr(s) 167 print >> sys.stderr, '>', self.debugrepr(s)
88 while s: 168 while s:
144 # remove the marker file that we tell make to use to see if 224 # remove the marker file that we tell make to use to see if
145 # this run succeeded 225 # this run succeeded
146 maybe_unlink(self.name + '.run') 226 maybe_unlink(self.name + '.run')
147 227
148 rcfile = os.path.join(tmpdir, '.hgrc') 228 rcfile = os.path.join(tmpdir, '.hgrc')
149 rcfp = open(rcfile, 'w') 229 rcfp = wopen(rcfile)
150 print >> rcfp, '[ui]' 230 print >> rcfp, '[ui]'
151 print >> rcfp, "username = Bryan O'Sullivan <bos@serpentine.com>" 231 print >> rcfp, "username = Bryan O'Sullivan <bos@serpentine.com>"
152 232
153 rcfile = os.path.join(tmpdir, '.bashrc') 233 rcfile = os.path.join(tmpdir, '.bashrc')
154 rcfp = open(rcfile, 'w') 234 rcfp = wopen(rcfile)
155 print >> rcfp, 'PS1="%s"' % self.ps1 235 print >> rcfp, 'PS1="%s"' % self.ps1
156 print >> rcfp, 'PS2="%s"' % self.ps2 236 print >> rcfp, 'PS2="%s"' % self.ps2
157 print >> rcfp, 'unset HISTFILE' 237 print >> rcfp, 'unset HISTFILE'
158 path = ['/usr/bin', '/bin'] 238 path = ['/usr/bin', '/bin']
159 hg = find_path_to('hg') 239 hg = find_path_to('hg')
228 'name %r' % 308 'name %r' %
229 (self.name, out)) 309 (self.name, out))
230 return 1 310 return 1
231 assert os.sep not in out 311 assert os.sep not in out
232 if ofp is not None: 312 if ofp is not None:
313 ofp.write('</screen>\n')
314 ofp.write('<!-- END %s -->\n' % ofp_basename)
233 ofp.close() 315 ofp.close()
234 err |= self.rename_output(ofp_basename, ignore) 316 err |= self.rename_output(ofp_basename, ignore)
235 if out: 317 if out:
236 ofp_basename = '%s.%s' % (self.name, out) 318 ofp_basename = '%s.%s' % (self.name, out)
319 norm = os.path.normpath(ofp_basename)
320 norm = norm.replace(os.sep, '-')
321 example.entities[
322 '<!ENTITY interaction.%s '
323 'SYSTEM "results/%s.lxo">'
324 % (norm, norm)] = 1
237 read_hint = ofp_basename + ' ' 325 read_hint = ofp_basename + ' '
238 ofp = open(ofp_basename + '.tmp', 'w') 326 ofp = wopen(result_name(ofp_basename + '.tmp'))
327 ofp.write('<!-- BEGIN %s -->\n' % ofp_basename)
328 ofp.write('<screen>')
239 else: 329 else:
240 ofp = None 330 ofp = None
241 elif pi == 'ignore': 331 elif pi == 'ignore':
242 ignore.append(rest) 332 ignore.append(rest)
243 elif hunk.strip(): 333 elif hunk.strip():
246 if not ofp: 336 if not ofp:
247 continue 337 continue
248 # first, print the command we ran 338 # first, print the command we ran
249 if not hunk.startswith('#'): 339 if not hunk.startswith('#'):
250 nl = hunk.endswith('\n') 340 nl = hunk.endswith('\n')
251 hunk = ('%s \\textbf{%s}' % 341 hunk = ('<prompt>%s</prompt> '
342 '<userinput>%s</userinput>' %
252 (prompts[ps], 343 (prompts[ps],
253 tex_escape(hunk.rstrip('\n')))) 344 xml_escape(hunk.rstrip('\n'))))
254 if nl: hunk += '\n' 345 if nl: hunk += '\n'
255 ofp.write(hunk) 346 ofp.write(hunk)
256 # then its output 347 # then its output
257 ofp.write(tex_escape(output)) 348 ofp.write(xml_escape(output))
258 ps = newps 349 ps = newps
259 self.status('\n') 350 self.status('\n')
260 except: 351 except:
261 print >> sys.stderr, '(killed)' 352 print >> sys.stderr, '(killed)'
262 os.kill(self.pid, signal.SIGKILL) 353 os.kill(self.pid, signal.SIGKILL)
263 pid, rc = os.wait() 354 pid, rc = os.wait()
265 else: 356 else:
266 try: 357 try:
267 ps, output = self.sendreceive('exit\n', read_hint) 358 ps, output = self.sendreceive('exit\n', read_hint)
268 if ofp is not None: 359 if ofp is not None:
269 ofp.write(output) 360 ofp.write(output)
361 ofp.write('</screen>\n')
362 ofp.write('<!-- END %s -->\n' % ofp_basename)
270 ofp.close() 363 ofp.close()
271 err |= self.rename_output(ofp_basename, ignore) 364 err |= self.rename_output(ofp_basename, ignore)
272 os.close(self.cfd) 365 os.close(self.cfd)
273 except IOError: 366 except IOError:
274 pass 367 pass
279 if os.WIFEXITED(rc): 372 if os.WIFEXITED(rc):
280 print >> sys.stderr, '(exit %s)' % os.WEXITSTATUS(rc) 373 print >> sys.stderr, '(exit %s)' % os.WEXITSTATUS(rc)
281 elif os.WIFSIGNALED(rc): 374 elif os.WIFSIGNALED(rc):
282 print >> sys.stderr, '(signal %s)' % os.WTERMSIG(rc) 375 print >> sys.stderr, '(signal %s)' % os.WTERMSIG(rc)
283 else: 376 else:
284 open(self.name + '.run', 'w') 377 wopen(result_name(self.name + '.run'))
285 # return err 378 return err
286 return 0
287 finally: 379 finally:
288 shutil.rmtree(tmpdir) 380 shutil.rmtree(tmpdir)
289
290 def rename_output(self, base, ignore):
291 mangle_re = re.compile('(?:' + '|'.join(ignore) + ')')
292 def mangle(s):
293 return mangle_re.sub('', s)
294 def matchfp(fp1, fp2):
295 while True:
296 s1 = mangle(fp1.readline())
297 s2 = mangle(fp2.readline())
298 if cmp(s1, s2):
299 break
300 if not s1:
301 return True
302 return False
303
304 oldname = base + '.out'
305 tmpname = base + '.tmp'
306 errname = base + '.err'
307 errfp = open(errname, 'w+')
308 for line in open(tmpname):
309 errfp.write(mangle_re.sub('', line))
310 os.rename(tmpname, base + '.lxo')
311 errfp.seek(0)
312 try:
313 oldfp = open(oldname)
314 except IOError, err:
315 if err.errno != errno.ENOENT:
316 raise
317 os.rename(errname, oldname)
318 return False
319 if matchfp(oldfp, errfp):
320 os.unlink(errname)
321 return False
322 else:
323 print >> sys.stderr, '\nOutput of %s has changed!' % base
324 os.system('diff -u %s %s 1>&2' % (oldname, errname))
325 return True
326 381
327 def print_help(exit, msg=None): 382 def print_help(exit, msg=None):
328 if msg: 383 if msg:
329 print >> sys.stderr, 'Error:', msg 384 print >> sys.stderr, 'Error:', msg
330 print >> sys.stderr, 'Usage: run-example [options] [test...]' 385 print >> sys.stderr, 'Usage: run-example [options] [test...]'
331 print >> sys.stderr, 'Options:' 386 print >> sys.stderr, 'Options:'
332 print >> sys.stderr, ' -a --all run all tests in this directory' 387 print >> sys.stderr, ' -a --all run all examples in this directory'
333 print >> sys.stderr, ' -h --help print this help message' 388 print >> sys.stderr, ' -h --help print this help message'
389 print >> sys.stderr, ' --keep keep new output as desired output'
334 print >> sys.stderr, ' -v --verbose display extra debug output' 390 print >> sys.stderr, ' -v --verbose display extra debug output'
335 sys.exit(exit) 391 sys.exit(exit)
336 392
337 def main(path='.'): 393 def main(path='.'):
394 if os.path.realpath(path).split(os.sep)[-1] != 'examples':
395 print >> sys.stderr, 'Not being run from the examples directory!'
396 sys.exit(1)
397
338 opts, args = getopt.getopt(sys.argv[1:], '?ahv', 398 opts, args = getopt.getopt(sys.argv[1:], '?ahv',
339 ['all', 'help', 'verbose']) 399 ['all', 'help', 'keep', 'verbose'])
340 verbose = False 400 verbose = False
341 run_all = False 401 run_all = False
402 keep_change = False
403
342 for o, a in opts: 404 for o, a in opts:
343 if o in ('-h', '-?', '--help'): 405 if o in ('-h', '-?', '--help'):
344 print_help(0) 406 print_help(0)
345 if o in ('-a', '--all'): 407 if o in ('-a', '--all'):
346 run_all = True 408 run_all = True
409 if o in ('--keep',):
410 keep_change = True
347 if o in ('-v', '--verbose'): 411 if o in ('-v', '--verbose'):
348 verbose = True 412 verbose = True
349 errs = 0 413 errs = 0
350 if args: 414 if args:
351 for a in args: 415 for a in args:
353 st = os.lstat(a) 417 st = os.lstat(a)
354 except OSError, err: 418 except OSError, err:
355 print >> sys.stderr, '%s: %s' % (a, err.strerror) 419 print >> sys.stderr, '%s: %s' % (a, err.strerror)
356 errs += 1 420 errs += 1
357 continue 421 continue
358 if stat.S_ISREG(st.st_mode) and st.st_mode & 0111: 422 if stat.S_ISREG(st.st_mode):
359 if example(a, verbose).run(): 423 if st.st_mode & 0111:
360 errs += 1 424 if shell_example(a, verbose, keep_change).run():
425 errs += 1
426 elif a.endswith('.lst'):
427 static_example(a, verbose, keep_change).run()
361 else: 428 else:
362 print >> sys.stderr, '%s: not a file, or not executable' % a 429 print >> sys.stderr, '%s: not a file, or not executable' % a
363 errs += 1 430 errs += 1
364 elif run_all: 431 elif run_all:
365 names = os.listdir(path) 432 names = glob.glob("*") + glob.glob("app*/*") + glob.glob("ch*/*")
366 names.sort() 433 names.sort()
367 for name in names: 434 for name in names:
368 if name == 'run-example' or name.startswith('.'): continue 435 if name == 'run-example' or name.endswith('~'): continue
369 if name.endswith('.out') or name.endswith('~'): continue
370 if name.endswith('.run'): continue
371 pathname = os.path.join(path, name) 436 pathname = os.path.join(path, name)
372 try: 437 try:
373 st = os.lstat(pathname) 438 st = os.lstat(pathname)
374 except OSError, err: 439 except OSError, err:
375 # could be an output file that was removed while we ran 440 # could be an output file that was removed while we ran
376 if err.errno != errno.ENOENT: 441 if err.errno != errno.ENOENT:
377 raise 442 raise
378 continue 443 continue
379 if stat.S_ISREG(st.st_mode) and st.st_mode & 0111: 444 if stat.S_ISREG(st.st_mode):
380 if example(pathname, verbose).run(): 445 if st.st_mode & 0111:
381 errs += 1 446 if shell_example(pathname, verbose, keep_change).run():
382 print >> open(os.path.join(path, '.run'), 'w'), time.asctime() 447 errs += 1
448 elif pathname.endswith('.lst'):
449 static_example(pathname, verbose, keep_change).run()
450 print >> wopen(os.path.join(path, '.run')), time.asctime()
383 else: 451 else:
384 print_help(1, msg='no test names given, and --all not provided') 452 print_help(1, msg='no test names given, and --all not provided')
453
454 fp = wopen('auto-snippets.xml')
455 for key in sorted(example.entities.iterkeys()):
456 print >> fp, key
457 fp.close()
385 return errs 458 return errs
386 459
387 if __name__ == '__main__': 460 if __name__ == '__main__':
388 try: 461 try:
389 sys.exit(main()) 462 sys.exit(main())