xref: /dokuwiki/_test/tests/Parsing/ParserMode/ParserTestBase.php (revision b73ece99c18919754d993a1d1f5cb27140555705)
1<?php
2
3namespace dokuwiki\test\Parsing\ParserMode;
4
5use dokuwiki\Parsing\Handler;
6use dokuwiki\Parsing\ModeRegistry;
7use dokuwiki\Parsing\Parser;
8
9/**
10 * Base class for parser mode tests
11 *
12 * Sets up a fresh Parser and Handler for each test. Provides assertion helpers
13 * for comparing handler call sequences.
14 */
15abstract class ParserTestBase extends \DokuWikiTest
16{
17    /** @var Parser parser instance for the current test */
18    protected Parser $P;
19    /** @var Handler handler instance that records calls made by the parser */
20    protected Handler $H;
21    /** @var ModeRegistry registry attached to $P, injected into modes on addMode() */
22    protected ModeRegistry $registry;
23
24    /** @inheritdoc */
25    public function setUp(): void
26    {
27        parent::setUp();
28        $this->buildParser();
29    }
30
31    /**
32     * (Re)build $P/$H/$registry for the current $conf['syntax'].
33     *
34     * Modes a test adds via $this->P->addMode() are injected with this
35     * registry, so its syntax must already be the one under test — use
36     * setSyntax() to switch.
37     */
38    protected function buildParser(): void
39    {
40        global $conf;
41        $this->registry = new ModeRegistry($conf['syntax']);
42        $this->H = new Handler($this->registry);
43        $this->P = new Parser($this->H, $this->registry);
44    }
45
46    /**
47     * Switch the syntax flavour under test and rebuild the parser so a
48     * fresh registry carries it. Call before adding modes.
49     */
50    protected function setSyntax(string $syntax): void
51    {
52        global $conf;
53        $conf['syntax'] = $syntax;
54        $this->buildParser();
55    }
56
57    /** @inheritdoc */
58    public function tearDown(): void
59    {
60        unset($this->P, $this->H);
61        parent::tearDown();
62    }
63
64    /**
65     * Assert that handler calls match the expected calls, ignoring byte index positions
66     *
67     * The byte index (element [2] in each call) is stripped before comparison because
68     * it depends on internal parser state and is not relevant for most tests.
69     *
70     * @param array $expected the expected call sequence
71     * @param array $actual the actual handler calls (typically $this->H->calls)
72     * @param string $message optional failure message
73     */
74    protected function assertCalls(array $expected, array $actual, string $message = ''): void
75    {
76        $this->assertEquals($expected, array_map($this->stripByteIndex(...), $actual), $message);
77    }
78
79    /**
80     * Remove the byte index from a single handler call
81     *
82     * Recursively processes nested calls (e.g. footnotes).
83     *
84     * @param array $call a single handler call [method, args, byteindex]
85     * @return array the call with the byte index removed
86     */
87    private function stripByteIndex(array $call): array
88    {
89        unset($call[2]);
90        if ($call[0] === 'nest') {
91            $call[1][0] = array_map($this->stripByteIndex(...), $call[1][0]);
92        }
93        return $call;
94    }
95}
96