Subversion Repositories Kolibri OS

Rev

Rev 9342 | Rev 9397 | Go to most recent revision | Only display areas with differences | Ignore whitespace | Details | Blame | Last modification | View Log | RSS feed

Rev 9342 Rev 9387
1
#!/usr/bin/python3
1
#!/usr/bin/python3
2
# Copyright 2021 Magomed Kostoev
2
# Copyright 2021 Magomed Kostoev
3
# Published under MIT License
3
# Published under MIT License
4
 
4
 
5
import os
5
import os
6
import sys
6
import sys
7
import urllib
7
import urllib
8
from importlib.machinery import SourceFileLoader
8
from importlib.machinery import SourceFileLoader
9
from shutil import which
9
from shutil import which
10
import timeit
10
import timeit
11
import urllib.request
11
import urllib.request
12
import subprocess
12
import subprocess
13
from threading import Thread
13
from threading import Thread
14
import filecmp
14
import filecmp
15
 
15
 
16
sys.path.append('test')
16
sys.path.append('test')
17
import common
17
import common
18
 
-
 
19
enable_umka = True
-
 
20
 
18
 
21
def log(s, end = "\n"):
19
def log(s, end = "\n"):
22
    print(s, end = end, flush = True)
20
    print(s, end = end, flush = True)
23
 
21
 
24
def execute(s, mute = False):
22
def execute(s, mute = False):
25
    mute = ">/dev/null" if mute else ""
23
    mute = ">/dev/null" if mute else ""
26
    code = os.system(f"{s}{mute}")
24
    code = os.system(f"{s}{mute}")
27
    if code:
25
    if code:
28
        print(f"Command returned {code}: \"{s}\"")
26
        print(f"Command returned {code}: \"{s}\"")
29
        exit(-1)
27
        exit(-1)
30
 
28
 
31
def stage(name, command, mute = False):
29
def stage(name, command, mute = False):
32
    print(f"{name}... ", end = "")
30
    print(f"{name}... ", end = "")
33
    execute(command, mute = mute)
31
    execute(command, mute = mute)
34
    print("Done.")
32
    print("Done.")
35
 
33
 
36
def download(link, path):
34
def download(link, path):
37
    log(f"Downloading {path}... ", end = "")
35
    log(f"Downloading {path}... ", end = "")
38
    urllib.request.urlretrieve(link, path)
36
    urllib.request.urlretrieve(link, path)
39
    log("Done.")
37
    log("Done.")
40
 
38
 
41
def tool_exists(name):
39
def tool_exists(name):
42
    assert(type(name) == str)
40
    assert(type(name) == str)
43
    return which(name) != None
41
    return which(name) != None
44
 
42
 
45
def check_tools(tools):
43
def check_tools(tools):
46
    assert(type(tools) == tuple)
44
    assert(type(tools) == tuple)
47
    for name_package_pair in tools:
45
    for name_package_pair in tools:
48
        assert(type(name_package_pair) == tuple)
46
        assert(type(name_package_pair) == tuple)
49
        assert(len(name_package_pair) == 2)
47
        assert(len(name_package_pair) == 2)
50
        assert(type(name_package_pair[0]) == str)
48
        assert(type(name_package_pair[0]) == str)
51
        assert(type(name_package_pair[1]) == str)
49
        assert(type(name_package_pair[1]) == str)
52
    
50
    
53
    not_exists = []
51
    not_exists = []
54
    for name, package in tools:
52
    for name, package in tools:
55
        if not tool_exists(name):
53
        if not tool_exists(name):
56
            not_exists.append((name, package))
54
            not_exists.append((name, package))
57
    if len(not_exists) != 0:
55
    if len(not_exists) != 0:
58
        log("Sorry, I can't find some tools:")
56
        log("Sorry, I can't find some tools:")
59
 
57
 
60
        header_name = "Name"
58
        header_name = "Name"
61
        header_package = "Package (probably)"
59
        header_package = "Package (probably)"
62
 
60
 
63
        max_name_len = len(header_name)
61
        max_name_len = len(header_name)
64
        max_package_name_len = len(header_package)
62
        max_package_name_len = len(header_package)
65
        for name, package in not_exists:
63
        for name, package in not_exists:
66
            if len(package) > max_package_name_len:
64
            if len(package) > max_package_name_len:
67
                max_package_name_len = len(package)
65
                max_package_name_len = len(package)
68
            if len(name) > max_name_len:
66
            if len(name) > max_name_len:
69
                max_name_len = len(name)
67
                max_name_len = len(name)
70
 
68
 
71
        def draw_row(name, package):
69
        def draw_row(name, package):
72
            log(f" | {name.ljust(max_name_len)} | {package.ljust(max_package_name_len)} |")
70
            log(f" | {name.ljust(max_name_len)} | {package.ljust(max_package_name_len)} |")
73
 
71
 
74
        def draw_line():
72
        def draw_line():
75
            log(f" +-{'-' * max_name_len}-+-{'-' * max_package_name_len}-+")
73
            log(f" +-{'-' * max_name_len}-+-{'-' * max_package_name_len}-+")
