summaryrefslogtreecommitdiff
path: root/test/signatures/run_test.py
blob: 4cfefe05f3147803df4f999a59198d23f2d21780 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
#!/usr/bin/env python
#####################
## run_test.py
## Top contributors (to current version):
##   Andres Noetzli, Alex Ozdemir
## This file is part of the CVC4 project.
## Copyright (c) 2009-2021 by the authors listed in the file AUTHORS
## in the top-level source directory and their institutional affiliations.
## All rights reserved.  See the file COPYING in the top-level source
## directory for licensing information.
##

import argparse
import re
import os.path
import sys
import subprocess


class TestConfiguration(object):
    """Represents a test to run."""
    def __init__(self):
        """Initialized from program arguments.
        Exists with code 2 and prints usage message on invalid arguments."""
        parser = argparse.ArgumentParser(
            description='Runs `lfscc on a test file.')
        parser.add_argument('lfscc_binary')
        parser.add_argument('input')
        parser.add_argument('include_dirs', nargs='*')
        args = parser.parse_args()

        self.lfscc = args.lfscc_binary
        self.dep_graph = DepGraph(args.input, args.include_dirs)


class DepGraph(object):
    """Represents a dependency graph of LFSC input files"""
    def __init__(self, root_path, include_dirs):
        """Creates a dependency graph rooted a `root_path`.
        Computes a root-last topological sort.
        Exits with exitcode 1 on cyclic dependencies"""

        # Root of the graph
        self._r = root_path

        # Nodes (paths) that have been visited
        self._visited = set()

        # Nodes (paths) that have been ordered
        self._ordered_set = set()

        # The order of nodes (paths). Root is last.
        self._ordered_paths = []

        self.include_dirs = include_dirs

        # Start DFS topo-order
        self._visit(root_path)

    def _visit(self, p):
        """Puts the descendents of p in the order, parent-last"""
        node = TestFile(p, self.include_dirs)
        self._visited.add(p)
        for n in node.dep_paths:
            if n not in self._ordered_set:
                if n in self._visited:
                    # Our child is is an ancestor our ours!?
                    print("{} and {} are in a dependency cycle".format(p, n))
                    sys.exit(1)
                else:
                    self._visit(n)
        self._ordered_paths.append(p)
        self._ordered_set.add(p)

    def getPathsInOrder(self):
        return self._ordered_paths


class TestFile(object):
    """Represents a testable input file to LFSC"""
    def __init__(self, path, include_dirs):
        """Read the file at `path` and determine its immediate dependencies"""
        self.path = path
        self._get_config_map()
        self.deps = self.config_map['deps'].split() if (
            'deps' in self.config_map) else []
        self.dir = os.path.dirname(self.path)
        self.dep_paths = []
        include_paths = include_dirs + [self.dir]
        for dep in self.deps:
            found_dep = False
            for include_path in include_paths:
                dep_path = os.path.join(include_path, dep)
                if os.path.isfile(dep_path):
                    self.dep_paths.append(dep_path)
                    found_dep = True
                    break
            assert found_dep

    def _get_comment_lines(self):
        """Return an iterator over comment lines, ;'s included"""
        with open(self.path, 'r') as test_file:
            return (line for line in test_file.readlines() if \
                    re.match(r'^\s*;.*$', line) is not None)

    def _get_config_map(self):
        """Populate self.config_map.
        Config variables are set using the syntax
        ; Var Name Spaces Okay: space separated values"""
        m = {}
        for l in self._get_comment_lines():
            match = re.match(r'^.*;\s*(\w+(?:\s+\w+)*)\s*:(.*)$', l)
            if match is not None:
                m[match.group(1).replace(' ', '').lower()] = match.group(2)
        self.config_map = m


def main():
    configuration = TestConfiguration()
    cmd = [configuration.lfscc] + configuration.dep_graph.getPathsInOrder()
    result = subprocess.Popen(cmd,
                              stderr=subprocess.STDOUT,
                              stdout=subprocess.PIPE)
    (stdout, _) = result.communicate()
    if 0 != result.returncode:
        if stdout:
            print(stdout.decode())
    return result.returncode


if __name__ == '__main__':
    sys.exit(main())
generated by cgit on debian on lair
contact matthew@masot.net with questions or feedback