Subversion Repositories Kolibri OS

Rev

Rev 9409 | Rev 9414 | Go to most recent revision | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
9249 Boppan 1
#!/usr/bin/python3
9312 Boppan 2
# Copyright 2021 Magomed Kostoev
9249 Boppan 3
# Published under MIT License
4
 
5
import os
6
import sys
9314 Boppan 7
import urllib
9249 Boppan 8
from importlib.machinery import SourceFileLoader
9310 Boppan 9
from shutil import which
9249 Boppan 10
import timeit
11
import urllib.request
12
import subprocess
9322 Boppan 13
from threading import Thread
9340 Boppan 14
import filecmp
9249 Boppan 15
 
16
sys.path.append('test')
17
import common
18
 
9397 Boppan 19
use_umka = False
20
 
9249 Boppan 21
 
9409 Boppan 22
def log(s, end="\n"):
23
    print(s, end=end, flush=True)
24
 
25
 
26
def execute(s, mute=False):
9249 Boppan 27
    mute = ">/dev/null" if mute else ""
28
    code = os.system(f"{s}{mute}")
29
    if code:
30
        print(f"Command returned {code}: \"{s}\"")
31
        exit(-1)
32
 
9409 Boppan 33
 
34
def stage(name, command, mute=False):
35
    print(f"{name}... ", end="")
36
    execute(command, mute=mute)
9249 Boppan 37
    print("Done.")
38
 
9409 Boppan 39
 
9314 Boppan 40
def download(link, path):
9409 Boppan 41
    log(f"Downloading {path}... ", end="")
9315 Boppan 42
    urllib.request.urlretrieve(link, path)
9314 Boppan 43
    log("Done.")
44
 
9409 Boppan 45
 
9310 Boppan 46
def tool_exists(name):
47
    assert(type(name) == str)
9409 Boppan 48
    return which(name) is not None
9310 Boppan 49
 
9409 Boppan 50
 
9310 Boppan 51
def check_tools(tools):
52
    assert(type(tools) == tuple)
53
    for name_package_pair in tools:
54
        assert(type(name_package_pair) == tuple)
55
        assert(len(name_package_pair) == 2)
56
        assert(type(name_package_pair[0]) == str)
57
        assert(type(name_package_pair[1]) == str)
9409 Boppan 58
 
9310 Boppan 59
    not_exists = []
60
    for name, package in tools:
61
        if not tool_exists(name):
62
            not_exists.append((name, package))
63
    if len(not_exists) != 0:
64
        log("Sorry, I can't find some tools:")
9311 Boppan 65
 
66
        header_name = "Name"
67
        header_package = "Package (probably)"
68
 
69
        max_name_len = len(header_name)
70
        max_package_name_len = len(header_package)
9310 Boppan 71
        for name, package in not_exists:
72
            if len(package) > max_package_name_len:
73
                max_package_name_len = len(package)
74
            if len(name) > max_name_len:
75
                max_name_len = len(name)
76
 
77
        def draw_row(name, package):
9409 Boppan 78
            log((f" | {name.ljust(max_name_len)}" +
79
                 f" | {package.ljust(max_package_name_len)} |"))
9310 Boppan 80
 
81
        def draw_line():
82
            log(f" +-{'-' * max_name_len}-+-{'-' * max_package_name_len}-+")
83
 
84
        draw_line()
9311 Boppan 85
        draw_row(header_name, header_package)
9310 Boppan 86
        draw_line()
87
        for name, package in not_exists:
88
            draw_row(name, package)
89
        draw_line()
90
        exit()
91
 
9409 Boppan 92
 
9321 Boppan 93
def prepare_test_img():
94
    # TODO: Always recompile the kernel (after build system is done?)
9313 Boppan 95
    # Get IMG
96
    if not os.path.exists("kolibri_test.img"):
97
        if len(sys.argv) == 1:
9409 Boppan 98
            download("http://builds.kolibrios.org/eng/data/data/kolibri.img",
99
                     "kolibri_test.img")
9313 Boppan 100
        else:
101
            builds_eng = sys.argv[1]
102
            execute(f"cp {builds_eng}/data/data/kolibri.img kolibri_test.img")
9409 Boppan 103
 
9316 Boppan 104
    # Open the IMG
105
    with open("kolibri_test.img", "rb") as img:
106
        img_data = img.read()
107
    img = common.Floppy(img_data)
9321 Boppan 108
 
9316 Boppan 109
    # Remove unuseful folders
110
    img.delete_path("GAMES")
111
    img.delete_path("DEMOS")
112
    img.delete_path("3D")
9409 Boppan 113
 
9313 Boppan 114
    # Get test kernel
115
    if not os.path.exists("kernel.mnt.pretest"):
