summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authormaruel@chromium.org <maruel@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2012-06-04 23:58:17 +0000
committermaruel@chromium.org <maruel@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2012-06-04 23:58:17 +0000
commit568427d2d4c6f5600ff8c6d2af5c81c2e785297e (patch)
treefa5a8d90ad7fd38dfc9d7e74f6a289773c00cf8c
parent49c29f2cbfc0c06b2cd34331a3d1fd2d8013a189 (diff)
downloadchromium_src-568427d2d4c6f5600ff8c6d2af5c81c2e785297e.zip
chromium_src-568427d2d4c6f5600ff8c6d2af5c81c2e785297e.tar.gz
chromium_src-568427d2d4c6f5600ff8c6d2af5c81c2e785297e.tar.bz2
Make trace_inputs.py a real stand alone application with inner commands.
Remove any knowledge about '.isolate' from trace_inputs.py and move the code into isolate_common.py. NOTRY=true R=cmp@chromium.org BUG= TEST= Review URL: https://chromiumcodereview.appspot.com/10459040 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@140435 0039d316-1c4b-4281-b951-d872f2087c98
-rwxr-xr-xtools/isolate/isolate.py45
-rw-r--r--tools/isolate/isolate_common.py152
-rwxr-xr-xtools/isolate/isolate_common_test.py97
-rwxr-xr-xtools/isolate/isolate_smoke_test.py94
-rwxr-xr-xtools/isolate/merge_isolate.py6
-rwxr-xr-xtools/isolate/read_trace.py5
-rwxr-xr-xtools/isolate/trace_inputs.py425
-rwxr-xr-xtools/isolate/trace_inputs_smoke_test.py211
-rwxr-xr-xtools/isolate/trace_inputs_test.py81
-rwxr-xr-xtools/isolate/trace_test_cases.py3
10 files changed, 642 insertions, 477 deletions
diff --git a/tools/isolate/isolate.py b/tools/isolate/isolate.py
index c5f51ae..8220873 100755
--- a/tools/isolate/isolate.py
+++ b/tools/isolate/isolate.py
@@ -31,6 +31,7 @@ import subprocess
import sys
import tempfile
+import isolate_common
import merge_isolate
import trace_inputs
import run_test_from_archive
@@ -123,7 +124,7 @@ def load_isolate(content, error):
# Load the .isolate file, process its conditions, retrieve the command and
# dependencies.
configs = merge_isolate.load_gyp(merge_isolate.eval_content(content))
- flavor = trace_inputs.get_flavor()
+ flavor = isolate_common.get_flavor()
config = configs.per_os.get(flavor) or configs.per_os.get(None)
if not config:
error('Failed to load configuration for \'%s\'' % flavor)
@@ -152,7 +153,7 @@ def process_input(filepath, prevdict, level, read_only):
out = {}
if level >= STATS_ONLY:
filestats = os.stat(filepath)
- if trace_inputs.get_flavor() != 'win':
+ if isolate_common.get_flavor() != 'win':
filemode = stat.S_IMODE(filestats.st_mode)
# Remove write access for group and all access to 'others'.
filemode &= ~(stat.S_IWGRP | stat.S_IRWXO)
@@ -588,13 +589,37 @@ def MODEtrace(_outdir, state):
if not state.result.command:
print 'No command to run'
return 1
- return trace_inputs.trace_inputs(
- state.result_file + '.log',
- state.result.command,
- state.root_dir,
- state.result.relative_cwd,
- product_dir,
- False)
+ api = trace_inputs.get_api()
+ logfile = state.result_file + '.log'
+ try:
+ result = 0
+ if not os.path.isfile(logfile):
+ result, _ = api.gen_trace(
+ state.result.command,
+ os.path.join(state.root_dir, state.result.relative_cwd),
+ logfile,
+ True)
+
+ _, simplified = trace_inputs.load_trace(logfile, state.root_dir, api)
+ variables = isolate_common.generate_dict(
+ (f.path for f in simplified),
+ state.result.relative_cwd,
+ product_dir)
+ # Outputs in a way that is easy to merge with merge_isolate.py.
+ value = {
+ 'conditions': [
+ ['OS=="%s"' % isolate_common.get_flavor(), {
+ 'variables': variables,
+ }],
+ ],
+ }
+ isolate_common.pretty_print(value, sys.stdout)
+ return result
+ except trace_inputs.TracingFailure, e:
+ print >> sys.stderr, (
+ '\nTracing failed for: %s' % ' '.join(state.result.command))
+ print >> sys.stderr, str(e)
+ return 1
# Must be declared after all the functions.
@@ -669,7 +694,7 @@ def isolate(result_file, isolate_file, mode, variables, out_dir, error):
def main():
"""Handles CLI and normalizes the input arguments to pass them to isolate().
"""
- default_variables = [('OS', trace_inputs.get_flavor())]
+ default_variables = [('OS', isolate_common.get_flavor())]
if sys.platform in ('win32', 'cygwin'):
default_variables.append(('EXECUTABLE_SUFFIX', '.exe'))
else:
diff --git a/tools/isolate/isolate_common.py b/tools/isolate/isolate_common.py
new file mode 100644
index 0000000..3e51e25
--- /dev/null
+++ b/tools/isolate/isolate_common.py
@@ -0,0 +1,152 @@
+# coding=utf-8
+# Copyright (c) 2012 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+"""Common code to manage .isolate format.
+"""
+
+import logging
+import os
+import posixpath
+import sys
+
+
+KEY_TRACKED = 'isolate_dependency_tracked'
+KEY_UNTRACKED = 'isolate_dependency_untracked'
+
+
+def posix_relpath(path, root):
+ """posix.relpath() that keeps trailing slash."""
+ out = posixpath.relpath(path, root)
+ if path.endswith('/'):
+ out += '/'
+ return out
+
+
+def cleanup_path(x):
+ """Cleans up a relative path. Converts any os.path.sep to '/' on Windows."""
+ if x:
+ x = x.rstrip(os.path.sep).replace(os.path.sep, '/')
+ if x == '.':
+ x = ''
+ if x:
+ x += '/'
+ return x
+
+
+def get_flavor():
+ """Returns the system default flavor. Copied from gyp/pylib/gyp/common.py."""
+ flavors = {
+ 'cygwin': 'win',
+ 'win32': 'win',
+ 'darwin': 'mac',
+ 'sunos5': 'solaris',
+ 'freebsd7': 'freebsd',
+ 'freebsd8': 'freebsd',
+ }
+ return flavors.get(sys.platform, 'linux')
+
+
+def generate_dict(files, cwd_dir, product_dir):
+ """Converts the list of files into a .isolate dictionary.
+
+ Arguments:
+ - files: list of files to generate a dictionary out of.
+ - cwd_dir: directory to base all the files from, relative to root_dir.
+ - product_dir: directory to replace with <(PRODUCT_DIR), relative to root_dir.
+ """
+ cwd_dir = cleanup_path(cwd_dir)
+ product_dir = cleanup_path(product_dir)
+
+ def fix(f):
+ """Bases the file on the most restrictive variable."""
+ logging.debug('fix(%s)' % f)
+ # Important, GYP stores the files with / and not \.
+ f = f.replace(os.path.sep, '/')
+ if product_dir and f.startswith(product_dir):
+ return '<(PRODUCT_DIR)/%s' % f[len(product_dir):]
+ else:
+ # cwd_dir is usually the directory containing the gyp file. It may be
+ # empty if the whole directory containing the gyp file is needed.
+ return posix_relpath(f, cwd_dir) or './'
+
+ corrected = [fix(f) for f in files]
+ tracked = [f for f in corrected if not f.endswith('/') and ' ' not in f]
+ untracked = [f for f in corrected if f.endswith('/') or ' ' in f]
+ variables = {}
+ if tracked:
+ variables[KEY_TRACKED] = tracked
+ if untracked:
+ variables[KEY_UNTRACKED] = untracked
+ return variables
+
+
+def pretty_print(variables, stdout):
+ """Outputs a gyp compatible list from the decoded variables.
+
+ Similar to pprint.print() but with NIH syndrome.
+ """
+ # Order the dictionary keys by these keys in priority.
+ ORDER = (
+ 'variables', 'condition', 'command', 'relative_cwd', 'read_only',
+ KEY_TRACKED, KEY_UNTRACKED)
+
+ def sorting_key(x):
+ """Gives priority to 'most important' keys before the others."""
+ if x in ORDER:
+ return str(ORDER.index(x))
+ return x
+
+ def loop_list(indent, items):
+ for item in items:
+ if isinstance(item, basestring):
+ stdout.write('%s\'%s\',\n' % (indent, item))
+ elif isinstance(item, dict):
+ stdout.write('%s{\n' % indent)
+ loop_dict(indent + ' ', item)
+ stdout.write('%s},\n' % indent)
+ elif isinstance(item, list):
+ # A list inside a list will write the first item embedded.
+ stdout.write('%s[' % indent)
+ for index, i in enumerate(item):
+ if isinstance(i, basestring):
+ stdout.write(
+ '\'%s\', ' % i.replace('\\', '\\\\').replace('\'', '\\\''))
+ elif isinstance(i, dict):
+ stdout.write('{\n')
+ loop_dict(indent + ' ', i)
+ if index != len(item) - 1:
+ x = ', '
+ else:
+ x = ''
+ stdout.write('%s}%s' % (indent, x))
+ else:
+ assert False
+ stdout.write('],\n')
+ else:
+ assert False
+
+ def loop_dict(indent, items):
+ for key in sorted(items, key=sorting_key):
+ item = items[key]
+ stdout.write("%s'%s': " % (indent, key))
+ if isinstance(item, dict):
+ stdout.write('{\n')
+ loop_dict(indent + ' ', item)
+ stdout.write(indent + '},\n')
+ elif isinstance(item, list):
+ stdout.write('[\n')
+ loop_list(indent + ' ', item)
+ stdout.write(indent + '],\n')
+ elif isinstance(item, basestring):
+ stdout.write(
+ '\'%s\',\n' % item.replace('\\', '\\\\').replace('\'', '\\\''))
+ elif item in (True, False, None):
+ stdout.write('%s\n' % item)
+ else:
+ assert False, item
+
+ stdout.write('{\n')
+ loop_dict(' ', variables)
+ stdout.write('}\n')
diff --git a/tools/isolate/isolate_common_test.py b/tools/isolate/isolate_common_test.py
new file mode 100755
index 0000000..eea87f0
--- /dev/null
+++ b/tools/isolate/isolate_common_test.py
@@ -0,0 +1,97 @@
+#!/usr/bin/env python
+# Copyright (c) 2012 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import cStringIO
+import logging
+import unittest
+import sys
+
+import isolate_common
+
+
+class TraceInputs(unittest.TestCase):
+ def _test(self, value, expected):
+ actual = cStringIO.StringIO()
+ isolate_common.pretty_print(value, actual)
+ self.assertEquals(expected, actual.getvalue())
+
+ def test_pretty_print_empty(self):
+ self._test({}, '{\n}\n')
+
+ def test_pretty_print_mid_size(self):
+ value = {
+ 'variables': {
+ 'bar': [
+ 'file1',
+ 'file2',
+ ],
+ },
+ 'conditions': [
+ ['OS=\"foo\"', {
+ 'variables': {
+ isolate_common.KEY_UNTRACKED: [
+ 'dir1',
+ 'dir2',
+ ],
+ isolate_common.KEY_TRACKED: [
+ 'file4',
+ 'file3',
+ ],
+ 'command': ['python', '-c', 'print "H\\i\'"'],
+ 'read_only': True,
+ 'relative_cwd': 'isol\'at\\e',
+ },
+ }],
+ ['OS=\"bar\"', {
+ 'variables': {},
+ }, {
+ 'variables': {},
+ }],
+ ],
+ }
+ expected = (
+ "{\n"
+ " 'variables': {\n"
+ " 'bar': [\n"
+ " 'file1',\n"
+ " 'file2',\n"
+ " ],\n"
+ " },\n"
+ " 'conditions': [\n"
+ " ['OS=\"foo\"', {\n"
+ " 'variables': {\n"
+ " 'command': [\n"
+ " 'python',\n"
+ " '-c',\n"
+ " 'print \"H\\i\'\"',\n"
+ " ],\n"
+ " 'relative_cwd': 'isol\\'at\\\\e',\n"
+ " 'read_only': True\n"
+ " 'isolate_dependency_tracked': [\n"
+ " 'file4',\n"
+ " 'file3',\n"
+ " ],\n"
+ " 'isolate_dependency_untracked': [\n"
+ " 'dir1',\n"
+ " 'dir2',\n"
+ " ],\n"
+ " },\n"
+ " }],\n"
+ " ['OS=\"bar\"', {\n"
+ " 'variables': {\n"
+ " },\n"
+ " }, {\n"
+ " 'variables': {\n"
+ " },\n"
+ " }],\n"
+ " ],\n"
+ "}\n")
+ self._test(value, expected)
+
+
+if __name__ == '__main__':
+ VERBOSE = '-v' in sys.argv
+ logging.basicConfig(level=logging.DEBUG if VERBOSE else logging.ERROR)
+ unittest.main()
diff --git a/tools/isolate/isolate_smoke_test.py b/tools/isolate/isolate_smoke_test.py
index b38207c..3ca391b 100755
--- a/tools/isolate/isolate_smoke_test.py
+++ b/tools/isolate/isolate_smoke_test.py
@@ -54,15 +54,20 @@ DEPENDENCIES = {
class CalledProcessError(subprocess.CalledProcessError):
"""Makes 2.6 version act like 2.7"""
- def __init__(self, returncode, cmd, output, cwd):
+ def __init__(self, returncode, cmd, output, stderr, cwd):
super(CalledProcessError, self).__init__(returncode, cmd)
self.output = output
+ self.stderr = stderr
self.cwd = cwd
def __str__(self):
return super(CalledProcessError, self).__str__() + (
'\n'
- 'cwd=%s\n%s') % (self.cwd, self.output)
+ 'cwd=%s\n%s\n%s\n%s') % (
+ self.cwd,
+ self.output,
+ self.stderr,
+ ' '.join(self.cmd))
def list_files_tree(directory):
@@ -120,7 +125,7 @@ class IsolateBase(unittest.TestCase):
if self.LEVEL >= isolate.STATS_ONLY:
for k, v in files.iteritems():
- if isolate.trace_inputs.get_flavor() != 'win':
+ if isolate.isolate_common.get_flavor() != 'win':
v[u'mode'] = self._fix_file_mode(k, read_only)
filestats = os.stat(os.path.join(root_dir, k))
v[u'size'] = filestats.st_size
@@ -148,7 +153,7 @@ class IsolateBase(unittest.TestCase):
self.assertEquals(expected, json.load(open(self.result, 'r')))
def _expected_saved_state(self, extra_vars):
- flavor = isolate.trace_inputs.get_flavor()
+ flavor = isolate.isolate_common.get_flavor()
expected = {
u'isolate_file': unicode(self.filename()),
u'variables': {
@@ -189,7 +194,7 @@ class IsolateBase(unittest.TestCase):
if need_output or not VERBOSE:
stdout = subprocess.PIPE
- stderr = subprocess.STDOUT
+ stderr = subprocess.PIPE
else:
cmd.extend(['-v'] * 3)
stdout = None
@@ -204,9 +209,13 @@ class IsolateBase(unittest.TestCase):
cwd=cwd,
env=env,
universal_newlines=True)
- out = p.communicate()[0]
+ out, err = p.communicate()
if p.returncode:
- raise CalledProcessError(p.returncode, cmd, out, cwd)
+ raise CalledProcessError(p.returncode, cmd, out, err, cwd)
+
+ # Do not check on Windows since a lot of spew is generated there.
+ if sys.platform != 'win32':
+ self.assertEquals('', err)
return out
def mode(self):
@@ -463,7 +472,7 @@ class Isolate_trace(IsolateBase):
@staticmethod
def _to_string(values):
buf = cStringIO.StringIO()
- isolate.trace_inputs.pretty_print(values, buf)
+ isolate.isolate_common.pretty_print(values, buf)
return buf.getvalue()
def test_fail(self):
@@ -474,22 +483,29 @@ class Isolate_trace(IsolateBase):
out = e.output
self._expect_no_tree()
self._expect_results(['fail.py'], None, None)
- expected = 'Failing'
+ # Even if it returns an error, isolate.py still prints the trace.
+ expected = self._to_string(
+ {
+ 'conditions': [
+ ['OS=="%s"' % isolate.isolate_common.get_flavor(), {
+ 'variables': {
+ isolate.isolate_common.KEY_TRACKED: [
+ 'fail.py',
+ ],
+ },
+ }],
+ ],
+ })
lines = out.strip().splitlines()
- self.assertTrue(lines.pop(0).startswith('WARNING'))
- self.assertTrue(lines.pop(0).startswith('WARNING'))
- if sys.platform == 'win32':
- # Includes spew from tracerpt.exe.
- self.assertEquals(expected, lines[0], (lines, expected))
- else:
- self.assertEquals([expected], lines)
+ self.assertEquals(expected.splitlines(), lines)
def test_missing_trailing_slash(self):
try:
self._execute('trace', 'missing_trailing_slash.isolate', [], True)
self.fail()
except subprocess.CalledProcessError, e:
- out = e.output
+ self.assertEquals('', e.output)
+ out = e.stderr
self._expect_no_tree()
self._expect_no_result()
expected = (
@@ -504,7 +520,8 @@ class Isolate_trace(IsolateBase):
self._execute('trace', 'non_existent.isolate', [], True)
self.fail()
except subprocess.CalledProcessError, e:
- out = e.output
+ self.assertEquals('', e.output)
+ out = e.stderr
self._expect_no_tree()
self._expect_no_result()
expected = (
@@ -529,19 +546,20 @@ class Isolate_trace(IsolateBase):
out = self._execute('trace', 'touch_root.isolate', [], True)
self._expect_no_tree()
self._expect_results(['touch_root.py'], None, None)
- expected = {
- 'conditions': [
- ['OS=="%s"' % isolate.trace_inputs.get_flavor(), {
- 'variables': {
- isolate.trace_inputs.KEY_TRACKED: [
- 'touch_root.py',
- '../../isolate.py',
- ],
- },
- }],
- ],
- }
- self.assertEquals(self._to_string(expected), out)
+ expected = self._to_string(
+ {
+ 'conditions': [
+ ['OS=="%s"' % isolate.isolate_common.get_flavor(), {
+ 'variables': {
+ isolate.isolate_common.KEY_TRACKED: [
+ 'touch_root.py',
+ '../../isolate.py',
+ ],
+ },
+ }],
+ ],
+ })
+ self.assertEquals(expected, out)
def test_with_flag(self):
out = self._execute(
@@ -550,12 +568,12 @@ class Isolate_trace(IsolateBase):
self._expect_results(['with_flag.py', 'trace'], None, {u'FLAG': u'trace'})
expected = {
'conditions': [
- ['OS=="%s"' % isolate.trace_inputs.get_flavor(), {
+ ['OS=="%s"' % isolate.isolate_common.get_flavor(), {
'variables': {
- isolate.trace_inputs.KEY_TRACKED: [
+ isolate.isolate_common.KEY_TRACKED: [
'with_flag.py',
],
- isolate.trace_inputs.KEY_UNTRACKED: [
+ isolate.isolate_common.KEY_UNTRACKED: [
# Note that .isolate format mandates / and not os.path.sep.
'files1/',
],
@@ -619,9 +637,9 @@ class IsolateNoOutdir(IsolateBase):
cwd=cwd,
env=env,
universal_newlines=True)
- out = p.communicate()[0]
+ out, err = p.communicate()
if p.returncode:
- raise CalledProcessError(p.returncode, cmd, out, cwd)
+ raise CalledProcessError(p.returncode, cmd, out, err, cwd)
return out
def mode(self):
@@ -655,7 +673,7 @@ class IsolateNoOutdir(IsolateBase):
def test_hashtable(self):
self._execute('hashtable', 'touch_root.isolate', [], False)
- files = [
+ files = sorted([
os.path.join(
'hashtable', calc_sha1(os.path.join(ROOT_DIR, 'isolate.py'))),
os.path.join(
@@ -667,7 +685,7 @@ class IsolateNoOutdir(IsolateBase):
os.path.join('root', 'data', 'isolate', 'touch_root.isolate'),
os.path.join('root', 'data', 'isolate', 'touch_root.py'),
os.path.join('root', 'isolate.py'),
- ]
+ ])
self.assertEquals(files, list_files_tree(self.tempdir))
def test_remap(self):
diff --git a/tools/isolate/merge_isolate.py b/tools/isolate/merge_isolate.py
index df6f92b..187556b 100755
--- a/tools/isolate/merge_isolate.py
+++ b/tools/isolate/merge_isolate.py
@@ -17,9 +17,7 @@ import optparse
import re
import sys
-import trace_inputs
-# Create shortcuts.
-from trace_inputs import KEY_TRACKED, KEY_UNTRACKED
+from isolate_common import pretty_print, KEY_TRACKED, KEY_UNTRACKED
def union(lhs, rhs):
@@ -378,7 +376,7 @@ def main(args=None):
level=level,
format='%(levelname)5s %(module)15s(%(lineno)3d):%(message)s')
- trace_inputs.pretty_print(
+ pretty_print(
convert_map_to_gyp(
*reduce_inputs(
*invert_map(
diff --git a/tools/isolate/read_trace.py b/tools/isolate/read_trace.py
index 642ca28..152d2f8 100755
--- a/tools/isolate/read_trace.py
+++ b/tools/isolate/read_trace.py
@@ -10,6 +10,7 @@ import optparse
import os
import sys
+import isolate_common
import trace_inputs
BASE_DIR = os.path.dirname(os.path.abspath(__file__))
@@ -21,8 +22,8 @@ def read_trace(logname, root_dir, cwd_dir, product_dir):
root_dir = os.path.realpath(root_dir)
api = trace_inputs.get_api()
_, _, _, _, simplified, _ = trace_inputs.load_trace(logname, root_dir, api)
- variables = trace_inputs.generate_dict(simplified, cwd_dir, product_dir)
- trace_inputs.pretty_print(variables, sys.stdout)
+ variables = isolate_common.generate_dict(simplified, cwd_dir, product_dir)
+ isolate_common.pretty_print(variables, sys.stdout)
def main():
diff --git a/tools/isolate/trace_inputs.py b/tools/isolate/trace_inputs.py
index 0b40766..8e1c11f 100755
--- a/tools/isolate/trace_inputs.py
+++ b/tools/isolate/trace_inputs.py
@@ -20,12 +20,12 @@ from the log.
import codecs
import csv
+import getpass
import glob
import json
import logging
import optparse
import os
-import posixpath
import re
import subprocess
import sys
@@ -44,9 +44,6 @@ elif sys.platform == 'darwin':
BASE_DIR = os.path.dirname(os.path.abspath(__file__))
ROOT_DIR = os.path.dirname(os.path.dirname(BASE_DIR))
-KEY_TRACKED = 'isolate_dependency_tracked'
-KEY_UNTRACKED = 'isolate_dependency_untracked'
-
class TracingFailure(Exception):
"""An exception occured during tracing."""
@@ -251,23 +248,6 @@ else: # OSes other than Windows and OSX.
return path
-def get_flavor():
- """Returns the system default flavor. Copied from gyp/pylib/gyp/common.py."""
- flavors = {
- 'cygwin': 'win',
- 'win32': 'win',
- 'darwin': 'mac',
- 'sunos5': 'solaris',
- 'freebsd7': 'freebsd',
- 'freebsd8': 'freebsd',
- }
- return flavors.get(sys.platform, 'linux')
-
-
-def isEnabledFor(level):
- return logging.getLogger().isEnabledFor(level)
-
-
def fix_python_path(cmd):
"""Returns the fixed command line to call the right python executable."""
out = cmd[:]
@@ -278,25 +258,6 @@ def fix_python_path(cmd):
return out
-def posix_relpath(path, root):
- """posix.relpath() that keeps trailing slash."""
- out = posixpath.relpath(path, root)
- if path.endswith('/'):
- out += '/'
- return out
-
-
-def cleanup_path(x):
- """Cleans up a relative path. Converts any os.path.sep to '/' on Windows."""
- if x:
- x = x.rstrip(os.path.sep).replace(os.path.sep, '/')
- if x == '.':
- x = ''
- if x:
- x += '/'
- return x
-
-
def process_quoted_arguments(text):
"""Extracts quoted arguments on a string and return the arguments as a list.
@@ -637,6 +598,8 @@ class Results(object):
"""Returns a clone with all the files outside the directory |root| removed
and converts all the path to be relative paths.
"""
+ # Resolve any symlink
+ root = os.path.realpath(root)
root = get_native_path_case(root).rstrip(os.path.sep) + os.path.sep
logging.debug('strip_root(%s)' % root)
return Results(self.process.strip_root(root))
@@ -2191,7 +2154,7 @@ class LogmanTrace(ApiBase):
stderr=subprocess.STDOUT)
@staticmethod
- def _convert_log(logname, logformat, stdout, stderr):
+ def _convert_log(logname, logformat):
"""Converts the ETL trace to text representation.
Normally, 'csv' is sufficient. If complex scripts are used (like eastern
@@ -2229,8 +2192,12 @@ class LogmanTrace(ApiBase):
raise ValueError('Unexpected log format \'%s\'' % logformat)
logging.debug('Running: %s' % cmd_convert)
# This can takes tens of minutes for large logs.
+ # Redirects all output to stderr.
subprocess.check_call(
- cmd_convert, stdin=subprocess.PIPE, stdout=stdout, stderr=stderr)
+ cmd_convert,
+ stdin=subprocess.PIPE,
+ stdout=sys.stderr,
+ stderr=sys.stderr)
@classmethod
def gen_trace(cls, cmd, cwd, logname, output):
@@ -2273,7 +2240,7 @@ class LogmanTrace(ApiBase):
cls._stop_log()
# 4. Convert the traces to text representation.
- cls._convert_log(logname, 'csv', stdout, stderr)
+ cls._convert_log(logname, 'csv')
# 5. Save metadata.
json.dump({
@@ -2338,87 +2305,20 @@ class LogmanTrace(ApiBase):
return context.to_results()
-def pretty_print(variables, stdout):
- """Outputs a gyp compatible list from the decoded variables.
-
- Similar to pprint.print() but with NIH syndrome.
- """
- # Order the dictionary keys by these keys in priority.
- ORDER = (
- 'variables', 'condition', 'command', 'relative_cwd', 'read_only',
- KEY_TRACKED, KEY_UNTRACKED)
-
- def sorting_key(x):
- """Gives priority to 'most important' keys before the others."""
- if x in ORDER:
- return str(ORDER.index(x))
- return x
-
- def loop_list(indent, items):
- for item in items:
- if isinstance(item, basestring):
- stdout.write('%s\'%s\',\n' % (indent, item))
- elif isinstance(item, dict):
- stdout.write('%s{\n' % indent)
- loop_dict(indent + ' ', item)
- stdout.write('%s},\n' % indent)
- elif isinstance(item, list):
- # A list inside a list will write the first item embedded.
- stdout.write('%s[' % indent)
- for index, i in enumerate(item):
- if isinstance(i, basestring):
- stdout.write(
- '\'%s\', ' % i.replace('\\', '\\\\').replace('\'', '\\\''))
- elif isinstance(i, dict):
- stdout.write('{\n')
- loop_dict(indent + ' ', i)
- if index != len(item) - 1:
- x = ', '
- else:
- x = ''
- stdout.write('%s}%s' % (indent, x))
- else:
- assert False
- stdout.write('],\n')
- else:
- assert False
-
- def loop_dict(indent, items):
- for key in sorted(items, key=sorting_key):
- item = items[key]
- stdout.write("%s'%s': " % (indent, key))
- if isinstance(item, dict):
- stdout.write('{\n')
- loop_dict(indent + ' ', item)
- stdout.write(indent + '},\n')
- elif isinstance(item, list):
- stdout.write('[\n')
- loop_list(indent + ' ', item)
- stdout.write(indent + '],\n')
- elif isinstance(item, basestring):
- stdout.write(
- '\'%s\',\n' % item.replace('\\', '\\\\').replace('\'', '\\\''))
- elif item in (True, False, None):
- stdout.write('%s\n' % item)
- else:
- assert False, item
-
- stdout.write('{\n')
- loop_dict(' ', variables)
- stdout.write('}\n')
-
-
def get_api():
- flavor = get_flavor()
- if flavor == 'linux':
- return Strace()
- elif flavor == 'mac':
- return Dtrace()
- elif sys.platform == 'win32':
- return LogmanTrace()
- else:
- print >> sys.stderr, 'Unsupported platform %s' % sys.platform
- sys.exit(1)
+ """Returns the correct implementation for the current OS."""
+ if sys.platform == 'cygwin':
+ raise NotImplementedError(
+ 'Not implemented for cygwin, start the script from Win32 python')
+ flavors = {
+ 'win32': LogmanTrace,
+ 'darwin': Dtrace,
+ 'sunos5': Dtrace,
+ 'freebsd7': Dtrace,
+ 'freebsd8': Dtrace,
+ }
+ # Defaults to strace.
+ return flavors.get(sys.platform, Strace)()
def get_blacklist(api):
@@ -2432,40 +2332,6 @@ def get_blacklist(api):
svn_path in f)
-def generate_dict(files, cwd_dir, product_dir):
- """Converts the list of files into a .isolate dictionary.
-
- Arguments:
- - files: list of files to generate a dictionary out of.
- - cwd_dir: directory to base all the files from, relative to root_dir.
- - product_dir: directory to replace with <(PRODUCT_DIR), relative to root_dir.
- """
- cwd_dir = cleanup_path(cwd_dir)
- product_dir = cleanup_path(product_dir)
-
- def fix(f):
- """Bases the file on the most restrictive variable."""
- logging.debug('fix(%s)' % f)
- # Important, GYP stores the files with / and not \.
- f = f.replace(os.path.sep, '/')
- if product_dir and f.startswith(product_dir):
- return '<(PRODUCT_DIR)/%s' % f[len(product_dir):]
- else:
- # cwd_dir is usually the directory containing the gyp file. It may be
- # empty if the whole directory containing the gyp file is needed.
- return posix_relpath(f, cwd_dir) or './'
-
- corrected = [fix(f) for f in files]
- tracked = [f for f in corrected if not f.endswith('/') and ' ' not in f]
- untracked = [f for f in corrected if f.endswith('/') or ' ' in f]
- variables = {}
- if tracked:
- variables[KEY_TRACKED] = tracked
- if untracked:
- variables[KEY_UNTRACKED] = untracked
- return variables
-
-
def trace(logfile, cmd, cwd, api, output):
"""Traces an executable. Returns (returncode, output) from api.
@@ -2492,130 +2358,161 @@ def load_trace(logfile, root_dir, api):
- api: a tracing api instance.
"""
results = api.parse_log(logfile, get_blacklist(api))
- results = results.strip_root(root_dir)
+ if root_dir:
+ results = results.strip_root(root_dir)
simplified = extract_directories(results.files)
return results, simplified
-def trace_inputs(logfile, cmd, root_dir, cwd_dir, product_dir, force_trace):
- """Tries to load the logs if available. If not, trace the test.
-
- Symlinks are not processed at all.
-
- Arguments:
- - logfile: Absolute path to the OS-specific trace.
- - cmd: Command list to run.
- - root_dir: Base directory where the files we care about live.
- - cwd_dir: Cwd to use to start the process, relative to the root_dir
- directory.
- - product_dir: Directory containing the executables built by the build
- process, relative to the root_dir directory. It is used to
- properly replace paths with <(PRODUCT_DIR) for gyp output.
- - force_trace: Will force to trace unconditionally even if a trace already
- exist.
- """
- logging.debug(
- 'trace_inputs(%s, %s, %s, %s, %s, %s)' % (
- logfile, cmd, root_dir, cwd_dir, product_dir, force_trace))
-
- def print_if(txt):
- if cwd_dir is None:
- print txt
-
- # It is important to have unambiguous path.
- assert os.path.isabs(root_dir), root_dir
- assert os.path.isabs(logfile), logfile
- assert not cwd_dir or not os.path.isabs(cwd_dir), cwd_dir
- assert not product_dir or not os.path.isabs(product_dir), product_dir
-
+def CMDclean(parser, args):
+ """Cleans up traces."""
+ options, args = parser.parse_args(args)
api = get_api()
- # Resolve any symlink
- root_dir = os.path.realpath(root_dir)
- if not os.path.isfile(logfile) or force_trace:
- print_if('Tracing... %s' % cmd)
- # Use the proper relative directory.
- cwd = root_dir if not cwd_dir else os.path.join(root_dir, cwd_dir)
- silent = not isEnabledFor(logging.WARNING)
- returncode, _ = trace(logfile, cmd, cwd, api, silent)
- if returncode and not force_trace:
- return returncode
-
- print_if('Loading traces... %s' % logfile)
- results, simplified = load_trace(logfile, root_dir, api)
-
- print_if('Total: %d' % len(results.files))
- print_if('Non existent: %d' % len(results.non_existent))
- for f in results.non_existent:
- print_if(' %s' % f.path)
- print_if(
- 'Interesting: %d reduced to %d' % (
- len(results.existent), len(simplified)))
- for f in simplified:
- print_if(' %s' % f.path)
-
- if cwd_dir is not None:
- value = {
- 'conditions': [
- ['OS=="%s"' % get_flavor(), {
- 'variables': generate_dict(
- [f.path for f in simplified], cwd_dir, product_dir),
- }],
- ],
- }
- pretty_print(value, sys.stdout)
+ api.clean_trace(options.log)
return 0
-def main():
- parser = optparse.OptionParser(
- usage='%prog <options> [cmd line...]')
+def CMDtrace(parser, args):
+ """Traces an executable."""
parser.allow_interspersed_args = False
parser.add_option(
- '-v', '--verbose', action='count', default=0, help='Use multiple times')
- parser.add_option('-l', '--log', help='Log file')
- parser.add_option(
- '-c', '--cwd',
- help='Signal to start the process from this relative directory. When '
- 'specified, outputs the inputs files in a way compatible for '
- 'gyp processing. Should be set to the relative path containing the '
- 'gyp file, e.g. \'chrome\' or \'net\'')
+ '-q', '--quiet', action='store_true',
+ help='Redirects traced executable output to /dev/null')
+ options, args = parser.parse_args(args)
+
+ api = get_api()
+ try:
+ return trace(options.log, args, os.getcwd(), api, options.quiet)[0]
+ except TracingFailure, e:
+ print >> sys.stderr, 'Failed to trace executable'
+ print >> sys.stderr, e
+ return 1
+
+
+def CMDread(parser, args):
+ """Reads the logs and prints the result."""
parser.add_option(
- '-p', '--product-dir', default='out/Release',
- help='Directory for PRODUCT_DIR. Default: %default')
+ '-V', '--variable',
+ nargs=2,
+ action='append',
+ dest='variables',
+ metavar='VAR_NAME directory',
+ help=('Variables to replace relative directories against. Example: '
+ '"-v HOME /home/%s" will replace all occurence of your home dir '
+ 'with <(HOME)') % getpass.getuser())
parser.add_option(
- '--root-dir', default=ROOT_DIR,
- help='Root directory to base everything off. Default: %default')
+ '--root-dir',
+ help='Root directory to base everything off it. Anything outside of this '
+ 'this directory will not be reported')
parser.add_option(
- '-f', '--force',
- action='store_true',
- default=False,
- help='Force to retrace the file')
-
- options, args = parser.parse_args()
- level = [logging.ERROR, logging.INFO, logging.DEBUG][min(2, options.verbose)]
- logging.basicConfig(
- level=level,
- format='%(levelname)5s %(module)15s(%(lineno)3d):%(message)s')
-
- if not options.log:
- parser.error('Must supply a log file with -l')
- if not args:
- if not os.path.isfile(options.log) or options.force:
- parser.error('Must supply a command to run')
- else:
- args[0] = os.path.abspath(args[0])
+ '-j', '--json', action='store_true',
+ help='Outputs raw result data as json')
+ options, args = parser.parse_args(args)
if options.root_dir:
options.root_dir = os.path.abspath(options.root_dir)
- return trace_inputs(
- os.path.abspath(options.log),
- args,
- options.root_dir,
- options.cwd,
- options.product_dir,
- options.force)
+ api = get_api()
+ try:
+ results, simplified = load_trace(options.log, options.root_dir, api)
+
+ if options.json:
+ json.dump(results.flatten(), sys.stdout)
+ else:
+ print('Total: %d' % len(results.files))
+ print('Non existent: %d' % len(results.non_existent))
+ for f in results.non_existent:
+ print(' %s' % f.path)
+ print(
+ 'Interesting: %d reduced to %d' % (
+ len(results.existent), len(simplified)))
+ for f in simplified:
+ print(' %s' % f.path)
+ return 0
+ except TracingFailure, e:
+ print >> sys.stderr, 'Failed to read trace'
+ print >> sys.stderr, e
+ return 1
+
+
+def CMDhelp(parser, args):
+ """Prints list of commands or help for a specific command"""
+ _, args = parser.parse_args(args)
+ if len(args) == 1:
+ # The command was "%prog help command", replaces ourself with
+ # "%prog command --help" so help is correctly printed out.
+ return main(args + ['--help'])
+ parser.print_help()
+ return 0
+
+
+def get_command_handler(name):
+ """Returns the command handler or CMDhelp if it doesn't exist."""
+ return getattr(sys.modules[__name__], 'CMD%s' % name, CMDhelp)
+
+
+def gen_parser(command):
+ """Returns the default OptionParser instance for the corresponding command
+ handler.
+ """
+ more = getattr(command, 'usage_more', '')
+ command_display = command_name = command.__name__[3:]
+ if command_name == 'help':
+ command_display = '<command>'
+ description = None
+ else:
+ # OptParser.description prefer nicely non-formatted strings.
+ description = re.sub('[\r\n ]{2,}', ' ', command.__doc__)
+
+ parser = optparse.OptionParser(
+ usage='usage: %%prog %s [options] %s' % (command_display, more),
+ description=description)
+
+ # Default options.
+ parser.add_option(
+ '-v', '--verbose', action='count', default=0,
+ help='Use multiple times to increase verbosity')
+ if command_name != 'help':
+ parser.add_option(
+ '-l', '--log', help='Log file to generate or read, required')
+
+ old_parser_args = parser.parse_args
+ def parse_args_with_logging(args=None):
+ """Processes the default options.
+
+ Enforces and sets options.log to an absolute path.
+ Configures logging.
+ """
+ options, args = old_parser_args(args)
+ level = [
+ logging.ERROR, logging.INFO, logging.DEBUG,
+ ][min(2, options.verbose)]
+ logging.basicConfig(
+ level=level,
+ format='%(levelname)5s %(module)15s(%(lineno)3d):%(message)s')
+ if command_name != 'help':
+ if not options.log:
+ parser.error('Must supply a log file with -l')
+ options.log = os.path.abspath(options.log)
+ return options, args
+ parser.parse_args = parse_args_with_logging
+ return parser
+
+
+def main(argv):
+ # Generates the command help late so all commands are listed.
+ CMDhelp.usage_more = (
+ '\n\nCommands are:\n' +
+ '\n'.join(
+ ' %-10s %s' % (
+ fn[3:], get_command_handler(fn[3:]).__doc__.split('\n')[0].strip())
+ for fn in dir(sys.modules[__name__])
+ if fn.startswith('CMD')))
+
+ command = get_command_handler(argv[0] if argv else None)
+ parser = gen_parser(command)
+ return command(parser, argv[1:])
if __name__ == '__main__':
- sys.exit(main())
+ sys.exit(main(sys.argv[1:]))
diff --git a/tools/isolate/trace_inputs_smoke_test.py b/tools/isolate/trace_inputs_smoke_test.py
index bfd36a1..75b7965 100755
--- a/tools/isolate/trace_inputs_smoke_test.py
+++ b/tools/isolate/trace_inputs_smoke_test.py
@@ -3,7 +3,7 @@
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
-import cStringIO
+import json
import logging
import os
import shutil
@@ -35,7 +35,47 @@ class TraceInputsBase(unittest.TestCase):
def setUp(self):
self.tempdir = tempfile.mkdtemp(prefix='trace_smoke_test')
self.log = os.path.join(self.tempdir, 'log')
- os.chdir(ROOT_DIR)
+ self.trace_inputs_path = os.path.join(ROOT_DIR, 'trace_inputs.py')
+
+ # Wraps up all the differences between OSes here.
+ # - Windows doesn't track initial_cwd.
+ # - OSX replaces /usr/bin/python with /usr/bin/python2.7.
+ self.cwd = os.path.join(ROOT_DIR, u'data')
+ self.initial_cwd = self.cwd
+ if sys.platform == 'win32':
+ # Not supported on Windows.
+ self.initial_cwd = None
+
+ # There's 3 kinds of references to python, self.executable,
+ # self.real_executable and self.naked_executable. It depends how python was
+ # started.
+ self.executable = sys.executable
+ if sys.platform == 'darwin':
+ # /usr/bin/python is a thunk executable that decides which version of
+ # python gets executed.
+ suffix = '.'.join(map(str, sys.version_info[0:2]))
+ if os.access(self.executable + suffix, os.X_OK):
+ # So it'll look like /usr/bin/python2.7
+ self.executable += suffix
+
+ import trace_inputs
+ self.real_executable = trace_inputs.get_native_path_case(
+ self.executable)
+ trace_inputs = None
+
+ if sys.platform == 'darwin':
+ # Interestingly, only OSX does resolve the symlink manually before
+ # starting the executable.
+ if os.path.islink(self.real_executable):
+ self.real_executable = os.path.normpath(
+ os.path.join(
+ os.path.dirname(self.real_executable),
+ os.readlink(self.real_executable)))
+
+ # self.naked_executable will only be naked on Windows.
+ self.naked_executable = sys.executable
+ if sys.platform == 'win32':
+ self.naked_executable = os.path.basename(sys.executable)
def tearDown(self):
if VERBOSE:
@@ -44,7 +84,8 @@ class TraceInputsBase(unittest.TestCase):
shutil.rmtree(self.tempdir)
@staticmethod
- def command(from_data):
+ def get_child_command(from_data):
+ """Returns command to run the child1.py."""
cmd = [sys.executable]
if from_data:
# When the gyp argument is specified, the command is started from --cwd
@@ -58,22 +99,22 @@ class TraceInputsBase(unittest.TestCase):
class TraceInputs(TraceInputsBase):
- def _execute(self, command):
+ def _execute(self, mode, command, cwd):
cmd = [
- sys.executable, os.path.join('..', '..', 'trace_inputs.py'),
+ sys.executable,
+ self.trace_inputs_path,
+ mode,
'--log', self.log,
- '--root-dir', ROOT_DIR,
]
if VERBOSE:
cmd.extend(['-v'] * 3)
cmd.extend(command)
- # The current directory doesn't matter, the traced process will be called
- # from the correct cwd.
- cwd = os.path.join('data', 'trace_inputs')
- # Ignore stderr.
logging.info('Command: %s' % ' '.join(cmd))
p = subprocess.Popen(
- cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, cwd=cwd,
+ cmd,
+ stdout=subprocess.PIPE,
+ stderr=subprocess.PIPE,
+ cwd=cwd,
universal_newlines=True)
out, err = p.communicate()
if VERBOSE:
@@ -82,55 +123,94 @@ class TraceInputs(TraceInputsBase):
raise CalledProcessError(p.returncode, cmd, out + err, cwd)
return out or ''
+ def _trace(self, from_data):
+ if from_data:
+ cwd = os.path.join(ROOT_DIR, 'data')
+ else:
+ cwd = ROOT_DIR
+ return self._execute('trace', self.get_child_command(from_data), cwd=cwd)
+
def test_trace(self):
- expected_end = [
+ expected = '\n'.join((
+ 'Total: 5',
+ 'Non existent: 0',
'Interesting: 5 reduced to 3',
' data/trace_inputs/'.replace('/', os.path.sep),
' trace_inputs.py',
' %s' % FILENAME,
- ]
- actual = self._execute(self.command(False)).splitlines()
- self.assertTrue(actual[0].startswith('Tracing... ['), actual)
- self.assertTrue(actual[1].startswith('Loading traces... '), actual)
- self.assertTrue(actual[2].startswith('Total: '), actual)
- if sys.platform == 'win32':
- # On windows, python searches the current path for python stdlib like
- # subprocess.py and others, I'm not sure why.
- self.assertTrue(actual[3].startswith('Non existent: '), actual[3])
- else:
- self.assertEquals('Non existent: 0', actual[3])
- # Ignore any Unexpected part.
- # TODO(maruel): Make sure there is no Unexpected part, even in the case of
- # virtualenv usage.
- self.assertEquals(expected_end, actual[-len(expected_end):])
+ )) + '\n'
+ trace_expected = '\n'.join((
+ 'child from %s' % ROOT_DIR,
+ 'child2',
+ )) + '\n'
+ trace_actual = self._trace(False)
+ actual = self._execute('read', ['--root-dir', ROOT_DIR], cwd=ROOT_DIR)
+ self.assertEquals(expected, actual)
+ self.assertEquals(trace_expected, trace_actual)
- def test_trace_gyp(self):
- import trace_inputs
- expected_value = {
- 'conditions': [
- ['OS=="%s"' % trace_inputs.get_flavor(), {
- 'variables': {
- 'isolate_dependency_tracked': [
- # It is run from data/trace_inputs.
- '../trace_inputs.py',
- '../%s' % FILENAME,
- ],
- 'isolate_dependency_untracked': [
- 'trace_inputs/',
+ @staticmethod
+ def _size(*args):
+ return os.stat(os.path.join(ROOT_DIR, *args)).st_size
+
+ def test_trace_json(self):
+ expected = {
+ u'root': {
+ u'children': [
+ {
+ u'children': [],
+ u'command': [u'python', u'child2.py'],
+ u'executable': self.naked_executable,
+ u'files': [
+ {
+ u'path': os.path.join(u'data', 'trace_inputs', 'child2.py'),
+ u'size': self._size('data', 'trace_inputs', 'child2.py'),
+ },
+ {
+ u'path': os.path.join(u'data', 'trace_inputs', 'test_file.txt'),
+ u'size': self._size('data', 'trace_inputs', 'test_file.txt'),
+ },
],
+ u'initial_cwd': self.initial_cwd,
+ #u'pid': 123,
},
- }],
- ],
+ ],
+ u'command': [
+ self.executable,
+ os.path.join(u'trace_inputs', 'child1.py'),
+ u'--child-gyp',
+ ],
+ u'executable': self.real_executable,
+ u'files': [
+ {
+ u'path': os.path.join(u'data', 'trace_inputs', 'child1.py'),
+ u'size': self._size('data', 'trace_inputs', 'child1.py'),
+ },
+ {
+ u'path': u'trace_inputs.py',
+ u'size': self._size('trace_inputs.py'),
+ },
+ {
+ u'path': u'trace_inputs_smoke_test.py',
+ u'size': self._size('trace_inputs_smoke_test.py'),
+ },
+ ],
+ u'initial_cwd': self.initial_cwd,
+ #u'pid': 123,
+ },
}
- expected_buffer = cStringIO.StringIO()
- trace_inputs.pretty_print(expected_value, expected_buffer)
-
- cmd = [
- '--cwd', 'data',
- '--product', '.', # Not tested.
- ] + self.command(True)
- actual = self._execute(cmd)
- self.assertEquals(expected_buffer.getvalue(), actual)
+ trace_expected = '\n'.join((
+ 'child_gyp from %s' % os.path.join(ROOT_DIR, 'data'),
+ 'child2',
+ )) + '\n'
+ trace_actual = self._trace(True)
+ actual_text = self._execute(
+ 'read', ['--root-dir', ROOT_DIR, '--json'], cwd=ROOT_DIR)
+ actual_json = json.loads(actual_text)
+ # Removes the pids.
+ self.assertTrue(actual_json['root'].pop('pid'))
+ self.assertTrue(actual_json['root']['children'][0].pop('pid'))
+ self.assertEquals(expected, actual_json)
+ self.assertEquals(trace_expected, trace_actual)
class TraceInputsImport(TraceInputsBase):
@@ -138,31 +218,6 @@ class TraceInputsImport(TraceInputsBase):
super(TraceInputsImport, self).setUp()
import trace_inputs
self.trace_inputs = trace_inputs
- self.cwd = os.path.join(ROOT_DIR, u'data')
- self.initial_cwd = self.cwd
- if sys.platform == 'win32':
- # Still not supported on Windows.
- self.initial_cwd = None
- self.executable = sys.executable
- if sys.platform == 'darwin':
- # /usr/bin/python is a thunk executable that decides which version of
- # python gets executed.
- suffix = '.'.join(map(str, sys.version_info[0:2]))
- if os.access(self.executable + suffix, os.X_OK):
- self.executable += suffix
- self.real_executable = self.trace_inputs.get_native_path_case(
- self.executable)
- if sys.platform == 'darwin':
- # Interestingly, only OSX does resolve the symlink manually before
- # starting the executable.
- if os.path.islink(self.real_executable):
- self.real_executable = os.path.normpath(
- os.path.join(
- os.path.dirname(self.real_executable),
- os.readlink(self.real_executable)))
- self.naked_executable = sys.executable
- if sys.platform == 'win32':
- self.naked_executable = os.path.basename(sys.executable)
def tearDown(self):
del self.trace_inputs
@@ -185,7 +240,7 @@ class TraceInputsImport(TraceInputsBase):
# Deliberately start the trace from the wrong path. Starts it from the
# directory 'data' so 'data/data/trace_inputs/child1.py' is not accessible,
# so child2.py process is not started.
- results, simplified = self._execute(self.command(False))
+ results, simplified = self._execute(self.get_child_command(False))
expected = {
'root': {
'children': [],
@@ -250,7 +305,7 @@ class TraceInputsImport(TraceInputsBase):
'initial_cwd': self.initial_cwd,
},
}
- results, simplified = self._execute(self.command(True))
+ results, simplified = self._execute(self.get_child_command(True))
actual = results.flatten()
self.assertTrue(actual['root'].pop('pid'))
self.assertTrue(actual['root']['children'][0].pop('pid'))
diff --git a/tools/isolate/trace_inputs_test.py b/tools/isolate/trace_inputs_test.py
index ccc03b2..d95be16 100755
--- a/tools/isolate/trace_inputs_test.py
+++ b/tools/isolate/trace_inputs_test.py
@@ -3,7 +3,6 @@
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
-import cStringIO
import logging
import os
import unittest
@@ -16,84 +15,6 @@ import trace_inputs
class TraceInputs(unittest.TestCase):
- def _test(self, value, expected):
- actual = cStringIO.StringIO()
- trace_inputs.pretty_print(value, actual)
- self.assertEquals(expected, actual.getvalue())
-
- def test_pretty_print_empty(self):
- self._test({}, '{\n}\n')
-
- def test_pretty_print_mid_size(self):
- value = {
- 'variables': {
- 'bar': [
- 'file1',
- 'file2',
- ],
- },
- 'conditions': [
- ['OS=\"foo\"', {
- 'variables': {
- trace_inputs.KEY_UNTRACKED: [
- 'dir1',
- 'dir2',
- ],
- trace_inputs.KEY_TRACKED: [
- 'file4',
- 'file3',
- ],
- 'command': ['python', '-c', 'print "H\\i\'"'],
- 'read_only': True,
- 'relative_cwd': 'isol\'at\\e',
- },
- }],
- ['OS=\"bar\"', {
- 'variables': {},
- }, {
- 'variables': {},
- }],
- ],
- }
- expected = (
- "{\n"
- " 'variables': {\n"
- " 'bar': [\n"
- " 'file1',\n"
- " 'file2',\n"
- " ],\n"
- " },\n"
- " 'conditions': [\n"
- " ['OS=\"foo\"', {\n"
- " 'variables': {\n"
- " 'command': [\n"
- " 'python',\n"
- " '-c',\n"
- " 'print \"H\\i\'\"',\n"
- " ],\n"
- " 'relative_cwd': 'isol\\'at\\\\e',\n"
- " 'read_only': True\n"
- " 'isolate_dependency_tracked': [\n"
- " 'file4',\n"
- " 'file3',\n"
- " ],\n"
- " 'isolate_dependency_untracked': [\n"
- " 'dir1',\n"
- " 'dir2',\n"
- " ],\n"
- " },\n"
- " }],\n"
- " ['OS=\"bar\"', {\n"
- " 'variables': {\n"
- " },\n"
- " }, {\n"
- " 'variables': {\n"
- " },\n"
- " }],\n"
- " ],\n"
- "}\n")
- self._test(value, expected)
-
def test_process_quoted_arguments(self):
test_cases = (
('"foo"', ['foo']),
@@ -110,7 +31,7 @@ def join_norm(*args):
return unicode(os.path.normpath(os.path.join(*args)))
-if trace_inputs.get_flavor() == 'linux':
+if sys.platform != 'win32':
class StraceInputs(unittest.TestCase):
# Represents the root process pid (an arbitrary number).
_ROOT_PID = 27
diff --git a/tools/isolate/trace_test_cases.py b/tools/isolate/trace_test_cases.py
index 9cf2406..791d0cd 100755
--- a/tools/isolate/trace_test_cases.py
+++ b/tools/isolate/trace_test_cases.py
@@ -18,6 +18,7 @@ import sys
import tempfile
import time
+import isolate_common
import list_test_cases
import trace_inputs
@@ -61,7 +62,7 @@ def trace_test_case(
print >> sys.stderr, '\nTracing failed for: %s' % ' '.join(cmd)
print >> sys.stderr, str(e)
if simplified:
- variables = trace_inputs.generate_dict(simplified, cwd_dir, product_dir)
+ variables = isolate_common.generate_dict(simplified, cwd_dir, product_dir)
else:
variables = {}
return {