source: tests/pybin/tools.py@ 5c1a531

ADT arm-eh ast-experimental cleanup-dtors enum forall-pointer-decay jacob/cs343-translation jenkins-sandbox new-ast new-ast-unique-expr pthread-emulation qualifiedEnum
Last change on this file since 5c1a531 was f806b61, checked in by tdelisle <tdelisle@…>, 6 years ago

Tests are now run in temporary directory

  • Property mode set to 100644
File size: 7.8 KB
RevLine 
[c07d724]1import __main__
2import argparse
[a45fc7b]3import contextlib
[0c13238]4import fileinput
[bacc36c]5import multiprocessing
[c07d724]6import os
7import re
[0c13238]8import resource
[bacc36c]9import signal
[c07d724]10import stat
[1bb2488]11import subprocess
[bacc36c]12import sys
[f806b61]13import tempfile
[0c13238]14import time
[5b993e0]15import types
[c07d724]16
[bacc36c]17from pybin import settings
[c07d724]18
[bacc36c]19################################################################################
20# shell helpers
21################################################################################
22
[c07d724]23# helper functions to run terminal commands
[a45fc7b]24def sh(*cmd, timeout = False, output = None, input = None, error = subprocess.STDOUT):
25 cmd = list(cmd)
[1bb2488]26
[bacc36c]27 # if this is a dry_run, only print the commands that would be ran
28 if settings.dry_run :
[f806b61]29 cmd = "{} cmd: {}".format(os.getcwd(), ' '.join(cmd))
30 if output and not isinstance(output, int):
[ea62265]31 cmd += " > "
32 cmd += output
33
[f806b61]34 if error and not isinstance(error, int):
[ea62265]35 cmd += " 2> "
36 cmd += error
37
[f806b61]38 if input and not isinstance(input, int) and os.path.isfile(input):
[ea62265]39 cmd += " < "
40 cmd += input
41
42 print(cmd)
[c07d724]43 return 0, None
[bacc36c]44
[a45fc7b]45 with contextlib.ExitStack() as onexit:
46 # add input redirection if needed
[f806b61]47 input = openfd(input, 'r', onexit, True)
[a45fc7b]48
49 # add output redirection if needed
[f806b61]50 output = openfd(output, 'w', onexit, False)
51
52 # add error redirection if needed
53 error = openfd(error, 'w', onexit, False)
[a45fc7b]54
55 # run the desired command
[2b10f95]56 try:
57 proc = subprocess.run(
58 cmd,
[a45fc7b]59 stdin =input,
60 stdout=output,
[f806b61]61 stderr=error,
[2b10f95]62 timeout=settings.timeout.single if timeout else None
63 )
64 return proc.returncode, proc.stdout.decode("utf-8") if proc.stdout else None
65 except subprocess.TimeoutExpired:
66 return 124, str(None)
[c07d724]67
[f85bc15]68def is_ascii(fname):
[202ad72]69 if settings.dry_run:
70 print("is_ascii: %s" % fname)
71 return True
72
[f85bc15]73 if not os.path.isfile(fname):
74 return False
75
[a45fc7b]76 code, out = sh("file %s" % fname, output=subprocess.PIPE)
[f85bc15]77 if code != 0:
78 return False
79
80 match = re.search(".*: (.*)", out)
81
82 if not match:
83 return False
84
[e1bdccb]85 return match.group(1).startswith("ASCII text")
[f85bc15]86
[5bf1f3e]87def is_exe(fname):
88 return os.path.isfile(fname) and os.access(fname, os.X_OK)
89
[f806b61]90def openfd(file, mode, exitstack, checkfile):
91 if not file:
92 return file
93
94 if isinstance(file, int):
95 return file
96
97 if checkfile and not os.path.isfile(file):
98 return None
99
100 file = open(file, mode)
101 exitstack.push(file)
102 return file
103
[c07d724]104# Remove 1 or more files silently
[bacc36c]105def rm( files ):
[5b993e0]106 if isinstance(files, str ): files = [ files ]
107 for file in files:
[a45fc7b]108 sh( 'rm', '-f', file, output=subprocess.DEVNULL, error=subprocess.DEVNULL )
[c07d724]109
[a95c117]110# Create 1 or more directory
111def mkdir( files ):
[5b993e0]112 if isinstance(files, str ): files = [ files ]
113 for file in files:
114 p = os.path.normpath( file )
115 d = os.path.dirname ( p )
[a45fc7b]116 sh( 'mkdir', '-p', d, output=subprocess.DEVNULL, error=subprocess.DEVNULL )
[28582b2]117
[a95c117]118
[c07d724]119def chdir( dest = __main__.__file__ ):
120 abspath = os.path.abspath(dest)
121 dname = os.path.dirname(abspath)
122 os.chdir(dname)
123
[bacc36c]124# diff two files
125def diff( lhs, rhs ):
126 # fetch return code and error from the diff command
[a45fc7b]127 return sh(
128 '''diff''',
129 '''--text''',
130 '''--old-group-format=\t\tmissing lines :\n%<''',
131 '''--new-line-format=\t\t%dn\t%L''',
132 '''--new-group-format=\t\tnew lines : \n%>''',
133 '''--old-line-format=\t\t%dn\t%L''',
134 '''--unchanged-group-format=%=''',
135 '''--changed-group-format=\t\texpected :\n%<\t\tgot :\n%>''',
136 '''--unchanged-line-format=''',
137 lhs,
138 rhs,
139 output=subprocess.PIPE
140 )
[bacc36c]141
142# call make
[a45fc7b]143def make(target, *, flags = '', output = None, error = None, error_file = None, silent = False):
144 test_param = """test="%s" """ % (error_file) if error_file else None
145 cmd = [
146 *settings.make,
147 '-s' if silent else None,
[bacc36c]148 test_param,
[575a6e5]149 settings.arch.flags,
[f3b9efc]150 settings.debug.flags,
[a5121bf]151 settings.install.flags,
[bacc36c]152 flags,
[a45fc7b]153 target
154 ]
155 cmd = [s for s in cmd if s]
156 return sh(*cmd, output=output, error=error)
[bacc36c]157
[ed45af6]158def which(program):
159 fpath, fname = os.path.split(program)
160 if fpath:
161 if is_exe(program):
162 return program
163 else:
164 for path in os.environ["PATH"].split(os.pathsep):
165 exe_file = os.path.join(path, program)
166 if is_exe(exe_file):
167 return exe_file
168
169 return None
[0c13238]170
[f806b61]171@contextlib.contextmanager
172def tempdir():
173 cwd = os.getcwd()
174 with tempfile.TemporaryDirectory() as temp:
175 os.chdir(temp)
176 try:
177 yield temp
178 finally:
179 os.chdir(cwd)
180
[bacc36c]181################################################################################
182# file handling
183################################################################################
[0c13238]184# move a file
185def mv(source, dest):
[a45fc7b]186 ret, _ = sh("mv", source, dest)
[0c13238]187 return ret
188
189# cat one file into the other
190def cat(source, dest):
[a45fc7b]191 ret, _ = sh("cat", source, output=dest)
[0c13238]192 return ret
[bacc36c]193
[c07d724]194# helper function to replace patterns in a file
195def file_replace(fname, pat, s_after):
[202ad72]196 if settings.dry_run:
197 print("replacing '%s' with '%s' in %s" % (pat, s_after, fname))
198 return
199
[f85bc15]200 file = fileinput.FileInput(fname, inplace=True, backup='.bak')
201 for line in file:
202 print(line.replace(pat, s_after), end='')
203 file.close()
[c07d724]204
[0ad0c55]205# helper function to check if a files contains only a specific string
[5bf1f3e]206def file_contains_only(file, text) :
[c07d724]207 with open(file) as f:
208 ff = f.read().strip()
209 result = ff == text.strip()
210
[5bf1f3e]211 return result
[c07d724]212
[bacc36c]213# transform path to canonical form
[5bf1f3e]214def canonical_path(path):
[f85bc15]215 abspath = os.path.abspath(__main__.__file__)
216 dname = os.path.dirname(abspath)
217 return os.path.join(dname, os.path.normpath(path) )
[c07d724]218
[bacc36c]219# compare path even if form is different
[5bf1f3e]220def path_cmp(lhs, rhs):
221 return canonical_path( lhs ) == canonical_path( rhs )
[c07d724]222
[bacc36c]223# walk all files in a path
[5bf1f3e]224def path_walk( op ):
[56de5932]225 dname = settings.SRCDIR
[5b993e0]226 for dirname, _, names in os.walk(dname):
227 for name in names:
228 path = os.path.join(dirname, name)
229 op( path )
[bacc36c]230
231################################################################################
232# system
233################################################################################
234# count number of jobs to create
[5bf1f3e]235def job_count( options, tests ):
[bacc36c]236 # check if the user already passed in a number of jobs for multi-threading
[d142ec5]237 if not options.jobs:
238 make_flags = os.environ.get('MAKEFLAGS')
239 force = bool(make_flags)
240 make_jobs_fds = re.search("--jobserver-(auth|fds)=\s*([0-9]+),([0-9]+)", make_flags) if make_flags else None
241 if make_jobs_fds :
242 tokens = os.read(int(make_jobs_fds.group(2)), 1024)
243 options.jobs = len(tokens)
244 os.write(int(make_jobs_fds.group(3)), tokens)
245 else :
246 options.jobs = multiprocessing.cpu_count()
[bacc36c]247 else :
[d142ec5]248 force = True
[bacc36c]249
250 # make sure we have a valid number of jobs that corresponds to user input
251 if options.jobs <= 0 :
252 print('ERROR: Invalid number of jobs', file=sys.stderr)
253 sys.exit(1)
254
[d142ec5]255 return min( options.jobs, len(tests) ), force
[bacc36c]256
[0c13238]257# enable core dumps for all the test children
258resource.setrlimit(resource.RLIMIT_CORE, (resource.RLIM_INFINITY, resource.RLIM_INFINITY))
259
[bacc36c]260################################################################################
261# misc
262################################################################################
263
264# check if arguments is yes or no
265def yes_no(string):
266 if string == "yes" :
267 return True
268 if string == "no" :
269 return False
270 raise argparse.ArgumentTypeError(msg)
[f3b9efc]271
[ed45af6]272def fancy_print(text):
273 column = which('column')
274 if column:
[1bb2488]275 subprocess.run(column, input=bytes(text + "\n", "UTF-8"))
[ed45af6]276 else:
277 print(text)
[0c13238]278
279
[5bf1f3e]280def core_info(path):
[f806b61]281 if not os.path.isfile(path):
282 return 1, "ERR Executable path is wrong"
283
[0c13238]284 cmd = os.path.join(settings.SRCDIR, "pybin/print-core.gdb")
285 if not os.path.isfile(cmd):
286 return 1, "ERR Printing format for core dumps not found"
287
[f806b61]288 core = os.path.join(os.getcwd(), "core" )
[0c13238]289
290 if not os.path.isfile(core):
291 return 1, "ERR No core dump"
292
[a45fc7b]293 return sh('gdb', '-n', path, core, '-batch', '-x', cmd, output=subprocess.PIPE)
[0c13238]294
295class Timed:
296 def __enter__(self):
297 self.start = time.time()
298 return self
299
300 def __exit__(self, *args):
301 self.end = time.time()
302 self.duration = self.end - self.start
Note: See TracBrowser for help on using the repository browser.