116
        if len(sys.argv) == 1:
117
            with open("lang.inc", "w") as lang_inc:
118
                lang_inc.write("lang fix en\n")
119
            execute("fasm bootbios.asm bootbios.bin.pretest -dpretest_build=1")
9409 Boppan 120
            command = "fasm "
121
            command += "-dpretest_build=1 -ddebug_com_base=0xe9 "
122
            command += "-m 65536 "
123
            command += "kernel.asm kernel.mnt.pretest"
124
            execute(command)
9313 Boppan 125
        else:
126
            builds_eng = sys.argv[1]
9409 Boppan 127
            kernel_mnt_pretest_subpath = "data/kernel/trunk/kernel.mnt.pretest"
128
            kernel_mnt_pretest = f"{builds_eng}/{kernel_mnt_pretest_subpath}"
129
            execute(f"cp {kernel_mnt_pretest} kernel.mnt.pretest", mute=True)
130
 
9313 Boppan 131
    # Put the kernel into IMG
9316 Boppan 132
    with open("kernel.mnt.pretest", "rb") as kernel_mnt_pretest:
133
        kernel_mnt_pretest_data = kernel_mnt_pretest.read()
9317 Boppan 134
    img.add_file_path("KERNEL.MNT", kernel_mnt_pretest_data)
9316 Boppan 135
    img.save("kolibri_test.img")
9321 Boppan 136
 
9409 Boppan 137
 
9321 Boppan 138
def collect_tests():
139
    tests = []
140
 
9313 Boppan 141
    # Collect tests from test folder (not recursively yet)
142
    for test_folder in os.listdir("test"):
143
        test_folder_path = f"test/{test_folder}"
144
        test_file = f"{test_folder_path}/test.py"
9321 Boppan 145
 
9313 Boppan 146
        if not os.path.isdir(test_folder_path):
147
            continue
9321 Boppan 148
 
9313 Boppan 149
        if os.path.exists(test_file):
150
            tests.append(test_folder_path)
9321 Boppan 151
    return tests
152
 
9409 Boppan 153
 
9323 Boppan 154
def run_tests_serially_thread(test, root_dir):
9313 Boppan 155
    test_number = 1
156
    for test in tests:
157
        test_dir = f"{root_dir}/{test}"
9409 Boppan 158
 
159
        print(f"[{test_number}/{len(tests)}] {test}... ", end="", flush=True)
9313 Boppan 160
        start = timeit.default_timer()
161
        try:
9409 Boppan 162
            loader = SourceFileLoader("test", f"{test_dir}/test.py")
163
            loader.load_module().run(root_dir, test_dir)
9313 Boppan 164
        except common.TestTimeoutException:
165
            result = "TIMEOUT"
166
        except common.TestFailureException:
167
            result = "FAILURE"
168
        else:
169
            result = "SUCCESS"
170
        finish = timeit.default_timer()
171
        print(f"{result} ({finish - start:.2f} seconds)")
9409 Boppan 172
 
9313 Boppan 173
        test_number += 1
9322 Boppan 174
 
9409 Boppan 175
 
9323 Boppan 176
def run_tests_serially(tests, root_dir):
9409 Boppan 177
    thread = Thread(target=run_tests_serially_thread, args=(tests, root_dir))
9323 Boppan 178
    thread.start()
179
    return thread
180
 
9409 Boppan 181
 
9397 Boppan 182
def build_umka_asm(object_output_dir):
183
    umka_o = f"{object_output_dir}/umka.o"
9409 Boppan 184
    kolibri_kernel_trunk_runtests_py = os.path.abspath(__file__)
185
    kolibri_kernel_trunk = os.path.dirname(kolibri_kernel_trunk_runtests_py)
186
    kolibri_kernel = os.path.dirname(kolibri_kernel_trunk)
187
    kolibrios_folder = os.path.dirname(kolibri_kernel)
9397 Boppan 188
    env = os.environ
9409 Boppan 189
    libcrash = "programs/develop/libraries/libcrash/hash"
9397 Boppan 190
    env["INCLUDE"] = ""
191
    env["INCLUDE"] += f"{kolibrios_folder}/kernel/trunk;"
9409 Boppan 192
    env["INCLUDE"] += f"{kolibrios_folder}/{libcrash}"
193
    command = "fasm "
9412 Boppan 194
    command += "-dWIN32=1 " if sys.platform == "win32" else ""
9409 Boppan 195
    command += "-dUEFI=1 -dextended_primary_loader=1 -dUMKA=1 "
196
    command += "umka/umka.asm umka/build/umka.o -s umka/build/umka.fas "
197
    command += "-m 2000000 "
9412 Boppan 198
    print(command)
