| OLD | NEW | 
|---|
|  | (Empty) | 
| 1 #!/usr/bin/env python |  | 
| 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 |  | 
| 4 # found in the LICENSE file. |  | 
| 5 |  | 
| 6 import logging |  | 
| 7 import os |  | 
| 8 import unittest |  | 
| 9 import sys |  | 
| 10 |  | 
| 11 BASE_DIR = unicode(os.path.dirname(os.path.abspath(__file__))) |  | 
| 12 ROOT_DIR = os.path.dirname(BASE_DIR) |  | 
| 13 sys.path.insert(0, ROOT_DIR) |  | 
| 14 |  | 
| 15 FILE_PATH = unicode(os.path.abspath(__file__)) |  | 
| 16 |  | 
| 17 import trace_inputs |  | 
| 18 |  | 
| 19 |  | 
| 20 def join_norm(*args): |  | 
| 21   """Joins and normalizes path in a single step.""" |  | 
| 22   return unicode(os.path.normpath(os.path.join(*args))) |  | 
| 23 |  | 
| 24 |  | 
| 25 class TraceInputs(unittest.TestCase): |  | 
| 26   def test_process_quoted_arguments(self): |  | 
| 27     test_cases = ( |  | 
| 28       ('"foo"', ['foo']), |  | 
| 29       ('"foo", "bar"', ['foo', 'bar']), |  | 
| 30       ('"foo"..., "bar"', ['foo', 'bar']), |  | 
| 31       ('"foo", "bar"...', ['foo', 'bar']), |  | 
| 32       ( |  | 
| 33         '"/browser_tests", "--type=use,comma"', |  | 
| 34         ['/browser_tests', '--type=use,comma'] |  | 
| 35       ), |  | 
| 36       ( |  | 
| 37         '"/browser_tests", "--ignored=\\" --type=renderer \\""', |  | 
| 38         ['/browser_tests', '--ignored=" --type=renderer "'] |  | 
| 39       ), |  | 
| 40     ) |  | 
| 41     for actual, expected in test_cases: |  | 
| 42       self.assertEquals( |  | 
| 43           expected, trace_inputs.strace_process_quoted_arguments(actual)) |  | 
| 44 |  | 
| 45   def test_process_escaped_arguments(self): |  | 
| 46     test_cases = ( |  | 
| 47       ('foo\\0', ['foo']), |  | 
| 48       ('foo\\001bar\\0', ['foo', 'bar']), |  | 
| 49       ('\\"foo\\"\\0', ['"foo"']), |  | 
| 50     ) |  | 
| 51     for actual, expected in test_cases: |  | 
| 52       self.assertEquals( |  | 
| 53           expected, |  | 
| 54           trace_inputs.Dtrace.Context.process_escaped_arguments(actual)) |  | 
| 55 |  | 
| 56   def test_variable_abs(self): |  | 
| 57     value = trace_inputs.Results.File(None, '/foo/bar', False, False) |  | 
| 58     actual = value.replace_variables({'$FOO': '/foo'}) |  | 
| 59     self.assertEquals('$FOO/bar', actual.path) |  | 
| 60     self.assertEquals('$FOO/bar', actual.full_path) |  | 
| 61     self.assertEquals(True, actual.tainted) |  | 
| 62 |  | 
| 63   def test_variable_rel(self): |  | 
| 64     value = trace_inputs.Results.File('/usr', 'foo/bar', False, False) |  | 
| 65     actual = value.replace_variables({'$FOO': 'foo'}) |  | 
| 66     self.assertEquals('$FOO/bar', actual.path) |  | 
| 67     self.assertEquals(os.path.join('/usr', '$FOO/bar'), actual.full_path) |  | 
| 68     self.assertEquals(True, actual.tainted) |  | 
| 69 |  | 
| 70   def test_native_case_end_with_os_path_sep(self): |  | 
| 71     # Make sure the trailing os.path.sep is kept. |  | 
| 72     path = trace_inputs.get_native_path_case(ROOT_DIR) + os.path.sep |  | 
| 73     self.assertEquals(trace_inputs.get_native_path_case(path), path) |  | 
| 74 |  | 
| 75   def test_native_case_non_existing(self): |  | 
| 76     # Make sure it doesn't throw on non-existing files. |  | 
| 77     non_existing = 'trace_input_test_this_file_should_not_exist' |  | 
| 78     path = os.path.expanduser('~/' + non_existing) |  | 
| 79     self.assertFalse(os.path.exists(path)) |  | 
| 80     path = trace_inputs.get_native_path_case(ROOT_DIR) + os.path.sep |  | 
| 81     self.assertEquals(trace_inputs.get_native_path_case(path), path) |  | 
| 82 |  | 
| 83   if sys.platform in ('darwin', 'win32'): |  | 
| 84     def test_native_case_not_sensitive(self): |  | 
| 85       # The home directory is almost guaranteed to have mixed upper/lower case |  | 
| 86       # letters on both Windows and OSX. |  | 
| 87       # This test also ensures that the output is independent on the input |  | 
| 88       # string case. |  | 
| 89       path = os.path.expanduser('~') |  | 
| 90       self.assertTrue(os.path.isdir(path)) |  | 
| 91       # This test assumes the variable is in the native path case on disk, this |  | 
| 92       # should be the case. Verify this assumption: |  | 
| 93       self.assertEquals(path, trace_inputs.get_native_path_case(path)) |  | 
| 94       self.assertEquals( |  | 
| 95           trace_inputs.get_native_path_case(path.lower()), |  | 
| 96           trace_inputs.get_native_path_case(path.upper())) |  | 
| 97 |  | 
| 98     def test_native_case_not_sensitive_non_existent(self): |  | 
| 99       # This test also ensures that the output is independent on the input |  | 
| 100       # string case. |  | 
| 101       non_existing = os.path.join( |  | 
| 102           'trace_input_test_this_dir_should_not_exist', 'really not', '') |  | 
| 103       path = os.path.expanduser(os.path.join('~', non_existing)) |  | 
| 104       self.assertFalse(os.path.exists(path)) |  | 
| 105       lower = trace_inputs.get_native_path_case(path.lower()) |  | 
| 106       upper = trace_inputs.get_native_path_case(path.upper()) |  | 
| 107       # Make sure non-existing element is not modified: |  | 
| 108       self.assertTrue(lower.endswith(non_existing.lower())) |  | 
| 109       self.assertTrue(upper.endswith(non_existing.upper())) |  | 
| 110       self.assertEquals(lower[:-len(non_existing)], upper[:-len(non_existing)]) |  | 
| 111 |  | 
| 112   if sys.platform != 'win32': |  | 
| 113     def test_symlink(self): |  | 
| 114       # This test will fail if the checkout is in a symlink. |  | 
| 115       actual = trace_inputs.split_at_symlink(None, ROOT_DIR) |  | 
| 116       expected = (ROOT_DIR, None, None) |  | 
| 117       self.assertEquals(expected, actual) |  | 
| 118 |  | 
| 119       actual = trace_inputs.split_at_symlink( |  | 
| 120           None, os.path.join(BASE_DIR, 'trace_inputs')) |  | 
| 121       expected = ( |  | 
| 122           os.path.join(BASE_DIR, 'trace_inputs'), None, None) |  | 
| 123       self.assertEquals(expected, actual) |  | 
| 124 |  | 
| 125       actual = trace_inputs.split_at_symlink( |  | 
| 126           None, os.path.join(BASE_DIR, 'trace_inputs', 'files2')) |  | 
| 127       expected = ( |  | 
| 128           os.path.join(BASE_DIR, 'trace_inputs'), 'files2', '') |  | 
| 129       self.assertEquals(expected, actual) |  | 
| 130 |  | 
| 131       actual = trace_inputs.split_at_symlink( |  | 
| 132           ROOT_DIR, os.path.join('tests', 'trace_inputs', 'files2')) |  | 
| 133       expected = ( |  | 
| 134           os.path.join('tests', 'trace_inputs'), 'files2', '') |  | 
| 135       self.assertEquals(expected, actual) |  | 
| 136       actual = trace_inputs.split_at_symlink( |  | 
| 137           ROOT_DIR, os.path.join('tests', 'trace_inputs', 'files2', 'bar')) |  | 
| 138       expected = ( |  | 
| 139           os.path.join('tests', 'trace_inputs'), 'files2', '/bar') |  | 
| 140       self.assertEquals(expected, actual) |  | 
| 141 |  | 
| 142     def test_native_case_symlink_right_case(self): |  | 
| 143       actual = trace_inputs.get_native_path_case( |  | 
| 144           os.path.join(BASE_DIR, 'trace_inputs')) |  | 
| 145       self.assertEquals('trace_inputs', os.path.basename(actual)) |  | 
| 146 |  | 
| 147       # Make sure the symlink is not resolved. |  | 
| 148       actual = trace_inputs.get_native_path_case( |  | 
| 149           os.path.join(BASE_DIR, 'trace_inputs', 'files2')) |  | 
| 150       self.assertEquals('files2', os.path.basename(actual)) |  | 
| 151 |  | 
| 152   if sys.platform == 'darwin': |  | 
| 153     def test_native_case_symlink_wrong_case(self): |  | 
| 154       actual = trace_inputs.get_native_path_case( |  | 
| 155           os.path.join(BASE_DIR, 'trace_inputs')) |  | 
| 156       self.assertEquals('trace_inputs', os.path.basename(actual)) |  | 
| 157 |  | 
| 158       # Make sure the symlink is not resolved. |  | 
| 159       actual = trace_inputs.get_native_path_case( |  | 
| 160           os.path.join(BASE_DIR, 'trace_inputs', 'Files2')) |  | 
| 161       self.assertEquals('files2', os.path.basename(actual)) |  | 
| 162 |  | 
| 163 |  | 
| 164 if sys.platform != 'win32': |  | 
| 165   class StraceInputs(unittest.TestCase): |  | 
| 166     # Represents the root process pid (an arbitrary number). |  | 
| 167     _ROOT_PID = 27 |  | 
| 168     _CHILD_PID = 14 |  | 
| 169     _GRAND_CHILD_PID = 70 |  | 
| 170 |  | 
| 171     @staticmethod |  | 
| 172     def _load_context(lines, initial_cwd): |  | 
| 173       context = trace_inputs.Strace.Context(lambda _: False, initial_cwd) |  | 
| 174       for line in lines: |  | 
| 175         context.on_line(*line) |  | 
| 176       return context.to_results().flatten() |  | 
| 177 |  | 
| 178     def _test_lines(self, lines, initial_cwd, files, command=None): |  | 
| 179       filepath = join_norm(initial_cwd, '../out/unittests') |  | 
| 180       command = command or ['../out/unittests'] |  | 
| 181       expected = { |  | 
| 182         'root': { |  | 
| 183           'children': [], |  | 
| 184           'command': command, |  | 
| 185           'executable': filepath, |  | 
| 186           'files': files, |  | 
| 187           'initial_cwd': initial_cwd, |  | 
| 188           'pid': self._ROOT_PID, |  | 
| 189         } |  | 
| 190       } |  | 
| 191       if not files: |  | 
| 192         expected['root']['command'] = None |  | 
| 193         expected['root']['executable'] = None |  | 
| 194       self.assertEquals(expected, self._load_context(lines, initial_cwd)) |  | 
| 195 |  | 
| 196     def test_execve(self): |  | 
| 197       lines = [ |  | 
| 198         (self._ROOT_PID, |  | 
| 199           'execve("/home/foo_bar_user/out/unittests", ' |  | 
| 200             '["/home/foo_bar_user/out/unittests", ' |  | 
| 201             '"--gtest_filter=AtExitTest.Basic"], [/* 44 vars */])         = 0'), |  | 
| 202         (self._ROOT_PID, |  | 
| 203           'open("out/unittests.log", O_WRONLY|O_CREAT|O_APPEND, 0666) = 8'), |  | 
| 204       ] |  | 
| 205       files = [ |  | 
| 206         { |  | 
| 207           'path': u'/home/foo_bar_user/out/unittests', |  | 
| 208           'size': -1, |  | 
| 209         }, |  | 
| 210         { |  | 
| 211           'path': u'/home/foo_bar_user/src/out/unittests.log', |  | 
| 212           'size': -1, |  | 
| 213         }, |  | 
| 214       ] |  | 
| 215       command = [ |  | 
| 216         '/home/foo_bar_user/out/unittests', '--gtest_filter=AtExitTest.Basic', |  | 
| 217       ] |  | 
| 218       self._test_lines(lines, '/home/foo_bar_user/src', files, command) |  | 
| 219 |  | 
| 220     def test_empty(self): |  | 
| 221       try: |  | 
| 222         self._load_context([], None) |  | 
| 223         self.fail() |  | 
| 224       except trace_inputs.TracingFailure, e: |  | 
| 225         expected = ( |  | 
| 226           'Found internal inconsitency in process lifetime detection ' |  | 
| 227           'while finding the root process', |  | 
| 228           None, |  | 
| 229           None, |  | 
| 230           None, |  | 
| 231           []) |  | 
| 232         self.assertEquals(expected, e.args) |  | 
| 233 |  | 
| 234     def test_chmod(self): |  | 
| 235       lines = [ |  | 
| 236           (self._ROOT_PID, 'chmod("temp/file", 0100644) = 0'), |  | 
| 237       ] |  | 
| 238       self._test_lines(lines, '/home/foo_bar_user/src', []) |  | 
| 239 |  | 
| 240     def test_close(self): |  | 
| 241       lines = [ |  | 
| 242         (self._ROOT_PID, 'close(7)                          = 0'), |  | 
| 243       ] |  | 
| 244       self._test_lines(lines, '/home/foo_bar_user/src', []) |  | 
| 245 |  | 
| 246     def test_clone(self): |  | 
| 247       # Grand-child with relative directory. |  | 
| 248       lines = [ |  | 
| 249         (self._ROOT_PID, |  | 
| 250           'clone(child_stack=0, flags=CLONE_CHILD_CLEARTID' |  | 
| 251             '|CLONE_CHILD_SETTID|SIGCHLD, child_tidptr=0x7f5350f829d0) = %d' % |  | 
| 252             self._CHILD_PID), |  | 
| 253         (self._CHILD_PID, |  | 
| 254           'clone(child_stack=0, flags=CLONE_CHILD_CLEARTID' |  | 
| 255             '|CLONE_CHILD_SETTID|SIGCHLD, child_tidptr=0x7f5350f829d0) = %d' % |  | 
| 256             self._GRAND_CHILD_PID), |  | 
| 257         (self._GRAND_CHILD_PID, |  | 
| 258           'open("%s", O_RDONLY)       = 76' % os.path.basename(FILE_PATH)), |  | 
| 259       ] |  | 
| 260       size = os.stat(FILE_PATH).st_size |  | 
| 261       expected = { |  | 
| 262         'root': { |  | 
| 263           'children': [ |  | 
| 264             { |  | 
| 265               'children': [ |  | 
| 266                 { |  | 
| 267                   'children': [], |  | 
| 268                   'command': None, |  | 
| 269                   'executable': None, |  | 
| 270                   'files': [ |  | 
| 271                     { |  | 
| 272                       'path': FILE_PATH, |  | 
| 273                       'size': size, |  | 
| 274                     }, |  | 
| 275                   ], |  | 
| 276                   'initial_cwd': BASE_DIR, |  | 
| 277                   'pid': self._GRAND_CHILD_PID, |  | 
| 278                 }, |  | 
| 279               ], |  | 
| 280               'command': None, |  | 
| 281               'executable': None, |  | 
| 282               'files': [], |  | 
| 283               'initial_cwd': BASE_DIR, |  | 
| 284               'pid': self._CHILD_PID, |  | 
| 285             }, |  | 
| 286           ], |  | 
| 287           'command': None, |  | 
| 288           'executable': None, |  | 
| 289           'files': [], |  | 
| 290           'initial_cwd': BASE_DIR, |  | 
| 291           'pid': self._ROOT_PID, |  | 
| 292         }, |  | 
| 293       } |  | 
| 294       self.assertEquals(expected, self._load_context(lines, BASE_DIR)) |  | 
| 295 |  | 
| 296     def test_clone_chdir(self): |  | 
| 297       # Grand-child with relative directory. |  | 
| 298       lines = [ |  | 
| 299         (self._ROOT_PID, |  | 
| 300           'execve("../out/unittests", ' |  | 
| 301             '["../out/unittests"...], [/* 44 vars */])         = 0'), |  | 
| 302         (self._ROOT_PID, |  | 
| 303           'clone(child_stack=0, flags=CLONE_CHILD_CLEARTID' |  | 
| 304             '|CLONE_CHILD_SETTID|SIGCHLD, child_tidptr=0x7f5350f829d0) = %d' % |  | 
| 305             self._CHILD_PID), |  | 
| 306         (self._CHILD_PID, |  | 
| 307           'chdir("/home_foo_bar_user/path1") = 0'), |  | 
| 308         (self._CHILD_PID, |  | 
| 309           'clone(child_stack=0, flags=CLONE_CHILD_CLEARTID' |  | 
| 310             '|CLONE_CHILD_SETTID|SIGCHLD, child_tidptr=0x7f5350f829d0) = %d' % |  | 
| 311             self._GRAND_CHILD_PID), |  | 
| 312         (self._GRAND_CHILD_PID, |  | 
| 313           'execve("../out/unittests", ' |  | 
| 314             '["../out/unittests"...], [/* 44 vars */])         = 0'), |  | 
| 315         (self._ROOT_PID, 'chdir("/home_foo_bar_user/path2") = 0'), |  | 
| 316         (self._GRAND_CHILD_PID, |  | 
| 317           'open("random.txt", O_RDONLY)       = 76'), |  | 
| 318       ] |  | 
| 319       expected = { |  | 
| 320         'root': { |  | 
| 321           'children': [ |  | 
| 322             { |  | 
| 323               'children': [ |  | 
| 324                 { |  | 
| 325                   'children': [], |  | 
| 326                   'command': ['../out/unittests'], |  | 
| 327                   'executable': '/home_foo_bar_user/out/unittests', |  | 
| 328                   'files': [ |  | 
| 329                     { |  | 
| 330                       'path': u'/home_foo_bar_user/out/unittests', |  | 
| 331                       'size': -1, |  | 
| 332                     }, |  | 
| 333                     { |  | 
| 334                       'path': u'/home_foo_bar_user/path1/random.txt', |  | 
| 335                       'size': -1, |  | 
| 336                     }, |  | 
| 337                   ], |  | 
| 338                   'initial_cwd': u'/home_foo_bar_user/path1', |  | 
| 339                   'pid': self._GRAND_CHILD_PID, |  | 
| 340                 }, |  | 
| 341               ], |  | 
| 342               # clone does not carry over the command and executable so it is |  | 
| 343               # clear if an execve() call was done or not. |  | 
| 344               'command': None, |  | 
| 345               'executable': None, |  | 
| 346               # This is important, since no execve call was done, it didn't |  | 
| 347               # touch the executable file. |  | 
| 348               'files': [], |  | 
| 349               'initial_cwd': unicode(ROOT_DIR), |  | 
| 350               'pid': self._CHILD_PID, |  | 
| 351             }, |  | 
| 352           ], |  | 
| 353           'command': ['../out/unittests'], |  | 
| 354           'executable': join_norm(ROOT_DIR, '../out/unittests'), |  | 
| 355           'files': [ |  | 
| 356             { |  | 
| 357               'path': join_norm(ROOT_DIR, '../out/unittests'), |  | 
| 358               'size': -1, |  | 
| 359             }, |  | 
| 360           ], |  | 
| 361           'initial_cwd': unicode(ROOT_DIR), |  | 
| 362           'pid': self._ROOT_PID, |  | 
| 363         }, |  | 
| 364       } |  | 
| 365       self.assertEquals(expected, self._load_context(lines, ROOT_DIR)) |  | 
| 366 |  | 
| 367     def test_open(self): |  | 
| 368       lines = [ |  | 
| 369         (self._ROOT_PID, |  | 
| 370           'execve("../out/unittests", ' |  | 
| 371             '["../out/unittests"...], [/* 44 vars */])         = 0'), |  | 
| 372         (self._ROOT_PID, |  | 
| 373           'open("out/unittests.log", O_WRONLY|O_CREAT|O_APPEND, 0666) = 8'), |  | 
| 374       ] |  | 
| 375       files = [ |  | 
| 376         { |  | 
| 377           'path': u'/home/foo_bar_user/out/unittests', |  | 
| 378           'size': -1, |  | 
| 379         }, |  | 
| 380         { |  | 
| 381           'path': u'/home/foo_bar_user/src/out/unittests.log', |  | 
| 382           'size': -1, |  | 
| 383         }, |  | 
| 384       ] |  | 
| 385       self._test_lines(lines, '/home/foo_bar_user/src', files) |  | 
| 386 |  | 
| 387     def test_open_resumed(self): |  | 
| 388       lines = [ |  | 
| 389         (self._ROOT_PID, |  | 
| 390           'execve("../out/unittests", ' |  | 
| 391             '["../out/unittests"...], [/* 44 vars */])         = 0'), |  | 
| 392         (self._ROOT_PID, |  | 
| 393           'open("out/unittests.log", O_WRONLY|O_CREAT|O_APPEND ' |  | 
| 394             '<unfinished ...>'), |  | 
| 395         (self._ROOT_PID, '<... open resumed> )              = 3'), |  | 
| 396       ] |  | 
| 397       files = [ |  | 
| 398         { |  | 
| 399           'path': u'/home/foo_bar_user/out/unittests', |  | 
| 400           'size': -1, |  | 
| 401         }, |  | 
| 402         { |  | 
| 403           'path': u'/home/foo_bar_user/src/out/unittests.log', |  | 
| 404           'size': -1, |  | 
| 405         }, |  | 
| 406       ] |  | 
| 407       self._test_lines(lines, '/home/foo_bar_user/src', files) |  | 
| 408 |  | 
| 409     def test_rmdir(self): |  | 
| 410       lines = [ |  | 
| 411           (self._ROOT_PID, 'rmdir("directory/to/delete") = 0'), |  | 
| 412       ] |  | 
| 413       self._test_lines(lines, '/home/foo_bar_user/src', []) |  | 
| 414 |  | 
| 415     def test_setxattr(self): |  | 
| 416       lines = [ |  | 
| 417           (self._ROOT_PID, |  | 
| 418            'setxattr("file.exe", "attribute", "value", 0, 0) = 0'), |  | 
| 419       ] |  | 
| 420       self._test_lines(lines, '/home/foo_bar_user/src', []) |  | 
| 421 |  | 
| 422     def test_sig_unexpected(self): |  | 
| 423       lines = [ |  | 
| 424         (self._ROOT_PID, 'exit_group(0)                     = ?'), |  | 
| 425       ] |  | 
| 426       self._test_lines(lines, '/home/foo_bar_user/src', []) |  | 
| 427 |  | 
| 428     def test_stray(self): |  | 
| 429       lines = [ |  | 
| 430         (self._ROOT_PID, |  | 
| 431           'execve("../out/unittests", ' |  | 
| 432             '["../out/unittests"...], [/* 44 vars */])         = 0'), |  | 
| 433         (self._ROOT_PID, |  | 
| 434           ')                                       = ? <unavailable>'), |  | 
| 435       ] |  | 
| 436       files = [ |  | 
| 437         { |  | 
| 438           'path': u'/home/foo_bar_user/out/unittests', |  | 
| 439           'size': -1, |  | 
| 440         }, |  | 
| 441       ] |  | 
| 442       self._test_lines(lines, '/home/foo_bar_user/src', files) |  | 
| 443 |  | 
| 444 |  | 
| 445 if __name__ == '__main__': |  | 
| 446   VERBOSE = '-v' in sys.argv |  | 
| 447   logging.basicConfig(level=logging.DEBUG if VERBOSE else logging.ERROR) |  | 
| 448   unittest.main() |  | 
| OLD | NEW | 
|---|