change look-up -> look up
[scons.git] / testing / framework / TestSCons_time.py
blob6ee4b10e27f88bcdb7a28df9b6f613eda088771e
1 # MIT License
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.
24 """
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.
35 """
37 import os
38 import os.path
39 import sys
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',])
49 SConstruct = """\
50 import os
51 print("SConstruct file directory:", os.getcwd())
52 """
54 scons_py = """\
55 #!/usr/bin/env python
56 import os
57 import sys
59 def write_args(fp, args):
60 fp.write(args[0] + '\\n')
61 for arg in args[1:]:
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)
70 break
71 sys.stdout.write('SCONS_LIB_DIR = ' + os.environ['SCONS_LIB_DIR'] + '\\n')
72 with open('SConstruct', 'r') as f:
73 script = f.read()
74 exec(script)
75 """
77 svn_py = f"""#!/usr/bin/env python
78 import os
79 import sys
81 dir = sys.argv[-1]
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}''')
86 """
89 git_py = f"""#!/usr/bin/env python
90 import os
91 import sys
93 dir = sys.argv[-1]
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}''')
98 """
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
106 Object counts:
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
136 profile_py = """\
137 %(body)s
139 import profile
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
156 initializations.
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']
167 match = match_exact
168 workdir = ''
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()
178 try:
179 script_dir = os.environ['SCONS_TOOLS_DIR']
180 except KeyError:
181 pass
182 else:
183 os.chdir(script_dir)
184 if 'program' not in kw:
185 p = os.environ.get('SCONS_TIME')
186 if not p:
187 p = 'scons-time'
188 if not os.path.exists(p):
189 p = 'scons-time.py'
190 kw['program'] = 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:
199 kw['workdir'] = ''
201 super().__init__(**kw)
203 def archive_split(self, path):
204 if path[-7:] == '.tar.gz':
205 return path[:-7], path[-7:]
206 else:
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)
215 d = {
216 'profile_name' : profile_name,
217 'python_name' : python_name,
218 'call' : call,
219 'body' : body,
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
227 temporary directory.
229 import re
230 import tempfile
232 sep = re.escape(os.sep)
233 tempdir = tempfile.gettempdir()
235 try:
236 realpath = os.path.realpath
237 except AttributeError:
238 pass
239 else:
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!
243 if not IS_WINDOWS:
244 tempdir = realpath(tempdir)
246 args = (tempdir, 'scons-time-',) + args
247 x = os.path.join(*args)
248 x = re.escape(x)
249 x = x.replace('time\\-', f'time\\-[^{sep}]*')
250 return x
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)
260 return name
262 def write_fake_git_py(self, name):
263 name = self.workpath(name)
264 self.write(name, git_py)
265 os.chmod(name, 0o755)
266 return name
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):
274 os.makedirs(d)
275 with open(path, 'w') as f:
276 f.write(content)
277 return dir
279 def write_sample_tarfile(self, archive, dir, files):
280 import shutil
281 import tarfile
282 base, suffix = self.archive_split(archive)
284 mode = {
285 '.tar' : 'w',
286 '.tar.gz' : 'w:gz',
287 '.tgz' : 'w:gz',
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)
296 tarinfo.uid = 111
297 tarinfo.gid = 111
298 tarinfo.uname = 'fake_user'
299 tarinfo.gname = 'fake_group'
300 with open(path, 'rb') as f:
301 tar.addfile(tarinfo, f)
302 shutil.rmtree(dir)
303 return self.workpath(archive)
305 def write_sample_zipfile(self, archive, dir, files):
306 import shutil
307 import zipfile
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:
312 f.write(content)
313 zip.write(path)
314 shutil.rmtree(dir)
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)
324 write_sample = {
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)
331 if not dir:
332 dir = base
334 os.mkdir(dir)
335 path = write_sample(archive, dir, self.sample_project_files)
337 return path
339 # Local Variables:
340 # tab-width:4
341 # indent-tabs-mode:nil
342 # End:
343 # vim: set expandtab tabstop=4 shiftwidth=4: