|
1 # This Source Code Form is subject to the terms of the Mozilla Public |
|
2 # License, v. 2.0. If a copy of the MPL was not distributed with this |
|
3 # file, You can obtain one at http://mozilla.org/MPL/2.0/. |
|
4 |
|
5 from unittest import TextTestRunner as _TestRunner, TestResult as _TestResult |
|
6 import unittest |
|
7 import inspect |
|
8 from StringIO import StringIO |
|
9 import os |
|
10 |
|
11 '''Helper to make python unit tests report the way that the Mozilla |
|
12 unit test infrastructure expects tests to report. |
|
13 |
|
14 Usage: |
|
15 |
|
16 import unittest |
|
17 import mozunit |
|
18 |
|
19 if __name__ == '__main__': |
|
20 mozunit.main() |
|
21 ''' |
|
22 |
|
23 class _MozTestResult(_TestResult): |
|
24 def __init__(self, stream, descriptions): |
|
25 _TestResult.__init__(self) |
|
26 self.stream = stream |
|
27 self.descriptions = descriptions |
|
28 |
|
29 def getDescription(self, test): |
|
30 if self.descriptions: |
|
31 return test.shortDescription() or str(test) |
|
32 else: |
|
33 return str(test) |
|
34 |
|
35 def addSuccess(self, test): |
|
36 _TestResult.addSuccess(self, test) |
|
37 filename = inspect.getfile(test.__class__) |
|
38 testname = test._testMethodName |
|
39 self.stream.writeln("TEST-PASS | {0} | {1}".format(filename, testname)) |
|
40 |
|
41 def addError(self, test, err): |
|
42 _TestResult.addError(self, test, err) |
|
43 self.printFail(test, err) |
|
44 |
|
45 def addFailure(self, test, err): |
|
46 _TestResult.addFailure(self, test, err) |
|
47 self.printFail(test,err) |
|
48 |
|
49 def printFail(self, test, err): |
|
50 exctype, value, tb = err |
|
51 # Skip test runner traceback levels |
|
52 while tb and self._is_relevant_tb_level(tb): |
|
53 tb = tb.tb_next |
|
54 if not tb: |
|
55 self.stream.writeln("TEST-UNEXPECTED-FAIL | NO TRACEBACK |") |
|
56 _f, _ln, _t = inspect.getframeinfo(tb)[:3] |
|
57 self.stream.writeln("TEST-UNEXPECTED-FAIL | {0} | line {1}, {2}: {3}" |
|
58 .format(_f, _ln, _t, value.message)) |
|
59 |
|
60 def printErrorList(self): |
|
61 for test, err in self.errors: |
|
62 self.stream.writeln("ERROR: {0}".format(self.getDescription(test))) |
|
63 self.stream.writeln("{0}".format(err)) |
|
64 |
|
65 |
|
66 class MozTestRunner(_TestRunner): |
|
67 def _makeResult(self): |
|
68 return _MozTestResult(self.stream, self.descriptions) |
|
69 def run(self, test): |
|
70 result = self._makeResult() |
|
71 test(result) |
|
72 result.printErrorList() |
|
73 return result |
|
74 |
|
75 class MockedFile(StringIO): |
|
76 def __init__(self, context, filename, content = ''): |
|
77 self.context = context |
|
78 self.name = filename |
|
79 StringIO.__init__(self, content) |
|
80 |
|
81 def close(self): |
|
82 self.context.files[self.name] = self.getvalue() |
|
83 StringIO.close(self) |
|
84 |
|
85 def __enter__(self): |
|
86 return self |
|
87 |
|
88 def __exit__(self, type, value, traceback): |
|
89 self.close() |
|
90 |
|
91 class MockedOpen(object): |
|
92 ''' |
|
93 Context manager diverting the open builtin such that opening files |
|
94 can open "virtual" file instances given when creating a MockedOpen. |
|
95 |
|
96 with MockedOpen({'foo': 'foo', 'bar': 'bar'}): |
|
97 f = open('foo', 'r') |
|
98 |
|
99 will thus open the virtual file instance for the file 'foo' to f. |
|
100 |
|
101 MockedOpen also masks writes, so that creating or replacing files |
|
102 doesn't touch the file system, while subsequently opening the file |
|
103 will return the recorded content. |
|
104 |
|
105 with MockedOpen(): |
|
106 f = open('foo', 'w') |
|
107 f.write('foo') |
|
108 self.assertRaises(Exception,f.open('foo', 'r')) |
|
109 ''' |
|
110 def __init__(self, files = {}): |
|
111 self.files = {} |
|
112 for name, content in files.iteritems(): |
|
113 self.files[os.path.abspath(name)] = content |
|
114 |
|
115 def __call__(self, name, mode = 'r'): |
|
116 absname = os.path.abspath(name) |
|
117 if 'w' in mode: |
|
118 file = MockedFile(self, absname) |
|
119 elif absname in self.files: |
|
120 file = MockedFile(self, absname, self.files[absname]) |
|
121 elif 'a' in mode: |
|
122 file = MockedFile(self, absname, self.open(name, 'r').read()) |
|
123 else: |
|
124 file = self.open(name, mode) |
|
125 if 'a' in mode: |
|
126 file.seek(0, os.SEEK_END) |
|
127 return file |
|
128 |
|
129 def __enter__(self): |
|
130 import __builtin__ |
|
131 self.open = __builtin__.open |
|
132 self._orig_path_exists = os.path.exists |
|
133 __builtin__.open = self |
|
134 os.path.exists = self._wrapped_exists |
|
135 |
|
136 def __exit__(self, type, value, traceback): |
|
137 import __builtin__ |
|
138 __builtin__.open = self.open |
|
139 os.path.exists = self._orig_path_exists |
|
140 |
|
141 def _wrapped_exists(self, p): |
|
142 if p in self.files: |
|
143 return True |
|
144 |
|
145 abspath = os.path.abspath(p) |
|
146 if abspath in self.files: |
|
147 return True |
|
148 |
|
149 return self._orig_path_exists(p) |
|
150 |
|
151 def main(*args): |
|
152 unittest.main(testRunner=MozTestRunner(),*args) |