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):
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)
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)
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)
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))
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})
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")
])
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)
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)
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):
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):
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):
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")
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):
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
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):
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):
"(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):
"(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):
"(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):
"(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")
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):
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):
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):
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):
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):
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):
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):
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):
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):
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):
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")
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):
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")
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)
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)
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):
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()
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
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 '<module>.d.submodules' would add statements to clock domain "
- "'submodules'; did you mean <module>.submodules instead?"):
+ with self.assertWarnsRegex(SyntaxWarning,
+ (r"^Using '<module>\.d\.submodules' would add statements to clock domain "
+ r"'submodules'; did you mean <module>\.submodules instead\?$")):
m.d.submodules += []
def test_clock_signal(self):
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
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
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
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, """
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
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")
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"
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
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):
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):
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):
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)
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):
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=(<signal>,)`, rather than `ports=<signal>`?)"):
+ with self.assertRaisesRegex(TypeError,
+ (r"^`ports` must be either a list or a tuple, not \(const 1'd1\)"
+ r" \(did you mean `ports=\(<signal>,\)`, rather than `ports=<signal>`\?\)$")):
f.prepare(ports=Const(1))
class FragmentDomainsTestCase(FHDLTestCase):
f.add_subfragment(fa, "a")
f.add_subfragment(fb)
- with self.assertRaises(DomainError,
- msg="Domain 'sync' is defined by subfragments 'a', <unnamed #1> 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', <unnamed #1> 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):
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):
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):
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)
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.<unnamed #1>"):
+ with self.assertRaisesRegex(DriverConflict,
+ r"^Signal '\(sig s1\)' is driven from multiple fragments: top, top.<unnamed #1>$"):
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.<unnamed #1>; "
- "hierarchy will be flattened"):
+ with self.assertWarnsRegex(DriverConflict,
+ (r"^Signal '\(sig s1\)' is driven from multiple fragments: top, top.<unnamed #1>; "
+ r"hierarchy will be flattened$")):
self.f1._resolve_hierarchy_conflicts(mode="warn")
def setUp_sub_sub(self):
def test_conflict_memory_error(self):
self.setUp_memory()
- with self.assertRaises(DriverConflict,
- msg="Memory 'm' is accessed from multiple fragments: top.<unnamed #0>, "
- "top.<unnamed #1>"):
+ with self.assertRaisesRegex(DriverConflict,
+ r"^Memory 'm' is accessed from multiple fragments: top\.<unnamed #0>, "
+ r"top\.<unnamed #1>$"):
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.<unnamed #0>, "
- "top.<unnamed #1>; hierarchy will be flattened"):
+ with self.assertWarnsRegex(DriverConflict,
+ (r"^Memory 'm' is accessed from multiple fragments: top.<unnamed #0>, "
+ r"top.<unnamed #1>; hierarchy will be flattened$")):
self.f1._resolve_hierarchy_conflicts(mode="warn")
def test_explicit_flatten(self):
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):
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):
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):
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):
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)
"('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")])
("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):
("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):
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)
})
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"})
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):
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)
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):
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):
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):
# 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):
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):
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):
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):
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):
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):
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)