示例#1
0
def test_prologue_evaluate_inner_line_span(mocker):
    """ Test use of line spanning using '\' to escape new line """
    pro = Prologue()
    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 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
    intro = [random_str(10, 50, spaces=True) for _x in range(randint(5, 10))]
    span = [(random_str(10, 50, spaces=True) + "\\")
            for _x in range(randint(5, 10))]
    span += [random_str(10, 50, spaces=True)]
    outro = [random_str(10, 50, spaces=True) for _x in range(randint(5, 10))]
    m_con.return_value = intro + span + outro
    # Pull all lines out of the evaluate loop
    result = [x for x in pro.evaluate_inner(r_file.filename, ctx)]
    # Checks
    assert result == intro + ["".join([x.replace("\\", "")
                                       for x in span])] + outro
    m_reg.resolve.assert_has_calls([call(r_file.filename)])
    assert ctx.stack == []
示例#2
0
def test_prologue_evaluate_inner_break_loop(mocker):
    """ Check that an infinite include loop is detected """
    pro = Prologue()
    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 context with a bunch of mock files
    ctx = Context(pro)
    for _x in range(randint(10, 30)):
        ctx.stack_push(RegistryFile())
        ctx.stack[-1].path = Path(random_str(5, 10) + "." + random_str(5, 10))
    # Try evaluating files that are already on the stack
    for _x in range(100):
        r_file = choice(ctx.stack)
        m_reg.resolve.side_effect = [r_file]
        with pytest.raises(PrologueError) as excinfo:
            next(pro.evaluate_inner(r_file.filename, ctx))
        assert (
            f"Detected infinite recursion when including file '{r_file.filename}'"
            f" - file stack: {', '.join([x.filename for x in ctx.stack])}"
        ) == str(excinfo.value)
        m_reg.resolve.assert_has_calls([call(r_file.filename)])
        m_reg.reset_mock()
    # Check a new file is pushed to the stack
    new_file = RegistryFile()
    new_file.path = Path(random_str(5, 10) + "." + random_str(5, 10))
    m_reg.resolve.side_effect = [new_file]
    m_con.return_value = [random_str(5, 10), random_str(5, 10)]
    next(pro.evaluate_inner(new_file.filename, ctx))
    assert ctx.stack[-1] == new_file
示例#3
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)
示例#4
0
def test_prologue_evaluate_inner_plain(mocker):
    """ Check that a plain sequence of lines is reproduced within alteration """
    pro = Prologue()
    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 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 = [
        random_str(10, 50, spaces=True) for _x in range(randint(50, 100))
    ]
    m_con.return_value = contents
    # Pull all lines out of the evaluate loop
    result = [x for x in pro.evaluate_inner(r_file.filename, ctx)]
    # Checks
    assert result == contents
    m_reg.resolve.assert_has_calls([call(r_file.filename)])
    assert ctx.stack == []
示例#5
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}:"
        )
示例#6
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)
示例#7
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
示例#8
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