File: TestToolBase.py

package info (click to toggle)
swiftlang 6.0.3-2
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 2,519,992 kB
  • sloc: cpp: 9,107,863; ansic: 2,040,022; asm: 1,135,751; python: 296,500; objc: 82,456; f90: 60,502; lisp: 34,951; pascal: 19,946; sh: 18,133; perl: 7,482; ml: 4,937; javascript: 4,117; makefile: 3,840; awk: 3,535; xml: 914; fortran: 619; cs: 573; ruby: 573
file content (171 lines) | stat: -rw-r--r-- 6,566 bytes parent folder | download | duplicates (2)
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
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
# DExTer : Debugging Experience Tester
# ~~~~~~   ~         ~~         ~   ~~
#
# Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
# See https://llvm.org/LICENSE.txt for license information.
# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
"""Base class for subtools that do build/run tests."""

import abc
from datetime import datetime
import os
import sys

from dex.builder import add_builder_tool_arguments
from dex.builder import handle_builder_tool_options
from dex.debugger.Debuggers import add_debugger_tool_arguments
from dex.debugger.Debuggers import handle_debugger_tool_options
from dex.heuristic.Heuristic import add_heuristic_tool_arguments
from dex.tools.ToolBase import ToolBase
from dex.utils import get_root_directory
from dex.utils.Exceptions import Error, ToolArgumentError
from dex.utils.ReturnCode import ReturnCode


class TestToolBase(ToolBase):
    def __init__(self, *args, **kwargs):
        super(TestToolBase, self).__init__(*args, **kwargs)
        self.build_script: str = None

    def add_tool_arguments(self, parser, defaults):
        parser.description = self.__doc__
        add_builder_tool_arguments(parser)
        add_debugger_tool_arguments(parser, self.context, defaults)
        add_heuristic_tool_arguments(parser)

        parser.add_argument(
            "test_path",
            type=str,
            metavar="<test-path>",
            nargs="?",
            default=os.path.abspath(os.path.join(get_root_directory(), "..", "tests")),
            help="directory containing test(s)",
        )

        parser.add_argument(
            "--results-directory",
            type=str,
            metavar="<directory>",
            default=None,
            help="directory to save results (default: none)",
        )

    def handle_options(self, defaults):
        options = self.context.options

        if not options.builder and (options.cflags or options.ldflags):
            self.context.logger.warning(
                "--cflags and --ldflags will be ignored when not using --builder",
                enable_prefix=True,
            )

        if options.vs_solution:
            options.vs_solution = os.path.abspath(options.vs_solution)
            if not os.path.isfile(options.vs_solution):
                raise Error(
                    '<d>could not find VS solution file</> <r>"{}"</>'.format(
                        options.vs_solution
                    )
                )
        elif options.binary:
            options.binary = os.path.abspath(options.binary)
            if not os.path.isfile(options.binary):
                raise Error(
                    '<d>could not find binary file</> <r>"{}"</>'.format(options.binary)
                )
        else:
            try:
                self.build_script = handle_builder_tool_options(self.context)
            except ToolArgumentError as e:
                raise Error(e)

        try:
            handle_debugger_tool_options(self.context, defaults)
        except ToolArgumentError as e:
            raise Error(e)

        options.test_path = os.path.abspath(options.test_path)
        options.test_path = os.path.normcase(options.test_path)
        if not os.path.isfile(options.test_path) and not os.path.isdir(
            options.test_path
        ):
            raise Error(
                '<d>could not find test path</> <r>"{}"</>'.format(options.test_path)
            )

        if options.results_directory:
            options.results_directory = os.path.abspath(options.results_directory)
            if not os.path.isdir(options.results_directory):
                try:
                    os.makedirs(options.results_directory, exist_ok=True)
                except OSError as e:
                    raise Error(
                        '<d>could not create directory</> <r>"{}"</> <y>({})</>'.format(
                            options.results_directory, e.strerror
                        )
                    )

    def go(self) -> ReturnCode:  # noqa
        options = self.context.options

        options.executable = os.path.join(
            self.context.working_directory.path, "tmp.exe"
        )

        # Test files contain dexter commands.
        options.test_files = []
        # Source files are to be compiled by the builder script and may also
        # contains dexter commands.
        options.source_files = []
        if os.path.isdir(options.test_path):
            subdirs = sorted(
                [r for r, _, f in os.walk(options.test_path) if "test.cfg" in f]
            )

            for subdir in subdirs:
                for f in os.listdir(subdir):
                    # TODO: read file extensions from the test.cfg file instead so
                    # that this isn't just limited to C and C++.
                    file_path = os.path.normcase(os.path.join(subdir, f))
                    if f.endswith(".cpp"):
                        options.source_files.append(file_path)
                    elif f.endswith(".c"):
                        options.source_files.append(file_path)
                    elif f.endswith(".dex"):
                        options.test_files.append(file_path)
                # Source files can contain dexter commands too.
                options.test_files = options.test_files + options.source_files

                self._run_test(self._get_test_name(subdir))
        else:
            # We're dealing with a direct file path to a test file. If the file is non
            # .dex, then it must be a source file.
            if not options.test_path.endswith(".dex"):
                options.source_files = [options.test_path]
            options.test_files = [options.test_path]
            self._run_test(self._get_test_name(options.test_path))

        return self._handle_results()

    @staticmethod
    def _is_current_directory(test_directory):
        return test_directory == "."

    def _get_test_name(self, test_path):
        """Get the test name from either the test file, or the sub directory
        path it's stored in.
        """
        # test names are distinguished by their relative path from the
        # specified test path.
        test_name = os.path.relpath(test_path, self.context.options.test_path)
        if self._is_current_directory(test_name):
            test_name = os.path.basename(test_path)
        return test_name

    @abc.abstractmethod
    def _run_test(self, test_dir):
        pass

    @abc.abstractmethod
    def _handle_results(self) -> ReturnCode:
        pass