Beispiel #1
0
def test_prologue_register_directive():
    """ Test registration of block and line directives """
    pro = Prologue()

    class LineDirx(LineDirective):
        pass

    class BlockDirx(BlockDirective):
        pass

    wrap_line = DirectiveWrap(LineDirx, [random_str(5, 10)])
    wrap_block = DirectiveWrap(BlockDirx, [random_str(5, 10)],
                               closing=[random_str(5, 10)])
    # Try registration
    pro.register_directive(wrap_line)
    pro.register_directive(wrap_block)
    # Check registrations
    assert pro.get_directive(wrap_line.opening[0]) == wrap_line
    assert pro.get_directive(wrap_block.opening[0]) == wrap_block
    # Try registration a bad directive
    for obj in (random_str(5, 10), randint(1, 1000), Prologue):
        with pytest.raises(PrologueError) as excinfo:
            pro.register_directive(obj)
        assert "Directive type is not known, is it decorated?" == str(
            excinfo.value)
    # Try overriding existing directive
    class OtherLineDirx(LineDirective):
        pass

    class OtherBlockDirx(BlockDirective):
        pass

    other_line = DirectiveWrap(OtherLineDirx, wrap_line.opening)
    other_block = DirectiveWrap(OtherBlockDirx,
                                wrap_block.opening,
                                closing=wrap_block.closing)
    with pytest.raises(PrologueError) as excinfo:
        pro.register_directive(other_line)
    assert f"Directive already registered for tag '{wrap_line.opening[0]}'" == str(
        excinfo.value)
    with pytest.raises(PrologueError) as excinfo:
        pro.register_directive(other_block)
    assert f"Directive already registered for tag '{wrap_block.opening[0]}'" == str(
        excinfo.value)
Beispiel #2
0
def test_prologue_evaluate_inner_block_floating(mocker):
    """ Test that floating block directives are flagged """
    # Choose a delimiter
    delim = choice(("#", "@", "$", "%", "!"))
    # Create preprocessor, context, etc
    pro = Prologue(delimiter=delim)
    ctx = Context(pro)
    m_reg = mocker.patch.object(pro, "registry", autospec=True)
    mocker.patch.object(RegistryFile, "__init__", lambda x: None)
    m_con = mocker.patch.object(RegistryFile,
                                "contents",
                                new_callable=PropertyMock)

    # Create a block directive
    class BlockDirx(BlockDirective):
        def __init__(self, parent, src_file=None, src_line=0, callback=None):
            super().__init__(
                parent,
                yields=True,
                src_file=src_file,
                src_line=src_line,
                callback=callback,
            )

    opening = [random_str(5, 10) for _x in range(randint(1, 5))]
    closing = [random_str(5, 10, avoid=opening) for _x in range(1, 5)]
    transit = [
        random_str(5, 10, avoid=opening + closing) for _x in range(1, 5)
    ]
    pro.register_directive(
        DirectiveWrap(BlockDirx, opening, transition=transit, closing=closing))
    # Create a fake file
    r_file = RegistryFile()
    r_file.path = Path(random_str(5, 10) + "." + random_str(5, 10))
    m_reg.resolve.side_effect = [r_file]
    # Setup fake file contents
    contents = []
    used_open = []
    for idx in range(randint(50, 100)):
        if choice((True, False)):
            used_open.append(choice(opening))
            contents.append(
                random_str(50, 100, spaces=True) +
                f" {delim}{used_open[-1]} {random_str(50, 100, spaces=True)}")
        else:
            contents.append(random_str(50, 100, spaces=True))
    m_con.return_value = [
        Line(x, r_file, i + 1) for i, x in enumerate(contents)
    ]
    # Catch the floating block error
    with pytest.raises(PrologueError) as excinfo:
        [x for x in pro.evaluate_inner(r_file.filename, ctx)]
    assert (f"The directive '{used_open[0].lower()}' can only be used with an "
            f"anchored delimiter as it is a block directive") == str(
                excinfo.value)
