1# JS unit test support for py.test - (c) 2007 Guido Wesdorp. All rights 2# reserved 3# 4# This software is distributed under the terms of the JSBase 5# License. See LICENSE.txt for license text. 6 7import py 8 9here = py.magic.autopath().dirpath() 10 11class JSTest(py.test.collect.Item): 12 def run(self): 13 path = self.fspath 14 test = self.name.split('/')[-1] 15 paths = [path.strpath, 16 (here / 'exception.js').strpath, 17 (here / 'testing.js').strpath, 18 (here / 'misclib.js').strpath, 19 ] 20 testjs = (here / 'testing/testbase.js').read() % ( 21 paths, test, '__main__') 22 curdir = str(py.path.local('.')) 23 py.std.os.chdir(str(self.fspath.dirpath())) 24 try: 25 jspath = self.fspath.new(basename='__testbase_temp.js') 26 try: 27 jspath.write(testjs) 28 pipe = py.std.os.popen('js "%s"' % (jspath,)) 29 try: 30 data = {} 31 for line in pipe: 32 done = self._handle_line(line, data) 33 if done: 34 errdata = data[data['current']] 35 if errdata: 36 self.fail(errdata) 37 finally: 38 pipe.close() 39 finally: 40 jspath.remove() 41 finally: 42 py.std.os.chdir(curdir) 43 44 def fail(self, errdata): 45 py.test.fail( 46 '\nJS traceback (most recent last): \n%s\n%s\n' % ( 47 (errdata[1:] and 48 self._format_tb(errdata[1:-5]) or 49 'no traceback available' 50 ), 51 errdata[0], 52 ) 53 ) 54 55 _handling_traceback = False 56 def _handle_line(self, line, data): 57 line = line[:-1] 58 if line.startswith('end test'): 59 return True 60 if self._handling_traceback and line != 'end traceback': 61 data[data['current']].append(line) 62 if line.startswith('PRINTED: '): 63 print line[9:] 64 elif line.startswith('running test '): 65 testname = line[13:] 66 data['current'] = testname 67 data[testname] = [] 68 elif line.startswith('success'): 69 pass 70 elif line.startswith('failure: '): 71 data[data['current']].append(line[9:]) 72 elif line.startswith('traceback'): 73 self._handling_traceback = True 74 elif line.startswith('end traceback'): 75 self._handling_traceback = False 76 77 def _format_tb(self, tb): 78 tb.reverse() 79 ret = [] 80 for line in tb: 81 line = line.strip() 82 if not line: 83 continue 84 funcsig, lineinfo = line.split('@', 1) 85 fpath, lineno = lineinfo.rsplit(':', 1) 86 fname = py.path.local(fpath).basename 87 # XXX might filter out too much... but it's better than leaving it 88 # all in (since it adds a couple of lines to the end of the tb, 89 # making it harder to find the problem line) 90 if fname in ['__testbase_temp.js', '__testbase_find.js', 91 'exception.js']: 92 continue 93 lineno = int(lineno) 94 if lineno == 0: 95 fname = "<unknown>" 96 ret.append('File "%s", line %s, in %s' % ( 97 fname, lineno, funcsig or '?')) 98 if lineno > 0: 99 line = py.path.local(fpath).readlines()[lineno - 1] 100 ret.append(' %s' % (line.strip(),)) 101 return '\n'.join([' %s' % (r,) for r in ret]) 102 103class JSChecker(py.test.collect.Module): 104 def __repr__(self): 105 return py.test.collect.Collector.__repr__(self) 106 107 def setup(self): 108 pass 109 110 def teardown(self): 111 pass 112 113 def run(self): 114 findjs = here.join('testing/findtests.js').read() % ( 115 self.fspath.strpath, '__main__') 116 curdir = str(py.path.local('.')) 117 py.std.os.chdir(str(self.fspath.dirpath())) 118 tests = [] 119 try: 120 jspath = self.fspath.new(basename='__findtests.js') 121 try: 122 jspath.write(findjs) 123 stdin, pipe, stderr = py.std.os.popen3('js "%s"' % (jspath,)) 124 try: 125 error = stderr.next() 126 print 'Error read:', error 127 except StopIteration: 128 pass 129 else: 130 if error.find('command not found') > -1: 131 py.test.skip( 132 'error running "js" (SpiderMonkey), which is ' 133 'required to run JS tests') 134 else: 135 py.test.fail(error) 136 return 137 try: 138 for line in pipe: 139 tests.append(line.strip()) 140 finally: 141 py.std.sys.stdout = py.std.sys.__stdout__ 142 pipe.close() 143 finally: 144 jspath.remove() 145 finally: 146 py.std.os.chdir(curdir) 147 return ['%s/%s' % (self.fspath.basename, test) for test in tests] 148 149 def join(self, name): 150 if py.path.local(name).dirpath().strpath.endswith('.js'): 151 return JSTest(name, self) 152 return super(JSChecker, self).join(name) 153 154class Directory(py.test.collect.Directory): 155 def run(self): 156 if self.fspath == here: 157 return [p.basename for p in self.fspath.listdir('test_*') if 158 p.ext in ['.py', '.js']] 159 return super(Directory, self).run() 160 161 def join(self, name): 162 if not name.endswith('.js'): 163 return super(Directory, self).join(name) 164 p = self.fspath.join(name) 165 if p.check(file=1): 166 return JSChecker(p, parent=self) 167