X-Git-Url: http://git.tremily.us/?a=blobdiff_plain;f=QMTest%2FTestSCons.py;h=d8dda8a9bc4d4bf2ab65ba395c28eb98b861efd9;hb=699ae9b4e931d17d21f6c445f6e93f3bb7c7e2b1;hp=929567d94efeae098c3d7733683a1a83a65769cd;hpb=a5b48aebc02f9bd8ee1059def7c7133d832d5789;p=scons.git diff --git a/QMTest/TestSCons.py b/QMTest/TestSCons.py index 929567d9..d8dda8a9 100644 --- a/QMTest/TestSCons.py +++ b/QMTest/TestSCons.py @@ -52,7 +52,7 @@ from TestCommon import __all__ default_version = '1.2.0' -copyright_years = '2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009' +copyright_years = '2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010' # In the checked-in source, the value of SConsVersion in the following # line must remain "__ VERSION __" (without the spaces) so the built @@ -241,6 +241,8 @@ class TestSCons(TestCommon): kw['program'] = 'scons' else: kw['program'] = 'scons.py' + elif not os.path.isabs(kw['program']): + kw['program'] = os.path.join(self.orig_cwd, kw['program']) if not kw.has_key('interpreter') and not os.environ.get('SCONS_EXEC'): kw['interpreter'] = [python, '-tt'] if not kw.has_key('match'): @@ -367,7 +369,13 @@ class TestSCons(TestCommon): sconsflags = [] if self.ignore_python_version and deprecated_python_version(): sconsflags = sconsflags + ['--warn=no-python-version'] - sconsflags = sconsflags + ['--warn=no-visual-c-missing'] + # Provide a way to suppress or provide alternate flags for + # TestSCons purposes by setting TESTSCONS_SCONSFLAGS. + # (The intended use case is to set it to null when running + # timing tests of earlier versions of SCons which don't + # support the --warn=no-visual-c-missing warning.) + sconsflags = sconsflags + [os.environ.get('TESTSCONS_SCONSFLAGS', + '--warn=no-visual-c-missing')] os.environ['SCONSFLAGS'] = string.join(sconsflags) try: result = apply(TestCommon.run, (self,)+args, kw) @@ -406,6 +414,24 @@ class TestSCons(TestCommon): kw['match'] = self.match_re_dotall apply(self.run, [], kw) + def option_not_yet_implemented(self, option, arguments=None, **kw): + """ + Verifies expected behavior for options that are not yet implemented: + a warning message, and exit status 1. + """ + msg = "Warning: the %s option is not yet implemented\n" % option + kw['stderr'] = msg + if arguments: + # If it's a long option and the argument string begins with '=', + # it's of the form --foo=bar and needs no separating space. + if option[:2] == '--' and arguments[0] == '=': + kw['arguments'] = option + arguments + else: + kw['arguments'] = option + ' ' + arguments + # TODO(1.5) + #return self.run(**kw) + return apply(self.run, (), kw) + def diff_substr(self, expect, actual, prelen=20, postlen=40): i = 0 for x, y in zip(expect, actual): @@ -649,7 +675,8 @@ import getopt import sys import string import re -cmd_opts, args = getopt.getopt(sys.argv[1:], 'io:', []) +# -w and -z are fake options used in test/QT/QTFLAGS.py +cmd_opts, args = getopt.getopt(sys.argv[1:], 'io:wz', []) output = None impl = 0 opt_string = '' @@ -657,6 +684,7 @@ for opt, arg in cmd_opts: if opt == '-o': output = open(arg, 'wb') elif opt == '-i': impl = 1 else: opt_string = opt_string + ' ' + opt +output.write("/* mymoc.py%s */\\n" % opt_string) for a in args: contents = open(a, 'rb').read() a = string.replace(a, '\\\\', '\\\\\\\\') @@ -677,6 +705,7 @@ output_arg = 0 impl_arg = 0 impl = None source = None +opt_string = '' for arg in sys.argv[1:]: if output_arg: output = open(arg, 'wb') @@ -688,11 +717,14 @@ for arg in sys.argv[1:]: output_arg = 1 elif arg == "-impl": impl_arg = 1 + elif arg[0:1] == "-": + opt_string = opt_string + ' ' + arg else: if source: sys.exit(1) source = open(arg, 'rb') sourceFile = arg +output.write("/* myuic.py%s */\\n" % opt_string) if impl: output.write( '#include "' + impl + '"\\n' ) includes = re.findall('(.*?)', source.read()) @@ -715,7 +747,7 @@ void my_qt_symbol(const char *arg); #include "../include/my_qobject.h" #include void my_qt_symbol(const char *arg) { - printf( arg ); + fputs( arg, stdout ); } """) @@ -927,6 +959,18 @@ print py_ver return [python] + string.split(string.strip(self.stdout()), '\n') + def start(self, *args, **kw): + """ + Starts SCons in the test environment. + + This method exists to tell Test{Cmd,Common} that we're going to + use standard input without forcing every .start() call in the + individual tests to do so explicitly. + """ + if not kw.has_key('stdin'): + kw['stdin'] = True + return apply(TestCommon.start, (self,) + args, kw) + def wait_for(self, fname, timeout=10.0, popen=None): """ Waits for the specified file name to exist. @@ -957,33 +1001,34 @@ print py_ver return alt_cpp_suffix -class Graph: - def __init__(self, name, units, expression, sort=None): +class Stat: + def __init__(self, name, units, expression, convert=None): + if convert is None: + convert = lambda x: x self.name = name self.units = units self.expression = re.compile(expression) - self.sort = sort - -GraphList = [ - Graph('TimeSCons-elapsed', 'seconds', - r'TimeSCons elapsed time:\s+([\d.]+)', - sort=0), - - Graph('memory-initial', 'bytes', - r'Memory before reading SConscript files:\s+(\d+)'), - Graph('memory-prebuild', 'bytes', - r'Memory before building targets:\s+(\d+)'), - Graph('memory-final', 'bytes', - r'Memory after building targets:\s+(\d+)'), - - Graph('time-sconscript', 'seconds', - r'Total SConscript file execution time:\s+([\d.]+) seconds'), - Graph('time-scons', 'seconds', - r'Total SCons execution time:\s+([\d.]+) seconds'), - Graph('time-commands', 'seconds', - r'Total command execution time:\s+([\d.]+) seconds'), - Graph('time-total', 'seconds', - r'Total build time:\s+([\d.]+) seconds'), + self.convert = convert + +StatList = [ + Stat('memory-initial', 'kbytes', + r'Memory before reading SConscript files:\s+(\d+)', + convert=lambda s: int(s) / 1024), + Stat('memory-prebuild', 'kbytes', + r'Memory before building targets:\s+(\d+)', + convert=lambda s: int(s) / 1024), + Stat('memory-final', 'kbytes', + r'Memory after building targets:\s+(\d+)', + convert=lambda s: int(s) / 1024), + + Stat('time-sconscript', 'seconds', + r'Total SConscript file execution time:\s+([\d.]+) seconds'), + Stat('time-scons', 'seconds', + r'Total SCons execution time:\s+([\d.]+) seconds'), + Stat('time-commands', 'seconds', + r'Total command execution time:\s+([\d.]+) seconds'), + Stat('time-total', 'seconds', + r'Total build time:\s+([\d.]+) seconds'), ] @@ -997,8 +1042,25 @@ class TimeSCons(TestSCons): directory containing the executing script to the temporary working directory. """ - if not kw.has_key('verbose'): + self.variables = kw.get('variables') + if self.variables is not None: + for variable, value in self.variables.items(): + value = os.environ.get(variable, value) + try: + value = int(value) + except ValueError: + try: + value = float(value) + except ValueError: + pass + self.variables[variable] = value + del kw['variables'] + + self.calibrate = os.environ.get('TIMESCONS_CALIBRATE', '0') != '0' + + if not kw.has_key('verbose') and not self.calibrate: kw['verbose'] = True + # TODO(1.5) #TestSCons.__init__(self, *args, **kw) apply(TestSCons.__init__, (self,)+args, kw) @@ -1028,13 +1090,24 @@ class TimeSCons(TestSCons): The elapsed time to execute each build is printed after it has finished. """ - # TODO(1.5) - #self.help(*args, **kw) - #self.full(*args, **kw) - #self.null(*args, **kw) - apply(self.help, args, kw) - apply(self.full, args, kw) - apply(self.null, args, kw) + if not kw.has_key('options') and self.variables: + options = [] + for variable, value in self.variables.items(): + options.append('%s=%s' % (variable, value)) + kw['options'] = ' '.join(options) + if self.calibrate: + # TODO(1.5) + #self.calibration(*args, **kw) + apply(self.calibration, args, kw) + else: + self.uptime() + # TODO(1.5) + #self.help(*args, **kw) + #self.full(*args, **kw) + #self.null(*args, **kw) + apply(self.help, args, kw) + apply(self.full, args, kw) + apply(self.null, args, kw) def trace(self, graph, name, value, units, sort=None): fmt = "TRACE: graph=%s name=%s value=%s units=%s" @@ -1045,16 +1118,40 @@ class TimeSCons(TestSCons): sys.stdout.write(line) sys.stdout.flush() - def report_traces(self, trace, input): + def report_traces(self, trace, stats): self.trace('TimeSCons-elapsed', trace, self.elapsed_time(), "seconds", sort=0) - for graph in GraphList: - m = graph.expression.search(input) + for name, args in stats.items(): + # TODO(1.5) + #self.trace(name, trace, *args) + apply(self.trace, (name, trace), args) + + def uptime(self): + try: + fp = open('/proc/loadavg') + except EnvironmentError: + pass + else: + avg1, avg5, avg15 = fp.readline().split(" ")[:3] + fp.close() + self.trace('load-average', 'average1', avg1, 'processes') + self.trace('load-average', 'average5', avg5, 'processes') + self.trace('load-average', 'average15', avg15, 'processes') + + def collect_stats(self, input): + result = {} + for stat in StatList: + m = stat.expression.search(input) if m: - self.trace(graph.name, trace, m.group(1), graph.units) + value = stat.convert(m.group(1)) + # The dict keys match the keyword= arguments + # of the trace() method above so they can be + # applied directly to that call. + result[stat.name] = {'value':value, 'units':stat.units} + return result def help(self, *args, **kw): """ @@ -1065,11 +1162,19 @@ class TimeSCons(TestSCons): "real work" is done. """ kw['options'] = kw.get('options', '') + ' --help' + # Ignore the exit status. If the --help run dies, we just + # won't report any statistics for it, but we can still execute + # the full and null builds. + kw['status'] = None # TODO(1.5) #self.run(*args, **kw) apply(self.run, args, kw) sys.stdout.write(self.stdout()) - self.report_traces('help', self.stdout()) + stats = self.collect_stats(self.stdout()) + # Delete the time-commands, since no commands are ever + # executed on the help run and it is (or should be) always 0.0. + del stats['time-commands'] + self.report_traces('help', stats) def full(self, *args, **kw): """ @@ -1079,7 +1184,29 @@ class TimeSCons(TestSCons): #self.run(*args, **kw) apply(self.run, args, kw) sys.stdout.write(self.stdout()) - self.report_traces('full', self.stdout()) + stats = self.collect_stats(self.stdout()) + self.report_traces('full', stats) + # TODO(1.5) + #self.trace('full-memory', 'initial', **stats['memory-initial']) + #self.trace('full-memory', 'prebuild', **stats['memory-prebuild']) + #self.trace('full-memory', 'final', **stats['memory-final']) + apply(self.trace, ('full-memory', 'initial'), stats['memory-initial']) + apply(self.trace, ('full-memory', 'prebuild'), stats['memory-prebuild']) + apply(self.trace, ('full-memory', 'final'), stats['memory-final']) + + def calibration(self, *args, **kw): + """ + Runs a full build of SCons, but only reports calibration + information (the variable(s) that were set for this configuration, + and the elapsed time to run. + """ + # TODO(1.5) + #self.run(*args, **kw) + apply(self.run, args, kw) + if self.variables: + for variable, value in self.variables.items(): + sys.stdout.write('VARIABLE: %s=%s\n' % (variable, value)) + sys.stdout.write('ELAPSED: %s\n' % self.elapsed_time()) def null(self, *args, **kw): """ @@ -1093,7 +1220,22 @@ class TimeSCons(TestSCons): kw['arguments'] = '.' apply(self.up_to_date, (), kw) sys.stdout.write(self.stdout()) - self.report_traces('null', self.stdout()) + stats = self.collect_stats(self.stdout()) + # time-commands should always be 0.0 on a null build, because + # no commands should be executed. Remove it from the stats + # so we don't trace it, but only if it *is* 0 so that we'll + # get some indication if a supposedly-null build actually does + # build something. + if float(stats['time-commands']['value']) == 0.0: + del stats['time-commands'] + self.report_traces('null', stats) + # TODO(1.5) + #self.trace('null-memory', 'initial', **stats['memory-initial']) + #self.trace('null-memory', 'prebuild', **stats['memory-prebuild']) + #self.trace('null-memory', 'final', **stats['memory-final']) + apply(self.trace, ('null-memory', 'initial'), stats['memory-initial']) + apply(self.trace, ('null-memory', 'prebuild'), stats['memory-prebuild']) + apply(self.trace, ('null-memory', 'final'), stats['memory-final']) def elapsed_time(self): """