Beispiel #3
0
def test_prologue_evaluate_inner_block_trailing(mocker):
    """ Check that unclosed blocks at the end of the file are detected """
    # Choose a delimiter
    delim = choice(("#", "@", "$", "%", "!"))
    # Create a pair of block directives
    dirx_inst = []

    class BlockDirx(BlockDirective):
        def __init__(self, parent, src_file, src_line, callback=None):
            super().__init__(
                parent,
                yields=True,
                src_file=src_file,
                src_line=src_line,
                callback=callback,
            )
            dirx_inst.append(self)

    opening = [random_str(5, 10) for _x in range(randint(1, 5))]
    closing = [random_str(5, 10, avoid=opening) for _x in range(randint(1, 5))]
    transit = [
        random_str(5, 10, avoid=opening + closing)
        for _x in range(randint(1, 5))
    ]
    BlockDirx.OPENING = opening
    # Create a fake file
    mocker.patch.object(RegistryFile, "__init__", lambda x: None)
    r_file = RegistryFile()
    r_file.path = Path(random_str(5, 10) + "." + random_str(5, 10))
    # Create preprocessor, context, etc
    for _x in range(100):
        pro = Prologue(delimiter=delim)
        ctx = Context(pro)
        m_reg = mocker.patch.object(pro, "registry", autospec=True)
        m_reg.resolve.side_effect = [r_file]
        m_con = mocker.patch.object(RegistryFile,
                                    "contents",
                                    new_callable=PropertyMock)
        pro.register_directive(
            DirectiveWrap(BlockDirx,
                          opening,
                          transition=transit,
                          closing=closing))
        # Setup fake file contents
        contents = []
        contents += [
            random_str(50, 100, spaces=True) for _x in range(randint(5, 10))
        ]
        open_idx = len(contents)
        contents += [
            f"{delim}{choice(opening)} {random_str(50, 100, spaces=True)}"
        ]
        contents += [
            random_str(50, 100, spaces=True) for _x in range(randint(5, 10))
        ]
        for _y in range(randint(0, 3)):
            contents += [
                f"{delim}{choice(transit)} {random_str(50, 100, spaces=True)}"
            ]
            contents += [
                random_str(50, 100, spaces=True)
                for _x in range(randint(5, 10))
            ]
        m_con.return_value = [
            Line(x, r_file, i + 1) for i, x in enumerate(contents)
        ]
        # Expected an unclosed directive
        with pytest.raises(PrologueError) as excinfo:
            [x for x in pro.evaluate_inner(r_file.filename, ctx)]
        assert str(excinfo.value).startswith(
            f"Unmatched BlockDirx block directive in {r_file.path}:{open_idx+1}:"
        )
