source: tests/pybin/tools.py @ 3221a2b

ADTarm-ehast-experimentalenumforall-pointer-decayjacob/cs343-translationjenkins-sandboxnew-astnew-ast-unique-exprpthread-emulationqualifiedEnum
Last change on this file since 3221a2b was dcfedca, checked in by Thierry Delisle <tdelisle@…>, 5 years ago

Test script can now archive errors (copy executables and core dumps to a specified directory)
run --help command for detail

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