diff options
Diffstat (limited to 'Tests/pens/reverseContourPen_test.py')
-rw-r--r-- | Tests/pens/reverseContourPen_test.py | 589 |
1 files changed, 385 insertions, 204 deletions
diff --git a/Tests/pens/reverseContourPen_test.py b/Tests/pens/reverseContourPen_test.py index 9c715404..c250847e 100644 --- a/Tests/pens/reverseContourPen_test.py +++ b/Tests/pens/reverseContourPen_test.py @@ -6,331 +6,512 @@ import pytest TEST_DATA = [ ( [ - ('moveTo', ((0, 0),)), - ('lineTo', ((1, 1),)), - ('lineTo', ((2, 2),)), - ('lineTo', ((3, 3),)), # last not on move, line is implied - ('closePath', ()), - ], - [ - ('moveTo', ((0, 0),)), - ('lineTo', ((3, 3),)), - ('lineTo', ((2, 2),)), - ('lineTo', ((1, 1),)), - ('closePath', ()), - ] + ("moveTo", ((0, 0),)), + ("lineTo", ((1, 1),)), + ("lineTo", ((2, 2),)), + ("lineTo", ((3, 3),)), # last not on move, line is implied + ("closePath", ()), + ], + False, # outputImpliedClosingLine + [ + ("moveTo", ((0, 0),)), + ("lineTo", ((3, 3),)), + ("lineTo", ((2, 2),)), + ("lineTo", ((1, 1),)), + ("closePath", ()), + ], ), ( [ - ('moveTo', ((0, 0),)), - ('lineTo', ((1, 1),)), - ('lineTo', ((2, 2),)), - ('lineTo', ((0, 0),)), # last on move, no implied line - ('closePath', ()), + ("moveTo", ((0, 0),)), + ("lineTo", ((1, 1),)), + ("lineTo", ((2, 2),)), + ("lineTo", ((3, 3),)), # last line does not overlap move... + ("closePath", ()), ], + True, # outputImpliedClosingLine + [ + ("moveTo", ((0, 0),)), + ("lineTo", ((3, 3),)), + ("lineTo", ((2, 2),)), + ("lineTo", ((1, 1),)), + ("lineTo", ((0, 0),)), # ... but closing line is NOT implied + ("closePath", ()), + ], + ), + ( + [ + ("moveTo", ((0, 0),)), + ("lineTo", ((1, 1),)), + ("lineTo", ((2, 2),)), + ("lineTo", ((0, 0),)), # last line overlaps move, explicit line + ("closePath", ()), + ], + False, [ - ('moveTo', ((0, 0),)), - ('lineTo', ((2, 2),)), - ('lineTo', ((1, 1),)), - ('closePath', ()), - ] + ("moveTo", ((0, 0),)), + ("lineTo", ((2, 2),)), + ("lineTo", ((1, 1),)), + ("closePath", ()), # closing line implied + ], ), ( [ - ('moveTo', ((0, 0),)), - ('lineTo', ((0, 0),)), - ('lineTo', ((1, 1),)), - ('lineTo', ((2, 2),)), - ('closePath', ()), + ("moveTo", ((0, 0),)), + ("lineTo", ((1, 1),)), + ("lineTo", ((2, 2),)), + ("lineTo", ((0, 0),)), # last line overlaps move... + ("closePath", ()), ], + True, [ - ('moveTo', ((0, 0),)), - ('lineTo', ((2, 2),)), - ('lineTo', ((1, 1),)), - ('lineTo', ((0, 0),)), - ('lineTo', ((0, 0),)), - ('closePath', ()), - ] + ("moveTo", ((0, 0),)), + ("lineTo", ((2, 2),)), + ("lineTo", ((1, 1),)), + ("lineTo", ((0, 0),)), # ... but line is NOT implied + ("closePath", ()), + ], ), ( [ - ('moveTo', ((0, 0),)), - ('lineTo', ((1, 1),)), - ('closePath', ()), + ("moveTo", ((0, 0),)), + ("lineTo", ((0, 0),)), # duplicate lineTo following moveTo + ("lineTo", ((1, 1),)), + ("lineTo", ((2, 2),)), + ("closePath", ()), + ], + False, + [ + ("moveTo", ((0, 0),)), + ("lineTo", ((2, 2),)), + ("lineTo", ((1, 1),)), + ("lineTo", ((0, 0),)), # extra explicit lineTo is always emitted to + ("lineTo", ((0, 0),)), # disambiguate from an implicit closing line + ("closePath", ()), ], + ), + ( [ - ('moveTo', ((0, 0),)), - ('lineTo', ((1, 1),)), - ('closePath', ()), - ] + ("moveTo", ((0, 0),)), + ("lineTo", ((0, 0),)), # duplicate lineTo following moveTo + ("lineTo", ((1, 1),)), + ("lineTo", ((2, 2),)), + ("closePath", ()), + ], + True, + [ + ("moveTo", ((0, 0),)), + ("lineTo", ((2, 2),)), + ("lineTo", ((1, 1),)), + ("lineTo", ((0, 0),)), # duplicate lineTo is retained also in this case, + ("lineTo", ((0, 0),)), # same result as with outputImpliedClosingLine=False + ("closePath", ()), + ], ), ( [ - ('moveTo', ((0, 0),)), - ('curveTo', ((1, 1), (2, 2), (3, 3))), - ('curveTo', ((4, 4), (5, 5), (0, 0))), - ('closePath', ()), + ("moveTo", ((0, 0),)), + ("lineTo", ((1, 1),)), + ("closePath", ()), ], + False, [ - ('moveTo', ((0, 0),)), - ('curveTo', ((5, 5), (4, 4), (3, 3))), - ('curveTo', ((2, 2), (1, 1), (0, 0))), - ('closePath', ()), - ] + ("moveTo", ((0, 0),)), + ("lineTo", ((1, 1),)), + ("closePath", ()), + ], ), ( [ - ('moveTo', ((0, 0),)), - ('curveTo', ((1, 1), (2, 2), (3, 3))), - ('curveTo', ((4, 4), (5, 5), (6, 6))), - ('closePath', ()), + ("moveTo", ((0, 0),)), + ("lineTo", ((1, 1),)), + ("closePath", ()), ], + True, [ - ('moveTo', ((0, 0),)), - ('lineTo', ((6, 6),)), # implied line - ('curveTo', ((5, 5), (4, 4), (3, 3))), - ('curveTo', ((2, 2), (1, 1), (0, 0))), - ('closePath', ()), - ] + ("moveTo", ((0, 0),)), + ("lineTo", ((1, 1),)), + ("lineTo", ((0, 0),)), + ("closePath", ()), + ], ), ( [ - ('moveTo', ((0, 0),)), - ('lineTo', ((1, 1),)), # this line becomes implied - ('curveTo', ((2, 2), (3, 3), (4, 4))), - ('curveTo', ((5, 5), (6, 6), (7, 7))), - ('closePath', ()), + ("moveTo", ((0, 0),)), + ("curveTo", ((1, 1), (2, 2), (3, 3))), + ("curveTo", ((4, 4), (5, 5), (0, 0))), # closed curveTo overlaps moveTo + ("closePath", ()), ], + False, [ - ('moveTo', ((0, 0),)), - ('lineTo', ((7, 7),)), - ('curveTo', ((6, 6), (5, 5), (4, 4))), - ('curveTo', ((3, 3), (2, 2), (1, 1))), - ('closePath', ()), - ] + ("moveTo", ((0, 0),)), # no extra lineTo added here + ("curveTo", ((5, 5), (4, 4), (3, 3))), + ("curveTo", ((2, 2), (1, 1), (0, 0))), + ("closePath", ()), + ], ), ( [ - ('moveTo', ((0, 0),)), - ('qCurveTo', ((1, 1), (2, 2))), - ('qCurveTo', ((3, 3), (0, 0))), - ('closePath', ()), + ("moveTo", ((0, 0),)), + ("curveTo", ((1, 1), (2, 2), (3, 3))), + ("curveTo", ((4, 4), (5, 5), (0, 0))), # closed curveTo overlaps moveTo + ("closePath", ()), ], + True, [ - ('moveTo', ((0, 0),)), - ('qCurveTo', ((3, 3), (2, 2))), - ('qCurveTo', ((1, 1), (0, 0))), - ('closePath', ()), - ] + ("moveTo", ((0, 0),)), # no extra lineTo added here, same as preceding + ("curveTo", ((5, 5), (4, 4), (3, 3))), + ("curveTo", ((2, 2), (1, 1), (0, 0))), + ("closePath", ()), + ], ), ( [ - ('moveTo', ((0, 0),)), - ('qCurveTo', ((1, 1), (2, 2))), - ('qCurveTo', ((3, 3), (4, 4))), - ('closePath', ()), + ("moveTo", ((0, 0),)), + ("curveTo", ((1, 1), (2, 2), (3, 3))), + ("curveTo", ((4, 4), (5, 5), (6, 6))), # closed curve not overlapping move + ("closePath", ()), ], + False, [ - ('moveTo', ((0, 0),)), - ('lineTo', ((4, 4),)), - ('qCurveTo', ((3, 3), (2, 2))), - ('qCurveTo', ((1, 1), (0, 0))), - ('closePath', ()), - ] + ("moveTo", ((0, 0),)), + ("lineTo", ((6, 6),)), # the previously implied line + ("curveTo", ((5, 5), (4, 4), (3, 3))), + ("curveTo", ((2, 2), (1, 1), (0, 0))), + ("closePath", ()), + ], ), ( [ - ('moveTo', ((0, 0),)), - ('lineTo', ((1, 1),)), - ('qCurveTo', ((2, 2), (3, 3))), - ('closePath', ()), + ("moveTo", ((0, 0),)), + ("curveTo", ((1, 1), (2, 2), (3, 3))), + ("curveTo", ((4, 4), (5, 5), (6, 6))), # closed curve not overlapping move + ("closePath", ()), ], + True, [ - ('moveTo', ((0, 0),)), - ('lineTo', ((3, 3),)), - ('qCurveTo', ((2, 2), (1, 1))), - ('closePath', ()), - ] + ("moveTo", ((0, 0),)), + ("lineTo", ((6, 6),)), # the previously implied line (same as above) + ("curveTo", ((5, 5), (4, 4), (3, 3))), + ("curveTo", ((2, 2), (1, 1), (0, 0))), + ("closePath", ()), + ], ), ( [ - ('addComponent', ('a', (1, 0, 0, 1, 0, 0))) + ("moveTo", ((0, 0),)), + ("lineTo", ((1, 1),)), # this line becomes implied + ("curveTo", ((2, 2), (3, 3), (4, 4))), + ("curveTo", ((5, 5), (6, 6), (7, 7))), + ("closePath", ()), ], + False, [ - ('addComponent', ('a', (1, 0, 0, 1, 0, 0))) - ] + ("moveTo", ((0, 0),)), + ("lineTo", ((7, 7),)), + ("curveTo", ((6, 6), (5, 5), (4, 4))), + ("curveTo", ((3, 3), (2, 2), (1, 1))), + ("closePath", ()), + ], ), ( - [], [] + [ + ("moveTo", ((0, 0),)), + ("lineTo", ((1, 1),)), # this line... + ("curveTo", ((2, 2), (3, 3), (4, 4))), + ("curveTo", ((5, 5), (6, 6), (7, 7))), + ("closePath", ()), + ], + True, + [ + ("moveTo", ((0, 0),)), + ("lineTo", ((7, 7),)), + ("curveTo", ((6, 6), (5, 5), (4, 4))), + ("curveTo", ((3, 3), (2, 2), (1, 1))), + ("lineTo", ((0, 0),)), # ... does NOT become implied + ("closePath", ()), + ], ), ( [ - ('moveTo', ((0, 0),)), - ('endPath', ()), + ("moveTo", ((0, 0),)), + ("qCurveTo", ((1, 1), (2, 2))), + ("qCurveTo", ((3, 3), (0, 0))), # closed qCurve overlaps move + ("closePath", ()), ], + False, [ - ('moveTo', ((0, 0),)), - ('endPath', ()), + ("moveTo", ((0, 0),)), # no extra lineTo added here + ("qCurveTo", ((3, 3), (2, 2))), + ("qCurveTo", ((1, 1), (0, 0))), + ("closePath", ()), ], ), ( [ - ('moveTo', ((0, 0),)), - ('closePath', ()), + ("moveTo", ((0, 0),)), + ("qCurveTo", ((1, 1), (2, 2))), + ("qCurveTo", ((3, 3), (0, 0))), # closed qCurve overlaps move + ("closePath", ()), ], + True, # <-- [ - ('moveTo', ((0, 0),)), - ('endPath', ()), # single-point paths is always open + ("moveTo", ((0, 0),)), # no extra lineTo added here, same as above + ("qCurveTo", ((3, 3), (2, 2))), + ("qCurveTo", ((1, 1), (0, 0))), + ("closePath", ()), ], ), ( [ - ('moveTo', ((0, 0),)), - ('lineTo', ((1, 1),)), - ('endPath', ()) + ("moveTo", ((0, 0),)), + ("qCurveTo", ((1, 1), (2, 2))), + ("qCurveTo", ((3, 3), (4, 4))), # closed qCurve not overlapping move + ("closePath", ()), ], + False, [ - ('moveTo', ((1, 1),)), - ('lineTo', ((0, 0),)), - ('endPath', ()) - ] + ("moveTo", ((0, 0),)), + ("lineTo", ((4, 4),)), # the previously implied line + ("qCurveTo", ((3, 3), (2, 2))), + ("qCurveTo", ((1, 1), (0, 0))), + ("closePath", ()), + ], ), ( [ - ('moveTo', ((0, 0),)), - ('curveTo', ((1, 1), (2, 2), (3, 3))), - ('endPath', ()) + ("moveTo", ((0, 0),)), + ("qCurveTo", ((1, 1), (2, 2))), + ("qCurveTo", ((3, 3), (4, 4))), # closed qCurve not overlapping move + ("closePath", ()), ], + True, [ - ('moveTo', ((3, 3),)), - ('curveTo', ((2, 2), (1, 1), (0, 0))), - ('endPath', ()) - ] + ("moveTo", ((0, 0),)), + ("lineTo", ((4, 4),)), # the previously implied line (same as above) + ("qCurveTo", ((3, 3), (2, 2))), + ("qCurveTo", ((1, 1), (0, 0))), + ("closePath", ()), + ], + ), + ( + [ + ("moveTo", ((0, 0),)), + ("lineTo", ((1, 1),)), + ("qCurveTo", ((2, 2), (3, 3))), + ("closePath", ()), + ], + False, + [ + ("moveTo", ((0, 0),)), + ("lineTo", ((3, 3),)), + ("qCurveTo", ((2, 2), (1, 1))), + ("closePath", ()), + ], + ), + ( + [("addComponent", ("a", (1, 0, 0, 1, 0, 0)))], + False, + [("addComponent", ("a", (1, 0, 0, 1, 0, 0)))], ), + ([], False, []), ( [ - ('moveTo', ((0, 0),)), - ('curveTo', ((1, 1), (2, 2), (3, 3))), - ('lineTo', ((4, 4),)), - ('endPath', ()) + ("moveTo", ((0, 0),)), + ("endPath", ()), ], + False, [ - ('moveTo', ((4, 4),)), - ('lineTo', ((3, 3),)), - ('curveTo', ((2, 2), (1, 1), (0, 0))), - ('endPath', ()) - ] + ("moveTo", ((0, 0),)), + ("endPath", ()), + ], ), ( [ - ('moveTo', ((0, 0),)), - ('lineTo', ((1, 1),)), - ('curveTo', ((2, 2), (3, 3), (4, 4))), - ('endPath', ()) + ("moveTo", ((0, 0),)), + ("closePath", ()), ], + False, [ - ('moveTo', ((4, 4),)), - ('curveTo', ((3, 3), (2, 2), (1, 1))), - ('lineTo', ((0, 0),)), - ('endPath', ()) - ] + ("moveTo", ((0, 0),)), + ("endPath", ()), # single-point paths is always open + ], + ), + ( + [("moveTo", ((0, 0),)), ("lineTo", ((1, 1),)), ("endPath", ())], + False, + [("moveTo", ((1, 1),)), ("lineTo", ((0, 0),)), ("endPath", ())], + ), + ( + [("moveTo", ((0, 0),)), ("curveTo", ((1, 1), (2, 2), (3, 3))), ("endPath", ())], + False, + [("moveTo", ((3, 3),)), ("curveTo", ((2, 2), (1, 1), (0, 0))), ("endPath", ())], ), ( [ - ('qCurveTo', ((0, 0), (1, 1), (2, 2), None)), - ('closePath', ()) + ("moveTo", ((0, 0),)), + ("curveTo", ((1, 1), (2, 2), (3, 3))), + ("lineTo", ((4, 4),)), + ("endPath", ()), ], + False, [ - ('qCurveTo', ((0, 0), (2, 2), (1, 1), None)), - ('closePath', ()) - ] + ("moveTo", ((4, 4),)), + ("lineTo", ((3, 3),)), + ("curveTo", ((2, 2), (1, 1), (0, 0))), + ("endPath", ()), + ], ), ( [ - ('qCurveTo', ((0, 0), (1, 1), (2, 2), None)), - ('endPath', ()) + ("moveTo", ((0, 0),)), + ("lineTo", ((1, 1),)), + ("curveTo", ((2, 2), (3, 3), (4, 4))), + ("endPath", ()), ], + False, [ - ('qCurveTo', ((0, 0), (2, 2), (1, 1), None)), - ('closePath', ()) # this is always "closed" - ] + ("moveTo", ((4, 4),)), + ("curveTo", ((3, 3), (2, 2), (1, 1))), + ("lineTo", ((0, 0),)), + ("endPath", ()), + ], + ), + ( + [("qCurveTo", ((0, 0), (1, 1), (2, 2), None)), ("closePath", ())], + False, + [("qCurveTo", ((0, 0), (2, 2), (1, 1), None)), ("closePath", ())], + ), + ( + [("qCurveTo", ((0, 0), (1, 1), (2, 2), None)), ("endPath", ())], + False, + [ + ("qCurveTo", ((0, 0), (2, 2), (1, 1), None)), + ("closePath", ()), # this is always "closed" + ], ), # Test case from: # https://github.com/googlei18n/cu2qu/issues/51#issue-179370514 ( [ - ('moveTo', ((848, 348),)), - ('lineTo', ((848, 348),)), # duplicate lineTo point after moveTo - ('qCurveTo', ((848, 526), (649, 704), (449, 704))), - ('qCurveTo', ((449, 704), (248, 704), (50, 526), (50, 348))), - ('lineTo', ((50, 348),)), - ('qCurveTo', ((50, 348), (50, 171), (248, -3), (449, -3))), - ('qCurveTo', ((449, -3), (649, -3), (848, 171), (848, 348))), - ('closePath', ()) - ], - [ - ('moveTo', ((848, 348),)), - ('qCurveTo', ((848, 171), (649, -3), (449, -3), (449, -3))), - ('qCurveTo', ((248, -3), (50, 171), (50, 348), (50, 348))), - ('lineTo', ((50, 348),)), - ('qCurveTo', ((50, 526), (248, 704), (449, 704), (449, 704))), - ('qCurveTo', ((649, 704), (848, 526), (848, 348))), - ('lineTo', ((848, 348),)), # the duplicate point is kept - ('closePath', ()) - ] + ("moveTo", ((848, 348),)), + ("lineTo", ((848, 348),)), # duplicate lineTo point after moveTo + ("qCurveTo", ((848, 526), (649, 704), (449, 704))), + ("qCurveTo", ((449, 704), (248, 704), (50, 526), (50, 348))), + ("lineTo", ((50, 348),)), + ("qCurveTo", ((50, 348), (50, 171), (248, -3), (449, -3))), + ("qCurveTo", ((449, -3), (649, -3), (848, 171), (848, 348))), + ("closePath", ()), + ], + False, + [ + ("moveTo", ((848, 348),)), + ("qCurveTo", ((848, 171), (649, -3), (449, -3), (449, -3))), + ("qCurveTo", ((248, -3), (50, 171), (50, 348), (50, 348))), + ("lineTo", ((50, 348),)), + ("qCurveTo", ((50, 526), (248, 704), (449, 704), (449, 704))), + ("qCurveTo", ((649, 704), (848, 526), (848, 348))), + ("lineTo", ((848, 348),)), # the duplicate point is kept + ("closePath", ()), + ], ), # Test case from https://github.com/googlefonts/fontmake/issues/572 # An additional closing lineTo is required to disambiguate a duplicate # point at the end of a contour from the implied closing line. ( [ - ('moveTo', ((0, 651),)), - ('lineTo', ((0, 101),)), - ('lineTo', ((0, 101),)), - ('lineTo', ((0, 651),)), - ('lineTo', ((0, 651),)), - ('closePath', ()) + ("moveTo", ((0, 651),)), + ("lineTo", ((0, 101),)), + ("lineTo", ((0, 101),)), + ("lineTo", ((0, 651),)), + ("lineTo", ((0, 651),)), + ("closePath", ()), ], + False, [ - ('moveTo', ((0, 651),)), - ('lineTo', ((0, 651),)), - ('lineTo', ((0, 101),)), - ('lineTo', ((0, 101),)), - ('closePath', ()) - ] - ) + ("moveTo", ((0, 651),)), + ("lineTo", ((0, 651),)), + ("lineTo", ((0, 101),)), + ("lineTo", ((0, 101),)), + ("closePath", ()), + ], + ), + ( + [ + ("moveTo", ((0, 651),)), + ("lineTo", ((0, 101),)), + ("lineTo", ((0, 101),)), + ("lineTo", ((0, 651),)), + ("lineTo", ((0, 651),)), + ("closePath", ()), + ], + True, + [ + ("moveTo", ((0, 651),)), + ("lineTo", ((0, 651),)), + ("lineTo", ((0, 101),)), + ("lineTo", ((0, 101),)), + ("lineTo", ((0, 651),)), # closing line not implied + ("closePath", ()), + ], + ), ] -@pytest.mark.parametrize("contour, expected", TEST_DATA) -def test_reverse_pen(contour, expected): +@pytest.mark.parametrize("contour, outputImpliedClosingLine, expected", TEST_DATA) +def test_reverse_pen(contour, outputImpliedClosingLine, expected): recpen = RecordingPen() - revpen = ReverseContourPen(recpen) + revpen = ReverseContourPen(recpen, outputImpliedClosingLine) for operator, operands in contour: getattr(revpen, operator)(*operands) assert recpen.value == expected -@pytest.mark.parametrize("contour, expected", TEST_DATA) -def test_reverse_point_pen(contour, expected): - from fontTools.ufoLib.pointPen import ( - ReverseContourPointPen, PointToSegmentPen, SegmentToPointPen) +def test_reverse_pen_outputImpliedClosingLine(): + recpen = RecordingPen() + revpen = ReverseContourPen(recpen) + revpen.moveTo((0, 0)) + revpen.lineTo((10, 0)) + revpen.lineTo((0, 10)) + revpen.lineTo((0, 0)) + revpen.closePath() + assert recpen.value == [ + ("moveTo", ((0, 0),)), + ("lineTo", ((0, 10),)), + ("lineTo", ((10, 0),)), + # ("lineTo", ((0, 0),)), # implied + ("closePath", ()), + ] + + recpen = RecordingPen() + revpen = ReverseContourPen(recpen, outputImpliedClosingLine=True) + revpen.moveTo((0, 0)) + revpen.lineTo((10, 0)) + revpen.lineTo((0, 10)) + revpen.lineTo((0, 0)) + revpen.closePath() + assert recpen.value == [ + ("moveTo", ((0, 0),)), + ("lineTo", ((0, 10),)), + ("lineTo", ((10, 0),)), + ("lineTo", ((0, 0),)), # not implied + ("closePath", ()), + ] + + +@pytest.mark.parametrize("contour, outputImpliedClosingLine, expected", TEST_DATA) +def test_reverse_point_pen(contour, outputImpliedClosingLine, expected): + from fontTools.pens.pointPen import ( + ReverseContourPointPen, + PointToSegmentPen, + SegmentToPointPen, + ) recpen = RecordingPen() - pt2seg = PointToSegmentPen(recpen, outputImpliedClosingLine=True) + pt2seg = PointToSegmentPen(recpen, outputImpliedClosingLine) revpen = ReverseContourPointPen(pt2seg) seg2pt = SegmentToPointPen(revpen) for operator, operands in contour: getattr(seg2pt, operator)(*operands) - # for closed contours that have a lineTo following the moveTo, - # and whose points don't overlap, our current implementation diverges - # from the ReverseContourPointPen as wrapped by ufoLib's pen converters. - # In the latter case, an extra lineTo is added because of - # outputImpliedClosingLine=True. This is redundant but not incorrect, - # as the number of points is the same in both. - if (contour and contour[-1][0] == "closePath" and - contour[1][0] == "lineTo" and contour[1][1] != contour[0][1]): - expected = expected[:-1] + [("lineTo", contour[0][1])] + expected[-1:] - assert recpen.value == expected |