Subversion Repositories Kolibri OS

Rev

Rev 9340 | Rev 9387 | 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
 
9337 Boppan 19
enable_umka = True
9326 Boppan 20
 
9249 Boppan 21
def log(s, end = "\n"):
22
    print(s, end = end, flush = True)
23
 
24
def execute(s, mute = False):
25
    mute = ">/dev/null" if mute else ""
26
    code = os.system(f"{s}{mute}")
27
    if code:
28
        print(f"Command returned {code}: \"{s}\"")
29
        exit(-1)
30
 
31
def stage(name, command, mute = False):
32
    print(f"{name}... ", end = "")
33
    execute(command, mute = mute)
34
    print("Done.")
35
 
9314 Boppan 36
def download(link, path):
37
    log(f"Downloading {path}... ", end = "")
9315 Boppan 38
    urllib.request.urlretrieve(link, path)
9314 Boppan 39
    log("Done.")
40
 
9310 Boppan 41
def tool_exists(name):
42
    assert(type(name) == str)
43
    return which(name) != None
44
 
45
def check_tools(tools):
46
    assert(type(tools) == tuple)
47
    for name_package_pair in tools:
48
        assert(type(name_package_pair) == tuple)
49
        assert(len(name_package_pair) == 2)
50
        assert(type(name_package_pair[0]) == str)
51
        assert(type(name_package_pair[1]) == str)
52
 
53
    not_exists = []
54
    for name, package in tools:
55
        if not tool_exists(name):
56
            not_exists.append((name, package))
57
    if len(not_exists) != 0:
58
        log("Sorry, I can't find some tools:")
9311 Boppan 59
 
60
        header_name = "Name"
61
        header_package = "Package (probably)"
62
 
63
        max_name_len = len(header_name)
64
        max_package_name_len = len(header_package)
9310 Boppan 65
        for name, package in not_exists:
66
            if len(package) > max_package_name_len:
67
                max_package_name_len = len(package)
68
            if len(name) > max_name_len:
69
                max_name_len = len(name)
70
 
71
        def draw_row(name, package):
72
            log(f" | {name.ljust(max_name_len)} | {package.ljust(max_package_name_len)} |")
73
 
74
        def draw_line():
75
            log(f" +-{'-' * max_name_len}-+-{'-' * max_package_name_len}-+")
76
 
77
        draw_line()
9311 Boppan 78
        draw_row(header_name, header_package)
9310 Boppan 79
        draw_line()
80
        for name, package in not_exists:
81
            draw_row(name, package)
82
        draw_line()
83
        exit()
84
 
9321 Boppan 85
def prepare_test_img():
86
    # TODO: Always recompile the kernel (after build system is done?)
9313 Boppan 87
    # Get IMG
88
    if not os.path.exists("kolibri_test.img"):
89
        if len(sys.argv) == 1:
9314 Boppan 90
            download("http://builds.kolibrios.org/eng/data/data/kolibri.img", "kolibri_test.img")
9313 Boppan 91
        else:
92
            builds_eng = sys.argv[1]
93
            execute(f"cp {builds_eng}/data/data/kolibri.img kolibri_test.img")
94
 
9316 Boppan 95
    # Open the IMG
96
    with open("kolibri_test.img", "rb") as img:
97
        img_data = img.read()
98
    img = common.Floppy(img_data)
9321 Boppan 99
 
9316 Boppan 100
    # Remove unuseful folders
101
    img.delete_path("GAMES")
102
    img.delete_path("DEMOS")
103
    img.delete_path("3D")
9313 Boppan 104
 
105
    # Get test kernel
106
    if not os.path.exists("kernel.mnt.pretest"):
107
        if len(sys.argv) == 1:
108
            with open("lang.inc", "w") as lang_inc:
109
                lang_inc.write("lang fix en\n")
110
            execute("fasm bootbios.asm bootbios.bin.pretest -dpretest_build=1")
111
            execute("fasm -m 65536 kernel.asm kernel.mnt.pretest -dpretest_build=1 -ddebug_com_base=0xe9")
112
        else:
113
            builds_eng = sys.argv[1]