76
 
74
 
77
        draw_line()
75
        draw_line()
78
        draw_row(header_name, header_package)
76
        draw_row(header_name, header_package)
79
        draw_line()
77
        draw_line()
80
        for name, package in not_exists:
78
        for name, package in not_exists:
81
            draw_row(name, package)
79
            draw_row(name, package)
82
        draw_line()
80
        draw_line()
83
        exit()
81
        exit()
84
 
82
 
85
def prepare_test_img():
83
def prepare_test_img():
86
    # TODO: Always recompile the kernel (after build system is done?)
84
    # TODO: Always recompile the kernel (after build system is done?)
87
    # Get IMG
85
    # Get IMG
88
    if not os.path.exists("kolibri_test.img"):
86
    if not os.path.exists("kolibri_test.img"):
89
        if len(sys.argv) == 1:
87
        if len(sys.argv) == 1:
90
            download("http://builds.kolibrios.org/eng/data/data/kolibri.img", "kolibri_test.img")
88
            download("http://builds.kolibrios.org/eng/data/data/kolibri.img", "kolibri_test.img")
91
        else:
89
        else:
92
            builds_eng = sys.argv[1]
90
            builds_eng = sys.argv[1]
93
            execute(f"cp {builds_eng}/data/data/kolibri.img kolibri_test.img")
91
            execute(f"cp {builds_eng}/data/data/kolibri.img kolibri_test.img")
94
    
92
    
95
    # Open the IMG
93
    # Open the IMG
96
    with open("kolibri_test.img", "rb") as img:
94
    with open("kolibri_test.img", "rb") as img:
97
        img_data = img.read()
95
        img_data = img.read()
98
    img = common.Floppy(img_data)
96
    img = common.Floppy(img_data)
99
 
97
 
100
    # Remove unuseful folders
98
    # Remove unuseful folders
101
    img.delete_path("GAMES")
99
    img.delete_path("GAMES")
102
    img.delete_path("DEMOS")
100
    img.delete_path("DEMOS")
103
    img.delete_path("3D")
101
    img.delete_path("3D")
104
    
102
    
105
    # Get test kernel
103
    # Get test kernel
106
    if not os.path.exists("kernel.mnt.pretest"):
104
    if not os.path.exists("kernel.mnt.pretest"):
107
        if len(sys.argv) == 1:
105
        if len(sys.argv) == 1:
108
            with open("lang.inc", "w") as lang_inc:
106
            with open("lang.inc", "w") as lang_inc:
109
                lang_inc.write("lang fix en\n")
107
                lang_inc.write("lang fix en\n")
110
            execute("fasm bootbios.asm bootbios.bin.pretest -dpretest_build=1")
108
            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")
109
            execute("fasm -m 65536 kernel.asm kernel.mnt.pretest -dpretest_build=1 -ddebug_com_base=0xe9")
112
        else:
110
        else:
113
            builds_eng = sys.argv[1]
111
            builds_eng = sys.argv[1]
114
            execute(f"cp {builds_eng}/data/kernel/trunk/kernel.mnt.pretest kernel.mnt.pretest", mute = True)
112
            execute(f"cp {builds_eng}/data/kernel/trunk/kernel.mnt.pretest kernel.mnt.pretest", mute = True)
115
    
113
    
116
    # Put the kernel into IMG
114
    # Put the kernel into IMG
117
    with open("kernel.mnt.pretest", "rb") as kernel_mnt_pretest:
115
    with open("kernel.mnt.pretest", "rb") as kernel_mnt_pretest:
118
        kernel_mnt_pretest_data = kernel_mnt_pretest.read()
116
        kernel_mnt_pretest_data = kernel_mnt_pretest.read()
119
    img.add_file_path("KERNEL.MNT", kernel_mnt_pretest_data)
117
    img.add_file_path("KERNEL.MNT", kernel_mnt_pretest_data)
120
    img.save("kolibri_test.img")
118
    img.save("kolibri_test.img")
121
 
119
 
122
def collect_tests():
120
def collect_tests():
123
    tests = []
121
    tests = []
124
 
122
 
125
    # Collect tests from test folder (not recursively yet)
123
    # Collect tests from test folder (not recursively yet)
126
    for test_folder in os.listdir("test"):
124
    for test_folder in os.listdir("test"):
127
        test_folder_path = f"test/{test_folder}"
125
        test_folder_path = f"test/{test_folder}"
128
        test_file = f"{test_folder_path}/test.py"
126
        test_file = f"{test_folder_path}/test.py"
129
 
127
 
130
        if not os.path.isdir(test_folder_path):
128
        if not os.path.isdir(test_folder_path):
131
            continue
129
            continue
132
 
130
 
133
        if os.path.exists(test_file):
131
        if os.path.exists(test_file):
134
            tests.append(test_folder_path)
132
            tests.append(test_folder_path)
135
    return tests
133
    return tests
136
 
-
 
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
-
 
146
        tests.append(test_file_path)
-
 
147
    return tests
-
 
148
 
134
 
