OLD | NEW |
1 #!/usr/bin/env python | 1 #!/usr/bin/env python |
2 # Copyright (c) 2012 The Chromium Authors. All rights reserved. | 2 # Copyright (c) 2012 The Chromium Authors. All rights reserved. |
3 # Use of this source code is governed by a BSD-style license that can be | 3 # Use of this source code is governed by a BSD-style license that can be |
4 # found in the LICENSE file. | 4 # found in the LICENSE file. |
5 | 5 |
6 import cStringIO | 6 import cStringIO |
7 import logging | 7 import logging |
8 import os | 8 import os |
9 import shutil | 9 import shutil |
10 import subprocess | 10 import subprocess |
11 import sys | 11 import sys |
12 import tempfile | 12 import tempfile |
13 import unittest | 13 import unittest |
14 | 14 |
15 ROOT_DIR = os.path.dirname(os.path.abspath(__file__)) | 15 FULLNAME = os.path.abspath(__file__) |
| 16 ROOT_DIR = os.path.dirname(FULLNAME) |
16 FILENAME = os.path.basename(__file__) | 17 FILENAME = os.path.basename(__file__) |
17 VERBOSE = False | 18 VERBOSE = False |
18 | 19 |
19 | 20 |
20 class CalledProcessError(subprocess.CalledProcessError): | 21 class CalledProcessError(subprocess.CalledProcessError): |
21 """Makes 2.6 version act like 2.7""" | 22 """Makes 2.6 version act like 2.7""" |
22 def __init__(self, returncode, cmd, output, cwd): | 23 def __init__(self, returncode, cmd, output, cwd): |
23 super(CalledProcessError, self).__init__(returncode, cmd) | 24 super(CalledProcessError, self).__init__(returncode, cmd) |
24 self.output = output | 25 self.output = output |
25 self.cwd = cwd | 26 self.cwd = cwd |
26 | 27 |
27 def __str__(self): | 28 def __str__(self): |
28 return super(CalledProcessError, self).__str__() + ( | 29 return super(CalledProcessError, self).__str__() + ( |
29 '\n' | 30 '\n' |
30 'cwd=%s\n%s') % (self.cwd, self.output) | 31 'cwd=%s\n%s') % (self.cwd, self.output) |
31 | 32 |
32 | 33 |
33 class TraceInputs(unittest.TestCase): | 34 class TraceInputsBase(unittest.TestCase): |
34 def setUp(self): | 35 def setUp(self): |
35 self.tempdir = tempfile.mkdtemp(prefix='trace_smoke_test') | 36 self.tempdir = tempfile.mkdtemp(prefix='trace_smoke_test') |
36 self.log = os.path.join(self.tempdir, 'log') | 37 self.log = os.path.join(self.tempdir, 'log') |
37 os.chdir(ROOT_DIR) | 38 os.chdir(ROOT_DIR) |
38 | 39 |
39 def tearDown(self): | 40 def tearDown(self): |
40 if VERBOSE: | 41 if VERBOSE: |
41 print 'Leaking: %s' % self.tempdir | 42 print 'Leaking: %s' % self.tempdir |
42 else: | 43 else: |
43 shutil.rmtree(self.tempdir) | 44 shutil.rmtree(self.tempdir) |
44 | 45 |
45 def _execute(self, is_gyp): | 46 @staticmethod |
46 cmd = [ | 47 def command(from_data): |
47 sys.executable, os.path.join('..', '..', 'trace_inputs.py'), | 48 cmd = [sys.executable] |
48 '--log', self.log, | 49 if from_data: |
49 '--root-dir', ROOT_DIR, | |
50 ] | |
51 if VERBOSE: | |
52 cmd.extend(['-v'] * 3) | |
53 if is_gyp: | |
54 cmd.extend( | |
55 [ | |
56 '--cwd', 'data', | |
57 '--product', '.', # Not tested. | |
58 ]) | |
59 cmd.append(sys.executable) | |
60 if is_gyp: | |
61 # When the gyp argument is specified, the command is started from --cwd | 50 # When the gyp argument is specified, the command is started from --cwd |
62 # directory. In this case, 'data'. | 51 # directory. In this case, 'data'. |
63 cmd.extend([os.path.join('trace_inputs', 'child1.py'), '--child-gyp']) | 52 cmd.extend([os.path.join('trace_inputs', 'child1.py'), '--child-gyp']) |
64 else: | 53 else: |
65 # When the gyp argument is not specified, the command is started from | 54 # When the gyp argument is not specified, the command is started from |
66 # --root-dir directory. | 55 # --root-dir directory. |
67 cmd.extend([os.path.join('data', 'trace_inputs', 'child1.py'), '--child']) | 56 cmd.extend([os.path.join('data', 'trace_inputs', 'child1.py'), '--child']) |
| 57 return cmd |
68 | 58 |
| 59 |
| 60 class TraceInputs(TraceInputsBase): |
| 61 def _execute(self, command): |
| 62 cmd = [ |
| 63 sys.executable, os.path.join('..', '..', 'trace_inputs.py'), |
| 64 '--log', self.log, |
| 65 '--root-dir', ROOT_DIR, |
| 66 ] |
| 67 if VERBOSE: |
| 68 cmd.extend(['-v'] * 3) |
| 69 cmd.extend(command) |
69 # The current directory doesn't matter, the traced process will be called | 70 # The current directory doesn't matter, the traced process will be called |
70 # from the correct cwd. | 71 # from the correct cwd. |
71 cwd = os.path.join('data', 'trace_inputs') | 72 cwd = os.path.join('data', 'trace_inputs') |
72 # Ignore stderr. | 73 # Ignore stderr. |
73 logging.info('Command: %s' % ' '.join(cmd)) | 74 logging.info('Command: %s' % ' '.join(cmd)) |
74 p = subprocess.Popen( | 75 p = subprocess.Popen( |
75 cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, cwd=cwd, | 76 cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, cwd=cwd, |
76 universal_newlines=True) | 77 universal_newlines=True) |
77 out, err = p.communicate() | 78 out, err = p.communicate() |
78 if VERBOSE: | 79 if VERBOSE: |
79 print err | 80 print err |
80 if p.returncode: | 81 if p.returncode: |
81 raise CalledProcessError(p.returncode, cmd, out + err, cwd) | 82 raise CalledProcessError(p.returncode, cmd, out + err, cwd) |
82 return out or '' | 83 return out or '' |
83 | 84 |
84 def test_trace(self): | 85 def test_trace(self): |
85 expected_end = [ | 86 expected_end = [ |
86 'Interesting: 5 reduced to 3', | 87 'Interesting: 5 reduced to 3', |
87 ' data/trace_inputs/'.replace('/', os.path.sep), | 88 ' data/trace_inputs/'.replace('/', os.path.sep), |
88 ' trace_inputs.py', | 89 ' trace_inputs.py', |
89 ' %s' % FILENAME, | 90 ' %s' % FILENAME, |
90 ] | 91 ] |
91 actual = self._execute(False).splitlines() | 92 actual = self._execute(self.command(False)).splitlines() |
92 self.assertTrue(actual[0].startswith('Tracing... ['), actual) | 93 self.assertTrue(actual[0].startswith('Tracing... ['), actual) |
93 self.assertTrue(actual[1].startswith('Loading traces... '), actual) | 94 self.assertTrue(actual[1].startswith('Loading traces... '), actual) |
94 self.assertTrue(actual[2].startswith('Total: '), actual) | 95 self.assertTrue(actual[2].startswith('Total: '), actual) |
95 if sys.platform == 'win32': | 96 if sys.platform == 'win32': |
96 # On windows, python searches the current path for python stdlib like | 97 # On windows, python searches the current path for python stdlib like |
97 # subprocess.py and others, I'm not sure why. | 98 # subprocess.py and others, I'm not sure why. |
98 self.assertTrue(actual[3].startswith('Non existent: '), actual[3]) | 99 self.assertTrue(actual[3].startswith('Non existent: '), actual[3]) |
99 else: | 100 else: |
100 self.assertEquals('Non existent: 0', actual[3]) | 101 self.assertEquals('Non existent: 0', actual[3]) |
101 # Ignore any Unexpected part. | 102 # Ignore any Unexpected part. |
(...skipping 15 matching lines...) Expand all Loading... |
117 'isolate_dependency_untracked': [ | 118 'isolate_dependency_untracked': [ |
118 'trace_inputs/', | 119 'trace_inputs/', |
119 ], | 120 ], |
120 }, | 121 }, |
121 }], | 122 }], |
122 ], | 123 ], |
123 } | 124 } |
124 expected_buffer = cStringIO.StringIO() | 125 expected_buffer = cStringIO.StringIO() |
125 trace_inputs.pretty_print(expected_value, expected_buffer) | 126 trace_inputs.pretty_print(expected_value, expected_buffer) |
126 | 127 |
127 actual = self._execute(True) | 128 cmd = [ |
| 129 '--cwd', 'data', |
| 130 '--product', '.', # Not tested. |
| 131 ] + self.command(True) |
| 132 actual = self._execute(cmd) |
128 self.assertEquals(expected_buffer.getvalue(), actual) | 133 self.assertEquals(expected_buffer.getvalue(), actual) |
129 | 134 |
130 | 135 |
| 136 class TraceInputsImport(TraceInputsBase): |
| 137 def setUp(self): |
| 138 super(TraceInputsImport, self).setUp() |
| 139 self.cwd = os.path.join(ROOT_DIR, u'data') |
| 140 self.initial_cwd = self.cwd |
| 141 if sys.platform == 'win32': |
| 142 # Still not supported on Windows. |
| 143 self.initial_cwd = None |
| 144 |
| 145 # Similar to TraceInputs test fixture except that it calls the function |
| 146 # directly, so the Results instance can be inspected. |
| 147 # Roughly, make sure the API is stable. |
| 148 def _execute(self, command): |
| 149 # Similar to what trace_test_cases.py does. |
| 150 import trace_inputs |
| 151 api = trace_inputs.get_api() |
| 152 _, _ = trace_inputs.trace( |
| 153 self.log, command, self.cwd, api, True) |
| 154 # TODO(maruel): Check |
| 155 #self.assertEquals(0, returncode) |
| 156 #self.assertEquals('', output) |
| 157 return trace_inputs.load_trace(self.log, ROOT_DIR, api) |
| 158 |
| 159 def test_trace_wrong_path(self): |
| 160 # Deliberately start the trace from the wrong path. Starts it from the |
| 161 # directory 'data' so 'data/data/trace_inputs/child1.py' is not accessible, |
| 162 # so child2.py process is not started. |
| 163 results, simplified = self._execute(self.command(False)) |
| 164 expected = { |
| 165 'root': { |
| 166 'children': [], |
| 167 'command': None, |
| 168 'executable': None, |
| 169 'files': [], |
| 170 'initial_cwd': self.initial_cwd, |
| 171 }, |
| 172 } |
| 173 actual = results.flatten() |
| 174 self.assertTrue(actual['root'].pop('pid')) |
| 175 self.assertEquals(expected, actual) |
| 176 self.assertEquals([], simplified) |
| 177 |
| 178 def test_trace(self): |
| 179 size_t_i_s = os.stat(FULLNAME).st_size |
| 180 size_t_i = os.stat(os.path.join(ROOT_DIR, 'trace_inputs.py')).st_size |
| 181 expected = { |
| 182 'root': { |
| 183 'children': [ |
| 184 { |
| 185 'children': [], |
| 186 'command': None, |
| 187 'executable': None, |
| 188 'files': [ |
| 189 { |
| 190 'path': os.path.join(u'data', 'trace_inputs', 'child2.py'), |
| 191 'size': 776, |
| 192 }, |
| 193 { |
| 194 'path': os.path.join(u'data', 'trace_inputs', 'test_file.txt'), |
| 195 'size': 4, |
| 196 }, |
| 197 ], |
| 198 'initial_cwd': self.initial_cwd, |
| 199 }, |
| 200 ], |
| 201 'command': None, |
| 202 'executable': None, |
| 203 'files': [ |
| 204 { |
| 205 'path': os.path.join(u'data', 'trace_inputs', 'child1.py'), |
| 206 'size': 1364, |
| 207 }, |
| 208 { |
| 209 'path': u'trace_inputs.py', |
| 210 'size': size_t_i, |
| 211 }, |
| 212 { |
| 213 'path': u'trace_inputs_smoke_test.py', |
| 214 'size': size_t_i_s, |
| 215 }, |
| 216 ], |
| 217 'initial_cwd': self.initial_cwd, |
| 218 }, |
| 219 } |
| 220 results, simplified = self._execute(self.command(True)) |
| 221 actual = results.flatten() |
| 222 self.assertTrue(actual['root'].pop('pid')) |
| 223 self.assertTrue(actual['root']['children'][0].pop('pid')) |
| 224 self.assertEquals(expected, actual) |
| 225 files = [ |
| 226 os.path.join(u'data', 'trace_inputs') + os.path.sep, |
| 227 u'trace_inputs.py', |
| 228 u'trace_inputs_smoke_test.py', |
| 229 ] |
| 230 self.assertEquals(files, [f.path for f in simplified]) |
| 231 |
| 232 |
| 233 |
131 if __name__ == '__main__': | 234 if __name__ == '__main__': |
132 VERBOSE = '-v' in sys.argv | 235 VERBOSE = '-v' in sys.argv |
133 logging.basicConfig(level=logging.DEBUG if VERBOSE else logging.ERROR) | 236 logging.basicConfig(level=logging.DEBUG if VERBOSE else logging.ERROR) |
134 unittest.main() | 237 unittest.main() |
OLD | NEW |