environment.py 6.03 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
9
10
from mjtest.util.shell import execute
from mjtest.util.utils import get_mjtest_basedir
from typing import Tuple, List
11
from preproc.preproc.preprocessor import PreProcessor, PreProcessorError, is_importable_file
Johannes Bechberger's avatar
Johannes Bechberger committed
12

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

class TestMode:
16
17
18

    lexer = "lexer"

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

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

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

    exec = "exec"

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

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

class Environment:

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

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

        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
67
            for d in TEST_MODES:
Johannes Bechberger's avatar
Johannes Bechberger committed
68
69
70
71
72
                os.mkdir(os.path.join(self.test_dir, d))

        self.only_incorrect_tests = only_incorrect_tests
        self.parallel = parallel
        self.timeout = timeout
73
74
75
76
77
        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")
78
79
80
81
            try:
                os.mkdir(self.report_dir)
            except IOError:
                pass
82
            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
83
        else:
84
            self.report_dir = None
Johannes Bechberger's avatar
Johannes Bechberger committed
85
        logging.basicConfig(level=self.LOG_LEVELS[log_level])
86
        self.produce_reports = not produce_no_reports # type: bool
87
88
        self.output_incorrect_reports = not output_no_incorrect_reports
        self.produce_all_reports = produce_all_reports
89
        self.ci_testing = ci_testing
Johannes Bechberger's avatar
Johannes Bechberger committed
90
        self._tmp_file_ctr = 0
91
        self._already_preprocessed_files = set()
Johannes Bechberger's avatar
Johannes Bechberger committed
92
93

    def create_tmpfile(self) -> str:
Johannes Bechberger's avatar
Johannes Bechberger committed
94
95
96
        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))
97
98
99
100
101

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

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

Johannes Bechberger's avatar
Johannes Bechberger committed
107
    def run_mj_command(self, mode: str, *args: Tuple[str]) -> Tuple[bytes, bytes, int]:
Johannes Bechberger's avatar
Johannes Bechberger committed
108
109
110
111
112
113
        """
        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
114
        mode_flag = {
115
            TestMode.lexer: "--lextest",
Johannes Bechberger's avatar
Johannes Bechberger committed
116
            TestMode.syntax: "--parsetest",
Johannes Bechberger's avatar
Johannes Bechberger committed
117
118
            TestMode.ast: "--print-ast",
            TestMode.semantic: "--check"
Johannes Bechberger's avatar
Johannes Bechberger committed
119
120
        }[mode]
        cmd = [self.mj_run_cmd, mode_flag] + list(args)
121
122
123
124
125
126
127
128
        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)
        """
129
        return execute([cmd] + list(args), timeout=self.timeout)
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

    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)

        if os.path.exists(dst_file) and os.path.isfile(dst_file) and os.path.getmtime(file) < os.path.getmtime(dst_file):
            _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