Subversion Repositories Kolibri OS

Rev

Rev 9918 | Rev 9922 | 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
9920 Boppan 15
import traceback
9249 Boppan 16
 
17
sys.path.append('test')
18
import common
19
 
9421 Boppan 20
use_umka = False
9397 Boppan 21
 
9249 Boppan 22
 
9409 Boppan 23
def log(s, end="\n"):
24
    print(s, end=end, flush=True)
25
 
26
 
27
def execute(s, mute=False):
9249 Boppan 28
    mute = ">/dev/null" if mute else ""
29
    code = os.system(f"{s}{mute}")
30
    if code:
31
        print(f"Command returned {code}: \"{s}\"")
32
        exit(-1)
33
 
9409 Boppan 34
 
35
def stage(name, command, mute=False):
36
    print(f"{name}... ", end="")
37
    execute(command, mute=mute)
9249 Boppan 38
    print("Done.")
39
 
9409 Boppan 40
 
9314 Boppan 41
def download(link, path):
9409 Boppan 42
    log(f"Downloading {path}... ", end="")
9315 Boppan 43
    urllib.request.urlretrieve(link, path)
9314 Boppan 44
    log("Done.")
45
 
9409 Boppan 46
 
9310 Boppan 47
def tool_exists(name):
48
    assert(type(name) == str)
9409 Boppan 49
    return which(name) is not None
9310 Boppan 50
 
9409 Boppan 51
 
9310 Boppan 52
def check_tools(tools):
9414 Boppan 53
    assert(type(tools) == list)
9310 Boppan 54
    for name_package_pair in tools:
9414 Boppan 55
        assert(type(name_package_pair) == list)
9310 Boppan 56
        assert(len(name_package_pair) == 2)
57
        assert(type(name_package_pair[0]) == str)
58
        assert(type(name_package_pair[1]) == str)
9409 Boppan 59
 
9310 Boppan 60
    not_exists = []
61
    for name, package in tools:
62
        if not tool_exists(name):
63
            not_exists.append((name, package))
64
    if len(not_exists) != 0:
65
        log("Sorry, I can't find some tools:")
9311 Boppan 66
 
67
        header_name = "Name"
68
        header_package = "Package (probably)"
69
 
70
        max_name_len = len(header_name)
71
        max_package_name_len = len(header_package)
9310 Boppan 72
        for name, package in not_exists:
73
            if len(package) > max_package_name_len:
74
                max_package_name_len = len(package)
75
            if len(name) > max_name_len:
76
                max_name_len = len(name)
77
 
78
        def draw_row(name, package):
9409 Boppan 79
            log((f" | {name.ljust(max_name_len)}" +
80
                 f" | {package.ljust(max_package_name_len)} |"))
9310 Boppan 81
 
82
        def draw_line():
83
            log(f" +-{'-' * max_name_len}-+-{'-' * max_package_name_len}-+")
84
 
85
        draw_line()
9311 Boppan 86
        draw_row(header_name, header_package)
9310 Boppan 87
        draw_line()
88
        for name, package in not_exists:
89
            draw_row(name, package)
90
        draw_line()
91
        exit()
92
 
9409 Boppan 93
 
9321 Boppan 94
def prepare_test_img():
95
    # TODO: Always recompile the kernel (after build system is done?)
9313 Boppan 96
    # Get IMG
97
    if not os.path.exists("kolibri_test.img"):
98
        if len(sys.argv) == 1:
9409 Boppan 99
            download("http://builds.kolibrios.org/eng/data/data/kolibri.img",
100
                     "kolibri_test.img")
9313 Boppan 101
        else:
102
            builds_eng = sys.argv[1]
103
            execute(f"cp {builds_eng}/data/data/kolibri.img kolibri_test.img")
9409 Boppan 104
 
9316 Boppan 105
    # Open the IMG
106
    with open("kolibri_test.img", "rb") as img:
107
        img_data = img.read()
108
    img = common.Floppy(img_data)
9321 Boppan 109
 
9316 Boppan 110
    # Remove unuseful folders
111
    img.delete_path("GAMES")
112
    img.delete_path("DEMOS")
113
    img.delete_path("3D")
9409 Boppan 114
 
9313 Boppan 115
    # Get test kernel
116
    if not os.path.exists("kernel.mnt.pretest"):
117
        if len(sys.argv) == 1:
9918 Boppan 118
            execute("tup kernel.mnt.pretest")
9313 Boppan 119
        else:
120
            builds_eng = sys.argv[1]
9409 Boppan 121
            kernel_mnt_pretest_subpath = "data/kernel/trunk/kernel.mnt.pretest"
122
            kernel_mnt_pretest = f"{builds_eng}/{kernel_mnt_pretest_subpath}"
123
            execute(f"cp {kernel_mnt_pretest} kernel.mnt.pretest", mute=True)
124
 
9313 Boppan 125
    # Put the kernel into IMG