Beispiel #4
0
def test_prologue_evaluate_inner_block_confused(mocker):
    """ Check that one block can't be closed by another's tags """
    # Choose a delimiter
    delim = choice(("#", "@", "$", "%", "!"))

    # Create a pair of block directives
    class BlockDirA(BlockDirective):
        def __init__(self, parent, src_file=None, src_line=0, callback=None):
            super().__init__(
                parent,
                yields=True,
                src_file=src_file,
                src_line=src_line,
                callback=callback,
            )

    class BlockDirB(BlockDirective):
        def __init__(self, parent, src_file=None, src_line=0, callback=None):
            super().__init__(
                parent,
                yields=True,
                src_file=src_file,
                src_line=src_line,
                callback=callback,
            )

    all_tags = []
    opening_a = [
        random_str(5, 10, avoid=all_tags) for _x in range(randint(1, 5))
    ]
    all_tags += opening_a
    closing_a = [
        random_str(5, 10, avoid=all_tags) for _x in range(randint(1, 5))
    ]
    all_tags += closing_a
    transit_a = [
        random_str(5, 10, avoid=all_tags) for _x in range(randint(1, 5))
    ]
    all_tags += transit_a
    opening_b = [
        random_str(5, 10, avoid=all_tags) for _x in range(randint(1, 5))
    ]
    all_tags += opening_b
    closing_b = [
        random_str(5, 10, avoid=all_tags) for _x in range(randint(1, 5))
    ]
    all_tags += closing_b
    transit_b = [
        random_str(5, 10, avoid=all_tags) for _x in range(randint(1, 5))
    ]
    # Create a fake file
    mocker.patch.object(RegistryFile, "__init__", lambda x: None)
    r_file = RegistryFile()
    r_file.path = Path(random_str(5, 10) + "." + random_str(5, 10))
    # Create preprocessor, context, etc
    for _x in range(100):
        pro = Prologue(delimiter=delim)
        ctx = Context(pro)
        m_reg = mocker.patch.object(pro, "registry", autospec=True)
        m_reg.resolve.side_effect = [r_file]
        m_con = mocker.patch.object(RegistryFile,
                                    "contents",
                                    new_callable=PropertyMock)
        pro.register_directive(
            DirectiveWrap(BlockDirA,
                          opening_a,
                          transition=transit_a,
                          closing=closing_a))
        pro.register_directive(
            DirectiveWrap(BlockDirB,
                          opening_b,
                          transition=transit_b,
                          closing=closing_b))
        # Setup fake file contents
        bad_tag = choice(transit_b + closing_b)
        contents = []
        contents += [
            random_str(50, 100, spaces=True) for _x in range(randint(5, 10))
        ]
        contents += [
            f"{delim}{choice(opening_a)} {random_str(50, 100, spaces=True)}"
        ]
        contents += [
            random_str(50, 100, spaces=True) for _x in range(randint(5, 10))
        ]
        contents += [f"{delim}{bad_tag} {random_str(50, 100, spaces=True)}"]
        contents += [
            random_str(50, 100, spaces=True) for _x in range(randint(5, 10))
        ]
        m_con.return_value = [
            Line(x, r_file, i + 1) for i, x in enumerate(contents)
        ]
        # Expect an unexpected transition tag
        with pytest.raises(PrologueError) as excinfo:
            [x for x in pro.evaluate_inner(r_file.filename, ctx)]
        if bad_tag in transit_b:
            assert (
                f"Transition tag '{bad_tag.lower()}' was not expected") == str(
                    excinfo.value)
        else:
            assert (
                f"Closing tag '{bad_tag.lower()}' was not expected") == str(
                    excinfo.value)
