exec_tests.py 10.7 KB
Newer Older
1
import hashlib
2 3 4
import logging
import os
import shutil
5
import signal
6 7 8
from os import path
from mjtest.environment import TestMode, Environment
from mjtest.test.syntax_tests import BasicSyntaxTest
9
from mjtest.test.tests import TestCase, BasicDiffTestResult, BasicTestResult, ExtensibleTestResult
10
from mjtest.util.shell import SigKill
11
from mjtest.util.utils import get_main_class_name, InsertionTimeOrderedDict
12 13 14 15 16 17 18 19

_LOG = logging.getLogger("exec_tests")

class JavaExecTest(BasicSyntaxTest):
    """
    The MiniJava compiler should behave the same as javac
    """

20 21
    FILE_ENDINGS = [".java", ".mj"]
    INVALID_FILE_ENDINGS = [".inf.java", ".inf.mj"]
22
    OUTPUT_FILE_ENDING = ".out"
23
    MODE = TestMode.compile_firm
24 25 26 27

    def __init__(self, env: Environment, type: str, file: str, preprocessed_file: str):
        super().__init__(env, type, file, preprocessed_file)
        self._expected_output_file = file + self.OUTPUT_FILE_ENDING
28 29 30 31
        prev_out_dir = path.join(path.dirname(file), ".java_output")
        if not path.exists(prev_out_dir):
            os.mkdir(prev_out_dir)
        self._prev_out_file = path.join(prev_out_dir, path.basename(self._expected_output_file))
32
        self._prev_out_hash_file = self._prev_out_file + "_hash"
33
        self._has_expected_output_file = path.exists(self._expected_output_file)
34
        if not self._has_expected_output_file:
35 36
            if path.exists(self._prev_out_file) and path.exists(self._prev_out_hash_file) \
                    and self._check_hash_sum(self.preprocessed_file, self._prev_out_hash_file):
37 38 39
                self._has_expected_output_file = True
                self._expected_output_file = self._prev_out_file
                _LOG.info("Reuse old java output file \"{}\"".format(path.relpath(self._prev_out_file)))
40 41 42 43
        self._should_succeed = True

    def run(self) -> BasicDiffTestResult:
        base_filename = path.basename(self.file).split(".")[0]
44
        tmp_dir = self.env.create_tmpdir()
45 46 47 48
        shutil.copy(self.preprocessed_file, path.join(tmp_dir, base_filename + ".java"))
        cwd = os.getcwd()
        os.chdir(tmp_dir)
        exp_out = None
49
        #print(base_filename, get_main_class_name(base_filename + ".java"))
50 51 52

        test_result = ExtensibleTestResult(self)

53
        if not self._has_expected_output_file:
54
            _, err, javac_rtcode = \
55 56 57
                self.env.run_command("javac", base_filename + ".java")
            if javac_rtcode != 0:
                _LOG.error("File \"{}\" isn't valid Java".format(self.preprocessed_file))
58 59 60 61
                test_result.incorrect_msg = "invalid java code, but output file missing"
                test_result.error_code = javac_rtcode
                test_result.add_long_text("Javac error message", err.decode())
                test_result.add_file("Source file", self.preprocessed_file)
62
                os.chdir(cwd)
63 64
                return test_result
            exp_out, err, java_rtcode = \
65
                self.env.run_command("java", get_main_class_name(base_filename + ".java"))
66 67 68 69 70 71 72 73
            test_result.add_long_text("Java output: ", exp_out.decode())
            if javac_rtcode != 0:
                test_result.incorrect_msg = "java runtime error"
                test_result.error_code = java_rtcode
                test_result.add_long_text("Java error message", err.decode())
                test_result.add_file("Source file", self.preprocessed_file)
                os.chdir(cwd)
                return test_result
74
            exp_out = exp_out.decode().strip()
75

76 77 78
            with open(self._prev_out_file, "w") as f:
                f.write(exp_out)
                f.flush()
79 80 81
            with open(self._prev_out_hash_file, "w") as f:
                f.write(self._hash_sum_for_file(base_filename + ".java"))
                f.flush()
82 83 84
        if self._has_expected_output_file and self.type == self.MODE and self.env.mode == self.MODE:
            with open(self._expected_output_file, "r") as f:
                exp_out = f.read()
85 86
            test_result.add_short_text("Expected output file", self._expected_output_file)
            test_result.add_long_text("Expected output", exp_out)
87
        try:
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
            out, err, rtcode = None, None, None
            try:
                out, err, rtcode = self.env.run_mj_command(self.MODE, base_filename + ".java")
                if rtcode != 0:
                    test_result.incorrect_msg = "file can't be compiled"
                    test_result.error_code = rtcode
                    test_result.add_long_text("Error output", err.decode())
                    test_result.add_long_text("Output", out.decode())
                    test_result.add_file("Source file", self.preprocessed_file)
                    os.chdir(cwd)
                    return test_result
            except SigKill as sig:
                test_result.incorrect_msg = "file can't be compiled: " + sig.name
                test_result.error_code = sig.retcode
                test_result.add_file("Source file", self.preprocessed_file)
                os.chdir(cwd)
                return test_result
            except:
                os.chdir(cwd)
                raise
            try:
                out, err, rtcode = self.env.run_command("./" + base_filename)
                if rtcode != 0:
                    test_result.incorrect_msg = "file can't be run"
                    test_result.error_code = rtcode
                    test_result.add_long_text("Error output", err.decode())
                    test_result.add_long_text("Output", out.decode())
                    test_result.add_file("Source file", self.preprocessed_file)
                    os.chdir(cwd)
                    return test_result
            except SigKill as sig:
                test_result.incorrect_msg = "binary can't be run: " + sig.name
                test_result.error_code = sig.retcode
                test_result.add_file("Source file", self.preprocessed_file)
                os.chdir(cwd)
                return test_result
            except:
                os.chdir(cwd)
                raise