9316 Boppan 126
    with open("kernel.mnt.pretest", "rb") as kernel_mnt_pretest:
127
        kernel_mnt_pretest_data = kernel_mnt_pretest.read()
9317 Boppan 128
    img.add_file_path("KERNEL.MNT", kernel_mnt_pretest_data)
9316 Boppan 129
    img.save("kolibri_test.img")
9321 Boppan 130
 
9409 Boppan 131
 
9321 Boppan 132
def collect_tests():
133
    tests = []
134
 
9313 Boppan 135
    # Collect tests from test folder (not recursively yet)
136
    for test_folder in os.listdir("test"):
137
        test_folder_path = f"test/{test_folder}"
138
        test_file = f"{test_folder_path}/test.py"
9321 Boppan 139
 
9313 Boppan 140
        if not os.path.isdir(test_folder_path):
141
            continue
9321 Boppan 142
 
9313 Boppan 143
        if os.path.exists(test_file):
144
            tests.append(test_folder_path)
9321 Boppan 145
    return tests
146
 
9409 Boppan 147
 
9323 Boppan 148
def run_tests_serially_thread(test, root_dir):
9920 Boppan 149
    errors = []
9313 Boppan 150
    test_number = 1
151
    for test in tests:
152
        test_dir = f"{root_dir}/{test}"
9409 Boppan 153
 
154
        print(f"[{test_number}/{len(tests)}] {test}... ", end="", flush=True)
9313 Boppan 155
        start = timeit.default_timer()
156
        try:
9409 Boppan 157
            loader = SourceFileLoader("test", f"{test_dir}/test.py")
158
            loader.load_module().run(root_dir, test_dir)
9920 Boppan 159
        except common.TestException as exception:
160
            result = exception.kind()
161
            errors.append((test, exception))
9313 Boppan 162
        else:
163
            result = "SUCCESS"
164
        finish = timeit.default_timer()
165
        print(f"{result} ({finish - start:.2f} seconds)")
9409 Boppan 166
 
9313 Boppan 167
        test_number += 1
9920 Boppan 168
    if len(errors) != 0:
169
        print("Some tests failed:")
170
        for error in errors:
171
            test, exception = error
172
            print(f"\n{test}: {str(exception)}\n\nTraceback:")
173
            traceback.print_tb(exception.__traceback__)
174
            print(f"\nQemu command:\n  {exception.cmd()}\n")
9322 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):
9418 Boppan 205
    if tool_exists("i686-w64-mingw32-gcc"):
206
        compiler = "i686-w64-mingw32-gcc"
207
    elif tool_exists("clang"):
208
        compiler = "clang"
209
    else:
210
        print("No compiler found to compile UMKa (tried i686-w64-mingw32-gcc and clang)")
211
        print("  Please make sure you have installed llvm-mingw or clang")
212
        print("  If they're installled consider updating PATH variable")
213
    command = f"{compiler} "
9409 Boppan 214
    command += "-Wno-everything -std=c11 -g -O0 -fno-pie -m32 -masm=intel -c "
215
    command += "-D_FILE_OFFSET_BITS=64 -DNDEBUG -D_POSIX_C_SOURCE=200809L "
9397 Boppan 216
    command += f"-I {include_path} {src} -o {obj}"
9412 Boppan 217
    print(command)
9397 Boppan 218
    if os.system(command) != 0:
219
        exit()
220
 
9409 Boppan 221
 
9397 Boppan 222
def link(objects):
9418 Boppan 223
    if tool_exists("i686-w64-mingw32-gcc"):
224
        compiler = "i686-w64-mingw32-gcc"
225
    elif tool_exists("clang"):
226
        compiler = "clang"
227
    else:
228
        print("No compiler found to compile UMKa (tried i686-w64-mingw32-gcc and clang)")
229
        print("  Please make sure you have installed llvm-mingw or clang")
230
        print("  If they're installled consider updating PATH variable")
9412 Boppan 231
    if sys.platform == "linux" or sys.platform == "linux2":
232
        linker_script = "-T umka/umka.ld"
233
    else:
234
        linker_script = "-Wl,/ALIGN:65536 -Wl,/MAP:umka.map "
9418 Boppan 235
    command = f"{compiler} "
9409 Boppan 236
    command += "-Wno-everything "
9412 Boppan 237
    command += f"-no-pie -m32 -o umka_shell.exe -static {linker_script} "
9397 Boppan 238
    command += " ".join(objects)
9412 Boppan 239
    print(command)
9397 Boppan 240
    if os.system(command) != 0:
241
        exit()
242
 
9409 Boppan 243
 
9397 Boppan 244
def build_umka():
245
    if not use_umka:
246
        return
247
 
9409 Boppan 248
    os.makedirs("umka/build", exist_ok=True)
9397 Boppan 249
 
9412 Boppan 250
    platform = "win32" if sys.platform == "win32" else "linux"
251
 