Beispiel #5
0
def test_prologue_evaluate_inner_block(mocker, should_yield):
    """ Check that a block directive is detected """
    # Choose a delimiter
    delim = choice(("#", "@", "$", "%", "!"))
    # Create preprocessor, context, etc
    pro = Prologue(delimiter=delim)
    ctx = Context(pro)
    m_reg = mocker.patch.object(pro, "registry", autospec=True)
    mocker.patch.object(RegistryFile, "__init__", lambda x: None)
    m_con = mocker.patch.object(RegistryFile,
                                "contents",
                                new_callable=PropertyMock)
    # Create a line directive
    dirx_inst = []

    class BlockDirx(BlockDirective):
        def __init__(self, parent, src_file=None, src_line=0, callback=None):
            super().__init__(
                parent,
                yields=should_yield,
                src_file=src_file,
                src_line=src_line,
                callback=callback,
            )
            dirx_inst.append(self)

    mocker.patch.object(BlockDirx, "open", autospec=True)
    mocker.patch.object(BlockDirx, "transition", autospec=True)
    mocker.patch.object(BlockDirx, "close", autospec=True)
    mocker.patch.object(BlockDirx, "evaluate", autospec=True)

    def do_open(self, tag, arguments):
        self._BlockDirective__opened = True

    def do_close(self, tag, arguments):
        self._BlockDirective__closed = True

    BlockDirx.open.side_effect = do_open
    BlockDirx.close.side_effect = do_close
    dirx_text = []
    for _x in range(randint(5, 10)):
        dirx_text.append(random_str(20, 30, spaces=True))

    def block_eval(self, context):
        for line in dirx_text:
            yield Line(line, None, randint(1, 10000))

    BlockDirx.evaluate.side_effect = block_eval
    opening = [random_str(5, 10) for _x in range(randint(1, 5))]
    closing = [random_str(5, 10, avoid=opening) for _x in range(1, 5)]
    transit = [
        random_str(5, 10, avoid=opening + closing) for _x in range(1, 5)
    ]
    pro.register_directive(
        DirectiveWrap(BlockDirx, opening, transition=transit, closing=closing))
    # Create a fake file
    r_file = RegistryFile()
    r_file.path = Path(random_str(5, 10) + "." + random_str(5, 10))
    m_reg.resolve.side_effect = [r_file]
    # Setup fake file contents
    contents = []
    output = []
    open_calls = []
    tran_calls = []
    close_calls = []
    for idx in range(randint(50, 100)):
        use_dirx = choice((True, False))
        open_arg = random_str(50, 100, spaces=True)
        tran_args = [
            random_str(50, 100, spaces=True) for _x in range(randint(0, 3))
        ]
        close_arg = random_str(50, 100, spaces=True)
        open_tag = choice(opening)
        close_tag = choice(closing)
        tran_tag = choice(transit)
        if use_dirx:
            contents.append(f"{delim}{open_tag} {open_arg}")
        else:
            contents.append(random_str(50, 100, spaces=True))
        # If this is a directive, generate transitions and closing
        if use_dirx:
            # Opening block contents
            for _x in range(randint(5, 10)):
                contents.append(random_str(20, 30, spaces=True))
            # Transitions
            for arg in tran_args:
                contents.append(f"{delim}{tran_tag} {arg}")
                for _x in range(5, 10):
                    contents.append(random_str(20, 30, spaces=True))
            contents.append(f"{delim}{close_tag} {close_arg}")
        # Setup expected output
        if use_dirx and should_yield: output += dirx_text
        elif not use_dirx: output.append(contents[-1])
        # Accumulate calls
        if use_dirx:
            open_calls.append(call(ANY, open_tag.lower(), open_arg))
            for arg in tran_args:
                tran_calls.append(call(ANY, tran_tag.lower(), arg))
            close_calls.append(call(ANY, close_tag.lower(), close_arg))
    m_con.return_value = [
        Line(x, r_file, i + 1) for i, x in enumerate(contents)
    ]

    # Create a dummy callback
    def dummy_cb():
        pass

    # Pull all lines out of the evaluate loop
    result = [
        x for x in pro.evaluate_inner(r_file.filename, ctx, callback=dummy_cb)
    ]
    # Checks
    assert len(result) == len(output)
    assert ctx.stack == []
    m_reg.resolve.assert_has_calls([call(r_file.filename)])
    for got_out, exp_out in zip(result, output):
        assert str(got_out) == exp_out.rstrip(" ")
    BlockDirx.open.assert_has_calls(open_calls)
    BlockDirx.transition.assert_has_calls(tran_calls)
    BlockDirx.close.assert_has_calls(close_calls)
    for dirx in dirx_inst:
        assert dirx.callback == dummy_cb
