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