127 128
            out = out.decode().strip()
            if self.type == self.MODE and self.env.mode == self.MODE:
129 130 131
                test_result.add_long_text("Output", out.decode())
                if exp_out.strip() != out.strip():
                    test_result.incorrect_msg = "incorrect output"
132
                    test_result.has_succeeded = False
133 134 135 136
                    test_result.add_diff("Output diff [expected <-> actual]", exp_out, out)
                    test_result.add_file("Source file", self.preprocessed_file)
                os.chdir(cwd)
                return test_result
Johannes Bechberger's avatar
Johannes Bechberger committed
137
            return BasicTestResult(self, rtcode, out, err.decode())
138 139
        except SigKill as sig:
            os.chdir(cwd)
140
            assert False
141 142
        except:
            os.chdir(cwd)
143
            assert False
144

145 146 147 148 149
    def _check_hash_sum(self, file: str, hash_sum_file: str) -> bool:
        old_hash = ""
        with open(hash_sum_file, "r") as f:
            old_hash = f.readline().strip()
        return self._hash_sum_for_file(file) == old_hash
150

151 152 153
    def _hash_sum_for_file(self, file: str) -> str:
        with open(file, "r") as f:
            return hashlib.sha256(f.read().encode()).hexdigest()
154

155 156 157 158 159
TestCase.TEST_CASE_CLASSES[TestMode.compile_firm].append(JavaExecTest)


class JavaInfiniteLoopTest(BasicSyntaxTest):

160 161
    FILE_ENDINGS = [".inf.java", ".inf.mj"]
    OUTPUT_FILE_ENDING = ".out"
162 163 164 165
    MODE = TestMode.compile_firm

    def __init__(self, env: Environment, type: str, file: str, preprocessed_file: str):
        super().__init__(env, type, file, preprocessed_file)
166 167
        self._output_file = self.file + self.OUTPUT_FILE_ENDING
        self._has_output_file = path.exists(self._output_file)
168 169 170 171 172 173 174 175

    def run(self) -> BasicTestResult:
        base_filename = path.basename(self.file).split(".")[0]
        tmp_dir = self.env.create_pid_local_tmpdir()
        shutil.copy(self.preprocessed_file, path.join(tmp_dir, base_filename + ".java"))
        cwd = os.getcwd()
        os.chdir(tmp_dir)
        timeout = 1
176 177 178 179
        err = None
        out = None
        rtcode = None
        test_result = ExtensibleTestResult(self)
180
        try:
181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201
            out, err, rtcode = self.env.run_mj_command(self.MODE, base_filename + ".java")
            if rtcode != 0:
                test_result.incorrect_msg = "file can't be compiled"
                test_result.error_code = rtcode
                test_result.add_long_text("Error output", err.decode())
                test_result.add_long_text("Output", out.decode())
                test_result.add_file("Source file", self.preprocessed_file)
                os.chdir(cwd)
                return test_result
            out, err, rtcode = self.env.run_command("./" + base_filename, timeout=timeout)
            if rtcode != 0:
                test_result.incorrect_msg = "file can't be run"
                test_result.error_code = rtcode
                test_result.add_long_text("Error output", err.decode())
                test_result.add_long_text("Output", out.decode())
                test_result.add_file("Source file", self.preprocessed_file)
                os.chdir(cwd)
                return test_result
        except SigKill as sig:
            if sig.retcode == signal.SIGXCPU:
                test_result.add_long_text("Output", out.decode())
202 203 204 205 206
                if self._has_output_file:
                    out = out.decode().strip()
                    exp_out = ""
                    with open(self._output_file, "r") as f:
                        exp_out = f.read().strip()
207
                    test_result.add_long_text("Expected output start", exp_out)
208
                    if not out.startswith(exp_out):
209 210 211 212 213 214 215 216 217
                        test_result.incorrect_msg = "incorrect output start"
                        test_result.has_succeeded = False
                    test_result.add_file("Source file", self.preprocessed_file)
                    os.chdir(cwd)
                    return test_result
                else:
                    test_result.add_file("Source file", self.preprocessed_file)
                    os.chdir(cwd)
                    return test_result
218
            else:
219 220 221 222 223
                test_result.incorrect_msg = "binary can't be run: " + sig.name
                test_result.error_code = sig.retcode
                test_result.add_file("Source file", self.preprocessed_file)
                os.chdir(cwd)
                return test_result
224 225 226 227
        except:
            os.chdir(cwd)
            raise
        os.chdir(cwd)
228 229 230 231 232 233
        test_result.incorrect_msg = "run shorter than one second"
        test_result.add_long_text("Output", out)
        test_result.has_succeeded = False
        test_result.add_file("Source file", self.preprocessed_file)
        os.chdir(cwd)
        return test_result
234 235 236


TestCase.TEST_CASE_CLASSES[TestMode.compile_firm].append(JavaInfiniteLoopTest)