Beispiel #6
0
def test_prologue_evaluate_inner_line(mocker, should_yield):
    """ Check that a line directive is detected """
    # Choose a delimiter
    delim = choice(("#", "@", "$", "%", "!"))
    # Create preprocessor, context, etc
    pro = Prologue(delimiter=delim)
    ctx = Context(pro)
    m_reg = mocker.patch.object(pro, "registry", autospec=True)
    mocker.patch.object(RegistryFile, "__init__", lambda x: None)
    m_con = mocker.patch.object(RegistryFile,
                                "contents",
                                new_callable=PropertyMock)
    # Create a line directive
    dirx_inst = []

    class LineDirx(LineDirective):
        def __init__(self, parent, src_file=None, src_line=0, callback=None):
            super().__init__(
                parent,
                yields=should_yield,
                src_file=src_file,
                src_line=src_line,
                callback=callback,
            )
            dirx_inst.append(self)

    mocker.patch.object(LineDirx, "invoke", autospec=True)
    mocker.patch.object(LineDirx, "evaluate", autospec=True)
    dirx_text = "LINE DIRX " + random_str(20, 30, spaces=True) + " END LINE"

    def line_eval(self, context):
        yield Line(dirx_text, None, randint(1, 10000))

    LineDirx.evaluate.side_effect = line_eval
    opening = [random_str(5, 10) for _x in range(randint(1, 5))]
    pro.register_directive(DirectiveWrap(LineDirx, opening))
    # Create a fake file
    r_file = RegistryFile()
    r_file.path = Path(random_str(5, 10) + "." + random_str(5, 10))
    m_reg.resolve.side_effect = [r_file]
    # Setup fake file contents
    contents = []
    output = []
    dirx_calls = []
    for idx in range(randint(50, 100)):
        use_dirx = choice((True, False))
        anchor = choice((True, False))
        argument = random_str(50, 100, spaces=True)
        use_tag = choice(opening)
        line_txt = ""
        if use_dirx:
            if not anchor: line_txt += random_str(50, 100, spaces=True) + " "
            line_txt += f"{delim}{use_tag} {argument}"
        else:
            line_txt += random_str(50, 100, spaces=True)
        # Accumulate the data to push into evaluate
        contents.append(line_txt)
        # Accumulate expected outputs
        if not (use_dirx and anchor): output.append(line_txt.split(delim)[0])
        if should_yield:
            if use_dirx and anchor: output.append(dirx_text)
            if use_dirx and not anchor: output.append(dirx_text)
        # Accumulate calls
        if use_dirx: dirx_calls.append(call(ANY, use_tag.lower(), argument))
    m_con.return_value = [
        Line(x, r_file, i + 1) for i, x in enumerate(contents)
    ]

    # Create a dummy callback
    def dummy_cb():
        pass

    # Pull all lines out of the evaluate loop
    result = [
        x for x in pro.evaluate_inner(r_file.filename, ctx, callback=dummy_cb)
    ]
    # Checks
    assert len(result) == len(output)
    assert ctx.stack == []
    m_reg.resolve.assert_has_calls([call(r_file.filename)])
    for got_out, exp_out in zip(result, output):
        assert str(got_out) == exp_out.rstrip(" ")
    LineDirx.invoke.assert_has_calls(dirx_calls)
    for dirx in dirx_inst:
        assert dirx.callback == dummy_cb