114
            execute(f"cp {builds_eng}/data/kernel/trunk/kernel.mnt.pretest kernel.mnt.pretest", mute = True)
115
 
116
    # Put the kernel into IMG
9316 Boppan 117
    with open("kernel.mnt.pretest", "rb") as kernel_mnt_pretest:
118
        kernel_mnt_pretest_data = kernel_mnt_pretest.read()
9317 Boppan 119
    img.add_file_path("KERNEL.MNT", kernel_mnt_pretest_data)
9316 Boppan 120
    img.save("kolibri_test.img")
9321 Boppan 121
 
122
def collect_tests():
123
    tests = []
124
 
9313 Boppan 125
    # Collect tests from test folder (not recursively yet)
126
    for test_folder in os.listdir("test"):
127
        test_folder_path = f"test/{test_folder}"
128
        test_file = f"{test_folder_path}/test.py"
9321 Boppan 129
 
9313 Boppan 130
        if not os.path.isdir(test_folder_path):
131
            continue
9321 Boppan 132
 
9313 Boppan 133
        if os.path.exists(test_file):
134
            tests.append(test_folder_path)
9321 Boppan 135
    return tests
136
 
9327 Boppan 137
def collect_umka_tests():
138
    tests = []
139
 
140
    for test_file in os.listdir("umka/test"):
141
        test_file_path = f"umka/test/{test_file}"
142
        if not test_file.endswith(".t"):
143
            continue
144
        if not os.path.isfile(test_file_path):
145
            continue
9333 Boppan 146
        tests.append(test_file_path)
9327 Boppan 147
    return tests
148
 
9323 Boppan 149
def run_tests_serially_thread(test, root_dir):
9313 Boppan 150
    test_number = 1
151
    for test in tests:
152
        test_dir = f"{root_dir}/{test}"
153
 
154
        print(f"[{test_number}/{len(tests)}] {test}... ", end = "", flush=True)
155
        start = timeit.default_timer()
156
        try:
9331 Boppan 157
            SourceFileLoader("test", f"{test_dir}/test.py").load_module().run(root_dir, test_dir)
9313 Boppan 158
        except common.TestTimeoutException:
159
            result = "TIMEOUT"
160
        except common.TestFailureException:
161
            result = "FAILURE"
162
        else:
163
            result = "SUCCESS"
164
        finish = timeit.default_timer()
165
        print(f"{result} ({finish - start:.2f} seconds)")
166
 
167
        test_number += 1
9322 Boppan 168
 
9323 Boppan 169
def run_tests_serially(tests, root_dir):
170
    thread = Thread(target = run_tests_serially_thread, args = (tests, root_dir))
171
    thread.start()
172
    return thread
173
 
9326 Boppan 174
def gcc(fin, fout):
9342 Boppan 175
    flags = "-m32 -std=c11 -g -O0 -fno-pie -w"
9329 Boppan 176
    defines = "-D_FILE_OFFSET_BITS=64 -DNDEBUG -D_POSIX_C_SOURCE=200809L"
177
    include = "-Iumka -Iumka/linux"
9338 Boppan 178
    command = f"clang {flags} {defines} {include} -c {fin} -o {fout}"
9326 Boppan 179
    print(command)
180
    os.system(command)
181
 
9329 Boppan 182
def build_umka_asm():
183
    include = "INCLUDE=\"../../programs/develop/libraries/libcrash/hash\""
184
    flags = "-dUEFI=1 -dextended_primary_loader=1 -dUMKA=1"
185
    files = "umka/umka.asm umka/build/umka.o -s umka/build/umka.fas"
186
    memory = "-m 2000000"
9337 Boppan 187
    command = f"{include} fasm {flags} {files} {memory}"
9342 Boppan 188
    if sys.platform != "win32":
189
        print(command)
190
        os.system(command)
191
    else:
192
        my_env = os.environ.copy()
193
        my_env["INCLUDE"] = "../../programs/develop/libraries/libcrash/hash"
194
        print(subprocess.check_output(f"fasm {flags} {files} {memory} -dwin32=1", shell = True, env = my_env))
9329 Boppan 195
 
9326 Boppan 196
def build_umka():
197
    if not enable_umka:
198
        return
