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