Update To 11.40.268.0
[platform/framework/web/crosswalk.git] / src / tools / swarming_client / tests / trace_inputs_smoke_test.py
1 #!/usr/bin/env python
2 # coding=utf-8
3 # Copyright 2012 The Swarming Authors. All rights reserved.
4 # Use of this source code is governed under the Apache License, Version 2.0 that
5 # can be found in the LICENSE file.
6
7 import functools
8 import json
9 import logging
10 import os
11 import shutil
12 import subprocess
13 import sys
14 import tempfile
15 import unicodedata
16 import unittest
17
18 ROOT_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
19 sys.path.insert(0, ROOT_DIR)
20
21 import trace_inputs
22 from utils import file_path
23 from utils import threading_utils
24
25
26 FILENAME = os.path.basename(__file__)
27 REL_DATA = os.path.join(u'tests', 'trace_inputs')
28 VERBOSE = False
29
30 # TODO(maruel): Have the kernel tracer on Windows differentiate between file
31 # read or file write.
32 MODE_R = trace_inputs.Results.File.READ if sys.platform != 'win32' else None
33 MODE_W = trace_inputs.Results.File.WRITE if sys.platform != 'win32' else None
34 MODE_T = trace_inputs.Results.File.TOUCHED
35
36
37 def check_can_trace(fn):
38   """Function decorator that skips test that need to be able trace."""
39   @functools.wraps(fn)
40   def hook(self, *args, **kwargs):
41     if not trace_inputs.can_trace():
42       self.fail('Please rerun this test with admin privileges.')
43     return fn(self, *args, **kwargs)
44   return hook
45
46
47 class CalledProcessError(subprocess.CalledProcessError):
48   """Makes 2.6 version act like 2.7"""
49   def __init__(self, returncode, cmd, output, cwd):
50     super(CalledProcessError, self).__init__(returncode, cmd)
51     self.output = output
52     self.cwd = cwd
53
54   def __str__(self):
55     return super(CalledProcessError, self).__str__() + (
56         '\n'
57         'cwd=%s\n%s') % (self.cwd, self.output)
58
59
60 class TraceInputsBase(unittest.TestCase):
61   def setUp(self):
62     self.tempdir = None
63     self.trace_inputs_path = os.path.join(ROOT_DIR, 'trace_inputs.py')
64
65     # Wraps up all the differences between OSes here.
66     # - Windows doesn't track initial_cwd.
67     # - OSX replaces /usr/bin/python with /usr/bin/python2.7.
68     self.cwd = os.path.join(ROOT_DIR, u'tests')
69     self.initial_cwd = unicode(self.cwd)
70     self.expected_cwd = unicode(ROOT_DIR)
71     if sys.platform == 'win32':
72       # Not supported on Windows.
73       self.initial_cwd = None
74       self.expected_cwd = None
75
76     # There's 3 kinds of references to python, self.executable,
77     # self.real_executable and self.naked_executable. It depends how python was
78     # started.
79     self.executable = sys.executable
80     if sys.platform == 'darwin':
81       # /usr/bin/python is a thunk executable that decides which version of
82       # python gets executed.
83       suffix = '.'.join(map(str, sys.version_info[0:2]))
84       if os.access(self.executable + suffix, os.X_OK):
85         # So it'll look like /usr/bin/python2.7
86         self.executable += suffix
87
88     self.real_executable = file_path.get_native_path_case(
89         unicode(self.executable))
90     self.tempdir = file_path.get_native_path_case(
91         unicode(tempfile.mkdtemp(prefix='trace_smoke_test')))
92     self.log = os.path.join(self.tempdir, 'log')
93
94     # self.naked_executable will only be naked on Windows.
95     self.naked_executable = unicode(sys.executable)
96     if sys.platform == 'win32':
97       self.naked_executable = os.path.basename(sys.executable)
98
99   def tearDown(self):
100     if self.tempdir:
101       if VERBOSE:
102         print 'Leaking: %s' % self.tempdir
103       else:
104         shutil.rmtree(self.tempdir)
105
106   @staticmethod
107   def get_child_command(from_data):
108     """Returns command to run the child1.py."""
109     cmd = [sys.executable]
110     if from_data:
111       # When the gyp argument is specified, the command is started from --cwd
112       # directory. In this case, 'tests'.
113       cmd.extend([os.path.join('trace_inputs', 'child1.py'), '--child-gyp'])
114     else:
115       # When the gyp argument is not specified, the command is started from
116       # --root-dir directory.
117       cmd.extend([os.path.join(REL_DATA, 'child1.py'), '--child'])
118     return cmd
119
120   @staticmethod
121   def _size(*args):
122     return os.stat(os.path.join(ROOT_DIR, *args)).st_size
123
124
125 class TraceInputs(TraceInputsBase):
126   def _execute(self, mode, command, cwd):
127     cmd = [
128       sys.executable,
129       self.trace_inputs_path,
130       mode,
131       '--log', self.log,
132     ]
133     if VERBOSE:
134       cmd.extend(['-v'] * 3)
135     cmd.extend(command)
136     logging.info('Command: %s' % ' '.join(cmd))
137     p = subprocess.Popen(
138         cmd,
139         stdout=subprocess.PIPE,
140         stderr=subprocess.PIPE,
141         cwd=cwd,
142         universal_newlines=True)
143     out, err = p.communicate()
144     if VERBOSE:
145       print err
146     if p.returncode:
147       raise CalledProcessError(p.returncode, cmd, out + err, cwd)
148     return out or ''
149
150   def _trace(self, from_data):
151     if from_data:
152       cwd = os.path.join(ROOT_DIR, 'tests')
153     else:
154       cwd = ROOT_DIR
155     return self._execute('trace', self.get_child_command(from_data), cwd=cwd)
156
157   @check_can_trace
158   def test_trace(self):
159     expected = '\n'.join((
160       'Total: 7',
161       'Non existent: 0',
162       'Interesting: 7 reduced to 6',
163       '  tests/trace_inputs/child1.py'.replace('/', os.path.sep),
164       '  tests/trace_inputs/child2.py'.replace('/', os.path.sep),
165       '  tests/trace_inputs/files1/'.replace('/', os.path.sep),
166       '  tests/trace_inputs/test_file.txt'.replace('/', os.path.sep),
167       ('  tests/%s' % FILENAME).replace('/', os.path.sep),
168       '  trace_inputs.py',
169     )) + '\n'
170     trace_expected = '\n'.join((
171       'child from %s' % ROOT_DIR,
172       'child2',
173     )) + '\n'
174     trace_actual = self._trace(False)
175     actual = self._execute(
176         'read',
177         [
178           '--root-dir', ROOT_DIR,
179           '--trace-blacklist', '.+\\.pyc',
180           '--trace-blacklist', '.*\\.svn',
181           '--trace-blacklist', '.*do_not_care\\.txt',
182         ],
183         cwd=unicode(ROOT_DIR))
184     self.assertEqual(expected, actual)
185     self.assertEqual(trace_expected, trace_actual)
186
187   @check_can_trace
188   def test_trace_json(self):
189     expected = {
190       u'root': {
191         u'children': [
192           {
193             u'children': [],
194             u'command': [u'python', u'child2.py'],
195             u'executable': self.naked_executable,
196             u'files': [
197               {
198                 'mode': MODE_R,
199                 u'path': os.path.join(REL_DATA, 'child2.py'),
200                 u'size': self._size(REL_DATA, 'child2.py'),
201               },
202               {
203                 'mode': MODE_R,
204                 u'path': os.path.join(REL_DATA, 'files1', 'bar'),
205                 u'size': self._size(REL_DATA, 'files1', 'bar'),
206               },
207               {
208                 'mode': MODE_R,
209                 u'path': os.path.join(REL_DATA, 'files1', 'foo'),
210                 u'size': self._size(REL_DATA, 'files1', 'foo'),
211               },
212               {
213                 'mode': MODE_R,
214                 u'path': os.path.join(REL_DATA, 'test_file.txt'),
215                 u'size': self._size(REL_DATA, 'test_file.txt'),
216               },
217             ],
218             u'initial_cwd': self.initial_cwd,
219             #u'pid': 123,
220           },
221         ],
222         u'command': [
223           unicode(self.executable),
224           os.path.join(u'trace_inputs', 'child1.py'),
225           u'--child-gyp',
226         ],
227         u'executable': self.real_executable,
228         u'files': [
229           {
230             u'mode': MODE_R,
231             u'path': os.path.join(REL_DATA, 'child1.py'),
232             u'size': self._size(REL_DATA, 'child1.py'),
233           },
234           {
235             u'mode': MODE_R,
236             u'path': os.path.join(u'tests', u'trace_inputs_smoke_test.py'),
237             u'size': self._size('tests', 'trace_inputs_smoke_test.py'),
238           },
239           {
240             u'mode': MODE_R,
241             u'path': u'trace_inputs.py',
242             u'size': self._size('trace_inputs.py'),
243           },
244         ],
245         u'initial_cwd': self.initial_cwd,
246         #u'pid': 123,
247       },
248     }
249     trace_expected = '\n'.join((
250       'child_gyp from %s' % os.path.join(ROOT_DIR, 'tests'),
251       'child2',
252     )) + '\n'
253     trace_actual = self._trace(True)
254     actual_text = self._execute(
255         'read',
256         [
257           '--root-dir', ROOT_DIR,
258           '--trace-blacklist', '.+\\.pyc',
259           '--trace-blacklist', '.*\\.svn',
260           '--trace-blacklist', '.*do_not_care\\.txt',
261           '--json',
262         ],
263         cwd=unicode(ROOT_DIR))
264     actual_json = json.loads(actual_text)
265     self.assertEqual(list, actual_json.__class__)
266     self.assertEqual(1, len(actual_json))
267     actual_json = actual_json[0]
268     # Removes the pids.
269     self.assertTrue(actual_json['root'].pop('pid'))
270     self.assertTrue(actual_json['root']['children'][0].pop('pid'))
271     self.assertEqual(expected, actual_json)
272     self.assertEqual(trace_expected, trace_actual)
273
274
275 class TraceInputsImport(TraceInputsBase):
276
277   # Similar to TraceInputs test fixture except that it calls the function
278   # directly, so the Results instance can be inspected.
279   # Roughly, make sure the API is stable.
280   def _execute_trace(self, command):
281     # Similar to what trace_test_cases.py does.
282     api = trace_inputs.get_api()
283     _, _ = trace_inputs.trace(self.log, command, self.cwd, api, True)
284     # TODO(maruel): Check
285     #self.assertEqual(0, returncode)
286     #self.assertEqual('', output)
287     def blacklist(f):
288       return f.endswith(('.pyc', '.svn', 'do_not_care.txt'))
289     data = api.parse_log(self.log, blacklist, None)
290     self.assertEqual(1, len(data))
291     if 'exception' in data[0]:
292       raise data[0]['exception'][0], \
293           data[0]['exception'][1], \
294           data[0]['exception'][2]
295
296     return data[0]['results'].strip_root(unicode(ROOT_DIR))
297
298   def _gen_dict_wrong_path(self):
299     """Returns the expected flattened Results when child1.py is called with the
300     wrong relative path.
301     """
302     return {
303       'root': {
304         'children': [],
305         'command': [
306           self.executable,
307           os.path.join(REL_DATA, 'child1.py'),
308           '--child',
309         ],
310         'executable': self.real_executable,
311         'files': [],
312         'initial_cwd': self.initial_cwd,
313       },
314     }
315
316   def _gen_dict_full(self):
317     """Returns the expected flattened Results when child1.py is called with
318     --child.
319     """
320     return {
321       'root': {
322         'children': [
323           {
324             'children': [],
325             'command': ['python', 'child2.py'],
326             'executable': self.naked_executable,
327             'files': [
328               {
329                 'mode': MODE_R,
330                 'path': os.path.join(REL_DATA, 'child2.py'),
331                 'size': self._size(REL_DATA, 'child2.py'),
332               },
333               {
334                 'mode': MODE_R,
335                 'path': os.path.join(REL_DATA, 'files1', 'bar'),
336                 'size': self._size(REL_DATA, 'files1', 'bar'),
337               },
338               {
339                 'mode': MODE_R,
340                 'path': os.path.join(REL_DATA, 'files1', 'foo'),
341                 'size': self._size(REL_DATA, 'files1', 'foo'),
342               },
343               {
344                 'mode': MODE_R,
345                 'path': os.path.join(REL_DATA, 'test_file.txt'),
346                 'size': self._size(REL_DATA, 'test_file.txt'),
347               },
348             ],
349             'initial_cwd': self.expected_cwd,
350           },
351         ],
352         'command': [
353           self.executable,
354           os.path.join(REL_DATA, 'child1.py'),
355           '--child',
356         ],
357         'executable': self.real_executable,
358         'files': [
359           {
360             'mode': MODE_R,
361             'path': os.path.join(REL_DATA, 'child1.py'),
362             'size': self._size(REL_DATA, 'child1.py'),
363           },
364           {
365             'mode': MODE_R,
366             'path': os.path.join(u'tests', u'trace_inputs_smoke_test.py'),
367             'size': self._size('tests', 'trace_inputs_smoke_test.py'),
368           },
369           {
370             'mode': MODE_R,
371             'path': u'trace_inputs.py',
372             'size': self._size('trace_inputs.py'),
373           },
374         ],
375         'initial_cwd': self.expected_cwd,
376       },
377     }
378
379   def _gen_dict_full_gyp(self):
380     """Returns the expected flattened results when child1.py is called with
381     --child-gyp.
382     """
383     return {
384       'root': {
385         'children': [
386           {
387             'children': [],
388             'command': [u'python', u'child2.py'],
389             'executable': self.naked_executable,
390             'files': [
391               {
392                 'mode': MODE_R,
393                 'path': os.path.join(REL_DATA, 'child2.py'),
394                 'size': self._size(REL_DATA, 'child2.py'),
395               },
396               {
397                 'mode': MODE_R,
398                 'path': os.path.join(REL_DATA, 'files1', 'bar'),
399                 'size': self._size(REL_DATA, 'files1', 'bar'),
400               },
401               {
402                 'mode': MODE_R,
403                 'path': os.path.join(REL_DATA, 'files1', 'foo'),
404                 'size': self._size(REL_DATA, 'files1', 'foo'),
405               },
406               {
407                 'mode': MODE_R,
408                 'path': os.path.join(REL_DATA, 'test_file.txt'),
409                 'size': self._size(REL_DATA, 'test_file.txt'),
410               },
411             ],
412             'initial_cwd': self.initial_cwd,
413           },
414         ],
415         'command': [
416           self.executable,
417           os.path.join('trace_inputs', 'child1.py'),
418           '--child-gyp',
419         ],
420         'executable': self.real_executable,
421         'files': [
422           {
423             'mode': MODE_R,
424             'path': os.path.join(REL_DATA, 'child1.py'),
425             'size': self._size(REL_DATA, 'child1.py'),
426           },
427           {
428             'mode': MODE_R,
429             'path': os.path.join(u'tests', u'trace_inputs_smoke_test.py'),
430             'size': self._size('tests', 'trace_inputs_smoke_test.py'),
431           },
432           {
433             'mode': MODE_R,
434             'path': u'trace_inputs.py',
435             'size': self._size('trace_inputs.py'),
436           },
437         ],
438         'initial_cwd': self.initial_cwd,
439       },
440     }
441
442   @check_can_trace
443   def test_trace_wrong_path(self):
444     # Deliberately start the trace from the wrong path. Starts it from the
445     # directory 'tests' so 'tests/tests/trace_inputs/child1.py' is not
446     # accessible, so child2.py process is not started.
447     results = self._execute_trace(self.get_child_command(False))
448     expected = self._gen_dict_wrong_path()
449     actual = results.flatten()
450     self.assertTrue(actual['root'].pop('pid'))
451     self.assertEqual(expected, actual)
452
453   @check_can_trace
454   def test_trace(self):
455     expected = self._gen_dict_full_gyp()
456     results = self._execute_trace(self.get_child_command(True))
457     actual = results.flatten()
458     self.assertTrue(actual['root'].pop('pid'))
459     self.assertTrue(actual['root']['children'][0].pop('pid'))
460     self.assertEqual(expected, actual)
461     files = [
462       u'tests/trace_inputs/child1.py'.replace('/', os.path.sep),
463       u'tests/trace_inputs/child2.py'.replace('/', os.path.sep),
464       u'tests/trace_inputs/files1/'.replace('/', os.path.sep),
465       u'tests/trace_inputs/test_file.txt'.replace('/', os.path.sep),
466       u'tests/trace_inputs_smoke_test.py'.replace('/', os.path.sep),
467       u'trace_inputs.py',
468     ]
469     def blacklist(f):
470       return f.endswith(('.pyc', 'do_not_care.txt', '.git', '.svn'))
471     simplified = trace_inputs.extract_directories(
472         file_path.get_native_path_case(unicode(ROOT_DIR)),
473         results.files,
474         blacklist)
475     self.assertEqual(files, [f.path for f in simplified])
476
477   @check_can_trace
478   def test_trace_multiple(self):
479     # Starts parallel threads and trace parallel child processes simultaneously.
480     # Some are started from 'tests' directory, others from this script's
481     # directory. One trace fails. Verify everything still goes one.
482     parallel = 8
483
484     def trace(tracer, cmd, cwd, tracename):
485       resultcode, output = tracer.trace(cmd, cwd, tracename, True)
486       return (tracename, resultcode, output)
487
488     with threading_utils.ThreadPool(parallel, parallel, 0) as pool:
489       api = trace_inputs.get_api()
490       with api.get_tracer(self.log) as tracer:
491         pool.add_task(
492             0, trace, tracer, self.get_child_command(False), ROOT_DIR, 'trace1')
493         pool.add_task(
494             0, trace, tracer, self.get_child_command(True), self.cwd, 'trace2')
495         pool.add_task(
496             0, trace, tracer, self.get_child_command(False), ROOT_DIR, 'trace3')
497         pool.add_task(
498             0, trace, tracer, self.get_child_command(True), self.cwd, 'trace4')
499         # Have this one fail since it's started from the wrong directory.
500         pool.add_task(
501             0, trace, tracer, self.get_child_command(False), self.cwd, 'trace5')
502         pool.add_task(
503             0, trace, tracer, self.get_child_command(True), self.cwd, 'trace6')
504         pool.add_task(
505             0, trace, tracer, self.get_child_command(False), ROOT_DIR, 'trace7')
506         pool.add_task(
507             0, trace, tracer, self.get_child_command(True), self.cwd, 'trace8')
508         trace_results = pool.join()
509     def blacklist(f):
510       return f.endswith(('.pyc', 'do_not_care.txt', '.git', '.svn'))
511     actual_results = api.parse_log(self.log, blacklist, None)
512     self.assertEqual(8, len(trace_results))
513     self.assertEqual(8, len(actual_results))
514
515     # Convert to dict keyed on the trace name, simpler to verify.
516     trace_results = dict((i[0], i[1:]) for i in trace_results)
517     actual_results = dict((x.pop('trace'), x) for x in actual_results)
518     self.assertEqual(sorted(trace_results), sorted(actual_results))
519
520     # It'd be nice to start different kinds of processes.
521     expected_results = [
522       self._gen_dict_full(),
523       self._gen_dict_full_gyp(),
524       self._gen_dict_full(),
525       self._gen_dict_full_gyp(),
526       self._gen_dict_wrong_path(),
527       self._gen_dict_full_gyp(),
528       self._gen_dict_full(),
529       self._gen_dict_full_gyp(),
530     ]
531     self.assertEqual(len(expected_results), len(trace_results))
532
533     # See the comment above about the trace that fails because it's started from
534     # the wrong directory.
535     busted = 4
536     for index, key in enumerate(sorted(actual_results)):
537       self.assertEqual('trace%d' % (index + 1), key)
538       self.assertEqual(2, len(trace_results[key]))
539       # returncode
540       self.assertEqual(0 if index != busted else 2, trace_results[key][0])
541       # output
542       self.assertEqual(actual_results[key]['output'], trace_results[key][1])
543
544       self.assertEqual(['output', 'results'], sorted(actual_results[key]))
545       results = actual_results[key]['results']
546       results = results.strip_root(unicode(ROOT_DIR))
547       actual = results.flatten()
548       self.assertTrue(actual['root'].pop('pid'))
549       if index != busted:
550         self.assertTrue(actual['root']['children'][0].pop('pid'))
551       self.assertEqual(expected_results[index], actual)
552
553   if sys.platform != 'win32':
554     def test_trace_symlink(self):
555       expected = {
556         'root': {
557           'children': [],
558           'command': [
559             self.executable,
560             os.path.join('trace_inputs', 'symlink.py'),
561           ],
562           'executable': self.real_executable,
563           'files': [
564             {
565               'mode': MODE_R,
566               'path': os.path.join(REL_DATA, 'files2', 'bar'),
567               'size': self._size(REL_DATA, 'files2', 'bar'),
568             },
569             {
570               'mode': MODE_R,
571               'path': os.path.join(REL_DATA, 'files2', 'foo'),
572               'size': self._size(REL_DATA, 'files2', 'foo'),
573             },
574             {
575               'mode': MODE_R,
576               'path': os.path.join(REL_DATA, 'symlink.py'),
577               'size': self._size(REL_DATA, 'symlink.py'),
578             },
579           ],
580           'initial_cwd': self.initial_cwd,
581         },
582       }
583       cmd = [sys.executable, os.path.join('trace_inputs', 'symlink.py')]
584       results = self._execute_trace(cmd)
585       actual = results.flatten()
586       self.assertTrue(actual['root'].pop('pid'))
587       self.assertEqual(expected, actual)
588       files = [
589         # In particular, the symlink is *not* resolved.
590         u'tests/trace_inputs/files2/'.replace('/', os.path.sep),
591         u'tests/trace_inputs/symlink.py'.replace('/', os.path.sep),
592       ]
593       def blacklist(f):
594         return f.endswith(('.pyc', '.svn', 'do_not_care.txt'))
595       simplified = trace_inputs.extract_directories(
596           unicode(ROOT_DIR), results.files, blacklist)
597       self.assertEqual(files, [f.path for f in simplified])
598
599   @check_can_trace
600   def test_trace_quoted(self):
601     results = self._execute_trace([sys.executable, '-c', 'print("hi")'])
602     expected = {
603       'root': {
604         'children': [],
605         'command': [
606           self.executable,
607           '-c',
608           'print("hi")',
609         ],
610         'executable': self.real_executable,
611         'files': [],
612         'initial_cwd': self.initial_cwd,
613       },
614     }
615     actual = results.flatten()
616     self.assertTrue(actual['root'].pop('pid'))
617     self.assertEqual(expected, actual)
618
619   @check_can_trace
620   def _touch_expected(self, command):
621     # Looks for file that were touched but not opened, using different apis.
622     results = self._execute_trace(
623       [sys.executable, os.path.join('trace_inputs', 'touch_only.py'), command])
624     expected = {
625       'root': {
626         'children': [],
627         'command': [
628           self.executable,
629           os.path.join('trace_inputs', 'touch_only.py'),
630           command,
631         ],
632         'executable': self.real_executable,
633         'files': [
634           {
635             'mode': MODE_T,
636             'path': os.path.join(REL_DATA, 'test_file.txt'),
637             'size': self._size(REL_DATA, 'test_file.txt'),
638           },
639           {
640             'mode': MODE_R,
641             'path': os.path.join(REL_DATA, 'touch_only.py'),
642             'size': self._size(REL_DATA, 'touch_only.py'),
643           },
644         ],
645         'initial_cwd': self.initial_cwd,
646       },
647     }
648     if sys.platform != 'linux2':
649       # TODO(maruel): Remove once properly implemented.
650       expected['root']['files'].pop(0)
651
652     actual = results.flatten()
653     self.assertTrue(actual['root'].pop('pid'))
654     self.assertEqual(expected, actual)
655
656   def test_trace_touch_only_access(self):
657     self._touch_expected('access')
658
659   def test_trace_touch_only_isfile(self):
660     self._touch_expected('isfile')
661
662   def test_trace_touch_only_stat(self):
663     self._touch_expected('stat')
664
665   @check_can_trace
666   def test_trace_tricky_filename(self):
667     # TODO(maruel):  On Windows, it's using the current code page so some
668     # characters can't be represented. As a nice North American, hard code the
669     # string to something representable in code page 1252. The exact code page
670     # depends on the user system.
671     if sys.platform == 'win32':
672       filename = u'foo, bar,  ~p#o,,ué^t%t .txt'
673     else:
674       filename = u'foo, bar,  ~p#o,,ué^t%t 和平.txt'
675
676     exe = os.path.join(self.tempdir, 'tricky_filename.py')
677     shutil.copyfile(
678         os.path.join(self.cwd, 'trace_inputs', 'tricky_filename.py'), exe)
679     expected = {
680       'root': {
681         'children': [],
682         'command': [
683           self.executable,
684           exe,
685         ],
686         'executable': self.real_executable,
687         'files': [
688           {
689             'mode': MODE_W,
690             'path':  filename,
691             'size': long(len('Bingo!')),
692           },
693           {
694             'mode': MODE_R,
695             'path': u'tricky_filename.py',
696             'size': self._size(REL_DATA, 'tricky_filename.py'),
697           },
698         ],
699         'initial_cwd': self.tempdir if sys.platform != 'win32' else None,
700       },
701     }
702
703     api = trace_inputs.get_api()
704     returncode, output = trace_inputs.trace(
705         self.log, [exe], self.tempdir, api, True)
706     self.assertEqual('', output)
707     self.assertEqual(0, returncode)
708     data = api.parse_log(self.log, lambda _: False, None)
709     self.assertEqual(1, len(data))
710     if 'exception' in data[0]:
711       raise data[0]['exception'][0], \
712           data[0]['exception'][1], \
713           data[0]['exception'][2]
714     actual = data[0]['results'].strip_root(self.tempdir).flatten()
715     self.assertTrue(actual['root'].pop('pid'))
716     self.assertEqual(expected, actual)
717     trace_inputs.get_api().clean_trace(self.log)
718     files = sorted(
719         unicodedata.normalize('NFC', i)
720         for i in os.listdir(unicode(self.tempdir)))
721     self.assertEqual([filename, 'tricky_filename.py'], files)
722
723
724 if __name__ == '__main__':
725   VERBOSE = '-v' in sys.argv
726   logging.basicConfig(level=logging.DEBUG if VERBOSE else logging.ERROR)
727   if VERBOSE:
728     unittest.TestCase.maxDiff = None
729   # Necessary for the dtrace logger to work around execve() hook. See
730   # trace_inputs.py for more details.
731   os.environ['TRACE_INPUTS_DTRACE_ENABLE_EXECVE'] = '1'
732   print >> sys.stderr, 'Test are currently disabled'
733   sys.exit(0)
734   #unittest.main()