Beispiel #7
0
def test_prologue_get_directive():
    """ Request registered and unregistered directives """
    pro = Prologue()

    # Register a bunch of directives
    class LineDirx(LineDirective):
        pass

    class BlockDirx(BlockDirective):
        pass

    line_opens = [random_str(3, 10) for _x in range(5)]
    block_opens = [random_str(3, 10, avoid=line_opens) for _x in range(5)]
    block_close = [
        random_str(3, 10, avoid=(line_opens + block_opens)) for _x in range(5)
    ]
    wrap_line = DirectiveWrap(LineDirx, line_opens)
    wrap_block = DirectiveWrap(BlockDirx, block_opens, closing=block_close)
    for opening in line_opens:
        assert not pro.has_directive(opening)
    pro.register_directive(wrap_line)
    for tag in line_opens:
        assert pro.has_directive(tag)
    for tag in (block_opens + block_close):
        assert not pro.has_directive(tag)
    pro.register_directive(wrap_block)
    for tag in (block_opens + block_close):
        assert pro.has_directive(tag)
    # List all of the registered directives
    reg_dirx = pro.list_directives()
    assert wrap_line in reg_dirx
    assert wrap_block in reg_dirx
    # Test that correct directive is returned each time
    all_tags = line_opens + block_opens + block_close
    for use_shared in (False, True):
        pro.shared_delimiter = use_shared
        for _x in range(100):
            use_bad = choice((True, False))
            tag = random_str(3, 10,
                             avoid=all_tags) if use_bad else choice(all_tags)
            # If using a bad directive without shared delimiters, expect an exception
            if use_bad and not use_shared:
                with pytest.raises(PrologueError) as excinfo:
                    pro.get_directive(tag)
                assert f"No directive known for tag '{tag}'" == str(
                    excinfo.value)
            # If using a bad directive with shared delimiters, expect None
            elif use_bad and use_shared:
                assert pro.get_directive(tag) == None
            # Otherwise, expect the correct directive to be returned
            else:
                if tag in line_opens:
                    assert pro.get_directive(tag) == wrap_line
                else:
                    assert pro.get_directive(tag) == wrap_block
    # Test de-registering directives
    pro.deregister_directive(choice(line_opens))
    for tag in line_opens:
        assert not pro.has_directive(tag)
    pro.deregister_directive(choice(block_opens + block_close))
    for tag in (block_opens + block_close):
        assert not pro.has_directive(tag)
    # Test deregistering directives again
    for tags in (line_opens, block_opens + block_close):
        use_tag = choice(tags)
        with pytest.raises(PrologueError) as excinfo:
            pro.deregister_directive(use_tag)
        assert str(
            excinfo.value) == f"No directive registered for tag '{use_tag}'"
Beispiel #8
0
def test_directive_wrap():
    """ Test wrapping a directive """
    class RootDirx(Directive):
        pass

    class BlockDirx(BlockDirective):
        pass

    class LineDirx(LineDirective):
        pass

    for _i in range(100):
        # Choose a directive and generate tags
        dirx = choice((RootDirx, BlockDirx, LineDirx))
        opening = [random_str(5, 10) for _x in range(randint(1, 10))]
        transition = [random_str(5, 10) for _x in range(randint(1, 10))
                      ] if (dirx == BlockDirx) else None
        closing = [random_str(5, 10) for _x in range(randint(1, 10))
                   ] if (dirx == BlockDirx) else None
        # Wrap the directive
        print(f"Trying {dirx}")
        wrapped = DirectiveWrap(dirx,
                                opening,
                                closing=closing,
                                transition=transition)
        assert wrapped.directive == dirx
        assert wrapped.opening == tuple([x.lower() for x in opening])
        assert wrapped.closing == (tuple([x.lower() for x in closing])
                                   if closing else tuple())
        assert wrapped.transition == (tuple([x.lower() for x in transition])
                                      if transition else tuple())
        # Check all tags
        assert wrapped.tags == (
            tuple([x.lower() for x in opening]) +
            (tuple([x.lower()
                    for x in transition]) if transition else tuple()) +
            (tuple([x.lower() for x in closing]) if closing else tuple()))
        # Check whether block and line directives are correctly identified
        assert wrapped.is_block == (dirx == BlockDirx)
        assert wrapped.is_line == (dirx == LineDirx)
        # Test the opening tags
        for _x in range(100):
            if choice((True, False)):
                assert wrapped.is_opening(choice(opening).lower())
            elif (dirx == BlockDirective) and choice((True, False)):
                assert not wrapped.is_opening(
                    choice(closing + transition).lower())
            else:
                rand_tag = random_str(
                    5,
                    10,
                    avoid=[
                        x.lower()
                        for x in (opening + (closing if closing else []) +
                                  (transition if transition else []))
                    ])
                with pytest.raises(PrologueError) as excinfo:
                    wrapped.is_opening(rand_tag)
                assert f"Tag is not known by directive: {rand_tag}" == str(
                    excinfo.value)
        # Test the transition tags
        for _x in range(100):
            if (dirx == BlockDirective) and choice((True, False)):
                assert wrapped.is_transition(choice(transition))
            elif (dirx == BlockDirective) and choice((True, False)):
                assert not wrapped.is_transition(choice(opening + closing))
            else:
                rand_tag = random_str(
                    5,
                    10,
                    avoid=(opening + (closing if closing else []) +
                           (transition if transition else [])))
                with pytest.raises(PrologueError) as excinfo:
                    wrapped.is_transition(rand_tag)
                assert f"Tag is not known by directive: {rand_tag}" == str(
                    excinfo.value)
        # Test the closing tags
        for _x in range(100):
            if (dirx == BlockDirective) and choice((True, False)):
                assert wrapped.is_closing(choice(closing))
            elif (dirx == BlockDirective) and choice((True, False)):
                assert not wrapped.is_closing(choice(opening + transition))
            else:
                rand_tag = random_str(
                    5,
                    10,
                    avoid=(opening + (closing if closing else []) +
                           (transition if transition else [])))
                with pytest.raises(PrologueError) as excinfo:
                    wrapped.is_closing(rand_tag)
                assert f"Tag is not known by directive: {rand_tag}" == str(
                    excinfo.value)
