environment.py 6.16 KB
Newer Older
Johannes Bechberger's avatar
Johannes Bechberger committed
1
2
import logging
import os
Johannes Bechberger's avatar
Johannes Bechberger committed
3
import random
Johannes Bechberger's avatar
Johannes Bechberger committed
4
5
import shutil
import tempfile
Johannes Bechberger's avatar
Johannes Bechberger committed
6
7
from datetime import datetime
import time
Johannes Bechberger's avatar
Johannes Bechberger committed
8
from mjtest.util.shell import execute
9
from mjtest.util.utils import get_mjtest_basedir, force_colored_output
10
import mjtest.util.utils
Johannes Bechberger's avatar
Johannes Bechberger committed
11
from typing import Tuple, List
12
from preproc.preproc.preprocessor import PreProcessor, PreProcessorError, is_importable_file
Johannes Bechberger's avatar
Johannes Bechberger committed
13

14
_LOG = logging.getLogger("env")
Johannes Bechberger's avatar
Johannes Bechberger committed
15
16

class TestMode:
17
18
19

    lexer = "lexer"

Johannes Bechberger's avatar
Johannes Bechberger committed
20
21
    syntax = "syntax"

Johannes Bechberger's avatar
Johannes Bechberger committed
22
23
    ast = "ast"

Johannes Bechberger's avatar
Johannes Bechberger committed
24
25
26
27
    semantic = "semantic"

    exec = "exec"

Johannes Bechberger's avatar
Johannes Bechberger committed
28
29
30
31
    USE_TESTS_OF_OTHER = {
        ast: [syntax]
    }

Johannes Bechberger's avatar
Johannes Bechberger committed
32
""" All 'success' tests of the n.th mode can used as 'success' tests for the n-1.th mode"""
33
TEST_MODES = [TestMode.lexer, TestMode.syntax, TestMode.ast, TestMode.semantic, TestMode.exec]
Johannes Bechberger's avatar
Johannes Bechberger committed
34
35
36
37
38
39
40
41
42
43

class Environment:

    LOG_LEVELS = {
        "info": logging.INFO,
        "error": logging.ERROR,
        "warn": logging.WARN,
        "debug": logging.DEBUG
    }

44
    def __init__(self, mode, mj_run: str, tmp_dir: str = "", test_dir: str = "",
Johannes Bechberger's avatar
Johannes Bechberger committed
45
                 only_incorrect_tests: bool = False, parallel: bool = False,
46
                 timeout: int = 30, report_dir: str = "", log_level: str = "warn",
47
                 produce_no_reports: bool = True, output_no_incorrect_reports: bool = False,
48
                 produce_all_reports: bool = False, report_subdir: str = None,
49
50
51
                 ci_testing: bool = False, color: bool = False):
        if color:
            force_colored_output()
Johannes Bechberger's avatar
Johannes Bechberger committed
52
        self.mode = mode
53
        self.mj_run_cmd = os.path.realpath(mj_run)
Johannes Bechberger's avatar
Johannes Bechberger committed
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69

        if tmp_dir:
            self.own_tmp_dir = True
            self.tmp_dir = os.path.abspath(os.path.expandvars(tmp_dir))
            if not os.path.exists(tmp_dir):
                os.mkdir(self.tmp_dir)
        else:
            self.own_tmp_dir = False
            self.tmp_dir = tempfile.mkdtemp("mjtest")

        if test_dir:
            self.test_dir = os.path.abspath(os.path.realpath(test_dir))
        else:
            self.test_dir = os.path.join(get_mjtest_basedir(), "tests")
        if not os.path.exists(self.test_dir):
            os.mkdir(self.test_dir)
Johannes Bechberger's avatar
Johannes Bechberger committed
70
            for d in TEST_MODES:
Johannes Bechberger's avatar
Johannes Bechberger committed
71
72
73
74
75
                os.mkdir(os.path.join(self.test_dir, d))

        self.only_incorrect_tests = only_incorrect_tests
        self.parallel = parallel
        self.timeout = timeout
76
77
78
79
80
        if not produce_no_reports:
            if report_dir:
                self.report_dir = os.path.abspath(os.path.expandvars(report_dir))
            else:
                self.report_dir = os.path.join(get_mjtest_basedir(), "reports")
81
82
83
84
            try:
                os.mkdir(self.report_dir)
            except IOError:
                pass
85
            self.report_dir = os.path.join(self.report_dir, report_subdir or datetime.now().strftime("%d-%m-%y_%H-%M-%S"))
