Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 101
0.00% covered (danger)
0.00%
0 / 27
CRAP
0.00% covered (danger)
0.00%
0 / 1
SiteConfig
0.00% covered (danger)
0.00%
0 / 101
0.00% covered (danger)
0.00%
0 / 27
1406
0.00% covered (danger)
0.00%
0 / 1
 __construct
0.00% covered (danger)
0.00%
0 / 8
0.00% covered (danger)
0.00%
0 / 1
2
 getCustomSiteConfigFileName
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 reset
0.00% covered (danger)
0.00%
0 / 36
0.00% covered (danger)
0.00%
0 / 1
6
 deleteNamespace
0.00% covered (danger)
0.00%
0 / 11
0.00% covered (danger)
0.00%
0 / 1
12
 disableSubpagesForNS
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 enableSubpagesForNS
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 updateNamespace
0.00% covered (danger)
0.00%
0 / 8
0.00% covered (danger)
0.00%
0 / 1
12
 setupInterwikiMap
0.00% covered (danger)
0.00%
0 / 3
0.00% covered (danger)
0.00%
0 / 1
2
 interwikiMap
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 server
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 script
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 scriptpath
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 baseURI
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 allowedExternalImagePrefixes
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getMWConfigValue
0.00% covered (danger)
0.00%
0 / 7
0.00% covered (danger)
0.00%
0 / 1
30
 setInterwikiMagic
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 interwikiMagic
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 setMagicLinkEnabled
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 magicLinkEnabled
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 fakeTimestamp
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 timezoneOffset
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 widthOption
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 registerParserTestExtension
0.00% covered (danger)
0.00%
0 / 4
0.00% covered (danger)
0.00%
0 / 1
2
 setExternalLinkTarget
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getExternalLinkTarget
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 setNoFollowConfig
0.00% covered (danger)
0.00%
0 / 3
0.00% covered (danger)
0.00%
0 / 1
2
 getNoFollowConfig
