aboutsummaryrefslogtreecommitdiffstats
path: root/resources/unittest_template.py
blob: 7e21b97c3c669ed8f73bce734eddf846b38ffa6f (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
import ast
import io
import os
import sys
import traceback
import unittest
from itertools import chain
from types import ModuleType, SimpleNamespace
from typing import NoReturn
from unittest import mock

### USER CODE


class RunnerTestCase(unittest.TestCase):
### UNIT CODE


# Fake file object not writing anything
DEVNULL = SimpleNamespace(write=lambda *_: None, flush=lambda *_: None)

RESULT = io.StringIO()
ORIGINAL_STDOUT = sys.stdout

sys.stdout = DEVNULL
sys.stderr = DEVNULL


def _exit_sandbox(code: int) -> NoReturn:
    """
    Codes:
    - 0: Executed with success
    - 5: Syntax error while parsing user code
    - 99: Internal error
    """
    result_content = RESULT.getvalue()

    print(
        f"{result_content}",
        file=ORIGINAL_STDOUT
    )
    sys.exit(code)


def _load_user_module() -> ModuleType:
    try:
        ast.parse(USER_CODE, "<input>")
    except SyntaxError:
        RESULT.write("".join(traceback.format_exception(*sys.exc_info(), limit=0)))
        _exit_sandbox(5)

    _module = ModuleType("module")
    exec(USER_CODE, _module.__dict__)

    return _module


def _main() -> None:
    suite = unittest.defaultTestLoader.loadTestsFromTestCase(RunnerTestCase)
    result = suite.run(unittest.TestResult())

    RESULT.write(str(int(result.wasSuccessful())))

    if not result.wasSuccessful():
        RESULT.write(
            ";".join(chain(
                (error[0]._testMethodName.lstrip("test_") for error in result.errors),
                (failure[0]._testMethodName.lstrip("test_") for failure in result.failures)
            ))
        )

    _exit_sandbox(0)


try:
    module = _load_user_module()
    _main()
except Exception:
    print("Uncaught exception:\n", file=RESULT)
    traceback.print_exc(file=RESULT)
    _exit_sandbox(99)