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