exec_tests.py 11 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
                test_result.incorrect_msg = "invalid java code, but output file missing"
59
                test_result.set_error_code(javac_rtcode)
60 61
                test_result.add_long_text("Javac error message", err.decode())
                test_result.add_file("Source file", self.preprocessed_file)
62
                os.chdir(cwd)
63
                return test_result
64 65 66 67
            main_class = get_main_class_name(base_filename + ".java")
            if not main_class:
                _LOG.debug("Can't find a main class, using the file name instead")
                main_class = base_filename
68
            exp_out, err, java_rtcode = \
69
                self.env.run_command("java", get_main_class_name(base_filename + ".java"))
70 71 72
            test_result.add_long_text("Java output: ", exp_out.decode())
            if javac_rtcode != 0:
                test_result.incorrect_msg = "java runtime error"
73
                test_result.set_error_code(java_rtcode)
74 75 76 77
                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
78
            exp_out = exp_out.decode()
79

80 81 82
            with open(self._prev_out_file, "w") as f:
                f.write(exp_out)
                f.flush()
83 84 85
            with open(self._prev_out_hash_file, "w") as f:
                f.write(self._hash_sum_for_file(base_filename + ".java"))
                f.flush()
86 87 88
        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()
89 90
            test_result.add_short_text("Expected output file", self._expected_output_file)
            test_result.add_long_text("Expected output", exp_out)
91
        try:
92 93 94 95 96
            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"
97
                    test_result.set_error_code(rtcode)
98 99 100 101 102 103 104
                    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
105
                test_result.set_error_code(sig.retcode)
106 107 108 109 110 111 112
                test_result.add_file("Source file", self.preprocessed_file)
                os.chdir(cwd)
                return test_result
            except:
                os.chdir(cwd)
                raise
            try:
113 114 115 116 117 118 119 120 121
                out, err, rtcode = self.env.run_command("./" + base_filename)
                if rtcode != 0:
                    test_result.incorrect_msg = "binary can't be run, non zero error code"
                    test_result.set_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
122
            except SigKill as sig:
123 124
                test_result.incorrect_msg = "binary can't be run: " + sig.name.strip()
                test_result.set_error_code(sig.retcode)
125 126 127 128 129 130
                test_result.add_file("Source file", self.preprocessed_file)
                os.chdir(cwd)
                return test_result
            except:
                os.chdir(cwd)
                raise
131
            out = out.decode()
132
            if self.type == self.MODE and self.env.mode == self.MODE:
133
                test_result.add_long_text("Output", out)
134 135
                if exp_out.strip() != out.strip():
                    test_result.incorrect_msg = "incorrect output"
136
                    test_result.has_succeeded = False
137
                    test_result.add_diff("Output diff [expected <-> actual]", exp_out, out, with_line_numbers=True)
138 139 140
                    test_result.add_file("Source file", self.preprocessed_file)
                os.chdir(cwd)
                return test_result
Johannes Bechberger's avatar
Johannes Bechberger committed
141
            return BasicTestResult(self, rtcode, out, err.decode())
142 143
        except SigKill as sig:
            os.chdir(cwd)
144
            assert False
145 146
        except:
            os.chdir(cwd)
147
            assert False
148

149 150 151 152 153
    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
154

155 156 157
    def _hash_sum_for_file(self, file: str) -> str:
        with open(file, "r") as f:
            return hashlib.sha256(f.read().encode()).hexdigest()
158

159 160 161 162 163
TestCase.TEST_CASE_CLASSES[TestMode.compile_firm].append(JavaExecTest)


class JavaInfiniteLoopTest(BasicSyntaxTest):

164 165
    FILE_ENDINGS = [".inf.java", ".inf.mj"]
    OUTPUT_FILE_ENDING = ".out"
166 167 168 169
    MODE = TestMode.compile_firm

    def __init__(self, env: Environment, type: str, file: str, preprocessed_file: str):
        super().__init__(env, type, file, preprocessed_file)
170 171
        self._output_file = self.file + self.OUTPUT_FILE_ENDING
        self._has_output_file = path.exists(self._output_file)
172 173 174 175 176 177 178 179

    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
180 181 182 183
        err = None
        out = None
        rtcode = None
        test_result = ExtensibleTestResult(self)
184
        try:
185 186 187
            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"
188
                test_result.set_error_code(rtcode)
189 190 191 192 193 194
                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)
195 196 197 198 199 200 201 202
            if rtcode != 0:
                test_result.incorrect_msg = "binary can't be run, non zero error code"
                test_result.set_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
203 204 205
        except SigKill as sig:
            if sig.retcode == signal.SIGXCPU:
                test_result.add_long_text("Output", out.decode())
206 207 208 209 210
                if self._has_output_file:
                    out = out.decode().strip()
                    exp_out = ""
                    with open(self._output_file, "r") as f:
                        exp_out = f.read().strip()
211
                    test_result.add_long_text("Expected output start", exp_out)
212
                    if not out.startswith(exp_out):
213 214 215 216 217 218 219 220 221
                        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
222
            else:
223
                test_result.incorrect_msg = "binary can't be run: " + sig.name
224
                test_result.set_error_code(sig.retcode)
225 226 227
                test_result.add_file("Source file", self.preprocessed_file)
                os.chdir(cwd)
                return test_result
228 229 230 231
        except:
            os.chdir(cwd)
            raise
        os.chdir(cwd)
232 233 234 235 236 237
        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
238 239 240


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