149
def run_tests_serially_thread(test, root_dir):
135
def run_tests_serially_thread(test, root_dir):
150
    test_number = 1
136
    test_number = 1
151
    for test in tests:
137
    for test in tests:
152
        test_dir = f"{root_dir}/{test}"
138
        test_dir = f"{root_dir}/{test}"
153
    
139
    
154
        print(f"[{test_number}/{len(tests)}] {test}... ", end = "", flush=True)
140
        print(f"[{test_number}/{len(tests)}] {test}... ", end = "", flush=True)
155
        start = timeit.default_timer()
141
        start = timeit.default_timer()
156
        try:
142
        try:
157
            SourceFileLoader("test", f"{test_dir}/test.py").load_module().run(root_dir, test_dir)
143
            SourceFileLoader("test", f"{test_dir}/test.py").load_module().run(root_dir, test_dir)
158
        except common.TestTimeoutException:
144
        except common.TestTimeoutException:
159
            result = "TIMEOUT"
145
            result = "TIMEOUT"
160
        except common.TestFailureException:
146
        except common.TestFailureException:
161
            result = "FAILURE"
147
            result = "FAILURE"
162
        else:
148
        else:
163
            result = "SUCCESS"
149
            result = "SUCCESS"
164
        finish = timeit.default_timer()
150
        finish = timeit.default_timer()
165
        print(f"{result} ({finish - start:.2f} seconds)")
151
        print(f"{result} ({finish - start:.2f} seconds)")
166
    
152
    
167
        test_number += 1
153
        test_number += 1
168
 
154
 
169
def run_tests_serially(tests, root_dir):
155
def run_tests_serially(tests, root_dir):
170
    thread = Thread(target = run_tests_serially_thread, args = (tests, root_dir))
156
    thread = Thread(target = run_tests_serially_thread, args = (tests, root_dir))
171
    thread.start()
157
    thread.start()
172
    return thread
158
    return thread
173
 
-
 
174
def gcc(fin, fout):
-
 
175
    flags = "-m32 -std=c11 -g -O0 -fno-pie -w"
-
 
176
    defines = "-D_FILE_OFFSET_BITS=64 -DNDEBUG -D_POSIX_C_SOURCE=200809L"
-
 
177
    include = "-Iumka -Iumka/linux"
-
 
178
    command = f"clang {flags} {defines} {include} -c {fin} -o {fout}"
-
 
179
    print(command)
-
 
180
    os.system(command)
-
 
181
 
-
 
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"
-
 
187
    command = f"{include} fasm {flags} {files} {memory}"
-
 
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))
-
 
195
 
-
 
196
def build_umka():
-
 
197
    if not enable_umka:
-
 
198
        return
-
 
199
    if os.path.exists("umka_shell.exe"):
-
 
200
        return
-
 
201
    os.makedirs("umka/build/linux", exist_ok = True)
-
 
202
    os.makedirs("umka/build/win32", exist_ok = True)
-
 
203
    sources = [ "umka_shell.c", 
-
 
204
                "shell.c",
-
 
205
                "vdisk.c",
-
 
206
                "lodepng.c",
-
 
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" ]
-
 
212
    sources = [f"umka/{f}" for f in sources]
-
 
213
    objects = []
-
 
214
    for source in sources:
-
 
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)
-
 
219
    build_umka_asm()
-
 
220
    objects.append("umka/build/umka.o")
-
 
221
    objects = " ".join(objects)
-
 
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}"
-
 
227
    print(command)
-
 
228
    os.system(command)
-
 
229
    if not os.path.exists("umka_shell.exe"):
-
 
230
        print("Could't compile umka_shell.exe")
-
 
231
        exit()
-
 
232
 
-
 
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")
-
 
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)
-
 
258
    if not filecmp.cmp(ref_log, out_log):
-
 
259
        print(f"FAILURE: {test_file_path}\n", end = "")
-
 
260
    else:
-
 
261
        print(f"SUCCESS: {test_file_path}\n", end = "")
-
 
262
 
159
 
263
if __name__ == "__main__":
160
if __name__ == "__main__":
264
    root_dir = os.getcwd()
161
    root_dir = os.getcwd()
265
 
162
 
266
    # Check available tools
163
    # Check available tools
267
    tools = (("qemu-system-i386", "qemu-system-x86"),
164
    tools = (("qemu-system-i386", "qemu-system-x86"),
268
             ("fasm", "fasm"))
165
             ("fasm", "fasm"))
269
    check_tools(tools)
166
    check_tools(tools)
270
    
167
    
271
    prepare_test_img()
168
    prepare_test_img()
272
    build_umka()
-
 
273
    tests = collect_tests()
169
    tests = collect_tests()
274
    umka_tests = collect_umka_tests()
-
 
275
    serial_executor_thread = run_tests_serially(tests, root_dir)
170
    serial_executor_thread = run_tests_serially(tests, root_dir)
276
    if enable_umka:
-
 
277
        for umka_test in umka_tests:
-
 
278
            run_umka_test(root_dir, umka_test)
-
 
279
    serial_executor_thread.join()
171
    serial_executor_thread.join()