0.00% covered (danger)
0.00%
0 / 3
0.00% covered (danger)
0.00%
0 / 1
6
1<?php
2declare( strict_types = 1 );
3
4namespace Wikimedia\Parsoid\ParserTests;
5
6use Monolog\Handler\FilterHandler;
7use Monolog\Logger;
8use Psr\Log\LoggerInterface;
9use Wikimedia\Assert\Assert;
10use Wikimedia\Parsoid\Config\Api\ApiHelper;
11use Wikimedia\Parsoid\Config\Api\SiteConfig as ApiSiteConfig;
12use Wikimedia\Parsoid\Ext\ExtensionModule;
13use Wikimedia\Parsoid\Utils\ConfigUtils;
14use Wikimedia\Parsoid\Utils\Utils;
15
16class SiteConfig extends ApiSiteConfig {
17    /** @var array overrides parent-class info */
18    private $interwikiMap;
19
20    /** @var bool overrides parent-class info */
21    private $interwikiMagic;
22
23    /** @var array<string,bool> overrides parent-class info */
24    private $enabledMagicLinks = [];
25
26    /** @var array overrides parent-class server info */
27    private $serverData;
28
29    /** @var array overrides parent-class info */
30    public $allowedExternalImagePrefixes = [ '' ];
31
32    /**
33     * Init to default value for parserTests. Overrides parent-class info.
34     * @var array
35     */
36    public $responsiveReferences;
37
38    /** @var ?int */
39    public $thumbsize;
40
41    /** @var LoggerInterface */
42    public $suppressLogger;
43
44    /** If set, generate experimental Parsoid HTML v3 parser function output
45     * Individual parser tests could change this
46     */
47    public bool $v3pf;
48
49    /** @var string|false */
50    private $externalLinkTarget = false;
51
52    /** @var ?array */
53    private $noFollowConfig;
54
55    /** @inheritDoc */
56    public function __construct( ApiHelper $api, array $opts ) {
57        $logger = self::createLogger();
58        $opts['logger'] = $logger;
59        parent::__construct( $api, $opts );
60
61        // Needed for bidi-char-scrubbing html2wt tests.
62        $this->scrubBidiChars = true;
63
64        // Logger to suppress all logs but fatals (critical errors)
65        $this->suppressLogger = new Logger( "ParserTests" );
66        $errorLogHandler = $logger->getHandlers()[0];
67        $filterHandler = new FilterHandler( $errorLogHandler, Logger::CRITICAL );
68        $this->suppressLogger->pushHandler( $filterHandler );
69    }
70
71    /** @inheritDoc */
72    protected function getCustomSiteConfigFileName(): string {
73        return ParserHook::getParserTestConfigFileName();
74    }
75
76    public function reset(): void {
77        parent::reset();
78
79        // adjust config to match that used for PHP tests
80        // see core/tests/parser/parserTest.inc:setupGlobals() for
81        // full set of config normalizations done.
82        $this->serverData = [
83            'server'      => 'http://example.org',
84            'scriptpath'  => '/',
85            'script'      => '/index.php',
86            'articlepath' => '/wiki/$1',
87            'baseURI'     => 'http://example.org/wiki/'
88        ];
89
90        // Add 'MemoryAlpha' namespace (T53680)
91        $this->updateNamespace( [
92            'id' => 100,
93            'case' => 'first-letter',
94            'subpages' => false,
95            'canonical' => 'MemoryAlpha',
96            'name' => 'MemoryAlpha',
97        ] );
98
99        // Testing
100        if ( $this->iwp() === 'enwiki' ) {
101            $this->updateNamespace( [
102                'id' => 4,
103                'case' => 'first-letter',
104                'subpages' => true,
105                'canonical' => 'Project',
106                'name' => 'Base MW'
107            ] );
108            $this->updateNamespace( [
109                'id' => 5,
110                'case' => 'first-letter',
111                'subpages' => true,
112                'canonical' => 'Project talk',
113                'name' => 'Base MW talk'
114            ] );
115        }
116
117        // Reset other values to defaults
118        $this->responsiveReferences = [ 'enabled' => true, 'threshold' => 10 ];
119        $this->disableSubpagesForNS( 0 );
120        $this->thumbsize = null;
121        $this->externalLinkTarget = false;
122        $this->noFollowConfig = null;
123        $this->v3pf = false;
124    }
125
126    private function deleteNamespace( string $name ): void {
127        $normName = Utils::normalizeNamespaceName( $name );
128        $id = $this->namespaceId( $normName );
129
130        if ( !$id ) {
131            $normName = $name;
132            $id = $this->namespaceId( $normName );
133        }
134
135        if ( $id ) {
136            unset( $this->nsCanon[$normName] );
137            unset( $this->nsIds[$normName] );
138            unset( $this->nsNames[$id] );
139            unset( $this->nsCase[$id] );
140            unset( $this->nsWithSubpages[$id] );
141        }
142    }
143
144    public function disableSubpagesForNS( int $ns ): void {
145        $this->nsWithSubpages[$ns] = false;
146    }
147
148    public function enableSubpagesForNS( int $ns ): void {
149        $this->nsWithSubpages[$ns] = true;
150    }
151
152    /**
153     * Update namespace info.
154     *
155     * Delete any existing namespace with the same id.
156     * Add new namespaces.
157     *
158     * @param array $ns
159     */
160    private function updateNamespace( array $ns ): void {
161        $old = $this->namespaceName( (int)$ns['id'] );
162        if ( $old ) { // Id may already be defined; if so, clear it.
163            if ( $old === Utils::normalizeNamespaceName( $ns['name'] ) ) {
164                // ParserTests does a lot redundantly.
165                return;
166            }
167            $this->deleteNamespace( $old );
168        }
169        $this->addNamespace( $ns );
170        Assert::invariant( $ns['case'] === 'first-letter',
171            'ParserTests/SiteConfig only supports first-letter case currently' );
172    }
173
174    /**
175     * Compute the interwiki map based on mock raw data.
176     * This replaces the previously computed interwiki map
177     * based on data from MockApiHelper
178     *
179     * @param array $iwData
180     */
181    public function setupInterwikiMap( array $iwData ): void {
182        $this->interwikiMap = ConfigUtils::computeInterwikiMap( $iwData );
183        $this->interwikiMapNoNamespaces = null;
184        $this->iwMatcher = null;
185    }
186
187    public function interwikiMap(): array {
188        return $this->interwikiMap;
189    }
190
191    public function server(): string {
192        return $this->serverData['server'];
193    }
194
195    public function script(): string {
196        return $this->serverData['script'];
197    }
198
199    public function scriptpath(): string {
200        return $this->serverData['scriptpath'];
201    }
202
203    public function baseURI(): string {
204        return $this->serverData['baseURI'];
205    }
206
207    public function allowedExternalImagePrefixes(): array {
208        return $this->allowedExternalImagePrefixes;
209    }
210
211    /** @inheritDoc */
212    public function getMWConfigValue( string $key ) {
213        switch ( $key ) {
214            case 'CiteResponsiveReferences':
215                return $this->responsiveReferences['enabled'];
216
217            case 'CiteResponsiveReferencesThreshold':
218                return $this->responsiveReferences['threshold'];
219
220            case 'ParsoidExperimentalParserFunctionOutput':
221                return $this->v3pf;
222
223            default:
224                return null;
225        }
226    }
227
228    public function setInterwikiMagic( bool $val ): void {
229        $this->interwikiMagic = $val;
230    }
231
232    public function interwikiMagic(): bool {
233        return $this->interwikiMagic;
234    }
235
236    /**
237     * @param string $which One of "RFC", "PMID", or "ISBN".
238     * @param bool $val
239     */
240    public function setMagicLinkEnabled( string $which, bool $val ): void {
241        $this->enabledMagicLinks[$which] = $val;
242    }
243
244    public function magicLinkEnabled( string $which ): bool {
245        // defaults to enabled
246        return $this->enabledMagicLinks[$which] ?? true;
247    }
248
249    public function fakeTimestamp(): ?int {
250        return 123;
251    }
252
253    /**
254     * Hardcode value for parser tests
255     *
256     * @return int
257     */
258    public function timezoneOffset(): int {
259        return 0;
260    }
261
262    public function widthOption(): int {
263        return $this->thumbsize ?? 180;  // wgThumbLimits setting in core ParserTestRunner
264    }
265
266    /**
267     * Register an extension for use in parser tests
268     * @param class-string<ExtensionModule> $extClass
269     * @return callable():void a cleanup function to unregister this extension
270     */
271    public function registerParserTestExtension( string $extClass ): callable {
272        $extId = $this->registerExtensionModule( $extClass );
273        return function () use ( $extId ): void {
274            $this->unregisterExtensionModule( $extId );
275        };
276    }
277
278    /**
279     * @param string|false $value
280     */
281    public function setExternalLinkTarget( $value ): void {
282        $this->externalLinkTarget = $value;
283    }
284
285    /**
286     * @inheritDoc
287     */
288    public function getExternalLinkTarget() {
289        return $this->externalLinkTarget;
290    }
291
292    /**
293     * @param string $key
294     * @param mixed $value
295     */
296    public function setNoFollowConfig( string $key, $value ): void {
297        $noFollowConfig = $this->getNoFollowConfig();
298        $noFollowConfig[$key] = $value;
299        $this->noFollowConfig = $noFollowConfig;
300    }
301
302    /**
303     * @inheritDoc
304     */
305    public function getNoFollowConfig(): array {
306        if ( $this->noFollowConfig === null ) {
307            $this->noFollowConfig = parent::getNoFollowConfig();
308        }
309        return $this->noFollowConfig;
310    }
311}