#!/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 os import shutil import subprocess import sys import tempfile import unittest ROOT_DIR = os.path.dirname(os.path.abspath(__file__)) FILENAME = os.path.basename(__file__) VERBOSE = False class CalledProcessError(subprocess.CalledProcessError): """Makes 2.6 version act like 2.7""" def __init__(self, returncode, cmd, output, cwd): super(CalledProcessError, self).__init__(returncode, cmd) self.output = output self.cwd = cwd def __str__(self): return super(CalledProcessError, self).__str__() + ( '\n' 'cwd=%s\n%s') % (self.cwd, self.output) class TraceInputs(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) def tearDown(self): if VERBOSE: print 'Leaking: %s' % self.tempdir else: shutil.rmtree(self.tempdir) def _execute(self, is_gyp): cmd = [ sys.executable, os.path.join('..', '..', 'trace_inputs.py'), '--log', self.log, '--root-dir', ROOT_DIR, ] if is_gyp: cmd.extend( [ '--cwd', 'data', '--product', '.', # Not tested. ]) cmd.append(sys.executable) if is_gyp: # When the gyp argument is specified, the command is started from --cwd # directory. In this case, 'data'. cmd.extend([os.path.join('trace_inputs', 'child1.py'), '--child-gyp']) else: # When the gyp argument is not specified, the command is started from # --root-dir directory. cmd.extend([os.path.join('data', 'trace_inputs', 'child1.py'), '--child']) # 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, universal_newlines=True) out, err = p.communicate() if p.returncode: raise CalledProcessError(p.returncode, cmd, out + err, cwd) return out or '' def test_trace(self): expected_end = [ 'Interesting: 5 reduced to 3', ' data/trace_inputs/'.replace('/', os.path.sep), ' trace_inputs.py', ' %s' % FILENAME, ] actual = self._execute(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):]) 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/', ], }, }], ], } expected_buffer = cStringIO.StringIO() trace_inputs.pretty_print(expected_value, expected_buffer) actual = self._execute(True) self.assertEquals(expected_buffer.getvalue(), actual) if __name__ == '__main__': VERBOSE = '-v' in sys.argv logging.basicConfig(level=logging.DEBUG if VERBOSE else logging.ERROR) unittest.main()