252
    os.makedirs(f"umka/build/{platform}", exist_ok=True)
253
 
9397 Boppan 254
    c_sources = [
255
        "umka_shell.c",
256
        "shell.c",
257
        "trace.c",
258
        "trace_lbr.c",
259
        "vdisk.c",
260
        "vnet.c",
9412 Boppan 261
        "getopt.c",
262
        "isatty.c",
9397 Boppan 263
        "lodepng.c",
9412 Boppan 264
        f"{platform}/pci.c",
265
        f"{platform}/thread.c",
9397 Boppan 266
        "util.c",
267
    ]
268
 
9409 Boppan 269
    src_obj_pairs = [
9412 Boppan 270
        (f"umka/{source}", f"umka/build/{source}.o") for source in c_sources
9409 Boppan 271
    ]
9397 Boppan 272
 
273
    for src, obj in src_obj_pairs:
274
        cc(src, obj, "umka/linux")
275
 
276
    umka_o = build_umka_asm("umka/build")
277
 
9409 Boppan 278
    objects = [obj for src, obj in src_obj_pairs] + [umka_o]
9397 Boppan 279
    link(objects)
280
 
281
    os.chdir("umka/test")
9409 Boppan 282
    for test in [t for t in os.listdir(".") if t.endswith(".t")]:
9397 Boppan 283
        out_log = f"{test[:-2]}.out.log"
284
        ref_log = f"{test[:-2]}.ref.log"
9412 Boppan 285
        cmd_umka = f"..{os.sep}..{os.sep}umka_shell.exe < {test} > {out_log}"
9397 Boppan 286
        print(cmd_umka)
287
        os.system(cmd_umka)
9412 Boppan 288
        with open(out_log, "rb") as f:
289
            crlf = bytes([0x0D, 0x0A])
290
            lf = bytes([0x0A])
291
            out_log_contents = f.read().replace(crlf, lf)
292
        with open(out_log, "wb") as f:
293
            f.write(out_log_contents)
294
        with open(ref_log, "rb") as f:
295
            ref_log_contents = f.read()
296
        if out_log_contents != ref_log_contents:
9397 Boppan 297
            print("FAILURE")
298
            exit()
299
    os.chdir("../../")
300
    print("SUCCESS")
301
 
9414 Boppan 302
 
303
def download_umka():
9415 Boppan 304
	if not use_umka:
305
		return
306
 
9414 Boppan 307
	if not os.path.exists("umka"):
308
		if os.system("git clone https://github.com/KolibriOS/umka") != 0:
309
			print("Couldn't clone UMKa repo")
310
			exit()
311
	os.chdir("umka")
9420 Boppan 312
	if os.system("git checkout trunk") != 0:
313
		print("Couldn't checkout trunk branch of UMKa")
314
		exit()
9414 Boppan 315
	os.system("git pull")
316
	os.chdir("../")
317
 
318
 
319
def download_umka_imgs():
9415 Boppan 320
	if not use_umka:
321
		return
322
 
9414 Boppan 323
	imgs = [
324
		"fat32_test0.img",
325
		"jfs.img",
326
		"kolibri.img",
327
		"xfs_borg_bit.img",
328
		"xfs_v4_btrees_l2.img",
329
		"xfs_v4_files_s05k_b4k_n8k.img",
330
		"xfs_v4_ftype0_s05k_b2k_n8k_xattr.img",
331
		"xfs_v4_ftype0_s05k_b2k_n8k.img",
332
		"xfs_v4_ftype0_s4k_b4k_n8k.img",
333
		"xfs_v4_ftype1_s05k_b2k_n8k.img",
334
		"xfs_v4_unicode.img",
335
		"xfs_v4_xattr.img",
336
		"xfs_v5_files_s05k_b4k_n8k.img",
337
		"xfs_v5_ftype1_s05k_b2k_n8k.img",
338
	]
339
 
340
	for img in imgs:
9416 Boppan 341
		if not os.path.exists(f"umka/img/{img}"):
342
			download(f"http://ftp.kolibrios.org/users/Boppan/img/{img}",
343
					 f"umka/img/{img}")
9414 Boppan 344
 
9322 Boppan 345
if __name__ == "__main__":
346
    root_dir = os.getcwd()
347
 
348
    # Check available tools
9414 Boppan 349
    tools = [["qemu-system-i386", "qemu-system-x86"],
350
             ["fasm", "fasm"]]
351
    if use_umka:
352
    	tools.append(["git", "git"]);
9322 Boppan 353
    check_tools(tools)
9409 Boppan 354
 
9322 Boppan 355
    prepare_test_img()
9414 Boppan 356
    download_umka()
357
    download_umka_imgs()
9397 Boppan 358
    build_umka()
9322 Boppan 359
    tests = collect_tests()
9323 Boppan 360
    serial_executor_thread = run_tests_serially(tests, root_dir)
9335 Boppan 361
    serial_executor_thread.join()