File indexing completed on 2024-05-12 05:43:23

0001 #!/usr/bin/env python3
0002 # encoding: utf-8
0003 
0004 import argparse
0005 import difflib
0006 import json
0007 import os
0008 import shutil
0009 import sys
0010 import subprocess
0011 
0012 from doxyqml.main import main as doxyqml_main
0013 
0014 
0015 def list_files(topdir):
0016     result = []
0017 
0018     for root, dirs, files in os.walk(topdir):
0019         for name in files:
0020             subdir = root[len(topdir) + 1:]
0021             result.append(subdir and os.path.join(subdir, name) or name)
0022 
0023     return result
0024 
0025 
0026 class SubprocessRunner:
0027     def __init__(self, executable):
0028         self.executable = executable
0029 
0030     def run(self, qml_file, args, stdout, cwd):
0031         return subprocess.call(
0032             [self.executable, qml_file] + args,
0033             stdout=stdout, cwd=cwd)
0034 
0035 
0036 class ImportRunner:
0037     def run(self, qml_file, args, stdout, cwd):
0038         pwd = os.getcwd()
0039         os.chdir(cwd)
0040         try:
0041             return doxyqml_main([qml_file] + args, out=stdout)
0042         finally:
0043             os.chdir(pwd)
0044 
0045 
0046 class Test(object):
0047     def __init__(self, name, runner):
0048         self.name = name
0049         self.runner = runner
0050         self.input_dir = os.path.join(self.name, "input")
0051         self.output_dir = os.path.join(self.name, "output")
0052         self.expected_dir = os.path.join(self.name, "expected")
0053         self._read_args()
0054 
0055     def _read_args(self):
0056         args_json = os.path.join(self.name, "args.json")
0057         if not os.path.exists(args_json):
0058             self.args = []
0059             return
0060         with open(args_json) as f:
0061             self.args = json.load(f)
0062             assert type(self.args) is list
0063 
0064     def build(self):
0065         ok = True
0066         if os.path.exists(self.output_dir):
0067             shutil.rmtree(self.output_dir)
0068         os.mkdir(self.output_dir)
0069 
0070         for name in list_files(self.input_dir):
0071             if not name.endswith(".qml"):
0072                 continue
0073 
0074             out_path = os.path.join(self.output_dir, name + ".cpp")
0075             out_dir = os.path.dirname(out_path)
0076 
0077             if not os.path.isdir(out_dir):
0078                 os.makedirs(out_dir)
0079 
0080             with open(out_path, "w", encoding="iso-8859-1") as out:
0081                 ret = self.runner.run(name, args=self.args, stdout=out, cwd=self.input_dir)
0082                 if ret != 0:
0083                     self.error("doxyqml failed on {}".format(name))
0084                     ok = False
0085         return ok
0086 
0087     def update(self):
0088         if os.path.exists(self.expected_dir):
0089             shutil.rmtree(self.expected_dir)
0090         shutil.copytree(self.output_dir, self.expected_dir)
0091 
0092     def compare(self):
0093         lst = list_files(self.expected_dir)
0094         if not lst:
0095             self.error("expected_dir '{}' is empty".format(self.expected_dir))
0096             return False
0097 
0098         ok = True
0099         for name in lst:
0100             if name.startswith("."):
0101                 continue
0102             out_path = os.path.join(self.output_dir, name)
0103             if not os.path.exists(out_path):
0104                 self.error("File {} does not exist".format(out_path))
0105                 ok = False
0106                 continue
0107 
0108             out_lines = open(out_path, encoding="utf-8").readlines()
0109 
0110             expected_path = os.path.join(self.expected_dir, name)
0111             expected_lines = open(expected_path, encoding="utf-8").readlines()
0112 
0113             delta = difflib.unified_diff(expected_lines, out_lines, fromfile="expected", tofile="output")
0114             delta_lines = list(delta)
0115             if delta_lines:
0116                 ok = False
0117                 self.error("Failure on {}".format(name))
0118                 for line in delta_lines:
0119                     sys.stderr.write(line)
0120         return ok
0121 
0122     def error(self, msg):
0123         print("{}: ERROR: {}".format(self.name, msg))
0124 
0125 
0126 def main():
0127     script_dir = os.path.dirname(__file__) or "."
0128 
0129     default_doxyqml = "doxyqml"
0130 
0131     parser = argparse.ArgumentParser()
0132     parser.add_argument("-u", "--update",
0133                         help="Update expected output from test ID", metavar="ID")
0134     parser.add_argument("--doxyqml", default=default_doxyqml,
0135                         help="Path to the doxyqml executable ({})".format(default_doxyqml))
0136     parser.add_argument("test_id", nargs="?",
0137                         help="Run specified test only")
0138     parser.add_argument("--import", dest="import_", action="store_true",
0139                         help="Import Doxyqml module instead of using the executable. Useful for code coverage.")
0140     args = parser.parse_args()
0141 
0142     if args.import_:
0143         runner = ImportRunner()
0144     else:
0145         runner = SubprocessRunner(args.doxyqml)
0146 
0147     os.chdir(script_dir)
0148 
0149     if args.update:
0150         print("Updating {}...".format(args.update))
0151         test = Test(args.update, runner)
0152         if not test.build():
0153             return 1
0154         test.update()
0155         return 0
0156 
0157     if args.test_id:
0158         if not os.path.isdir(args.test_id):
0159             parser.error("Invalid test id '{}'".format(args.test_id))
0160         test_list = [args.test_id]
0161     else:
0162         test_list = [x for x in os.listdir(".") if os.path.isdir(x)]
0163 
0164     errors = 0
0165     for test_dir in test_list:
0166         print("Testing {}...".format(test_dir))
0167         test = Test(test_dir, runner)
0168         if not (test.build() and test.compare()):
0169             errors += 1
0170             continue
0171 
0172     print("")
0173     if errors:
0174         print("Failure: {} errors".format(errors))
0175         return 1
0176     else:
0177         print("Success")
0178         return 0
0179 
0180 
0181 if __name__ == "__main__":
0182     sys.exit(main())
0183 # vi: ts=4 sw=4 et