Subversion Repositories Kolibri OS

Rev

Rev 9421 | Rev 9920 | Go to most recent revision | Blame | Compare with Previous | Last modification | View Log | Download | RSS feed

  1. #!/usr/bin/python3
  2. # Copyright 2021 Magomed Kostoev
  3. # Published under MIT License
  4.  
  5. import os
  6. import sys
  7. import urllib
  8. from importlib.machinery import SourceFileLoader
  9. from shutil import which
  10. import timeit
  11. import urllib.request
  12. import subprocess
  13. from threading import Thread
  14. import filecmp
  15.  
  16. sys.path.append('test')
  17. import common
  18.  
  19. use_umka = False
  20.  
  21.  
  22. def log(s, end="\n"):
  23.     print(s, end=end, flush=True)
  24.  
  25.  
  26. def execute(s, mute=False):
  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.  
  33.  
  34. def stage(name, command, mute=False):
  35.     print(f"{name}... ", end="")
  36.     execute(command, mute=mute)
  37.     print("Done.")
  38.  
  39.  
  40. def download(link, path):
  41.     log(f"Downloading {path}... ", end="")
  42.     urllib.request.urlretrieve(link, path)
  43.     log("Done.")
  44.  
  45.  
  46. def tool_exists(name):
  47.     assert(type(name) == str)
  48.     return which(name) is not None
  49.  
  50.  
  51. def check_tools(tools):
  52.     assert(type(tools) == list)
  53.     for name_package_pair in tools:
  54.         assert(type(name_package_pair) == list)
  55.         assert(len(name_package_pair) == 2)
  56.         assert(type(name_package_pair[0]) == str)
  57.         assert(type(name_package_pair[1]) == str)
  58.  
  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:")
  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)
  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):
  78.             log((f" | {name.ljust(max_name_len)}" +
  79.                  f" | {package.ljust(max_package_name_len)} |"))
  80.  
  81.         def draw_line():
  82.             log(f" +-{'-' * max_name_len}-+-{'-' * max_package_name_len}-+")
  83.  
  84.         draw_line()
  85.         draw_row(header_name, header_package)
  86.         draw_line()
  87.         for name, package in not_exists:
  88.             draw_row(name, package)
  89.         draw_line()
  90.         exit()
  91.  
  92.  
  93. def prepare_test_img():
  94.     # TODO: Always recompile the kernel (after build system is done?)
  95.     # Get IMG
  96.     if not os.path.exists("kolibri_test.img"):
  97.         if len(sys.argv) == 1:
  98.             download("http://builds.kolibrios.org/eng/data/data/kolibri.img",
  99.                      "kolibri_test.img")
  100.         else:
  101.             builds_eng = sys.argv[1]
  102.             execute(f"cp {builds_eng}/data/data/kolibri.img kolibri_test.img")
  103.  
  104.     # Open the IMG
  105.     with open("kolibri_test.img", "rb") as img:
  106.         img_data = img.read()
  107.     img = common.Floppy(img_data)
  108.  
  109.     # Remove unuseful folders
  110.     img.delete_path("GAMES")
  111.     img.delete_path("DEMOS")
  112.     img.delete_path("3D")
  113.  
  114.     # Get test kernel
  115.     if not os.path.exists("kernel.mnt.pretest"):
  116.         if len(sys.argv) == 1:
  117.             execute("tup kernel.mnt.pretest")
  118.         else:
  119.             builds_eng = sys.argv[1]
  120.             kernel_mnt_pretest_subpath = "data/kernel/trunk/kernel.mnt.pretest"
  121.             kernel_mnt_pretest = f"{builds_eng}/{kernel_mnt_pretest_subpath}"
  122.             execute(f"cp {kernel_mnt_pretest} kernel.mnt.pretest", mute=True)
  123.  
  124.     # Put the kernel into IMG
  125.     with open("kernel.mnt.pretest", "rb") as kernel_mnt_pretest:
  126.         kernel_mnt_pretest_data = kernel_mnt_pretest.read()
  127.     img.add_file_path("KERNEL.MNT", kernel_mnt_pretest_data)
  128.     img.save("kolibri_test.img")
  129.  
  130.  
  131. def collect_tests():
  132.     tests = []
  133.  
  134.     # Collect tests from test folder (not recursively yet)
  135.     for test_folder in os.listdir("test"):
  136.         test_folder_path = f"test/{test_folder}"
  137.         test_file = f"{test_folder_path}/test.py"
  138.  
  139.         if not os.path.isdir(test_folder_path):
  140.             continue
  141.  
  142.         if os.path.exists(test_file):
  143.             tests.append(test_folder_path)
  144.     return tests
  145.  
  146.  
  147. def run_tests_serially_thread(test, root_dir):
  148.     test_number = 1
  149.     for test in tests:
  150.         test_dir = f"{root_dir}/{test}"
  151.  
  152.         print(f"[{test_number}/{len(tests)}] {test}... ", end="", flush=True)
  153.         start = timeit.default_timer()
  154.         try:
  155.             loader = SourceFileLoader("test", f"{test_dir}/test.py")
  156.             loader.load_module().run(root_dir, test_dir)
  157.         except common.TestTimeoutException:
  158.             result = "TIMEOUT"
  159.         except common.TestFailureException:
  160.             result = "FAILURE"
  161.         else:
  162.             result = "SUCCESS"
  163.         finish = timeit.default_timer()
  164.         print(f"{result} ({finish - start:.2f} seconds)")
  165.  
  166.         test_number += 1
  167.  
  168.  
  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.  
  174.  
  175. def build_umka_asm(object_output_dir):
  176.     umka_o = f"{object_output_dir}/umka.o"
  177.     kolibri_kernel_trunk_runtests_py = os.path.abspath(__file__)
  178.     kolibri_kernel_trunk = os.path.dirname(kolibri_kernel_trunk_runtests_py)
  179.     kolibri_kernel = os.path.dirname(kolibri_kernel_trunk)
  180.     kolibrios_folder = os.path.dirname(kolibri_kernel)
  181.     env = os.environ
  182.     libcrash = "programs/develop/libraries/libcrash/hash"
  183.     env["INCLUDE"] = ""
  184.     env["INCLUDE"] += f"{kolibrios_folder}/kernel/trunk;"
  185.     env["INCLUDE"] += f"{kolibrios_folder}/{libcrash}"
  186.     command = "fasm "
  187.     command += "-dWIN32=1 " if sys.platform == "win32" else ""
  188.     command += "-dUEFI=1 -dextended_primary_loader=1 -dUMKA=1 "
  189.     command += "umka/umka.asm umka/build/umka.o -s umka/build/umka.fas "
  190.     command += "-m 2000000 "
  191.     print(command)
  192.     stdout = subprocess.check_output(command, shell=True, env=env)
  193.     print(stdout.decode("ascii"))
  194.     return umka_o
  195.  
  196.  
  197. def cc(src, obj, include_path):
  198.     if tool_exists("i686-w64-mingw32-gcc"):
  199.         compiler = "i686-w64-mingw32-gcc"
  200.     elif tool_exists("clang"):
  201.         compiler = "clang"
  202.     else:
  203.         print("No compiler found to compile UMKa (tried i686-w64-mingw32-gcc and clang)")
  204.         print("  Please make sure you have installed llvm-mingw or clang")
  205.         print("  If they're installled consider updating PATH variable")
  206.     command = f"{compiler} "
  207.     command += "-Wno-everything -std=c11 -g -O0 -fno-pie -m32 -masm=intel -c "
  208.     command += "-D_FILE_OFFSET_BITS=64 -DNDEBUG -D_POSIX_C_SOURCE=200809L "
  209.     command += f"-I {include_path} {src} -o {obj}"
  210.     print(command)
  211.     if os.system(command) != 0:
  212.         exit()
  213.  
  214.  
  215. def link(objects):
  216.     if tool_exists("i686-w64-mingw32-gcc"):
  217.         compiler = "i686-w64-mingw32-gcc"
  218.     elif tool_exists("clang"):
  219.         compiler = "clang"
  220.     else:
  221.         print("No compiler found to compile UMKa (tried i686-w64-mingw32-gcc and clang)")
  222.         print("  Please make sure you have installed llvm-mingw or clang")
  223.         print("  If they're installled consider updating PATH variable")
  224.     if sys.platform == "linux" or sys.platform == "linux2":
  225.         linker_script = "-T umka/umka.ld"
  226.     else:
  227.         linker_script = "-Wl,/ALIGN:65536 -Wl,/MAP:umka.map "
  228.     command = f"{compiler} "
  229.     command += "-Wno-everything "
  230.     command += f"-no-pie -m32 -o umka_shell.exe -static {linker_script} "
  231.     command += " ".join(objects)
  232.     print(command)
  233.     if os.system(command) != 0:
  234.         exit()
  235.  
  236.  
  237. def build_umka():
  238.     if not use_umka:
  239.         return
  240.  
  241.     os.makedirs("umka/build", exist_ok=True)
  242.  
  243.     platform = "win32" if sys.platform == "win32" else "linux"
  244.  
  245.     os.makedirs(f"umka/build/{platform}", exist_ok=True)
  246.  
  247.     c_sources = [
  248.         "umka_shell.c",
  249.         "shell.c",
  250.         "trace.c",
  251.         "trace_lbr.c",
  252.         "vdisk.c",
  253.         "vnet.c",
  254.         "getopt.c",
  255.         "isatty.c",
  256.         "lodepng.c",
  257.         f"{platform}/pci.c",
  258.         f"{platform}/thread.c",
  259.         "util.c",
  260.     ]
  261.  
  262.     src_obj_pairs = [
  263.         (f"umka/{source}", f"umka/build/{source}.o") for source in c_sources
  264.     ]
  265.  
  266.     for src, obj in src_obj_pairs:
  267.         cc(src, obj, "umka/linux")
  268.  
  269.     umka_o = build_umka_asm("umka/build")
  270.  
  271.     objects = [obj for src, obj in src_obj_pairs] + [umka_o]
  272.     link(objects)
  273.  
  274.     os.chdir("umka/test")
  275.     for test in [t for t in os.listdir(".") if t.endswith(".t")]:
  276.         out_log = f"{test[:-2]}.out.log"
  277.         ref_log = f"{test[:-2]}.ref.log"
  278.         cmd_umka = f"..{os.sep}..{os.sep}umka_shell.exe < {test} > {out_log}"
  279.         print(cmd_umka)
  280.         os.system(cmd_umka)
  281.         with open(out_log, "rb") as f:
  282.             crlf = bytes([0x0D, 0x0A])
  283.             lf = bytes([0x0A])
  284.             out_log_contents = f.read().replace(crlf, lf)
  285.         with open(out_log, "wb") as f:
  286.             f.write(out_log_contents)
  287.         with open(ref_log, "rb") as f:
  288.             ref_log_contents = f.read()
  289.         if out_log_contents != ref_log_contents:
  290.             print("FAILURE")
  291.             exit()
  292.     os.chdir("../../")
  293.     print("SUCCESS")
  294.  
  295.  
  296. def download_umka():
  297.         if not use_umka:
  298.                 return
  299.  
  300.         if not os.path.exists("umka"):
  301.                 if os.system("git clone https://github.com/KolibriOS/umka") != 0:
  302.                         print("Couldn't clone UMKa repo")
  303.                         exit()
  304.         os.chdir("umka")
  305.         if os.system("git checkout trunk") != 0:
  306.                 print("Couldn't checkout trunk branch of UMKa")
  307.                 exit()
  308.         os.system("git pull")
  309.         os.chdir("../")
  310.  
  311.  
  312. def download_umka_imgs():
  313.         if not use_umka:
  314.                 return
  315.  
  316.         imgs = [
  317.                 "fat32_test0.img",
  318.                 "jfs.img",
  319.                 "kolibri.img",
  320.                 "xfs_borg_bit.img",
  321.                 "xfs_v4_btrees_l2.img",
  322.                 "xfs_v4_files_s05k_b4k_n8k.img",
  323.                 "xfs_v4_ftype0_s05k_b2k_n8k_xattr.img",
  324.                 "xfs_v4_ftype0_s05k_b2k_n8k.img",
  325.                 "xfs_v4_ftype0_s4k_b4k_n8k.img",
  326.                 "xfs_v4_ftype1_s05k_b2k_n8k.img",
  327.                 "xfs_v4_unicode.img",
  328.                 "xfs_v4_xattr.img",
  329.                 "xfs_v5_files_s05k_b4k_n8k.img",
  330.                 "xfs_v5_ftype1_s05k_b2k_n8k.img",
  331.         ]
  332.  
  333.         for img in imgs:
  334.                 if not os.path.exists(f"umka/img/{img}"):
  335.                         download(f"http://ftp.kolibrios.org/users/Boppan/img/{img}",
  336.                                          f"umka/img/{img}")
  337.  
  338. if __name__ == "__main__":
  339.     root_dir = os.getcwd()
  340.  
  341.     # Check available tools
  342.     tools = [["qemu-system-i386", "qemu-system-x86"],
  343.              ["fasm", "fasm"]]
  344.     if use_umka:
  345.         tools.append(["git", "git"]);
  346.     check_tools(tools)
  347.  
  348.     prepare_test_img()
  349.     download_umka()
  350.     download_umka_imgs()
  351.     build_umka()
  352.     tests = collect_tests()
  353.     serial_executor_thread = run_tests_serially(tests, root_dir)
  354.     serial_executor_thread.join()
  355.