3 # Copyright The SCons Foundation
5 # Permission is hereby granted, free of charge, to any person obtaining
6 # a copy of this software and associated documentation files (the
7 # "Software"), to deal in the Software without restriction, including
8 # without limitation the rights to use, copy, modify, merge, publish,
9 # distribute, sublicense, and/or sell copies of the Software, and to
10 # permit persons to whom the Software is furnished to do so, subject to
11 # the following conditions:
13 # The above copyright notice and this permission notice shall be included
14 # in all copies or substantial portions of the Software.
16 # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
17 # KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
18 # WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19 # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20 # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21 # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22 # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
25 A testing framework for the scons-time.py script
27 A TestSCons_time environment object is created via the usual invocation:
29 test = TestSCons_time()
31 TestSCons_time is a subclass of TestCommon, which is in turn is a subclass
32 of TestCmd), and hence has available all of the methods and attributes
33 from those classes, as well as any overridden or additional methods or
34 attributes defined in this subclass.
41 from TestCommon
import *
42 from TestCommon
import __all__
43 from TestCmd
import IS_WINDOWS
44 # some of the scons_time tests may need regex-based matching:
45 from TestSCons
import search_re
, search_re_in_list
47 __all__
.extend(['TestSCons_time',])
51 print("SConstruct file directory:", os.getcwd())
59 def write_args(fp, args):
60 fp.write(args[0] + '\\n')
62 fp.write(' ' + arg + '\\n')
64 write_args(sys.stdout, sys.argv)
65 for arg in sys.argv[1:]:
66 if arg[:10] == '--profile=':
67 with open(arg[10:], 'w') as profile:
68 profile.write('--profile\\n')
69 write_args(profile, sys.argv)
71 sys.stdout.write('SCONS_LIB_DIR = ' + os.environ['SCONS_LIB_DIR'] + '\\n')
72 with open('SConstruct', 'r') as f:
77 svn_py
= f
"""#!/usr/bin/env python
82 script_dir = dir + '/scripts'
83 os.makedirs(script_dir)
84 with open(script_dir + '/scons.py', 'w') as f:
85 f.write(r'''{scons_py}''')
89 git_py
= f
"""#!/usr/bin/env python
94 script_dir = dir + '/scripts'
95 os.makedirs(script_dir)
96 with open(script_dir + '/scons.py', 'w') as f:
97 f.write(r'''{scons_py}''')
101 logfile_contents
= """\
102 Memory before reading SConscript files: 100%(index)s
103 Memory after reading SConscript files: 200%(index)s
104 Memory before building targets: 300%(index)s
105 Memory after building targets: 400%(index)s
107 pre- post- pre- post-
108 read read build build Class
109 101%(index)s 102%(index)s 103%(index)s 104%(index)s Action.CommandAction
110 201%(index)s 202%(index)s 203%(index)s 204%(index)s Action.CommandGeneratorAction
111 301%(index)s 302%(index)s 303%(index)s 304%(index)s Action.FunctionAction
112 401%(index)s 402%(index)s 403%(index)s 404%(index)s Action.LazyAction
113 501%(index)s 502%(index)s 503%(index)s 504%(index)s Action.ListAction
114 601%(index)s 602%(index)s 603%(index)s 604%(index)s Builder.BuilderBase
115 701%(index)s 702%(index)s 703%(index)s 704%(index)s Builder.CompositeBuilder
116 801%(index)s 802%(index)s 803%(index)s 804%(index)s Builder.ListBuilder
117 901%(index)s 902%(index)s 903%(index)s 904%(index)s Builder.MultiStepBuilder
118 1001%(index)s 1002%(index)s 1003%(index)s 1004%(index)s Builder.OverrideWarner
119 1101%(index)s 1102%(index)s 1103%(index)s 1104%(index)s Environment.Base
120 1201%(index)s 1202%(index)s 1203%(index)s 1204%(index)s Environment.EnvironmentClone
121 1301%(index)s 1302%(index)s 1303%(index)s 1304%(index)s Environment.OverrideEnvironment
122 1401%(index)s 1402%(index)s 1403%(index)s 1404%(index)s Executor.Executor
123 1501%(index)s 1502%(index)s 1503%(index)s 1504%(index)s Node.FS
124 1601%(index)s 1602%(index)s 1603%(index)s 1604%(index)s Node.FS.Base
125 1701%(index)s 1702%(index)s 1703%(index)s 1704%(index)s Node.FS.Dir
126 1801%(index)s 1802%(index)s 1803%(index)s 1804%(index)s Node.FS.File
127 1901%(index)s 1902%(index)s 1904%(index)s 1904%(index)s Node.FS.RootDir
128 2001%(index)s 2002%(index)s 2003%(index)s 2004%(index)s Node.Node
129 Total build time: 11.123456 seconds
130 Total SConscript file execution time: 22.234567 seconds
131 Total SCons execution time: 33.345678 seconds
132 Total command execution time: 44.456789 seconds
141 try: dispatch = profile.Profile.dispatch
142 except AttributeError: pass
143 else: dispatch['c_exception'] = profile.Profile.trace_dispatch_return
145 prof = profile.Profile()
146 prof.runcall(%(call)s)
147 prof.dump_stats(r'%(profile_name)s')
151 class TestSCons_time(TestCommon
):
152 """Class for testing the scons-time script.
154 This provides a common place for initializing scons-time tests,
155 eliminating the need to begin every test with the same repeated
159 def __init__(self
, **kw
) -> None:
160 """Initialize an SCons_time testing object.
162 If they're not overridden by keyword arguments, this
163 initializes the object with the following default values:
165 program = 'scons-time'
166 interpreter = ['python', '-tt']
170 The workdir value means that, by default, a temporary workspace
171 directory is created for a TestSCons_time environment.
172 In addition, this method changes directory (chdir) to the
173 workspace directory, so an explicit "chdir = '.'" on all of the
174 run() method calls is not necessary.
177 self
.orig_cwd
= os
.getcwd()
179 script_dir
= os
.environ
['SCONS_TOOLS_DIR']
184 if 'program' not in kw
:
185 p
= os
.environ
.get('SCONS_TIME')
188 if not os
.path
.exists(p
):
192 if 'interpreter' not in kw
:
193 kw
['interpreter'] = [python
,]
195 if 'match' not in kw
:
196 kw
['match'] = match_exact
198 if 'workdir' not in kw
:
201 super().__init
__(**kw
)
203 def archive_split(self
, path
):
204 if path
[-7:] == '.tar.gz':
205 return path
[:-7], path
[-7:]
207 return os
.path
.splitext(path
)
209 def fake_logfile(self
, logfile_name
, index
: int=0) -> None:
210 self
.write(self
.workpath(logfile_name
), logfile_contents
% locals())
212 def profile_data(self
, profile_name
, python_name
, call
, body
) -> None:
213 profile_name
= self
.workpath(profile_name
)
214 python_name
= self
.workpath(python_name
)
216 'profile_name' : profile_name
,
217 'python_name' : python_name
,
221 self
.write(python_name
, profile_py
% d
)
222 self
.run(program
= python_name
, interpreter
= sys
.executable
)
224 def tempdir_re(self
, *args
):
226 Returns a regular expression to match a scons-time
232 sep
= re
.escape(os
.sep
)
233 tempdir
= tempfile
.gettempdir()
236 realpath
= os
.path
.realpath
237 except AttributeError:
240 # Don't realpath on Windows, tempdir could contain 8+3 path
241 # E.g. username on GitHub runner is "runneradmin" -> "RUNNER~1"
242 # We don't want to convert that back!
244 tempdir
= realpath(tempdir
)
246 args
= (tempdir
, 'scons-time-',) + args
247 x
= os
.path
.join(*args
)
249 x
= x
.replace('time\\-', f
'time\\-[^{sep}]*')
252 def write_fake_scons_py(self
) -> None:
253 self
.subdir('scripts')
254 self
.write('scripts/scons.py', scons_py
)
256 def write_fake_svn_py(self
, name
):
257 name
= self
.workpath(name
)
258 self
.write(name
, svn_py
)
259 os
.chmod(name
, 0o755)
262 def write_fake_git_py(self
, name
):
263 name
= self
.workpath(name
)
264 self
.write(name
, git_py
)
265 os
.chmod(name
, 0o755)
268 def write_sample_directory(self
, archive
, dir, files
):
269 dir = self
.workpath(dir)
270 for name
, content
in files
:
271 path
= os
.path
.join(dir, name
)
272 d
, f
= os
.path
.split(path
)
273 if not os
.path
.isdir(d
):
275 with
open(path
, 'w') as f
:
279 def write_sample_tarfile(self
, archive
, dir, files
):
282 base
, suffix
= self
.archive_split(archive
)
290 with tarfile
.open(archive
, mode
[suffix
]) as tar
:
291 for name
, content
in files
:
292 path
= os
.path
.join(dir, name
)
293 with
open(path
, 'wb') as f
:
294 f
.write(bytearray(content
,'utf-8'))
295 tarinfo
= tar
.gettarinfo(path
, path
)
298 tarinfo
.uname
= 'fake_user'
299 tarinfo
.gname
= 'fake_group'
300 with
open(path
, 'rb') as f
:
301 tar
.addfile(tarinfo
, f
)
303 return self
.workpath(archive
)
305 def write_sample_zipfile(self
, archive
, dir, files
):
308 with zipfile
.ZipFile(archive
, 'w') as zip:
309 for name
, content
in files
:
310 path
= os
.path
.join(dir, name
)
311 with
open(path
, 'w') as f
:
315 return self
.workpath(archive
)
317 sample_project_files
= [
318 ('SConstruct', SConstruct
),
321 def write_sample_project(self
, archive
, dir=None):
322 base
, suffix
= self
.archive_split(archive
)
325 '.tar' : self
.write_sample_tarfile
,
326 '.tar.gz' : self
.write_sample_tarfile
,
327 '.tgz' : self
.write_sample_tarfile
,
328 '.zip' : self
.write_sample_zipfile
,
329 }.get(suffix
, self
.write_sample_directory
)
335 path
= write_sample(archive
, dir, self
.sample_project_files
)
341 # indent-tabs-mode:nil
343 # vim: set expandtab tabstop=4 shiftwidth=4: