run.py 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139
  1. import argparse
  2. import unittest
  3. import threading
  4. import queue
  5. import functools
  6. from sccd.util.os_tools import *
  7. from sccd.util.debug import *
  8. from sccd.cd.cd import *
  9. from sccd.controller.controller import *
  10. from sccd.test.parser import *
  11. from sccd.util import timer
  12. import sys
  13. if sys.version_info.minor >= 7:
  14. QueueImplementation = queue.SimpleQueue
  15. else:
  16. QueueImplementation = queue.Queue
  17. # A TestCase loading and executing a statechart test file.
  18. class Test(unittest.TestCase):
  19. def __init__(self, src: str):
  20. super().__init__()
  21. self.src = src
  22. def __str__(self):
  23. return self.src
  24. def runTest(self):
  25. # assume external statechart files in same directory as test
  26. path = os.path.dirname(self.src)
  27. sc_rules = functools.partial(statechart_parser_rules, path=path)
  28. test_rules = test_parser_rules(sc_rules)
  29. try:
  30. timer.start("parse test")
  31. test_variants = parse_f(self.src, test_rules)
  32. timer.stop("parse test")
  33. except Exception as e:
  34. print_debug(e)
  35. raise e
  36. for test in test_variants:
  37. print_debug('\n'+test.name)
  38. pipe = QueueImplementation()
  39. # interrupt = queue.Queue()
  40. controller = Controller(test.model)
  41. for i in test.input:
  42. controller.add_input(i)
  43. def controller_thread():
  44. try:
  45. # Run as-fast-as-possible, always advancing time to the next item in event queue, no sleeping.
  46. # The call returns when the event queue is empty and therefore the simulation is finished.
  47. controller.run_until(None, pipe)
  48. except Exception as e:
  49. print_debug(e)
  50. pipe.put(e, block=True, timeout=None)
  51. return
  52. # Signal end of output
  53. pipe.put(None, block=True, timeout=None)
  54. # start the controller
  55. thread = threading.Thread(target=controller_thread)
  56. thread.start()
  57. # check output
  58. expected = test.output
  59. actual = []
  60. def fail(msg):
  61. thread.join()
  62. def pretty(output):
  63. return '\n'.join("%d: %s" % (i, str(big_step)) for i, big_step in enumerate(output))
  64. self.fail('\n'+test.name + '\n'+msg + "\n\nActual:\n" + pretty(actual) + "\n\nExpected:\n" + pretty(expected))
  65. while True:
  66. data = pipe.get(block=True, timeout=None)
  67. if isinstance(data, Exception):
  68. raise data # Exception was caught in Controller thread, throw it here instead.
  69. elif data is None:
  70. # End of output
  71. if len(actual) < len(expected):
  72. fail("Less output than expected.")
  73. else:
  74. break
  75. else:
  76. big_step_index = len(actual)
  77. actual.append(data)
  78. if len(actual) > len(expected):
  79. fail("More output than expected.")
  80. actual_big_step = actual[big_step_index]
  81. expected_big_step = expected[big_step_index]
  82. if actual_big_step != expected_big_step:
  83. fail("Big step %d: output differs." % big_step_index)
  84. class FailingTest(Test):
  85. @unittest.expectedFailure
  86. def runTest(self):
  87. super().runTest()
  88. if __name__ == '__main__':
  89. argparser = argparse.ArgumentParser(
  90. description="Run SCCD tests.",
  91. epilog="Set environment variable SCCDDEBUG=1 to display debug information about the inner workings of the runtime.")
  92. argparser.add_argument('path', metavar='PATH', type=str, nargs='*', help="Tests to run. Can be a XML file or a directory. If a directory, it will be recursively scanned for XML files.")
  93. argparser.add_argument('--build-dir', metavar='BUILD_DIR', type=str, default='build', help="Directory for built tests. Defaults to 'build'")
  94. args = argparser.parse_args()
  95. src_files = get_files(args.path,
  96. filter=lambda file: (file.startswith("test_") or file.startswith("fail_")) and file.endswith(".xml"))
  97. if len(src_files) == 0:
  98. print("No input files specified.")
  99. print()
  100. argparser.print_usage()
  101. exit()
  102. suite = unittest.TestSuite()
  103. for src_file in src_files:
  104. should_fail = os.path.basename(src_file).startswith("fail_")
  105. if should_fail:
  106. suite.addTest(FailingTest(src_file))
  107. else:
  108. suite.addTest(Test(src_file))
  109. unittest.TextTestRunner(verbosity=2).run(suite)