Johannes Bechberger's avatar
Johannes Bechberger committed
86
        else:
87
            self.report_dir = None
Johannes Bechberger's avatar
Johannes Bechberger committed
88
        logging.basicConfig(level=self.LOG_LEVELS[log_level])
89
        self.produce_reports = not produce_no_reports # type: bool
90
91
        self.output_incorrect_reports = not output_no_incorrect_reports
        self.produce_all_reports = produce_all_reports
92
        self.ci_testing = ci_testing
Johannes Bechberger's avatar
Johannes Bechberger committed
93
        self._tmp_file_ctr = 0
94
        self._already_preprocessed_files = set()
Johannes Bechberger's avatar
Johannes Bechberger committed
95
96

    def create_tmpfile(self) -> str:
Johannes Bechberger's avatar
Johannes Bechberger committed
97
98
99
        self._tmp_file_ctr += 1
        return os.path.join(self.tmp_dir, str(round(time.time() * 100000))
                            + str(random.randrange(0, 10000, 1)) + str(self._tmp_file_ctr))
100
101
102
103
104

    def create_tmpdir(self) -> str:
        dir = self.create_tmpfile()
        os.mkdir(dir)
        return dir
Johannes Bechberger's avatar
Johannes Bechberger committed
105
106
107
108
109

    def clean_up(self):
        if not self.own_tmp_dir:
            shutil.rmtree(self.tmp_dir)

Johannes Bechberger's avatar
Johannes Bechberger committed
110
    def run_mj_command(self, mode: str, *args: Tuple[str]) -> Tuple[bytes, bytes, int]:
Johannes Bechberger's avatar
Johannes Bechberger committed
111
112
113
114
115
116
        """
        Execute the MiniJava `run` script with the given arguments.

        :param args: arguments for the MiniJava `run` script
        :return: (out, err, return code)
        """
Johannes Bechberger's avatar
Johannes Bechberger committed
117
        mode_flag = {
118
            TestMode.lexer: "--lextest",
Johannes Bechberger's avatar
Johannes Bechberger committed
119
            TestMode.syntax: "--parsetest",
Johannes Bechberger's avatar
Johannes Bechberger committed
120
121
            TestMode.ast: "--print-ast",
            TestMode.semantic: "--check"
Johannes Bechberger's avatar
Johannes Bechberger committed
122
123
        }[mode]
        cmd = [self.mj_run_cmd, mode_flag] + list(args)
124
125
126
127
128
129
130
131
        return execute(cmd, timeout=self.timeout)

    def run_command(self, cmd: str, *args: Tuple[str]) -> Tuple[bytes, bytes, int]:
        """
        Execute the passend command with its arguments

        :return: (out, err, return code)
        """
132
        return execute([cmd] + list(args), timeout=self.timeout)
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

    def has_to_preprocess(self, file: str) -> bool:
        return os.path.relpath(file, self.test_dir).startswith("exec")

    def is_lib_file(self, file: str) -> bool:
        return is_importable_file(file)

    def preprocess(self, file: str) -> str:
        """
        Pre process the passed file if needed and return the resulting file
        """
        if not self.has_to_preprocess(file):
            return file
        if ".preprocessed" in os.path.relpath(file, self.test_dir):
            return file

        import_base_dir = os.path.join(self.test_dir, os.path.relpath(file, self.test_dir).lstrip(os.sep).split(os.sep)[0])
        dst_dir = os.path.join(import_base_dir, ".preprocessed", os.path.split(os.path.relpath(file, import_base_dir))[0])
        dst_file = os.path.join(dst_dir, os.path.basename(file))
        if dst_file.endswith(".mj"):
            dst_file = dst_file.replace(".mj", ".java")
        if dst_file in self._already_preprocessed_files:
            return dst_file
        self._already_preprocessed_files.add(dst_file)

158
        if os.path.exists(dst_file) and os.path.isfile(dst_file) and os.path.getmtime(file) < os.path.getmtime(dst_file) and False:
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
            _LOG.debug("File '{}' already exists in a pre processed form".format(os.path.relpath(file)))
            return dst_file
        cur = os.path.split(dst_file)[0]
        while not os.path.exists(cur):
            os.mkdir(cur)
            cur = os.path.split(cur)[0]

        if not os.path.exists(dst_dir):
            os.mkdir(dst_dir)
        try:
            PreProcessor(file, import_base_dir, dst_file).preprocess()
        except:
            _LOG.exception("Pre processing file '{}'".format(file))
            raise
        return dst_file