From 8117ef6692ce5c638960cfe72800c2d81c872683 Mon Sep 17 00:00:00 2001 From: Jacob Graves Date: Tue, 28 Jul 2020 13:35:25 -0600 Subject: [PATCH] tests: fix remove unnecessary workaround for some unittest assertions. --- nmigen/test/test_build_dsl.py | 92 ++++++++--------- nmigen/test/test_build_plat.py | 12 +-- nmigen/test/test_build_res.py | 78 +++++++------- nmigen/test/test_hdl_ast.py | 182 ++++++++++++++++----------------- nmigen/test/test_hdl_cd.py | 12 +-- nmigen/test/test_hdl_dsl.py | 174 +++++++++++++++---------------- nmigen/test/test_hdl_ir.py | 94 ++++++++--------- nmigen/test/test_hdl_mem.py | 34 +++--- nmigen/test/test_hdl_rec.py | 58 +++++------ nmigen/test/test_hdl_xfrm.py | 16 +-- nmigen/test/test_lib_cdc.py | 36 +++---- nmigen/test/test_lib_fifo.py | 20 ++-- nmigen/test/test_sim.py | 12 +-- nmigen/test/utils.py | 17 --- 14 files changed, 410 insertions(+), 427 deletions(-) diff --git a/nmigen/test/test_build_dsl.py b/nmigen/test/test_build_dsl.py index bf901bf..87f082a 100644 --- a/nmigen/test/test_build_dsl.py +++ b/nmigen/test/test_build_dsl.py @@ -46,19 +46,19 @@ class PinsTestCase(FHDLTestCase): self.assertEqual(p.map_names(mapping, p), ["A1"]) def test_wrong_names(self): - with self.assertRaises(TypeError, - msg="Names must be a whitespace-separated string, not ['A0', 'A1', 'A2']"): + with self.assertRaisesRegex(TypeError, + r"^Names must be a whitespace-separated string, not \['A0', 'A1', 'A2'\]$"): p = Pins(["A0", "A1", "A2"]) def test_wrong_dir(self): - with self.assertRaises(TypeError, - msg="Direction must be one of \"i\", \"o\", \"oe\", or \"io\", not 'wrong'"): + with self.assertRaisesRegex(TypeError, + r"^Direction must be one of \"i\", \"o\", \"oe\", or \"io\", not 'wrong'$"): p = Pins("A0 A1", dir="wrong") def test_wrong_conn(self): - with self.assertRaises(TypeError, - msg="Connector must be None or a pair of string (connector name) and " - "integer/string (connector number), not ('foo', None)"): + with self.assertRaisesRegex(TypeError, + (r"^Connector must be None or a pair of string \(connector name\) and " + r"integer\/string \(connector number\), not \('foo', None\)$")): p = Pins("A0 A1", conn=("foo", None)) def test_wrong_map_names(self): @@ -66,14 +66,14 @@ class PinsTestCase(FHDLTestCase): mapping = { "pmod_0:0": "A0", } - with self.assertRaises(NameError, - msg="Resource (pins io pmod_0:0 pmod_0:1 pmod_0:2) refers to nonexistent " - "connector pin pmod_0:1"): + with self.assertRaisesRegex(NameError, + (r"^Resource \(pins io pmod_0:0 pmod_0:1 pmod_0:2\) refers to nonexistent " + r"connector pin pmod_0:1$")): p.map_names(mapping, p) def test_wrong_assert_width(self): - with self.assertRaises(AssertionError, - msg="3 names are specified (0 1 2), but 4 names are expected"): + with self.assertRaisesRegex(AssertionError, + r"^3 names are specified \(0 1 2\), but 4 names are expected$"): Pins("0 1 2", assert_width=4) @@ -108,14 +108,14 @@ class DiffPairsTestCase(FHDLTestCase): self.assertEqual(dp.n.dir, "o") def test_wrong_width(self): - with self.assertRaises(TypeError, - msg="Positive and negative pins must have the same width, but (pins io A0) " - "and (pins io B0 B1) do not"): + with self.assertRaisesRegex(TypeError, + (r"^Positive and negative pins must have the same width, but \(pins io A0\) " + r"and \(pins io B0 B1\) do not$")): dp = DiffPairs("A0", "B0 B1") def test_wrong_assert_width(self): - with self.assertRaises(AssertionError, - msg="3 names are specified (0 1 2), but 4 names are expected"): + with self.assertRaisesRegex(AssertionError, + r"^3 names are specified \(0 1 2\), but 4 names are expected$"): DiffPairs("0 1 2", "3 4 5", assert_width=4) @@ -137,8 +137,8 @@ class AttrsTestCase(FHDLTestCase): self.assertEqual(repr(a), "(attrs FOO={!r})".format(fn)) def test_wrong_value(self): - with self.assertRaises(TypeError, - msg="Value of attribute FOO must be None, int, str, or callable, not 1.0"): + with self.assertRaisesRegex(TypeError, + r"^Value of attribute FOO must be None, int, str, or callable, not 1\.0$"): a = Attrs(FOO=1.0) @@ -187,42 +187,42 @@ class SubsignalTestCase(FHDLTestCase): self.assertEqual(s.clock.frequency, 1e6) def test_wrong_empty_io(self): - with self.assertRaises(ValueError, msg="Missing I/O constraints"): + with self.assertRaisesRegex(ValueError, r"^Missing I\/O constraints$"): s = Subsignal("a") def test_wrong_io(self): - with self.assertRaises(TypeError, - msg="Constraint must be one of Pins, DiffPairs, Subsignal, Attrs, or Clock, " - "not 'wrong'"): + with self.assertRaisesRegex(TypeError, + (r"^Constraint must be one of Pins, DiffPairs, Subsignal, Attrs, or Clock, " + r"not 'wrong'$")): s = Subsignal("a", "wrong") def test_wrong_pins(self): - with self.assertRaises(TypeError, - msg="Pins and DiffPairs are incompatible with other location or subsignal " - "constraints, but (pins io A1) appears after (pins io A0)"): + with self.assertRaisesRegex(TypeError, + (r"^Pins and DiffPairs are incompatible with other location or subsignal " + r"constraints, but \(pins io A1\) appears after \(pins io A0\)$")): s = Subsignal("a", Pins("A0"), Pins("A1")) def test_wrong_diffpairs(self): - with self.assertRaises(TypeError, - msg="Pins and DiffPairs are incompatible with other location or subsignal " - "constraints, but (pins io A1) appears after (diffpairs io (p A0) (n B0))"): + with self.assertRaisesRegex(TypeError, + (r"^Pins and DiffPairs are incompatible with other location or subsignal " + r"constraints, but \(pins io A1\) appears after \(diffpairs io \(p A0\) \(n B0\)\)$")): s = Subsignal("a", DiffPairs("A0", "B0"), Pins("A1")) def test_wrong_subsignals(self): - with self.assertRaises(TypeError, - msg="Pins and DiffPairs are incompatible with other location or subsignal " - "constraints, but (pins io B0) appears after (subsignal b (pins io A0))"): + with self.assertRaisesRegex(TypeError, + (r"^Pins and DiffPairs are incompatible with other location or subsignal " + r"constraints, but \(pins io B0\) appears after \(subsignal b \(pins io A0\)\)$")): s = Subsignal("a", Subsignal("b", Pins("A0")), Pins("B0")) def test_wrong_clock(self): - with self.assertRaises(TypeError, - msg="Clock constraint can only be applied to Pins or DiffPairs, not " - "(subsignal b (pins io A0))"): + with self.assertRaisesRegex(TypeError, + (r"^Clock constraint can only be applied to Pins or DiffPairs, not " + r"\(subsignal b \(pins io A0\)\)$")): s = Subsignal("a", Subsignal("b", Pins("A0")), Clock(1e6)) def test_wrong_clock_many(self): - with self.assertRaises(ValueError, - msg="Clock constraint can be applied only once"): + with self.assertRaisesRegex(ValueError, + r"^Clock constraint can be applied only once$"): s = Subsignal("a", Pins("A0"), Clock(1e6), Clock(1e7)) @@ -309,20 +309,20 @@ class ConnectorTestCase(FHDLTestCase): self.assertEqual(c.number, "A") def test_conn_wrong_name(self): - with self.assertRaises(TypeError, - msg="Connector must be None or a pair of string (connector name) and " - "integer/string (connector number), not ('foo', None)"): + with self.assertRaisesRegex(TypeError, + (r"^Connector must be None or a pair of string \(connector name\) and " + r"integer\/string \(connector number\), not \('foo', None\)$")): Connector("ext", "A", "0 1 2", conn=("foo", None)) def test_wrong_io(self): - with self.assertRaises(TypeError, - msg="Connector I/Os must be a dictionary or a string, not []"): + with self.assertRaisesRegex(TypeError, + r"^Connector I\/Os must be a dictionary or a string, not \[\]$"): Connector("pmod", 0, []) def test_wrong_dict_key_value(self): - with self.assertRaises(TypeError, - msg="Connector pin name must be a string, not 0"): + with self.assertRaisesRegex(TypeError, + r"^Connector pin name must be a string, not 0$"): Connector("pmod", 0, {0: "A"}) - with self.assertRaises(TypeError, - msg="Platform pin name must be a string, not 0"): + with self.assertRaisesRegex(TypeError, + r"^Platform pin name must be a string, not 0$"): Connector("pmod", 0, {"A": 0}) diff --git a/nmigen/test/test_build_plat.py b/nmigen/test/test_build_plat.py index 2b2ec62..1342501 100644 --- a/nmigen/test/test_build_plat.py +++ b/nmigen/test/test_build_plat.py @@ -36,17 +36,17 @@ class PlatformTestCase(FHDLTestCase): self.assertEqual(self.platform.extra_files["x.txt"], f.read()) def test_add_file_wrong_filename(self): - with self.assertRaises(TypeError, - msg="File name must be a string, not 1"): + with self.assertRaisesRegex(TypeError, + r"^File name must be a string, not 1$"): self.platform.add_file(1, "") def test_add_file_wrong_contents(self): - with self.assertRaises(TypeError, - msg="File contents must be str, bytes, or a file-like object, not 1"): + with self.assertRaisesRegex(TypeError, + r"^File contents must be str, bytes, or a file-like object, not 1$"): self.platform.add_file("foo", 1) def test_add_file_wrong_duplicate(self): self.platform.add_file("foo", "") - with self.assertRaises(ValueError, - msg="File 'foo' already exists"): + with self.assertRaisesRegex(ValueError, + r"^File 'foo' already exists$"): self.platform.add_file("foo", "bar") diff --git a/nmigen/test/test_build_res.py b/nmigen/test/test_build_res.py index 3b3a9d5..db105a6 100644 --- a/nmigen/test/test_build_res.py +++ b/nmigen/test/test_build_res.py @@ -208,43 +208,43 @@ class ResourceManagerTestCase(FHDLTestCase): ]) def test_wrong_resources(self): - with self.assertRaises(TypeError, msg="Object 'wrong' is not a Resource"): + with self.assertRaisesRegex(TypeError, r"^Object 'wrong' is not a Resource$"): self.cm.add_resources(['wrong']) def test_wrong_resources_duplicate(self): - with self.assertRaises(NameError, - msg="Trying to add (resource user_led 0 (pins o A1)), but " - "(resource user_led 0 (pins o A0)) has the same name and number"): + with self.assertRaisesRegex(NameError, + (r"^Trying to add \(resource user_led 0 \(pins o A1\)\), but " + r"\(resource user_led 0 \(pins o A0\)\) has the same name and number$")): self.cm.add_resources([Resource("user_led", 0, Pins("A1", dir="o"))]) def test_wrong_connectors(self): - with self.assertRaises(TypeError, msg="Object 'wrong' is not a Connector"): + with self.assertRaisesRegex(TypeError, r"^Object 'wrong' is not a Connector$"): self.cm.add_connectors(['wrong']) def test_wrong_connectors_duplicate(self): - with self.assertRaises(NameError, - msg="Trying to add (connector pmod 0 1=>1 2=>2), but " - "(connector pmod 0 1=>B0 2=>B1 3=>B2 4=>B3) has the same name and number"): + with self.assertRaisesRegex(NameError, + (r"^Trying to add \(connector pmod 0 1=>1 2=>2\), but " + r"\(connector pmod 0 1=>B0 2=>B1 3=>B2 4=>B3\) has the same name and number$")): self.cm.add_connectors([Connector("pmod", 0, "1 2")]) def test_wrong_lookup(self): - with self.assertRaises(ResourceError, - msg="Resource user_led#1 does not exist"): + with self.assertRaisesRegex(ResourceError, + r"^Resource user_led#1 does not exist$"): r = self.cm.lookup("user_led", 1) def test_wrong_clock_signal(self): - with self.assertRaises(TypeError, - msg="Object None is not a Signal"): + with self.assertRaisesRegex(TypeError, + r"^Object None is not a Signal$"): self.cm.add_clock_constraint(None, 10e6) def test_wrong_clock_frequency(self): - with self.assertRaises(TypeError, - msg="Frequency must be a number, not None"): + with self.assertRaisesRegex(TypeError, + r"^Frequency must be a number, not None$"): self.cm.add_clock_constraint(Signal(), None) def test_wrong_request_duplicate(self): - with self.assertRaises(ResourceError, - msg="Resource user_led#0 has already been requested"): + with self.assertRaisesRegex(ResourceError, + r"^Resource user_led#0 has already been requested$"): self.cm.request("user_led", 0) self.cm.request("user_led", 0) @@ -253,46 +253,46 @@ class ResourceManagerTestCase(FHDLTestCase): Resource("clk20", 0, Pins("H1", dir="i")), ]) self.cm.request("clk100", 0) - with self.assertRaises(ResourceError, - msg="Resource component clk20_0 uses physical pin H1, but it is already " - "used by resource component clk100_0 that was requested earlier"): + with self.assertRaisesRegex(ResourceError, + (r"^Resource component clk20_0 uses physical pin H1, but it is already " + r"used by resource component clk100_0 that was requested earlier$")): self.cm.request("clk20", 0) def test_wrong_request_with_dir(self): - with self.assertRaises(TypeError, - msg="Direction must be one of \"i\", \"o\", \"oe\", \"io\", or \"-\", " - "not 'wrong'"): + with self.assertRaisesRegex(TypeError, + (r"^Direction must be one of \"i\", \"o\", \"oe\", \"io\", or \"-\", " + r"not 'wrong'$")): user_led = self.cm.request("user_led", 0, dir="wrong") def test_wrong_request_with_dir_io(self): - with self.assertRaises(ValueError, - msg="Direction of (pins o A0) cannot be changed from \"o\" to \"i\"; direction " - "can be changed from \"io\" to \"i\", \"o\", or \"oe\", or from anything " - "to \"-\""): + with self.assertRaisesRegex(ValueError, + (r"^Direction of \(pins o A0\) cannot be changed from \"o\" to \"i\"; direction " + r"can be changed from \"io\" to \"i\", \"o\", or \"oe\", or from anything " + r"to \"-\"$")): user_led = self.cm.request("user_led", 0, dir="i") def test_wrong_request_with_dir_dict(self): - with self.assertRaises(TypeError, - msg="Directions must be a dict, not 'i', because (resource i2c 0 (subsignal scl " - "(pins o N10)) (subsignal sda (pins io N11))) " - "has subsignals"): + with self.assertRaisesRegex(TypeError, + (r"^Directions must be a dict, not 'i', because \(resource i2c 0 \(subsignal scl " + r"\(pins o N10\)\) \(subsignal sda \(pins io N11\)\)\) " + r"has subsignals$")): i2c = self.cm.request("i2c", 0, dir="i") def test_wrong_request_with_wrong_xdr(self): - with self.assertRaises(ValueError, - msg="Data rate of (pins o A0) must be a non-negative integer, not -1"): + with self.assertRaisesRegex(ValueError, + r"^Data rate of \(pins o A0\) must be a non-negative integer, not -1$"): user_led = self.cm.request("user_led", 0, xdr=-1) def test_wrong_request_with_xdr_dict(self): - with self.assertRaises(TypeError, - msg="Data rate must be a dict, not 2, because (resource i2c 0 (subsignal scl " - "(pins o N10)) (subsignal sda (pins io N11))) " - "has subsignals"): + with self.assertRaisesRegex(TypeError, + r"^Data rate must be a dict, not 2, because \(resource i2c 0 \(subsignal scl " + r"\(pins o N10\)\) \(subsignal sda \(pins io N11\)\)\) " + r"has subsignals$"): i2c = self.cm.request("i2c", 0, xdr=2) def test_wrong_clock_constraint_twice(self): clk100 = self.cm.request("clk100") - with self.assertRaises(ValueError, - msg="Cannot add clock constraint on (sig clk100_0__i), which is already " - "constrained to 100000000.0 Hz"): + with self.assertRaisesRegex(ValueError, + (r"^Cannot add clock constraint on \(sig clk100_0__i\), which is already " + r"constrained to 100000000\.0 Hz$")): self.cm.add_clock_constraint(clk100.i, 1e6) diff --git a/nmigen/test/test_hdl_ast.py b/nmigen/test/test_hdl_ast.py index 691408a..af87415 100644 --- a/nmigen/test/test_hdl_ast.py +++ b/nmigen/test/test_hdl_ast.py @@ -35,18 +35,18 @@ class ShapeTestCase(FHDLTestCase): self.assertEqual(s3.signed, True) def test_make_wrong(self): - with self.assertRaises(TypeError, - msg="Width must be a non-negative integer, not -1"): + with self.assertRaisesRegex(TypeError, + r"^Width must be a non-negative integer, not -1$"): Shape(-1) def test_compare_wrong(self): - with self.assertRaises(TypeError, - msg="Shapes may be compared with other Shapes and (int, bool) tuples, not 'hi'"): + with self.assertRaisesRegex(TypeError, + r"^Shapes may be compared with other Shapes and \(int, bool\) tuples, not 'hi'$"): Shape(1, True) == 'hi' def test_compare_tuple_wrong(self): - with self.assertRaises(TypeError, - msg="Shapes may be compared with other Shapes and (int, bool) tuples, not (2, 3)"): + with self.assertRaisesRegex(TypeError, + r"^Shapes may be compared with other Shapes and \(int, bool\) tuples, not \(2, 3\)$"): Shape(1, True) == (2, 3) def test_repr(self): @@ -84,8 +84,8 @@ class ShapeTestCase(FHDLTestCase): self.assertEqual(s1.signed, False) def test_cast_int_wrong(self): - with self.assertRaises(TypeError, - msg="Width must be a non-negative integer, not -1"): + with self.assertRaisesRegex(TypeError, + r"^Width must be a non-negative integer, not -1$"): Shape.cast(-1) def test_cast_tuple(self): @@ -98,8 +98,8 @@ class ShapeTestCase(FHDLTestCase): def test_cast_tuple_wrong(self): with warnings.catch_warnings(): warnings.filterwarnings(action="ignore", category=DeprecationWarning) - with self.assertRaises(TypeError, - msg="Width must be a non-negative integer, not -1"): + with self.assertRaisesRegex(TypeError, + r"^Width must be a non-negative integer, not -1$"): Shape.cast((-1, True)) def test_cast_range(self): @@ -134,13 +134,13 @@ class ShapeTestCase(FHDLTestCase): self.assertEqual(s2.signed, True) def test_cast_enum_bad(self): - with self.assertRaises(TypeError, - msg="Only enumerations with integer values can be used as value shapes"): + with self.assertRaisesRegex(TypeError, + r"^Only enumerations with integer values can be used as value shapes$"): Shape.cast(StringEnum) def test_cast_bad(self): - with self.assertRaises(TypeError, - msg="Object 'foo' cannot be used as value shape"): + with self.assertRaisesRegex(TypeError, + r"^Object 'foo' cannot be used as value shape$"): Shape.cast("foo") @@ -150,8 +150,8 @@ class ValueTestCase(FHDLTestCase): self.assertIsInstance(Value.cast(True), Const) c = Const(0) self.assertIs(Value.cast(c), c) - with self.assertRaises(TypeError, - msg="Object 'str' cannot be converted to an nMigen value"): + with self.assertRaisesRegex(TypeError, + r"^Object 'str' cannot be converted to an nMigen value$"): Value.cast("str") def test_cast_enum(self): @@ -163,13 +163,13 @@ class ValueTestCase(FHDLTestCase): self.assertEqual(e2.shape(), signed(2)) def test_cast_enum_wrong(self): - with self.assertRaises(TypeError, - msg="Only enumerations with integer values can be used as value shapes"): + with self.assertRaisesRegex(TypeError, + r"^Only enumerations with integer values can be used as value shapes$"): Value.cast(StringEnum.FOO) def test_bool(self): - with self.assertRaises(TypeError, - msg="Attempted to convert nMigen value to Python boolean"): + with self.assertRaisesRegex(TypeError, + r"^Attempted to convert nMigen value to Python boolean$"): if Const(0): pass @@ -185,8 +185,8 @@ class ValueTestCase(FHDLTestCase): self.assertIsInstance(s2, Slice) self.assertEqual(s2.start, 3) self.assertEqual(s2.stop, 4) - with self.assertRaises(IndexError, - msg="Cannot index 5 bits into 4-bit value"): + with self.assertRaisesRegex(IndexError, + r"^Cannot index 5 bits into 4-bit value$"): Const(10)[5] def test_getitem_slice(self): @@ -211,8 +211,8 @@ class ValueTestCase(FHDLTestCase): self.assertEqual(s3.parts[2].stop, 5) def test_getitem_wrong(self): - with self.assertRaises(TypeError, - msg="Cannot index value with 'str'"): + with self.assertRaisesRegex(TypeError, + r"^Cannot index value with 'str'$"): Const(31)["str"] def test_shift_left(self): @@ -240,8 +240,8 @@ class ValueTestCase(FHDLTestCase): "(s (slice (const 9'sd-256) 9:9))") def test_shift_left_wrong(self): - with self.assertRaises(TypeError, - msg="Shift amount must be an integer, not 'str'"): + with self.assertRaisesRegex(TypeError, + r"^Shift amount must be an integer, not 'str'$"): Const(31).shift_left("str") def test_shift_right(self): @@ -269,8 +269,8 @@ class ValueTestCase(FHDLTestCase): "(s (slice (const 9'sd-256) 9:9))") def test_shift_right_wrong(self): - with self.assertRaises(TypeError, - msg="Shift amount must be an integer, not 'str'"): + with self.assertRaisesRegex(TypeError, + r"^Shift amount must be an integer, not 'str'$"): Const(31).shift_left("str") def test_rotate_left(self): @@ -284,8 +284,8 @@ class ValueTestCase(FHDLTestCase): "(cat (slice (const 9'd256) 7:9) (slice (const 9'd256) 0:7))") def test_rotate_left_wrong(self): - with self.assertRaises(TypeError, - msg="Rotate amount must be an integer, not 'str'"): + with self.assertRaisesRegex(TypeError, + r"^Rotate amount must be an integer, not 'str'$"): Const(31).rotate_left("str") def test_rotate_right(self): @@ -299,8 +299,8 @@ class ValueTestCase(FHDLTestCase): "(cat (slice (const 9'd256) 2:9) (slice (const 9'd256) 0:2))") def test_rotate_right_wrong(self): - with self.assertRaises(TypeError, - msg="Rotate amount must be an integer, not 'str'"): + with self.assertRaisesRegex(TypeError, + r"^Rotate amount must be an integer, not 'str'$"): Const(31).rotate_right("str") @@ -318,8 +318,8 @@ class ConstTestCase(FHDLTestCase): self.assertEqual(Const(0, unsigned(0)).shape(), unsigned(0)) def test_shape_wrong(self): - with self.assertRaises(TypeError, - msg="Width must be a non-negative integer, not -1"): + with self.assertRaisesRegex(TypeError, + r"^Width must be a non-negative integer, not -1$"): Const(1, -1) def test_normalization(self): @@ -413,8 +413,8 @@ class OperatorTestCase(FHDLTestCase): self.assertEqual(v5.shape(), unsigned(4)) def test_mod_wrong(self): - with self.assertRaises(NotImplementedError, - msg="Division by a signed value is not supported"): + with self.assertRaisesRegex(NotImplementedError, + r"^Division by a signed value is not supported$"): Const(0, signed(4)) % Const(0, signed(6)) def test_floordiv(self): @@ -427,8 +427,8 @@ class OperatorTestCase(FHDLTestCase): self.assertEqual(v5.shape(), unsigned(4)) def test_floordiv_wrong(self): - with self.assertRaises(NotImplementedError, - msg="Division by a signed value is not supported"): + with self.assertRaisesRegex(NotImplementedError, + r"^Division by a signed value is not supported$"): Const(0, signed(4)) // Const(0, signed(6)) def test_and(self): @@ -476,11 +476,11 @@ class OperatorTestCase(FHDLTestCase): self.assertEqual(v1.shape(), unsigned(11)) def test_shl_wrong(self): - with self.assertRaises(TypeError, - msg="Shift amount must be unsigned"): + with self.assertRaisesRegex(TypeError, + r"^Shift amount must be unsigned$"): 1 << Const(0, signed(6)) - with self.assertRaises(TypeError, - msg="Shift amount must be unsigned"): + with self.assertRaisesRegex(TypeError, + r"^Shift amount must be unsigned$"): Const(1, unsigned(4)) << -1 def test_shr(self): @@ -489,11 +489,11 @@ class OperatorTestCase(FHDLTestCase): self.assertEqual(v1.shape(), unsigned(4)) def test_shr_wrong(self): - with self.assertRaises(TypeError, - msg="Shift amount must be unsigned"): + with self.assertRaisesRegex(TypeError, + r"^Shift amount must be unsigned$"): 1 << Const(0, signed(6)) - with self.assertRaises(TypeError, - msg="Shift amount must be unsigned"): + with self.assertRaisesRegex(TypeError, + r"^Shift amount must be unsigned$"): Const(1, unsigned(4)) << -1 def test_lt(self): @@ -588,25 +588,25 @@ class OperatorTestCase(FHDLTestCase): def test_matches_width_wrong(self): s = Signal(4) - with self.assertRaises(SyntaxError, - msg="Match pattern '--' must have the same width as match value (which is 4)"): + with self.assertRaisesRegex(SyntaxError, + r"^Match pattern '--' must have the same width as match value \(which is 4\)$"): s.matches("--") - with self.assertWarns(SyntaxWarning, - msg="Match pattern '10110' is wider than match value (which has width 4); " - "comparison will never be true"): + with self.assertWarnsRegex(SyntaxWarning, + (r"^Match pattern '10110' is wider than match value \(which has width 4\); " + r"comparison will never be true$")): s.matches(0b10110) def test_matches_bits_wrong(self): s = Signal(4) - with self.assertRaises(SyntaxError, - msg="Match pattern 'abc' must consist of 0, 1, and - (don't care) bits, " - "and may include whitespace"): + with self.assertRaisesRegex(SyntaxError, + (r"^Match pattern 'abc' must consist of 0, 1, and - \(don't care\) bits, " + r"and may include whitespace$")): s.matches("abc") def test_matches_pattern_wrong(self): s = Signal(4) - with self.assertRaises(SyntaxError, - msg="Match pattern must be an integer, a string, or an enumeration, not 1.0"): + with self.assertRaisesRegex(SyntaxError, + r"^Match pattern must be an integer, a string, or an enumeration, not 1\.0$"): s.matches(1.0) def test_hash(self): @@ -630,23 +630,23 @@ class SliceTestCase(FHDLTestCase): self.assertEqual((s1.start, s1.stop), (4, 7)) def test_start_end_wrong(self): - with self.assertRaises(TypeError, - msg="Slice start must be an integer, not 'x'"): + with self.assertRaisesRegex(TypeError, + r"^Slice start must be an integer, not 'x'$"): Slice(0, "x", 1) - with self.assertRaises(TypeError, - msg="Slice stop must be an integer, not 'x'"): + with self.assertRaisesRegex(TypeError, + r"^Slice stop must be an integer, not 'x'$"): Slice(0, 1, "x") def test_start_end_out_of_range(self): c = Const(0, 8) - with self.assertRaises(IndexError, - msg="Cannot start slice 10 bits into 8-bit value"): + with self.assertRaisesRegex(IndexError, + r"^Cannot start slice 10 bits into 8-bit value$"): Slice(c, 10, 12) - with self.assertRaises(IndexError, - msg="Cannot stop slice 12 bits into 8-bit value"): + with self.assertRaisesRegex(IndexError, + r"^Cannot stop slice 12 bits into 8-bit value$"): Slice(c, 0, 12) - with self.assertRaises(IndexError, - msg="Slice start 4 must be less than slice stop 2"): + with self.assertRaisesRegex(IndexError, + r"^Slice start 4 must be less than slice stop 2$"): Slice(c, 4, 2) def test_repr(self): @@ -842,8 +842,8 @@ class SignalTestCase(FHDLTestCase): self.assertEqual(s11.shape(), unsigned(1)) def test_shape_wrong(self): - with self.assertRaises(TypeError, - msg="Width must be a non-negative integer, not -10"): + with self.assertRaisesRegex(TypeError, + r"^Width must be a non-negative integer, not -10$"): Signal(-10) def test_name(self): @@ -860,20 +860,20 @@ class SignalTestCase(FHDLTestCase): def test_reset_enum(self): s1 = Signal(2, reset=UnsignedEnum.BAR) self.assertEqual(s1.reset, 2) - with self.assertRaises(TypeError, - msg="Reset value has to be an int or an integral Enum" + with self.assertRaisesRegex(TypeError, + r"^Reset value has to be an int or an integral Enum$" ): Signal(1, reset=StringEnum.FOO) def test_reset_narrow(self): - with self.assertWarns(SyntaxWarning, - msg="Reset value 8 requires 4 bits to represent, but the signal only has 3 bits"): + with self.assertWarnsRegex(SyntaxWarning, + r"^Reset value 8 requires 4 bits to represent, but the signal only has 3 bits$"): Signal(3, reset=8) - with self.assertWarns(SyntaxWarning, - msg="Reset value 4 requires 4 bits to represent, but the signal only has 3 bits"): + with self.assertWarnsRegex(SyntaxWarning, + r"^Reset value 4 requires 4 bits to represent, but the signal only has 3 bits$"): Signal(signed(3), reset=4) - with self.assertWarns(SyntaxWarning, - msg="Reset value -5 requires 4 bits to represent, but the signal only has 3 bits"): + with self.assertWarnsRegex(SyntaxWarning, + r"^Reset value -5 requires 4 bits to represent, but the signal only has 3 bits$"): Signal(signed(3), reset=-5) def test_attrs(self): @@ -928,8 +928,8 @@ class ClockSignalTestCase(FHDLTestCase): s2 = ClockSignal("pix") self.assertEqual(s2.domain, "pix") - with self.assertRaises(TypeError, - msg="Clock domain name must be a string, not 1"): + with self.assertRaisesRegex(TypeError, + r"^Clock domain name must be a string, not 1$"): ClockSignal(1) def test_shape(self): @@ -942,8 +942,8 @@ class ClockSignalTestCase(FHDLTestCase): self.assertEqual(repr(s1), "(clk sync)") def test_wrong_name_comb(self): - with self.assertRaises(ValueError, - msg="Domain 'comb' does not have a clock"): + with self.assertRaisesRegex(ValueError, + r"^Domain 'comb' does not have a clock$"): ClockSignal("comb") @@ -954,8 +954,8 @@ class ResetSignalTestCase(FHDLTestCase): s2 = ResetSignal("pix") self.assertEqual(s2.domain, "pix") - with self.assertRaises(TypeError, - msg="Clock domain name must be a string, not 1"): + with self.assertRaisesRegex(TypeError, + r"^Clock domain name must be a string, not 1$"): ResetSignal(1) def test_shape(self): @@ -968,8 +968,8 @@ class ResetSignalTestCase(FHDLTestCase): self.assertEqual(repr(s1), "(rst sync)") def test_wrong_name_comb(self): - with self.assertRaises(ValueError, - msg="Domain 'comb' does not have a reset"): + with self.assertRaisesRegex(ValueError, + r"^Domain 'comb' does not have a reset$"): ResetSignal("comb") @@ -1014,19 +1014,19 @@ class SampleTestCase(FHDLTestCase): s3 = Sample(ResetSignal(), 1, "sync") def test_wrong_value_operator(self): - with self.assertRaises(TypeError, - "Sampled value must be a signal or a constant, not " - "(+ (sig $signal) (const 1'd1))"): + with self.assertRaisesRegex(TypeError, + (r"^Sampled value must be a signal or a constant, not " + r"\(\+ \(sig \$signal\) \(const 1'd1\)\)$")): Sample(Signal() + 1, 1, "sync") def test_wrong_clocks_neg(self): - with self.assertRaises(ValueError, - "Cannot sample a value 1 cycles in the future"): + with self.assertRaisesRegex(ValueError, + r"^Cannot sample a value 1 cycles in the future$"): Sample(Signal(), -1, "sync") def test_wrong_domain(self): - with self.assertRaises(TypeError, - "Domain name must be a string or None, not 0"): + with self.assertRaisesRegex(TypeError, + r"^Domain name must be a string or None, not 0$"): Sample(Signal(), 1, 0) diff --git a/nmigen/test/test_hdl_cd.py b/nmigen/test/test_hdl_cd.py index 85bcfc8..8927e08 100644 --- a/nmigen/test/test_hdl_cd.py +++ b/nmigen/test/test_hdl_cd.py @@ -17,8 +17,8 @@ class ClockDomainTestCase(FHDLTestCase): self.assertEqual(pix.name, "pix") dom = [ClockDomain("foo")][0] self.assertEqual(dom.name, "foo") - with self.assertRaises(ValueError, - msg="Clock domain name must be specified explicitly"): + with self.assertRaisesRegex(ValueError, + r"^Clock domain name must be specified explicitly$"): ClockDomain() cd_reset = ClockDomain(local=True) self.assertEqual(cd_reset.local, True) @@ -32,8 +32,8 @@ class ClockDomainTestCase(FHDLTestCase): self.assertEqual(sync.clk_edge, "neg") def test_edge_wrong(self): - with self.assertRaises(ValueError, - msg="Domain clock edge must be one of 'pos' or 'neg', not 'xxx'"): + with self.assertRaisesRegex(ValueError, + r"^Domain clock edge must be one of 'pos' or 'neg', not 'xxx'$"): ClockDomain("sync", clk_edge="xxx") def test_with_reset(self): @@ -73,6 +73,6 @@ class ClockDomainTestCase(FHDLTestCase): self.assertEqual(sync.clk.name, "pix_clk") def test_wrong_name_comb(self): - with self.assertRaises(ValueError, - msg="Domain 'comb' may not be clocked"): + with self.assertRaisesRegex(ValueError, + r"^Domain 'comb' may not be clocked$"): comb = ClockDomain() diff --git a/nmigen/test/test_hdl_dsl.py b/nmigen/test/test_hdl_dsl.py index c3a5b27..454415d 100644 --- a/nmigen/test/test_hdl_dsl.py +++ b/nmigen/test/test_hdl_dsl.py @@ -20,9 +20,9 @@ class DSLTestCase(FHDLTestCase): self.w1 = Signal(4) def test_cant_inherit(self): - with self.assertRaises(SyntaxError, - msg="Instead of inheriting from `Module`, inherit from `Elaboratable` and " - "return a `Module` from the `elaborate(self, platform)` method"): + with self.assertRaisesRegex(SyntaxError, + (r"^Instead of inheriting from `Module`, inherit from `Elaboratable` and " + r"return a `Module` from the `elaborate\(self, platform\)` method$")): class ORGate(Module): pass @@ -69,47 +69,47 @@ class DSLTestCase(FHDLTestCase): def test_d_conflict(self): m = Module() - with self.assertRaises(SyntaxError, - msg="Driver-driver conflict: trying to drive (sig c1) from d.sync, but it " - "is already driven from d.comb"): + with self.assertRaisesRegex(SyntaxError, + (r"^Driver-driver conflict: trying to drive \(sig c1\) from d\.sync, but it " + r"is already driven from d\.comb$")): m.d.comb += self.c1.eq(1) m.d.sync += self.c1.eq(1) def test_d_wrong(self): m = Module() - with self.assertRaises(AttributeError, - msg="Cannot assign 'd.pix' attribute; did you mean 'd.pix +='?"): + with self.assertRaisesRegex(AttributeError, + r"^Cannot assign 'd\.pix' attribute; did you mean 'd.pix \+='\?$"): m.d.pix = None def test_d_asgn_wrong(self): m = Module() - with self.assertRaises(SyntaxError, - msg="Only assignments and property checks may be appended to d.sync"): + with self.assertRaisesRegex(SyntaxError, + r"^Only assignments and property checks may be appended to d\.sync$"): m.d.sync += Switch(self.s1, {}) def test_comb_wrong(self): m = Module() - with self.assertRaises(AttributeError, - msg="'Module' object has no attribute 'comb'; did you mean 'd.comb'?"): + with self.assertRaisesRegex(AttributeError, + r"^'Module' object has no attribute 'comb'; did you mean 'd\.comb'\?$"): m.comb += self.c1.eq(1) def test_sync_wrong(self): m = Module() - with self.assertRaises(AttributeError, - msg="'Module' object has no attribute 'sync'; did you mean 'd.sync'?"): + with self.assertRaisesRegex(AttributeError, + r"^'Module' object has no attribute 'sync'; did you mean 'd\.sync'\?$"): m.sync += self.c1.eq(1) def test_attr_wrong(self): m = Module() - with self.assertRaises(AttributeError, - msg="'Module' object has no attribute 'nonexistentattr'"): + with self.assertRaisesRegex(AttributeError, + r"^'Module' object has no attribute 'nonexistentattr'$"): m.nonexistentattr def test_d_suspicious(self): m = Module() - with self.assertWarns(SyntaxWarning, - msg="Using '.d.submodules' would add statements to clock domain " - "'submodules'; did you mean .submodules instead?"): + with self.assertWarnsRegex(SyntaxWarning, + (r"^Using '\.d\.submodules' would add statements to clock domain " + r"'submodules'; did you mean \.submodules instead\?$")): m.d.submodules += [] def test_clock_signal(self): @@ -260,15 +260,15 @@ class DSLTestCase(FHDLTestCase): def test_Elif_wrong(self): m = Module() - with self.assertRaises(SyntaxError, - msg="Elif without preceding If"): + with self.assertRaisesRegex(SyntaxError, + r"^Elif without preceding If$"): with m.Elif(self.s2): pass def test_Else_wrong(self): m = Module() - with self.assertRaises(SyntaxError, - msg="Else without preceding If/Elif"): + with self.assertRaisesRegex(SyntaxError, + r"^Else without preceding If\/Elif$"): with m.Else(): pass @@ -287,11 +287,11 @@ class DSLTestCase(FHDLTestCase): def test_If_signed_suspicious(self): m = Module() - with self.assertWarns(SyntaxWarning, - msg="Signed values in If/Elif conditions usually result from inverting Python " - "booleans with ~, which leads to unexpected results. Replace `~flag` with " - "`not flag`. (If this is a false positive, silence this warning with " - "`m.If(x)` → `m.If(x.bool())`.)"): + with self.assertWarnsRegex(SyntaxWarning, + (r"^Signed values in If\/Elif conditions usually result from inverting Python " + r"booleans with ~, which leads to unexpected results\. Replace `~flag` with " + r"`not flag`\. \(If this is a false positive, silence this warning with " + r"`m\.If\(x\)` → `m\.If\(x\.bool\(\)\)`\.\)$")): with m.If(~True): pass @@ -299,28 +299,28 @@ class DSLTestCase(FHDLTestCase): m = Module() with m.If(0): pass - with self.assertWarns(SyntaxWarning, - msg="Signed values in If/Elif conditions usually result from inverting Python " - "booleans with ~, which leads to unexpected results. Replace `~flag` with " - "`not flag`. (If this is a false positive, silence this warning with " - "`m.If(x)` → `m.If(x.bool())`.)"): + with self.assertWarnsRegex(SyntaxWarning, + (r"^Signed values in If\/Elif conditions usually result from inverting Python " + r"booleans with ~, which leads to unexpected results\. Replace `~flag` with " + r"`not flag`\. \(If this is a false positive, silence this warning with " + r"`m\.If\(x\)` → `m\.If\(x\.bool\(\)\)`\.\)$")): with m.Elif(~True): pass def test_if_If_Elif_Else(self): m = Module() - with self.assertRaises(SyntaxError, - msg="`if m.If(...):` does not work; use `with m.If(...)`"): + with self.assertRaisesRegex(SyntaxError, + r"^`if m\.If\(\.\.\.\):` does not work; use `with m\.If\(\.\.\.\)`$"): if m.If(0): pass with m.If(0): pass - with self.assertRaises(SyntaxError, - msg="`if m.Elif(...):` does not work; use `with m.Elif(...)`"): + with self.assertRaisesRegex(SyntaxError, + r"^`if m\.Elif\(\.\.\.\):` does not work; use `with m\.Elif\(\.\.\.\)`$"): if m.Elif(0): pass - with self.assertRaises(SyntaxError, - msg="`if m.Else(...):` does not work; use `with m.Else(...)`"): + with self.assertRaisesRegex(SyntaxError, + r"^`if m\.Else\(\.\.\.\):` does not work; use `with m\.Else\(\.\.\.\)`$"): if m.Else(): pass @@ -414,18 +414,18 @@ class DSLTestCase(FHDLTestCase): RED = 0b10101010 m = Module() with m.Switch(self.w1): - with self.assertRaises(SyntaxError, - msg="Case pattern '--' must have the same width as switch value (which is 4)"): + with self.assertRaisesRegex(SyntaxError, + r"^Case pattern '--' must have the same width as switch value \(which is 4\)$"): with m.Case("--"): pass - with self.assertWarns(SyntaxWarning, - msg="Case pattern '10110' is wider than switch value (which has width 4); " - "comparison will never be true"): + with self.assertWarnsRegex(SyntaxWarning, + (r"^Case pattern '10110' is wider than switch value \(which has width 4\); " + r"comparison will never be true$")): with m.Case(0b10110): pass - with self.assertWarns(SyntaxWarning, - msg="Case pattern '10101010' (Color.RED) is wider than switch value " - "(which has width 4); comparison will never be true"): + with self.assertWarnsRegex(SyntaxWarning, + (r"^Case pattern '10101010' \(Color\.RED\) is wider than switch value " + r"\(which has width 4\); comparison will never be true$")): with m.Case(Color.RED): pass self.assertRepr(m._statements, """ @@ -437,33 +437,33 @@ class DSLTestCase(FHDLTestCase): def test_Case_bits_wrong(self): m = Module() with m.Switch(self.w1): - with self.assertRaises(SyntaxError, - msg="Case pattern 'abc' must consist of 0, 1, and - (don't care) bits, " - "and may include whitespace"): + with self.assertRaisesRegex(SyntaxError, + (r"^Case pattern 'abc' must consist of 0, 1, and - \(don't care\) bits, " + r"and may include whitespace$")): with m.Case("abc"): pass def test_Case_pattern_wrong(self): m = Module() with m.Switch(self.w1): - with self.assertRaises(SyntaxError, - msg="Case pattern must be an integer, a string, or an enumeration, not 1.0"): + with self.assertRaisesRegex(SyntaxError, + r"^Case pattern must be an integer, a string, or an enumeration, not 1\.0$"): with m.Case(1.0): pass def test_Case_outside_Switch_wrong(self): m = Module() - with self.assertRaises(SyntaxError, - msg="Case is not permitted outside of Switch"): + with self.assertRaisesRegex(SyntaxError, + r"^Case is not permitted outside of Switch$"): with m.Case(): pass def test_If_inside_Switch_wrong(self): m = Module() with m.Switch(self.s1): - with self.assertRaises(SyntaxError, - msg="If is not permitted directly inside of Switch; " - "it is permitted inside of Switch Case"): + with self.assertRaisesRegex(SyntaxError, + (r"^If is not permitted directly inside of Switch; " + r"it is permitted inside of Switch Case$")): with m.If(self.s2): pass @@ -577,15 +577,15 @@ class DSLTestCase(FHDLTestCase): def test_FSM_wrong_domain(self): m = Module() - with self.assertRaises(ValueError, - msg="FSM may not be driven by the 'comb' domain"): + with self.assertRaisesRegex(ValueError, + r"^FSM may not be driven by the 'comb' domain$"): with m.FSM(domain="comb"): pass def test_FSM_wrong_undefined(self): m = Module() - with self.assertRaises(NameError, - msg="FSM state 'FOO' is referenced but not defined"): + with self.assertRaisesRegex(NameError, + r"^FSM state 'FOO' is referenced but not defined$"): with m.FSM() as fsm: fsm.ongoing("FOO") @@ -594,21 +594,21 @@ class DSLTestCase(FHDLTestCase): with m.FSM(): with m.State("FOO"): pass - with self.assertRaises(NameError, - msg="FSM state 'FOO' is already defined"): + with self.assertRaisesRegex(NameError, + r"^FSM state 'FOO' is already defined$"): with m.State("FOO"): pass def test_FSM_wrong_next(self): m = Module() - with self.assertRaises(SyntaxError, - msg="Only assignment to `m.next` is permitted"): + with self.assertRaisesRegex(SyntaxError, + r"^Only assignment to `m\.next` is permitted$"): m.next - with self.assertRaises(SyntaxError, - msg="`m.next = <...>` is only permitted inside an FSM state"): + with self.assertRaisesRegex(SyntaxError, + r"^`m\.next = <\.\.\.>` is only permitted inside an FSM state$"): m.next = "FOO" - with self.assertRaises(SyntaxError, - msg="`m.next = <...>` is only permitted inside an FSM state"): + with self.assertRaisesRegex(SyntaxError, + r"^`m\.next = <\.\.\.>` is only permitted inside an FSM state$"): with m.FSM(): m.next = "FOO" @@ -617,9 +617,9 @@ class DSLTestCase(FHDLTestCase): with m.FSM(): with m.State("FOO"): pass - with self.assertRaises(SyntaxError, - msg="If is not permitted directly inside of FSM; " - "it is permitted inside of FSM State"): + with self.assertRaisesRegex(SyntaxError, + (r"^If is not permitted directly inside of FSM; " + r"it is permitted inside of FSM State$")): with m.If(self.s2): pass @@ -668,18 +668,18 @@ class DSLTestCase(FHDLTestCase): def test_submodule_wrong(self): m = Module() - with self.assertRaises(TypeError, - msg="Trying to add 1, which does not implement .elaborate(), as a submodule"): + with self.assertRaisesRegex(TypeError, + r"^Trying to add 1, which does not implement \.elaborate\(\), as a submodule$"): m.submodules.foo = 1 - with self.assertRaises(TypeError, - msg="Trying to add 1, which does not implement .elaborate(), as a submodule"): + with self.assertRaisesRegex(TypeError, + r"^Trying to add 1, which does not implement \.elaborate\(\), as a submodule$"): m.submodules += 1 def test_submodule_named_conflict(self): m1 = Module() m2 = Module() m1.submodules.foo = m2 - with self.assertRaises(NameError, msg="Submodule named 'foo' already exists"): + with self.assertRaisesRegex(NameError, r"^Submodule named 'foo' already exists$"): m1.submodules.foo = m2 def test_submodule_get(self): @@ -698,9 +698,9 @@ class DSLTestCase(FHDLTestCase): def test_submodule_get_unset(self): m1 = Module() - with self.assertRaises(AttributeError, msg="No submodule named 'foo' exists"): + with self.assertRaisesRegex(AttributeError, r"^No submodule named 'foo' exists$"): m2 = m1.submodules.foo - with self.assertRaises(AttributeError, msg="No submodule named 'foo' exists"): + with self.assertRaisesRegex(AttributeError, r"^No submodule named 'foo' exists$"): m2 = m1.submodules["foo"] def test_domain_named_implicit(self): @@ -716,24 +716,24 @@ class DSLTestCase(FHDLTestCase): def test_domain_add_wrong(self): m = Module() - with self.assertRaises(TypeError, - msg="Only clock domains may be added to `m.domains`, not 1"): + with self.assertRaisesRegex(TypeError, + r"^Only clock domains may be added to `m\.domains`, not 1$"): m.domains.foo = 1 - with self.assertRaises(TypeError, - msg="Only clock domains may be added to `m.domains`, not 1"): + with self.assertRaisesRegex(TypeError, + r"^Only clock domains may be added to `m\.domains`, not 1$"): m.domains += 1 def test_domain_add_wrong_name(self): m = Module() - with self.assertRaises(NameError, - msg="Clock domain name 'bar' must match name in `m.domains.foo += ...` syntax"): + with self.assertRaisesRegex(NameError, + r"^Clock domain name 'bar' must match name in `m\.domains\.foo \+= \.\.\.` syntax$"): m.domains.foo = ClockDomain("bar") def test_domain_add_wrong_duplicate(self): m = Module() m.domains += ClockDomain("foo") - with self.assertRaises(NameError, - msg="Clock domain named 'foo' already exists"): + with self.assertRaisesRegex(NameError, + r"^Clock domain named 'foo' already exists$"): m.domains += ClockDomain("foo") def test_lower(self): diff --git a/nmigen/test/test_hdl_ir.py b/nmigen/test/test_hdl_ir.py index b93537d..ab8bdd8 100644 --- a/nmigen/test/test_hdl_ir.py +++ b/nmigen/test/test_hdl_ir.py @@ -16,14 +16,14 @@ class BadElaboratable(Elaboratable): class FragmentGetTestCase(FHDLTestCase): def test_get_wrong(self): - with self.assertRaises(AttributeError, - msg="Object None cannot be elaborated"): + with self.assertRaisesRegex(AttributeError, + r"^Object None cannot be elaborated$"): Fragment.get(None, platform=None) - with self.assertWarns(UserWarning, - msg=".elaborate() returned None; missing return statement?"): - with self.assertRaises(AttributeError, - msg="Object None cannot be elaborated"): + with self.assertWarnsRegex(UserWarning, + r"^\.elaborate\(\) returned None; missing return statement\?$"): + with self.assertRaisesRegex(AttributeError, + r"^Object None cannot be elaborated$"): Fragment.get(BadElaboratable(), platform=None) @@ -41,11 +41,11 @@ class FragmentGeneratedTestCase(FHDLTestCase): f2 = Fragment() f1.add_subfragment(f2, "f2") - with self.assertRaises(NameError, - msg="No subfragment at index #1"): + with self.assertRaisesRegex(NameError, + r"^No subfragment at index #1$"): f1.find_subfragment(1) - with self.assertRaises(NameError, - msg="No subfragment with name 'fx'"): + with self.assertRaisesRegex(NameError, + r"^No subfragment with name 'fx'$"): f1.find_subfragment("fx") def test_find_generated(self): @@ -300,18 +300,18 @@ class FragmentPortsTestCase(FHDLTestCase): def test_port_wrong(self): f = Fragment() - with self.assertRaises(TypeError, - msg="Only signals may be added as ports, not (const 1'd1)"): + with self.assertRaisesRegex(TypeError, + r"^Only signals may be added as ports, not \(const 1'd1\)$"): f.prepare(ports=(Const(1),)) def test_port_not_iterable(self): f = Fragment() - with self.assertRaises(TypeError, - msg="`ports` must be either a list or a tuple, not 1"): + with self.assertRaisesRegex(TypeError, + r"^`ports` must be either a list or a tuple, not 1$"): f.prepare(ports=1) - with self.assertRaises(TypeError, - msg="`ports` must be either a list or a tuple, not (const 1'd1)" + - " (did you mean `ports=(,)`, rather than `ports=`?)"): + with self.assertRaisesRegex(TypeError, + (r"^`ports` must be either a list or a tuple, not \(const 1'd1\)" + r" \(did you mean `ports=\(,\)`, rather than `ports=`\?\)$")): f.prepare(ports=Const(1)) class FragmentDomainsTestCase(FHDLTestCase): @@ -380,10 +380,10 @@ class FragmentDomainsTestCase(FHDLTestCase): f.add_subfragment(fa, "a") f.add_subfragment(fb) - with self.assertRaises(DomainError, - msg="Domain 'sync' is defined by subfragments 'a', of fragment " - "'top'; it is necessary to either rename subfragment domains explicitly, " - "or give names to subfragments"): + with self.assertRaisesRegex(DomainError, + (r"^Domain 'sync' is defined by subfragments 'a', of fragment " + r"'top'; it is necessary to either rename subfragment domains explicitly, " + r"or give names to subfragments$")): f._propagate_domains_up() def test_domain_conflict_name(self): @@ -398,10 +398,10 @@ class FragmentDomainsTestCase(FHDLTestCase): f.add_subfragment(fa, "x") f.add_subfragment(fb, "x") - with self.assertRaises(DomainError, - msg="Domain 'sync' is defined by subfragments #0, #1 of fragment 'top', some " - "of which have identical names; it is necessary to either rename subfragment " - "domains explicitly, or give distinct names to subfragments"): + with self.assertRaisesRegex(DomainError, + (r"^Domain 'sync' is defined by subfragments #0, #1 of fragment 'top', some " + r"of which have identical names; it is necessary to either rename subfragment " + r"domains explicitly, or give distinct names to subfragments$")): f._propagate_domains_up() def test_domain_conflict_rename_drivers(self): @@ -481,8 +481,8 @@ class FragmentDomainsTestCase(FHDLTestCase): f1 = Fragment() f1.add_driver(s1, "sync") - with self.assertRaises(DomainError, - msg="Domain 'sync' is used but not defined"): + with self.assertRaisesRegex(DomainError, + r"^Domain 'sync' is used but not defined$"): f1._propagate_domains(missing_domain=lambda name: None) def test_propagate_create_missing(self): @@ -542,9 +542,9 @@ class FragmentDomainsTestCase(FHDLTestCase): f2 = Fragment() f2.add_domains(ClockDomain("foo")) - with self.assertRaises(DomainError, - msg="Fragment returned by missing domain callback does not define requested " - "domain 'sync' (defines 'foo')."): + with self.assertRaisesRegex(DomainError, + (r"^Fragment returned by missing domain callback does not define requested " + r"domain 'sync' \(defines 'foo'\)\.$")): f1._propagate_domains(missing_domain=lambda name: f2) @@ -597,16 +597,16 @@ class FragmentHierarchyConflictTestCase(FHDLTestCase): def test_conflict_self_sub_error(self): self.setUp_self_sub() - with self.assertRaises(DriverConflict, - msg="Signal '(sig s1)' is driven from multiple fragments: top, top."): + with self.assertRaisesRegex(DriverConflict, + r"^Signal '\(sig s1\)' is driven from multiple fragments: top, top.$"): self.f1._resolve_hierarchy_conflicts(mode="error") def test_conflict_self_sub_warning(self): self.setUp_self_sub() - with self.assertWarns(DriverConflict, - msg="Signal '(sig s1)' is driven from multiple fragments: top, top.; " - "hierarchy will be flattened"): + with self.assertWarnsRegex(DriverConflict, + (r"^Signal '\(sig s1\)' is driven from multiple fragments: top, top.; " + r"hierarchy will be flattened$")): self.f1._resolve_hierarchy_conflicts(mode="warn") def setUp_sub_sub(self): @@ -691,17 +691,17 @@ class FragmentHierarchyConflictTestCase(FHDLTestCase): def test_conflict_memory_error(self): self.setUp_memory() - with self.assertRaises(DriverConflict, - msg="Memory 'm' is accessed from multiple fragments: top., " - "top."): + with self.assertRaisesRegex(DriverConflict, + r"^Memory 'm' is accessed from multiple fragments: top\., " + r"top\.$"): self.f1._resolve_hierarchy_conflicts(mode="error") def test_conflict_memory_warning(self): self.setUp_memory() - with self.assertWarns(DriverConflict, - msg="Memory 'm' is accessed from multiple fragments: top., " - "top.; hierarchy will be flattened"): + with self.assertWarnsRegex(DriverConflict, + (r"^Memory 'm' is accessed from multiple fragments: top., " + r"top.; hierarchy will be flattened$")): self.f1._resolve_hierarchy_conflicts(mode="warn") def test_explicit_flatten(self): @@ -783,16 +783,16 @@ class InstanceTestCase(FHDLTestCase): def test_wrong_construct_arg(self): s = Signal() - with self.assertRaises(NameError, - msg="Instance argument ('', 's1', (sig s)) should be a tuple " - "(kind, name, value) where kind is one of \"p\", \"i\", \"o\", or \"io\""): + with self.assertRaisesRegex(NameError, + (r"^Instance argument \('', 's1', \(sig s\)\) should be a tuple " + r"\(kind, name, value\) where kind is one of \"p\", \"i\", \"o\", or \"io\"$")): Instance("foo", ("", "s1", s)) def test_wrong_construct_kwarg(self): s = Signal() - with self.assertRaises(NameError, - msg="Instance keyword argument x_s1=(sig s) does not start with one of " - "\"p_\", \"i_\", \"o_\", or \"io_\""): + with self.assertRaisesRegex(NameError, + (r"^Instance keyword argument x_s1=\(sig s\) does not start with one of " + r"\"p_\", \"i_\", \"o_\", or \"io_\"$")): Instance("foo", x_s1=s) def setUp_cpu(self): diff --git a/nmigen/test/test_hdl_mem.py b/nmigen/test/test_hdl_mem.py index 131c639..ed87aec 100644 --- a/nmigen/test/test_hdl_mem.py +++ b/nmigen/test/test_hdl_mem.py @@ -20,11 +20,11 @@ class MemoryTestCase(FHDLTestCase): self.assertEqual(m.depth, 4) def test_geometry_wrong(self): - with self.assertRaises(TypeError, - msg="Memory width must be a non-negative integer, not -1"): + with self.assertRaisesRegex(TypeError, + r"^Memory width must be a non-negative integer, not -1$"): m = Memory(width=-1, depth=4) - with self.assertRaises(TypeError, - msg="Memory depth must be a non-negative integer, not -1"): + with self.assertRaisesRegex(TypeError, + r"^Memory depth must be a non-negative integer, not -1$"): m = Memory(width=8, depth=-1) def test_init(self): @@ -32,14 +32,14 @@ class MemoryTestCase(FHDLTestCase): self.assertEqual(m.init, [0, 1, 2, 3]) def test_init_wrong_count(self): - with self.assertRaises(ValueError, - msg="Memory initialization value count exceed memory depth (8 > 4)"): + with self.assertRaisesRegex(ValueError, + r"^Memory initialization value count exceed memory depth \(8 > 4\)$"): m = Memory(width=8, depth=4, init=range(8)) def test_init_wrong_type(self): - with self.assertRaises(TypeError, - msg="Memory initialization value at address 1: " - "'str' object cannot be interpreted as an integer"): + with self.assertRaisesRegex(TypeError, + (r"^Memory initialization value at address 1: " + r"'str' object cannot be interpreted as an integer$")): m = Memory(width=8, depth=4, init=[1, "0"]) def test_attrs(self): @@ -82,8 +82,8 @@ class MemoryTestCase(FHDLTestCase): def test_read_port_wrong(self): mem = Memory(width=8, depth=4) - with self.assertRaises(ValueError, - msg="Read port cannot be simultaneously asynchronous and non-transparent"): + with self.assertRaisesRegex(ValueError, + r"^Read port cannot be simultaneously asynchronous and non-transparent$"): mem.read_port(domain="comb", transparent=False) def test_write_port(self): @@ -108,14 +108,14 @@ class MemoryTestCase(FHDLTestCase): def test_write_port_granularity_wrong(self): mem = Memory(width=8, depth=4) - with self.assertRaises(TypeError, - msg="Write port granularity must be a non-negative integer, not -1"): + with self.assertRaisesRegex(TypeError, + r"^Write port granularity must be a non-negative integer, not -1$"): mem.write_port(granularity=-1) - with self.assertRaises(ValueError, - msg="Write port granularity must not be greater than memory width (10 > 8)"): + with self.assertRaisesRegex(ValueError, + r"^Write port granularity must not be greater than memory width \(10 > 8\)$"): mem.write_port(granularity=10) - with self.assertRaises(ValueError, - msg="Write port granularity must divide memory width evenly"): + with self.assertRaisesRegex(ValueError, + r"^Write port granularity must divide memory width evenly$"): mem.write_port(granularity=3) diff --git a/nmigen/test/test_hdl_rec.py b/nmigen/test/test_hdl_rec.py index 4f99800..dfcadf7 100644 --- a/nmigen/test/test_hdl_rec.py +++ b/nmigen/test/test_hdl_rec.py @@ -72,31 +72,31 @@ class LayoutTestCase(FHDLTestCase): "('b', Layout([('c', signed(3))]))])") def test_wrong_field(self): - with self.assertRaises(TypeError, - msg="Field (1,) has invalid layout: should be either (name, shape) or " - "(name, shape, direction)"): + with self.assertRaisesRegex(TypeError, + (r"^Field \(1,\) has invalid layout: should be either \(name, shape\) or " + r"\(name, shape, direction\)$")): Layout.cast([(1,)]) def test_wrong_name(self): - with self.assertRaises(TypeError, - msg="Field (1, 1) has invalid name: should be a string"): + with self.assertRaisesRegex(TypeError, + r"^Field \(1, 1\) has invalid name: should be a string$"): Layout.cast([(1, 1)]) def test_wrong_name_duplicate(self): - with self.assertRaises(NameError, - msg="Field ('a', 2) has a name that is already present in the layout"): + with self.assertRaisesRegex(NameError, + r"^Field \('a', 2\) has a name that is already present in the layout$"): Layout.cast([("a", 1), ("a", 2)]) def test_wrong_direction(self): - with self.assertRaises(TypeError, - msg="Field ('a', 1, 0) has invalid direction: should be a Direction " - "instance like DIR_FANIN"): + with self.assertRaisesRegex(TypeError, + (r"^Field \('a', 1, 0\) has invalid direction: should be a Direction " + r"instance like DIR_FANIN$")): Layout.cast([("a", 1, 0)]) def test_wrong_shape(self): - with self.assertRaises(TypeError, - msg="Field ('a', 'x') has invalid shape: should be castable to Shape or " - "a list of fields of a nested record"): + with self.assertRaisesRegex(TypeError, + (r"^Field \('a', 'x'\) has invalid shape: should be castable to Shape or " + r"a list of fields of a nested record$")): Layout.cast([("a", "x")]) @@ -142,11 +142,11 @@ class RecordTestCase(FHDLTestCase): ("stb", 1), ("ack", 1), ]) - with self.assertRaises(AttributeError, - msg="Record 'r' does not have a field 'en'. Did you mean one of: stb, ack?"): + with self.assertRaisesRegex(AttributeError, + r"^Record 'r' does not have a field 'en'\. Did you mean one of: stb, ack\?$"): r["en"] - with self.assertRaises(AttributeError, - msg="Record 'r' does not have a field 'en'. Did you mean one of: stb, ack?"): + with self.assertRaisesRegex(AttributeError, + r"^Record 'r' does not have a field 'en'\. Did you mean one of: stb, ack\?$"): r.en def test_wrong_field_unnamed(self): @@ -154,8 +154,8 @@ class RecordTestCase(FHDLTestCase): ("stb", 1), ("ack", 1), ])][0] - with self.assertRaises(AttributeError, - msg="Unnamed record does not have a field 'en'. Did you mean one of: stb, ack?"): + with self.assertRaisesRegex(AttributeError, + r"^Unnamed record does not have a field 'en'\. Did you mean one of: stb, ack\?$"): r.en def test_construct_with_fields(self): @@ -303,27 +303,27 @@ class ConnectTestCase(FHDLTestCase): core = Record(self.core_layout) periph = Record(self.periph_layout) - with self.assertRaises(AttributeError, - msg="Cannot include field 'foo' because it is not present in record 'core'"): + with self.assertRaisesRegex(AttributeError, + r"^Cannot include field 'foo' because it is not present in record 'core'$"): core.connect(periph, include={"foo": True}) - with self.assertRaises(AttributeError, - msg="Cannot exclude field 'foo' because it is not present in record 'core'"): + with self.assertRaisesRegex(AttributeError, + r"^Cannot exclude field 'foo' because it is not present in record 'core'$"): core.connect(periph, exclude={"foo": True}) def test_wrong_direction(self): recs = [Record([("x", 1)]) for _ in range(2)] - with self.assertRaises(TypeError, - msg="Cannot connect field 'x' of unnamed record because it does not have " - "a direction"): + with self.assertRaisesRegex(TypeError, + (r"^Cannot connect field 'x' of unnamed record because it does not have " + r"a direction$")): recs[0].connect(recs[1]) def test_wrong_missing_field(self): core = Record([("addr", 32, DIR_FANOUT)]) periph = Record([]) - with self.assertRaises(AttributeError, - msg="Cannot connect field 'addr' of record 'core' to subordinate record 'periph' " - "because the subordinate record does not have this field"): + with self.assertRaisesRegex(AttributeError, + (r"^Cannot connect field 'addr' of record 'core' to subordinate record 'periph' " + r"because the subordinate record does not have this field$")): core.connect(periph) diff --git a/nmigen/test/test_hdl_xfrm.py b/nmigen/test/test_hdl_xfrm.py index 394d349..9ec6e1b 100644 --- a/nmigen/test/test_hdl_xfrm.py +++ b/nmigen/test/test_hdl_xfrm.py @@ -110,13 +110,13 @@ class DomainRenamerTestCase(FHDLTestCase): }) def test_rename_wrong_to_comb(self): - with self.assertRaises(ValueError, - msg="Domain 'sync' may not be renamed to 'comb'"): + with self.assertRaisesRegex(ValueError, + r"^Domain 'sync' may not be renamed to 'comb'$"): DomainRenamer("comb") def test_rename_wrong_from_comb(self): - with self.assertRaises(ValueError, - msg="Domain 'comb' may not be renamed"): + with self.assertRaisesRegex(ValueError, + r"^Domain 'comb' may not be renamed$"): DomainRenamer({"comb": "sync"}) @@ -189,8 +189,8 @@ class DomainLowererTestCase(FHDLTestCase): self.s.eq(ClockSignal("xxx")) ) - with self.assertRaises(DomainError, - msg="Signal (clk xxx) refers to nonexistent domain 'xxx'"): + with self.assertRaisesRegex(DomainError, + r"^Signal \(clk xxx\) refers to nonexistent domain 'xxx'$"): DomainLowerer()(f) def test_lower_wrong_reset_less_domain(self): @@ -201,8 +201,8 @@ class DomainLowererTestCase(FHDLTestCase): self.s.eq(ResetSignal("sync")) ) - with self.assertRaises(DomainError, - msg="Signal (rst sync) refers to reset of reset-less domain 'sync'"): + with self.assertRaisesRegex(DomainError, + r"^Signal \(rst sync\) refers to reset of reset-less domain 'sync'$"): DomainLowerer()(f) diff --git a/nmigen/test/test_lib_cdc.py b/nmigen/test/test_lib_cdc.py index 8b08122..5a169ac 100644 --- a/nmigen/test/test_lib_cdc.py +++ b/nmigen/test/test_lib_cdc.py @@ -8,11 +8,11 @@ from ..lib.cdc import * class FFSynchronizerTestCase(FHDLTestCase): def test_stages_wrong(self): - with self.assertRaises(TypeError, - msg="Synchronization stage count must be a positive integer, not 0"): + with self.assertRaisesRegex(TypeError, + r"^Synchronization stage count must be a positive integer, not 0$"): FFSynchronizer(Signal(), Signal(), stages=0) - with self.assertRaises(ValueError, - msg="Synchronization stage count may not safely be less than 2"): + with self.assertRaisesRegex(ValueError, + r"^Synchronization stage count may not safely be less than 2$"): FFSynchronizer(Signal(), Signal(), stages=1) def test_basic(self): @@ -56,16 +56,16 @@ class FFSynchronizerTestCase(FHDLTestCase): class AsyncFFSynchronizerTestCase(FHDLTestCase): def test_stages_wrong(self): - with self.assertRaises(TypeError, - msg="Synchronization stage count must be a positive integer, not 0"): + with self.assertRaisesRegex(TypeError, + r"^Synchronization stage count must be a positive integer, not 0$"): ResetSynchronizer(Signal(), stages=0) - with self.assertRaises(ValueError, - msg="Synchronization stage count may not safely be less than 2"): + with self.assertRaisesRegex(ValueError, + r"^Synchronization stage count may not safely be less than 2$"): ResetSynchronizer(Signal(), stages=1) def test_edge_wrong(self): - with self.assertRaises(ValueError, - msg="AsyncFFSynchronizer async edge must be one of 'pos' or 'neg', not 'xxx'"): + with self.assertRaisesRegex(ValueError, + r"^AsyncFFSynchronizer async edge must be one of 'pos' or 'neg', not 'xxx'$"): AsyncFFSynchronizer(Signal(), Signal(), domain="sync", async_edge="xxx") def test_pos_edge(self): @@ -147,11 +147,11 @@ class AsyncFFSynchronizerTestCase(FHDLTestCase): class ResetSynchronizerTestCase(FHDLTestCase): def test_stages_wrong(self): - with self.assertRaises(TypeError, - msg="Synchronization stage count must be a positive integer, not 0"): + with self.assertRaisesRegex(TypeError, + r"^Synchronization stage count must be a positive integer, not 0$"): ResetSynchronizer(Signal(), stages=0) - with self.assertRaises(ValueError, - msg="Synchronization stage count may not safely be less than 2"): + with self.assertRaisesRegex(ValueError, + r"^Synchronization stage count may not safely be less than 2$"): ResetSynchronizer(Signal(), stages=1) def test_basic(self): @@ -196,11 +196,11 @@ class ResetSynchronizerTestCase(FHDLTestCase): # TODO: test with distinct clocks class PulseSynchronizerTestCase(FHDLTestCase): def test_stages_wrong(self): - with self.assertRaises(TypeError, - msg="Synchronization stage count must be a positive integer, not 0"): + with self.assertRaisesRegex(TypeError, + r"^Synchronization stage count must be a positive integer, not 0$"): PulseSynchronizer("w", "r", stages=0) - with self.assertRaises(ValueError, - msg="Synchronization stage count may not safely be less than 2"): + with self.assertRaisesRegex(ValueError, + r"^Synchronization stage count may not safely be less than 2$"): PulseSynchronizer("w", "r", stages=1) def test_smoke(self): diff --git a/nmigen/test/test_lib_fifo.py b/nmigen/test/test_lib_fifo.py index 29a28d8..36698de 100644 --- a/nmigen/test/test_lib_fifo.py +++ b/nmigen/test/test_lib_fifo.py @@ -9,11 +9,11 @@ from ..lib.fifo import * class FIFOTestCase(FHDLTestCase): def test_depth_wrong(self): - with self.assertRaises(TypeError, - msg="FIFO width must be a non-negative integer, not -1"): + with self.assertRaisesRegex(TypeError, + r"^FIFO width must be a non-negative integer, not -1$"): FIFOInterface(width=-1, depth=8, fwft=True) - with self.assertRaises(TypeError, - msg="FIFO depth must be a non-negative integer, not -1"): + with self.assertRaisesRegex(TypeError, + r"^FIFO depth must be a non-negative integer, not -1$"): FIFOInterface(width=8, depth=-1, fwft=True) def test_sync_depth(self): @@ -37,9 +37,9 @@ class FIFOTestCase(FHDLTestCase): self.assertEqual(AsyncFIFO(width=8, depth=17).depth, 32) def test_async_depth_wrong(self): - with self.assertRaises(ValueError, - msg="AsyncFIFO only supports depths that are powers of 2; " - "requested exact depth 15 is not"): + with self.assertRaisesRegex(ValueError, + (r"^AsyncFIFO only supports depths that are powers of 2; " + r"requested exact depth 15 is not$")): AsyncFIFO(width=8, depth=15, exact_depth=True) def test_async_buffered_depth(self): @@ -54,9 +54,9 @@ class FIFOTestCase(FHDLTestCase): self.assertEqual(AsyncFIFOBuffered(width=8, depth=18).depth, 33) def test_async_buffered_depth_wrong(self): - with self.assertRaises(ValueError, - msg="AsyncFIFOBuffered only supports depths that are one higher than powers of 2; " - "requested exact depth 16 is not"): + with self.assertRaisesRegex(ValueError, + (r"^AsyncFIFOBuffered only supports depths that are one higher than powers of 2; " + r"requested exact depth 16 is not$")): AsyncFIFOBuffered(width=8, depth=16, exact_depth=True) class FIFOModel(Elaboratable, FIFOInterface): diff --git a/nmigen/test/test_sim.py b/nmigen/test/test_sim.py index bff3051..8626e71 100644 --- a/nmigen/test/test_sim.py +++ b/nmigen/test/test_sim.py @@ -541,8 +541,8 @@ class SimulatorIntegrationTestCase(FHDLTestCase): def test_add_process_wrong(self): with self.assertSimulation(Module()) as sim: - with self.assertRaises(TypeError, - msg="Cannot add a process 1 because it is not a generator function"): + with self.assertRaisesRegex(TypeError, + r"^Cannot add a process 1 because it is not a generator function$"): sim.add_process(1) def test_add_process_wrong_generator(self): @@ -559,15 +559,15 @@ class SimulatorIntegrationTestCase(FHDLTestCase): m.d.sync += s.eq(0) with self.assertSimulation(m) as sim: sim.add_clock(1) - with self.assertRaises(ValueError, - msg="Domain 'sync' already has a clock driving it"): + with self.assertRaisesRegex(ValueError, + r"^Domain 'sync' already has a clock driving it$"): sim.add_clock(1) def test_add_clock_wrong_missing(self): m = Module() with self.assertSimulation(m) as sim: - with self.assertRaises(ValueError, - msg="Domain 'sync' is not present in simulation"): + with self.assertRaisesRegex(ValueError, + r"^Domain 'sync' is not present in simulation$"): sim.add_clock(1) def test_add_clock_if_exists(self): diff --git a/nmigen/test/utils.py b/nmigen/test/utils.py index d8a168c..3c88403 100644 --- a/nmigen/test/utils.py +++ b/nmigen/test/utils.py @@ -28,23 +28,6 @@ class FHDLTestCase(unittest.TestCase): return repr_str.strip() self.assertEqual(prepare_repr(repr(obj)), prepare_repr(repr_str)) - @contextmanager - def assertRaises(self, exception, msg=None): - with super().assertRaises(exception) as cm: - yield - if msg is not None: - # WTF? unittest.assertRaises is completely broken. - self.assertEqual(str(cm.exception), msg) - - @contextmanager - def assertWarns(self, category, msg=None): - with warnings.catch_warnings(record=True) as warns: - yield - self.assertEqual(len(warns), 1) - self.assertEqual(warns[0].category, category) - if msg is not None: - self.assertEqual(str(warns[0].message), msg) - def assertFormal(self, spec, mode="bmc", depth=1): caller, *_ = traceback.extract_stack(limit=2) spec_root, _ = os.path.splitext(caller.filename) -- 2.30.2