win32 test portability fixes (Anthony Roach)
[scons.git] / etc / TestSCons.py
1 """
2 TestSCons.py:  a testing framework for the SCons software construction
3 tool.
4
5 A TestSCons environment object is created via the usual invocation:
6
7     test = TestSCons()
8
9 TestScons is a subclass of TestCmd, and hence has available all of its
10 methods and attributes, as well as any overridden or additional methods
11 or attributes defined in this subclass.
12 """
13
14 # Copyright 2001, 2002 Steven Knight
15
16 __revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__"
17
18 import os
19 import os.path
20 import string
21 import sys
22
23 import TestCmd
24
25 class TestFailed(Exception):
26     def __init__(self, args=None):
27         self.args = args
28
29 class TestNoResult(Exception):
30     def __init__(self, args=None):
31         self.args = args
32
33 if os.name == 'posix':
34     def _failed(self, status = 0):
35         if self.status is None:
36             return None
37         if os.WIFSIGNALED(status):
38             return None
39         return _status(self) != status
40     def _status(self):
41         if os.WIFEXITED(self.status):
42             return os.WEXITSTATUS(self.status)
43         else:
44             return None
45 elif os.name == 'nt':
46     def _failed(self, status = 0):
47         return not self.status is None and self.status != status
48     def _status(self):
49         return self.status
50
51 class TestSCons(TestCmd.TestCmd):
52     """Class for testing SCons.
53
54     This provides a common place for initializing SCons tests,
55     eliminating the need to begin every test with the same repeated
56     initializations.
57     """
58
59     def __init__(self, **kw):
60         """Initialize an SCons testing object.
61
62         If they're not overridden by keyword arguments, this
63         initializes the object with the following default values:
64
65                 program = 'scons' if it exists,
66                           else 'scons.py'
67                 interpreter = 'python'
68                 match = TestCmd.match_exact
69                 workdir = ''
70
71         The workdir value means that, by default, a temporary workspace
72         directory is created for a TestSCons environment.  In addition,
73         this method changes directory (chdir) to the workspace directory,
74         so an explicit "chdir = '.'" on all of the run() method calls
75         is not necessary.
76         """
77         if not kw.has_key('program'):
78             kw['program'] = os.environ.get('SCONS')
79             if not kw['program']:
80                 if os.path.exists('scons'):
81                     kw['program'] = 'scons'
82                 else:
83                     kw['program'] = 'scons.py'
84         if not kw.has_key('interpreter') and not os.environ.get('SCONS_EXEC'):
85             kw['interpreter'] = sys.executable
86         if not kw.has_key('match'):
87             kw['match'] = TestCmd.match_exact
88         if not kw.has_key('workdir'):
89             kw['workdir'] = ''
90         apply(TestCmd.TestCmd.__init__, [self], kw)
91         os.chdir(self.workdir)
92
93     def run(self, options = None, arguments = None,
94                   stdout = None, stderr = '', status = 0, **kw):
95         """Runs SCons.
96
97         This is the same as the base TestCmd.run() method, with
98         the addition of:
99
100                 stdout  The expected standard output from
101                         the command.  A value of None means
102                         don't test standard output.
103
104                 stderr  The expected error output from
105                         the command.  A value of None means
106                         don't test error output.
107
108                 status  The expected exit status from the 
109                         command. 
110
111         By default, this does not test standard output (stdout = None),
112         and expects that error output is empty (stderr = "").
113         """
114         if options:
115             arguments = options + " " + arguments
116         kw['arguments'] = arguments
117         try:
118             apply(TestCmd.TestCmd.run, [self], kw)
119         except:
120             print "STDOUT ============"
121             print self.stdout()
122             print "STDERR ============"
123             print self.stderr()
124             raise
125         if _failed(self, status):
126             expect = ''
127             if status != 0:
128                 expect = " (expected %d)" % status
129             print "%s returned %d%s" % (self.program, _status(self), expect)
130             print "STDOUT ============"
131             print self.stdout()
132             print "STDERR ============"
133             print self.stderr()
134             raise TestFailed
135         if not stdout is None and not self.match(self.stdout(), stdout):
136                 print "Expected STDOUT =========="
137                 print stdout
138                 print "Actual STDOUT ============"
139                 print self.stdout()
140                 stderr = self.stderr()
141                 if stderr:
142                     print "STDERR ==================="
143                     print stderr
144                 raise TestFailed
145         if not stderr is None and not self.match(self.stderr(), stderr):
146             print "STDOUT ==================="
147             print self.stdout()
148             print "Expected STDERR =========="
149             print stderr
150             print "Actual STDERR ============"
151             print self.stderr()
152             raise TestFailed
153
154     def detect(self, var, prog):
155         """
156         Detect a program named 'prog' by first checking  the construction 
157         variable named 'var' and finally searching the path. If either method
158         fails to detect the program, then false is returned, otherwise 
159         the programs full path is returned.
160         """
161
162         import SCons.Environment
163         try:
164             return SCons.Environment.Environment()[var] == prog and self.where_is(prog)
165         except KeyError:
166             return None
167
168     def wrap_stdout(self, build_str = "", read_str = ""):
169         """Wraps standard output string(s) in the normal
170         "Reading ... done" and "Building ... done" strings
171         """
172         return "scons: Reading SConscript files ...\n" + \
173                read_str + \
174                "scons: done reading SConscript files.\n" + \
175                "scons: Building targets ...\n" + \
176                build_str + \
177                "scons: done building targets.\n"
178
179     def up_to_date(self, options = None, arguments = None, **kw):
180         s = ""
181         for arg in string.split(arguments):
182             s = s + 'scons: "%s" is up to date.\n' % arg
183             if options:
184                 arguments = options + " " + arguments
185         kw['arguments'] = arguments
186         kw['stdout'] = self.wrap_stdout(build_str = s)
187         apply(self.run, [], kw)