From a16e7955c811f6c213d5fde431d2937c939546c4 Mon Sep 17 00:00:00 2001 From: Kristian Larsson Date: Sun, 26 Jan 2025 17:10:16 +0100 Subject: [PATCH 1/3] Testing render improvements - Now printing 'A: None' and not just 'None' for None values - the if/else logic was wrong - Changed default to always print values, even when custom error message is set - this seem to make more sense to me, let's see - added print_vals arg throughout for user to explicitly disable printing values and just print the custom error message - renamed assertIsTrue -> assertTrue & assertIsFalse -> assertFalse - in Python, None, True and False are singletons so it is idiomatic to use "is" "is not" but in Acton, they are not, None is C NULL and True and False are their own objects, so let's not encourage is checking these by having that in the assert names - add test_testing project to show testing - this is not run automatically, since the idea is to showcase errors, which would then just fail CI - but very useful for a human to look at stuff --- base/src/testing.act | 307 ++++++++++++++++------------ test/stdlib_tests/src/test_json.act | 4 +- test/stdlib_tests/src/test_re.act | 16 +- test/stdlib_tests/src/test_xml.act | 4 +- test/test_testing/Acton.toml | 0 test/test_testing/src/errs.act | 62 ++++++ 6 files changed, 245 insertions(+), 148 deletions(-) create mode 100644 test/test_testing/Acton.toml create mode 100644 test/test_testing/src/errs.act diff --git a/base/src/testing.act b/base/src/testing.act index d040f80b5..603dc753d 100644 --- a/base/src/testing.act +++ b/base/src/testing.act @@ -9,229 +9,264 @@ import time # -- assert --------------------------------------------------------------------- +def opt_str[T](s: ?T) -> str: + return str(s) if s != None else "None" + class NotEqualError[T](AssertionError): a: ?T b: ?T - def __init__(self, a, b, msg: ?str=None): + def __init__(self, a, b, msg: ?str=None, print_vals: bool=True): self.a = a self.b = b - self.error_message = msg if msg is not None else "Expected equal values but they are non-equal." - self.print_vals = True if msg is None else False + self.error_message = msg if msg != None else "Expected equal values but they are non-equal." + self.print_vals = print_vals def __str__(self): msg = "%s: %s" % (self._name(), self.error_message) if self.print_vals: - a = self.a - msg += " A: " + str(a) if a is not None else "None" - b = self.b - msg += " B: " + str(b) if b is not None else "None" + msg += " A: " + opt_str(self.a) + msg += " B: " + opt_str(self.b) return msg class EqualError[T](AssertionError): a: ?T b: ?T - def __init__(self, a, b, msg: ?str=None): + def __init__(self, a, b, msg: ?str=None, print_vals: bool=True): self.a = a self.b = b - self.error_message = msg if msg is not None else "Expected non-equal values but they are equal." - self.print_vals = True if msg is None else False + self.error_message = msg if msg != None else "Expected non-equal values but they are equal." + self.print_vals = print_vals def __str__(self): msg = "%s: %s" % (self._name(), self.error_message) if self.print_vals: - a = self.a - msg += " A: " + str(a) if a is not None else "None" - b = self.b - msg += " B: " + str(b) if b is not None else "None" + msg += " A: " + opt_str(self.a) + msg += " B: " + opt_str(self.b) return msg class NotTrueError[T](AssertionError): a: ?T - def __init__(self, a, msg: ?str=None): + def __init__(self, a, msg: ?str=None, print_vals: bool=True): self.a = a - self.error_message = msg if msg is not None else "Expected True but got non-True" - self.print_vals = True if msg is None else False + self.error_message = msg if msg != None else "Expected True but got non-True" + self.print_vals = print_vals def __str__(self): msg = "%s: %s" % (self._name(), self.error_message) if self.print_vals: - a = self.a - msg += ", value: " + str(a) if a is not None else "None" + msg += ", value: " + opt_str(self.a) return msg class NotFalseError[T](AssertionError): a: ?T - def __init__(self, a, msg: ?str=None): + def __init__(self, a, msg: ?str=None, print_vals: bool=True): self.a = a - self.error_message = msg if msg is not None else "Expected False but got non-False." - self.print_vals = True if msg is None else False + self.error_message = msg if msg != None else "Expected False but got non-False." + self.print_vals = print_vals def __str__(self): msg = "%s: %s" % (self._name(), self.error_message) if self.print_vals: - a = self.a - msg += ", value: " + str(a) if a is not None else "None" + msg += ", value: " + opt_str(self.a) return msg class NotNoneError[T](AssertionError): a: ?T - def __init__(self, a, msg: ?str=None): + def __init__(self, a, msg: ?str=None, print_vals: bool=True): self.a = a - self.error_message = msg if msg is not None else "Expected None but got non-None." - self.print_vals = True if msg is None else False + self.error_message = msg if msg != None else "Expected None but got non-None." + self.print_vals = print_vals def __str__(self): msg = "%s: %s" % (self._name(), self.error_message) if self.print_vals: - a = self.a - msg += ", value: " + str(a) if a is not None else "None" + msg += ", value: " + opt_str(self.a) return msg class NoneError[T](AssertionError): a: ?T - def __init__(self, a, msg: ?str=None): + def __init__(self, a, msg: ?str=None, print_vals: bool=True): self.a = a - self.error_message = msg if msg is not None else "Expected non-None but got None." - self.print_vals = True if msg is None else False + self.error_message = msg if msg != None else "Expected non-None but got None." + self.print_vals = print_vals def __str__(self): msg = "%s: %s" % (self._name(), self.error_message) if self.print_vals: a = self.a - msg += ", value: " + str(a) if a is not None else "None" + msg += ", value: " + opt_str(a) return msg class NotInError[T,U](AssertionError): a: ?T b: ?U - def __init__(self, a, b, msg: ?str=None): + def __init__(self, a, b, msg: ?str=None, print_vals: bool=True): self.a = a self.b = b - self.error_message = msg if msg is not None else "Expected element not in container" - self.print_vals = True if msg is None else False + self.error_message = msg if msg != None else "Expected element not in container" + self.print_vals = print_vals def __str__(self): msg = "%s: %s" % (self._name(), self.error_message) if self.print_vals: - a = self.a - msg += ", element: " + str(a) if a is not None else "None" - b = self.b - msg += ", container: " + str(b) if b is not None else "None" + msg += ", element: " + opt_str(self.a) + msg += ", container: " + opt_str(self.b) return msg class InError[T,U](AssertionError): a: ?T b: ?U - def __init__(self, a, b, msg: ?str=None): + def __init__(self, a, b, msg: ?str=None, print_vals: bool=True): self.a = a self.b = b - self.error_message = msg if msg is not None else "Expected element in container" - self.print_vals = True if msg is None else False + self.error_message = msg if msg != None else "Expected element in container" + self.print_vals = print_vals def __str__(self): msg = "%s: %s" % (self._name(), self.error_message) if self.print_vals: - a = self.a - msg += ", element: " + str(a) if a is not None else "None" - b = self.b - msg += ", container: " + str(b) if b is not None else "None" + msg += ", element: " + opt_str(self.a) + msg += ", container: " + opt_str(self.b) return msg -class NotIsError[T(Identity)](AssertionError): - a: ?T - b: ?T - def __init__(self, a, b, msg: ?str=None): +class NotIsError[T](AssertionError): + a: T + b: T + + def __init__(self, a, b, msg: ?str=None, print_vals: bool=True): self.a = a self.b = b - self.error_message = msg if msg is not None else "a is not b" + self.error_message = msg if msg != None else "Expected both objects to be the same identity (is)" + self.print_vals = print_vals -class IsError[T(Identity)](AssertionError): - a: ?T - b: ?T - def __init__(self, a, b, msg: ?str=None): + def __str__(self): + msg = "%s: %s" % (self._name(), self.error_message) + if self.print_vals: + msg += " A: " + str(self.a) + msg += " B: " + str(self.b) + return msg + +class IsError[T](AssertionError): + a: T + b: T + + def __init__(self, a, b, msg: ?str=None, print_vals: bool=True): self.a = a self.b = b - self.error_message = msg if msg is not None else "a is b" + self.error_message = msg if msg != None else "Expected both objects to be different identities (is not)" + self.print_vals = print_vals + + def __str__(self): + msg = "%s: %s" % (self._name(), self.error_message) + if self.print_vals: + msg += " A: " + str(self.a) + msg += " B: " + str(self.b) + return msg class NotRaisesError(AssertionError): - def __init__(self, msg): - self.error_message = msg - self.a = None - -class IsInstanceError(AssertionError): - def __init__(self, msg): - self.error_message = msg - self.a = None - self.b = None - -class NotIsInstanceError(AssertionError): - def __init__(self, msg): - self.error_message = msg - self.a = None - self.b = None - - -def assertEqual[T(Eq)](a: ?T, b: ?T, msg: ?str): - if ((a is None and b is not None) - or (a is not None and b is None) - or (a is not None and b is not None and not (a == b))): - raise NotEqualError(a, b, msg) - -def assertNotEqual[T(Eq)](a: ?T, b: ?T, msg: ?str): - if ((a is None and b is None) - or (a is not None and b is not None and a == b)): - raise EqualError(a, b, msg) - -def assertTrue(a, msg: ?str): + def __init__(self, msg: ?str=None, print_vals: bool=True): + self.error_message = msg if msg != None else "Expected exception not raised" + self.print_vals = print_vals + +class IsInstanceError[T](AssertionError): + a: ?T + t: str + + def __init__(self, a, t: str, msg: ?str=None, print_vals: bool=True): + self.a = a + self.t = t + self.error_message = msg if msg != None else "expected type of specific instance" + self.print_vals = print_vals + +class NotIsInstanceError[T](AssertionError): + a: ?T + t: str + + def __init__(self, a, t: str, msg: ?str=None, print_vals: bool=True): + self.a = a + self.t = t + self.error_message = msg if msg != None else "expected type of specific instance" + + +def assertEqual[T(Eq)](a: ?T, b: ?T, msg: ?str, print_vals: bool=True): + """Assert that two values are equal + """ + if ((a == None and b != None) + or (a != None and b == None) + or (a != None and b != None and not (a == b))): + raise NotEqualError(a, b, msg, print_vals) + +def assertNotEqual[T(Eq)](a: ?T, b: ?T, msg: ?str, print_vals: bool=True): + """Assert that two values are not equal + """ + if ((a == None and b == None) + or (a != None and b != None and a == b)): + raise EqualError(a, b, msg, print_vals) + +def assertTrue(a, msg: ?str, print_vals: bool=True): + """Assert that the boolean evaluation of a value is True + """ if not bool(a): - raise NotTrueError(a, msg) + raise NotTrueError(a, msg, print_vals) -def assertFalse(a, msg: ?str): +def assertFalse(a, msg: ?str, print_vals: bool=True): + """Assert that the boolean evaluation of a value is False + """ if bool(a): - raise NotFalseError(a, msg) - -# TODO: fix this -#def assertIs[T(Identity)](a: ?T, b: ?T, msg: ?str): -# if not (a is b): -# raise NotIsError(a, b, msg) -# TODO: fix this -#def assertIsNot[T(Identity)](a: ?T, b: ?T, msg: ?str): -# if not (a is not b): -# raise IsError(a, b, msg) - -def assertIsNone(a, msg: ?str): - if not (a is None): - raise NotNoneError(a, msg) - -def assertIsNotNone(a, msg: ?str): - if not (a is not None): - raise NoneError(a, msg) - -def assertIn(a, b, msg: ?str): + raise NotFalseError(a, msg, print_vals) + +def assertIs[T(Identity)](a: T, b: T, msg: ?str, print_vals: bool=True): + """Assert that two values are the same identity, i.e. the same object + """ + if not (a is b): + raise NotIsError(a, b, msg) + +def assertIsNot[T(Identity)](a: T, b: T, msg: ?str, print_vals: bool=True): + """Assert that two values are not the same identity, i.e. different objects + """ + if a is b: + raise IsError(a, b, msg) + +def assertNone(a, msg: ?str, print_vals: bool=True): + """Assert that a value equals None + """ + if not (a == None): + raise NotNoneError(a, msg, print_vals) + +def assertNotNone(a, msg: ?str, print_vals: bool=True): + """Assert that a value does not equal None + """ + if not (a != None): + raise NoneError(a, msg, print_vals) + +def assertIn(a, b, msg: ?str, print_vals: bool=True): + """Assert that a value is in a container + """ if not (a in b): - raise NotInError(a, b, msg) + raise NotInError(a, b, msg, print_vals) -def assertNotIn(a, b, msg: ?str): +def assertNotIn(a, b, msg: ?str, print_vals: bool=True): + """Assert that a value is not in a container + """ if a in b: - raise InError(a, b, msg) - + raise InError(a, b, msg, print_vals) def error(msg: ?str): """Raise a generic test error""" - raise AssertionError(msg if msg is not None else "Test error") + raise AssertionError(msg if msg != None else "Test error") # ------------------------------------------------------------------------------- def eq_opt[T(Eq)](a: ?T, b: ?T) -> bool: - return a is not None and b is not None and a == b or a is None and b is None + return a != None and b != None and a == b or a == None and b == None class TestLogger(logging.Logger): pass @@ -525,7 +560,7 @@ class TestInfo(object): res_success = result.success if res_success == False: self.num_failures += 1 - elif res_success is None: + elif res_success == None: self.num_errors += 1 for result in self.results[1:]: @@ -567,7 +602,7 @@ class TestInfo(object): non_gc_mem_usage_delta_avg: str, non_gc_mem_inc_count: str): def fmt_diff(new, old, unit="") -> str: - if old is not None: + if old != None: diff = float(new) - float(old) pct_diff = diff / float(old) * 100 sign = "+" if diff > 0 else "" @@ -577,14 +612,14 @@ class TestInfo(object): else: return "%10s" % "" - #return (min_duration=fmt_diff(self.min_duration, old.min_duration if old is not None else None, "ms")) + #return (min_duration=fmt_diff(self.min_duration, old.min_duration if old != None else None, "ms")) return ( - fmt_diff(self.min_duration, old.min_duration if old is not None else None, "ms"), - fmt_diff(self.max_duration, old.max_duration if old is not None else None, "ms"), - fmt_diff(self.avg_duration, old.avg_duration if old is not None else None, "ms"), - fmt_diff(self.mem_usage_delta_avg, old.mem_usage_delta_avg if old is not None else None, "B"), - fmt_diff(self.non_gc_mem_usage_delta_avg, old.non_gc_mem_usage_delta_avg if old is not None else None, "B"), - fmt_diff(self.non_gc_mem_inc_count, old.non_gc_mem_inc_count if old is not None else None) + fmt_diff(self.min_duration, old.min_duration if old != None else None, "ms"), + fmt_diff(self.max_duration, old.max_duration if old != None else None, "ms"), + fmt_diff(self.avg_duration, old.avg_duration if old != None else None, "ms"), + fmt_diff(self.mem_usage_delta_avg, old.mem_usage_delta_avg if old != None else None, "B"), + fmt_diff(self.non_gc_mem_usage_delta_avg, old.non_gc_mem_usage_delta_avg if old != None else None, "B"), + fmt_diff(self.non_gc_mem_inc_count, old.non_gc_mem_inc_count if old != None else None) ) def to_json(self, include_results: bool=False): @@ -628,23 +663,23 @@ class TestInfo(object): complete = json_data["complete"] suc = json_data["success"] success: ?bool = None - if suc is not None and isinstance(suc, bool): + if suc != None and isinstance(suc, bool): success = suc exc = json_data["exception"] exception: ?str = None - if exc is not None and isinstance(exc, str): + if exc != None and isinstance(exc, str): exception = exc out = json_data["output"] output: ?str = None - if out is not None and isinstance(out, str): + if out != None and isinstance(out, str): output = out std_out_d = json_data["std_out"] std_out: ?str = None - if std_out_d is not None and isinstance(std_out_d, str): + if std_out_d != None and isinstance(std_out_d, str): std_out = std_out_d std_err_d = json_data["std_err"] std_err: ?str = None - if std_err_d is not None and isinstance(std_err_d, str): + if std_err_d != None and isinstance(std_err_d, str): std_err = std_err_d flaky = json_data["flaky"] min_duration = json_data["min_duration"] @@ -753,11 +788,11 @@ actor TestExecutor(syscap, config, t: Test, report_complete, env): non_gc_mem_usage_delta = non_gc_mem_usage_after - non_gc_mem_usage_before #print("non-GC memory before: %d after: %d delta: %d" % (non_gc_mem_usage_before, non_gc_mem_usage_after, non_gc_mem_usage_delta)) complete = True if test_dur > config.min_test_duration else False - if test_info is not None: - exc = str(exception) if exception is not None else None + if test_info != None: + exc = str(exception) if exception != None else None test_info.update(complete, TestResult(success, exc, val, testiter_dur, mem_usage_delta, non_gc_mem_usage_delta), test_dur*1000.0) if last_report.elapsed().to_float() > 0.05 or complete: - if test_info is not None and config.output_enabled: + if test_info != None and config.output_enabled: print("\n" + json.encode({"test_info": test_info.to_json()}), err=True) last_report.reset() if not complete: @@ -779,10 +814,10 @@ actor TestExecutor(syscap, config, t: Test, report_complete, env): def repres(s: ?bool, e: ?Exception, val: ?str) -> None: # Compare expected golden value - if val is not None: + if val != None: exp_val = get_expected(t.module, t.name) - if exp_val is None or exp_val is not None and val != exp_val: - exc = NotEqualError(val, exp_val, "Test output does not match expected golden value.\nActual : %s\nExpected: %s" % (val, exp_val if exp_val is not None else "None")) + if exp_val == None or exp_val != None and val != exp_val: + exc = NotEqualError(val, exp_val, "Test output does not match expected golden value.\nActual : %s\nExpected: %s" % (val, exp_val if exp_val != None else "None")) _report_result(t, sw, non_gc_mem_usage_before, gc_total_bytes_start, gc_time_start, False, exc, val) return _report_result(t, sw, non_gc_mem_usage_before, gc_total_bytes_start, gc_time_start, s, e, val) @@ -965,7 +1000,7 @@ class ProjectTestResults(object): msg = "" run_info = "" if tinfo.complete: - if exc is not None: + if exc != None: msg += term.bold + term.red if tinfo.flaky: msg += "FLAKY " @@ -1151,7 +1186,7 @@ actor test_runner(env: Env, args = p.parse(env.argv) _cmd = args.cmd - if _cmd is not None: + if _cmd != None: _cmd(args) else: env.exit(0) diff --git a/test/stdlib_tests/src/test_json.act b/test/stdlib_tests/src/test_json.act index 2490aac0f..1edf309d9 100644 --- a/test/stdlib_tests/src/test_json.act +++ b/test/stdlib_tests/src/test_json.act @@ -16,9 +16,9 @@ def _test_json(): #log.debug("Input string", {"json_str": s}) d = json.decode(s) #log.debug("Decoded JSON string to dict", {"dict": d}) - testing.assertIsNotNone(d, "Failed to decode JSON") + testing.assertNotNone(d, "Failed to decode JSON") e = json.encode(d) #log.debug("Encoded to JSON", {"encoded": s}) - testing.assertIsNotNone(e, "Failed to encode to JSON") + testing.assertNotNone(e, "Failed to encode to JSON") testing.assertEqual(s, e, "Input output via JSON round trip does not match") diff --git a/test/stdlib_tests/src/test_re.act b/test/stdlib_tests/src/test_re.act index 4343e98bd..3ccd302b4 100644 --- a/test/stdlib_tests/src/test_re.act +++ b/test/stdlib_tests/src/test_re.act @@ -6,13 +6,13 @@ def _test_match_basic(): """Basic regexp matching """ m = re.match("foo[a-z]+", "åbc123 foobar åbc123") - testing.assertIsNotNone(m, "basic regexp matching failed") + testing.assertNotNone(m, "basic regexp matching failed") def _test_match_group(): """Regexp matching with groups """ m = re.match("foo([a-z]+)", "åbc123 foobar åbc123") - testing.assertIsNotNone(m, "group matching failed") + testing.assertNotNone(m, "group matching failed") if m is not None: testing.assertEqual(m.group[0], "foobar", "whole group match failed") testing.assertEqual(m.group[1], "bar", "first group match failed") @@ -21,7 +21,7 @@ def _test_match_nested_group(): """Regexp matching with groups """ m = re.match("foo((B)[A-Z]+)", "åbc123 fooBAR åbc123") - testing.assertIsNotNone(m, "group matching failed") + testing.assertNotNone(m, "group matching failed") if m is not None: testing.assertEqual(m.group[0], "fooBAR", "whole group match failed") testing.assertEqual(m.group[1], "BAR", "first group match failed") @@ -31,7 +31,7 @@ def _test_match_nested_group_without_match(): """Regexp matching with groups """ m = re.match("foo((Z?)[A-Z]+)", "åbc123 fooBAR åbc123") - testing.assertIsNotNone(m, "group matching failed") + testing.assertNotNone(m, "group matching failed") if m is not None: testing.assertEqual(m.group[0], "fooBAR", "whole group match failed") testing.assertEqual(m.group[1], "BAR", "first group match failed") @@ -41,7 +41,7 @@ def _test_match_named_groups(): """Regexp matching with named groups """ m = re.match("(?Pfoo[a-z]+)", "åbc123 foobar åbc123") - testing.assertIsNotNone(m, "regexp matching with named groups failed") + testing.assertNotNone(m, "regexp matching with named groups failed") if m is not None: testing.assertEqual(m.named["mypattern"], "foobar", "named group ('foobar') match failed") @@ -50,7 +50,7 @@ def _test_match_named_groups_without_match(): """Regexp matching with named groups but without a match on an inner group """ m = re.match("(?Pfoo((?PAAA)|[a-z]+))", "åbc123 foobar åbc123") - testing.assertIsNotNone(m, "regexp matching with named groups failed") + testing.assertNotNone(m, "regexp matching with named groups failed") if m is not None: testing.assertEqual(m.named["mypattern"], "foobar", "named group ('foobar') match failed") @@ -58,7 +58,7 @@ def _test_match_multi_groups(): """Regexp matching with multiple groups including named groups """ m = re.match("([0-9]+).*(?Pfoo[a-z]+) *(?Pbar[A-Za-z0-9]+)", "åbc123 foobar barEX4MPLE åbc123") - testing.assertIsNotNone(m, "regexp matching with multiple groups failed") + testing.assertNotNone(m, "regexp matching with multiple groups failed") if m is not None: testing.assertEqual(m.group[0], "123 foobar barEX4MPLE", "failed to match") testing.assertEqual(m.group[1], "123", "failed to match group 1") @@ -81,7 +81,7 @@ def _test_match_positions(): # "a"=0, "b"=1, "c"=2, " "=3, "d"=4, "e"=5, "f"=6, " "=7, "g"=8, ... m = re.match("def", "abc def ghi") - testing.assertIsNotNone(m, "Should have matched 'def' in 'abc def ghi'") + testing.assertNotNone(m, "Should have matched 'def' in 'abc def ghi'") if m is not None: testing.assertEqual(m.start_pos, 4) testing.assertEqual(m.end_pos, 7) diff --git a/test/stdlib_tests/src/test_xml.act b/test/stdlib_tests/src/test_xml.act index cab132a63..f24daf8b1 100644 --- a/test/stdlib_tests/src/test_xml.act +++ b/test/stdlib_tests/src/test_xml.act @@ -27,8 +27,8 @@ def _test_xml_roundtrip(): e = xml.encode(d) #print(e) - testing.assertIsNotNone(d, "xml.decode() returned None") - testing.assertIsNotNone(e, "xml.encode() returned None") + testing.assertNotNone(d, "xml.decode() returned None") + testing.assertNotNone(e, "xml.encode() returned None") if s is not None and e is not None: testing.assertEqual(s, e, "XML str -> data -> XML str") diff --git a/test/test_testing/Acton.toml b/test/test_testing/Acton.toml new file mode 100644 index 000000000..e69de29bb diff --git a/test/test_testing/src/errs.act b/test/test_testing/src/errs.act new file mode 100644 index 000000000..973629fe1 --- /dev/null +++ b/test/test_testing/src/errs.act @@ -0,0 +1,62 @@ +import testing + +def _test_equal_failure(): + """Demonstrate assertEqual failure with different strings""" + testing.assertEqual("foo", "bar", "Strings should be equal") + +def _test_equal_none_failure(): + """Demonstrate assertEqual failure with None comparison""" + testing.assertEqual("foo", None, "String should equal None") + +def _test_not_equal_failure(): + """Demonstrate assertNotEqual failure with identical strings""" + testing.assertNotEqual("foo", "foo", "Strings should be different") + +def _test_not_equal_none_failure(): + """Demonstrate assertNotEqual failure with None values""" + testing.assertNotEqual(None, None, "None values should be different") + +def _test_true_failure(): + """Demonstrate assertTrue failure""" + testing.assertTrue(False, "False should be True") + +def _test_false_failure(): + """Demonstrate assertFalse failure""" + testing.assertFalse(True, "True should be False") + +#def _test_is_failure(): +# """Demonstrate assertIs failure with equal but different objects""" +# abc: str = "a" +# testing.assertIs(abc, abc, "Different lists should be identical") + +#def _test_is_not_failure(): +# """Demonstrate assertIsNot failure with same object""" +# x = 1 +# testing.assertIsNot(x, x, "Same list should be different") + +def _test_none_failure(): + """Demonstrate assertNone failure""" + testing.assertNone("not none", "Non-None should be None") + +def _test_not_none_failure(): + """Demonstrate assertNotNone failure""" + testing.assertNotNone(None, "None should be non-None") + +def _test_in_failure(): + """Demonstrate assertIn failure""" + testing.assertIn(4, [1, 2, 3], "4 should be in [1, 2, 3]") + +def _test_not_in_failure(): + """Demonstrate assertNotIn failure""" + testing.assertNotIn(1, [1, 2, 3], "1 should not be in [1, 2, 3]") + +def _test_performance_failure(): + """Demonstrate a failure in a computationally intensive test""" + result = 0 + for i in range(100000): + result += i + testing.assertEqual(result, 1, "Long computation should equal 1") + +def _test_generic_error(): + """Demonstrate generic error""" + testing.error("This is a generic test error") From f8e8fabb6e302ecefe0291bda11900139cf37e6f Mon Sep 17 00:00:00 2001 From: Kristian Larsson Date: Mon, 27 Jan 2025 10:31:04 +0100 Subject: [PATCH 2/3] Capture log messages in testing If a test is using the logging library, those messages are now captured. Practically this happens simply by the test printing them to the console (stderr) where the test runner already captures messages. --- base/src/testing.act | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/base/src/testing.act b/base/src/testing.act index 603dc753d..6f8ac8ad8 100644 --- a/base/src/testing.act +++ b/base/src/testing.act @@ -755,7 +755,8 @@ class TimeoutError(Exception): actor TestExecutor(syscap, config, t: Test, report_complete, env): """The actual executor of tests """ - log_handler = logging.Handler("TestRunner") + log_handler = logging.Handler() + log_handler.add_sink(logging.ConsoleSink()) fcap = file.FileCap(env.cap) rfcap = file.ReadFileCap(fcap) fs = file.FS(fcap) @@ -1053,14 +1054,14 @@ class ProjectTestResults(object): std_out = tinfo.std_out if std_out != None and test_output(std_out): - print(" STD_OUT:") + print(" STDOUT:") self.printed_lines += 1 for line in std_out.strip().splitlines(): print(" " + line) self.printed_lines += 1 std_err = tinfo.std_err if std_err != None and test_output(std_err): - print(" STD_ERR:") + print(" STDERR:") self.printed_lines += 1 for line in std_err.strip().splitlines(): print(" " + line) From 54ebd04677df76c3d4b7dde97a078f0ce55a9519 Mon Sep 17 00:00:00 2001 From: Kristian Larsson Date: Mon, 27 Jan 2025 10:32:18 +0100 Subject: [PATCH 3/3] Add test of logging in tests Just to be run manually to inspect the output. --- test/test_testing/src/logg.act | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) create mode 100644 test/test_testing/src/logg.act diff --git a/test/test_testing/src/logg.act b/test/test_testing/src/logg.act new file mode 100644 index 000000000..bb5a9db9b --- /dev/null +++ b/test/test_testing/src/logg.act @@ -0,0 +1,17 @@ +import logging +import testing + +actor AsyncTester(report_result, log_handler): + log = logging.Logger(log_handler) + + def done(): + report_result(False, None) + + def test(): + log.info("AsyncTester.test()", None) + after 0.01: done() + +def _test_asyncact(report_result, log_handler: logging.Handler) -> None: + s = AsyncTester(report_result, log_handler) + s.test() +