9409 Boppan 199
    stdout = subprocess.check_output(command, shell=True, env=env)
9412 Boppan 200
    print(stdout.decode("ascii"))
9397 Boppan 201
    return umka_o
202
 
9409 Boppan 203
 
9397 Boppan 204
def cc(src, obj, include_path):
205
    command = "clang "
9409 Boppan 206
    command += "-Wno-everything -std=c11 -g -O0 -fno-pie -m32 -masm=intel -c "
207
    command += "-D_FILE_OFFSET_BITS=64 -DNDEBUG -D_POSIX_C_SOURCE=200809L "
9397 Boppan 208
    command += f"-I {include_path} {src} -o {obj}"
9412 Boppan 209
    print(command)
9397 Boppan 210
    if os.system(command) != 0:
211
        exit()
212
 
9409 Boppan 213
 
9397 Boppan 214
def link(objects):
9412 Boppan 215
    if sys.platform == "linux" or sys.platform == "linux2":
216
        linker_script = "-T umka/umka.ld"
217
    else:
218
        linker_script = "-Wl,/ALIGN:65536 -Wl,/MAP:umka.map "
9397 Boppan 219
    command = "clang "
9409 Boppan 220
    command += "-Wno-everything "
9412 Boppan 221
    command += f"-no-pie -m32 -o umka_shell.exe -static {linker_script} "
9397 Boppan 222
    command += " ".join(objects)
9412 Boppan 223
    print(command)
9397 Boppan 224
    if os.system(command) != 0:
225
        exit()
226
 
9409 Boppan 227
 
9397 Boppan 228
def build_umka():
229
    if not use_umka:
230
        return
231
 
9409 Boppan 232
    os.makedirs("umka/build", exist_ok=True)
9397 Boppan 233
 
9412 Boppan 234
    platform = "win32" if sys.platform == "win32" else "linux"
235
 
236
    os.makedirs(f"umka/build/{platform}", exist_ok=True)
237
 
9397 Boppan 238
    c_sources = [
239
        "umka_shell.c",
240
        "shell.c",
241
        "trace.c",
242
        "trace_lbr.c",
243
        "vdisk.c",
244
        "vnet.c",
9412 Boppan 245
        "getopt.c",
246
        "isatty.c",
9397 Boppan 247
        "lodepng.c",
9412 Boppan 248
        f"{platform}/pci.c",
249
        f"{platform}/thread.c",
9397 Boppan 250
        "util.c",
251
    ]
252
 
9409 Boppan 253
    src_obj_pairs = [
9412 Boppan 254
        (f"umka/{source}", f"umka/build/{source}.o") for source in c_sources
9409 Boppan 255
    ]
9397 Boppan 256
 
257
    for src, obj in src_obj_pairs:
258
        cc(src, obj, "umka/linux")
259
 
260
    umka_o = build_umka_asm("umka/build")
261
 
9409 Boppan 262
    objects = [obj for src, obj in src_obj_pairs] + [umka_o]
9397 Boppan 263
    link(objects)
264
 
265
    os.chdir("umka/test")
9409 Boppan 266
    for test in [t for t in os.listdir(".") if t.endswith(".t")]:
9397 Boppan 267
        out_log = f"{test[:-2]}.out.log"
268
        ref_log = f"{test[:-2]}.ref.log"
9412 Boppan 269
        cmd_umka = f"..{os.sep}..{os.sep}umka_shell.exe < {test} > {out_log}"
9397 Boppan 270
        print(cmd_umka)
271
        os.system(cmd_umka)
9412 Boppan 272
        with open(out_log, "rb") as f:
273
            crlf = bytes([0x0D, 0x0A])
274
            lf = bytes([0x0A])
275
            out_log_contents = f.read().replace(crlf, lf)
276
        with open(out_log, "wb") as f:
277
            f.write(out_log_contents)
278
        with open(ref_log, "rb") as f:
279
            ref_log_contents = f.read()
280
        if out_log_contents != ref_log_contents:
9397 Boppan 281
            print("FAILURE")
282
            exit()
283
    os.chdir("../../")
284
    print("SUCCESS")
285
    exit()
286
 
9322 Boppan 287
if __name__ == "__main__":
288
    root_dir = os.getcwd()
289
 
290
    # Check available tools
291
    tools = (("qemu-system-i386", "qemu-system-x86"),
292
             ("fasm", "fasm"))
293
    check_tools(tools)
9409 Boppan 294
 
9322 Boppan 295
    prepare_test_img()
9397 Boppan 296
    build_umka()
9322 Boppan 297
    tests = collect_tests()
9323 Boppan 298
    serial_executor_thread = run_tests_serially(tests, root_dir)
9335 Boppan 299
    serial_executor_thread.join()