Beispiel #9
0
def test_directive_wrap_bad():
    """ Try misusing the DirectiveWrap class """
    class DummyA:
        pass

    class DummyB(str):
        pass

    class DummyC(list):
        pass

    class DummyD(dict):
        pass

    for _i in range(100):
        # Check for a bad directive type - expects a subclass of 'Directive'
        for obj in [DummyA, DummyB, DummyC, DummyD]:
            with pytest.raises(PrologueError) as excinfo:
                DirectiveWrap(obj, [random_str(5, 10) for _x in range(5)])
            assert f"Item is not a subclass of Directive: {obj}" == str(
                excinfo.value)
        # Check for bad opening tags
        dirx = choice((LineDirective, BlockDirective))
        with pytest.raises(PrologueError) as excinfo:
            DirectiveWrap(
                dirx,
                [],
                transition=[random_str(5, 10) for _x in range(5)],
                closing=[random_str(5, 10) for _x in range(5)],
            )
        assert "At least one opening tag must be specified" == str(
            excinfo.value)
        # Check for bad closing tags
        with pytest.raises(PrologueError) as excinfo:
            DirectiveWrap(
                BlockDirective,
                [random_str(5, 10) for _x in range(5)],
                transition=[random_str(5, 10) for _x in range(5)],
                closing=[],
            )
        assert "At least one closing tag must be specified" == str(
            excinfo.value)
        # Check for populated closing tags with a non-block directive
        dirx = choice((LineDirective, Directive))
        with pytest.raises(PrologueError) as excinfo:
            DirectiveWrap(
                dirx,
                [random_str(5, 10) for _x in range(5)],
                transition=[random_str(5, 10) for _x in range(5)],
                closing=[random_str(5, 10) for _x in range(5)],
            )
        assert "Only a block directive can have closing tags" == str(
            excinfo.value)
        # Check for populated transition tags with a non-block directive
        dirx = choice((LineDirective, Directive))
        with pytest.raises(PrologueError) as excinfo:
            DirectiveWrap(
                dirx,
                [random_str(5, 10) for _x in range(5)],
                transition=[random_str(5, 10) for _x in range(5)],
                closing=None,
            )
        assert "Only a block directive can have transition tags" == str(
            excinfo.value)
        # Test for bad tags
        with pytest.raises(PrologueError) as excinfo:
            tags = [random_str(30, 50, spaces=True)
                    for _x in range(5)] if choice(
                        (True, False)) else ([""] * 5)
            DirectiveWrap(BlockDirective, tags, transition=tags, closing=tags)
        assert ("Directive tag cannot contain spaces and must be at least one "
                "character in length") == str(excinfo.value)