9339 Boppan 199
    if os.path.exists("umka_shell.exe"):
9326 Boppan 200
        return
9330 Boppan 201
    os.makedirs("umka/build/linux", exist_ok = True)
9342 Boppan 202
    os.makedirs("umka/build/win32", exist_ok = True)
9329 Boppan 203
    sources = [ "umka_shell.c",
204
                "shell.c",
205
                "vdisk.c",
206
                "lodepng.c",
9342 Boppan 207
                "getopt.c" ]
208
    if sys.platform == "win32":
209
        sources += [ "win32/pci.c", "win32/thread.c" ]
210
    else:
211
        sources += [ "linux/pci.c", "linux/thread.c" ]
9326 Boppan 212
    sources = [f"umka/{f}" for f in sources]
9330 Boppan 213
    objects = []
9326 Boppan 214
    for source in sources:
9330 Boppan 215
        object_path = source.replace("umka/", "umka/build/")
216
        object_path = f"{object_path}.o"
217
        gcc(source, object_path)
218
        objects.append(object_path)
9329 Boppan 219
    build_umka_asm()
9330 Boppan 220
    objects.append("umka/build/umka.o")
221
    objects = " ".join(objects)
9342 Boppan 222
    if sys.platform != "win32":
223
        ld_script = "-T umka/umka.ld"
224
    else:
225
        ld_script = ""
226
    command = f"clang -m32 -fno-pie -o umka_shell.exe -static {ld_script} {objects}"
9337 Boppan 227
    print(command)
228
    os.system(command)
9342 Boppan 229
    if not os.path.exists("umka_shell.exe"):
230
        print("Could't compile umka_shell.exe")
231
        exit()
9326 Boppan 232
 
9333 Boppan 233
def create_relocated(root_dir, fname):
234
    with open(fname, "rb") as f:
235
        contents = f.read()
236
    new_contents = contents.replace(b"../img", bytes(f"{root_dir}/umka/img", "ascii"))
237
    new_contents = new_contents.replace(b"chess_image.rgb", bytes(f"{root_dir}/umka/test/chess_image.rgb", "ascii"))
238
    outname = f"{fname}.o" # .o extension just to avoid indexing of the file
239
    with open(outname, "wb") as f:
240
        f.write(new_contents)
241
    return outname
242
 
243
def run_umka_test(root_dir, test_file_path):
244
    test = create_relocated(root_dir, test_file_path)
245
    ref_log = create_relocated(root_dir, f"{test_file_path[:-2]}.ref.log")
9342 Boppan 246
    out_log = f"{test_file_path[:-2]}.out.log"
247
    if sys.platform != "win32":
248
        prefix = "./"
249
    else:
250
        prefix = ""
251
    os.system(f"{prefix}umka_shell.exe < {test} > {out_log}")
252
    if sys.platform == "win32":
253
        with open(out_log, "rb") as f:
254
            contents = f.read()
255
        contents_no_crlf = contents.replace(b"\r\n", b"\n")
256
        with open(out_log, "wb") as f:
257
            f.write(contents_no_crlf)
9340 Boppan 258
    if not filecmp.cmp(ref_log, out_log):
9328 Boppan 259
        print(f"FAILURE: {test_file_path}\n", end = "")
260
    else:
261
        print(f"SUCCESS: {test_file_path}\n", end = "")
262
 
9322 Boppan 263
if __name__ == "__main__":
264
    root_dir = os.getcwd()
265
 
266
    # Check available tools
267
    tools = (("qemu-system-i386", "qemu-system-x86"),
268
             ("fasm", "fasm"))
269
    check_tools(tools)
9313 Boppan 270
 
9322 Boppan 271
    prepare_test_img()
9326 Boppan 272
    build_umka()
9322 Boppan 273
    tests = collect_tests()
9328 Boppan 274
    umka_tests = collect_umka_tests()
9323 Boppan 275
    serial_executor_thread = run_tests_serially(tests, root_dir)
9328 Boppan 276
    if enable_umka:
277
        for umka_test in umka_tests:
9333 Boppan 278
            run_umka_test(root_dir, umka_test)
9335 Boppan 279
    serial_executor_thread.join()
9323 Boppan 280