Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 107
0.00% covered (danger)
0.00%
0 / 15
CRAP
0.00% covered (danger)
0.00%
0 / 1
SpecialMathStatus
0.00% covered (danger)
0.00%
0 / 107
0.00% covered (danger)
0.00%
0 / 15
812
0.00% covered (danger)
0.00%
0 / 1
 __construct
0.00% covered (danger)
0.00%
0 / 4
0.00% covered (danger)
0.00%
0 / 1
2
 execute
0.00% covered (danger)
0.00%
0 / 19
0.00% covered (danger)
0.00%
0 / 1
42
 runNativeTest
0.00% covered (danger)
0.00%
0 / 7
0.00% covered (danger)
0.00%
0 / 1
2
 runMathMLTest
0.00% covered (danger)
0.00%
0 / 5
0.00% covered (danger)
0.00%
0 / 1
2
 runMathLaTeXMLTest
0.00% covered (danger)
0.00%
0 / 4
0.00% covered (danger)
0.00%
0 / 1
2
 testSpecialCaseText
0.00% covered (danger)
0.00%
0 / 6
0.00% covered (danger)
0.00%
0 / 1
2
 testMathMLIntegration
0.00% covered (danger)
0.00%
0 / 10
0.00% covered (danger)
0.00%
0 / 1
2
 testPmmlInput
0.00% covered (danger)
0.00%
0 / 7
0.00% covered (danger)
0.00%
0 / 1
2
 testLaTeXMLIntegration
0.00% covered (danger)
0.00%
0 / 7
0.00% covered (danger)
0.00%
0 / 1
2
 testLaTeXMLLinebreak
0.00% covered (danger)
0.00%
0 / 13
0.00% covered (danger)
0.00%
0 / 1
6
 assertTrue
0.00% covered (danger)
0.00%
0 / 5
0.00% covered (danger)
0.00%
0 / 1
6
 assertContains
0.00% covered (danger)
0.00%
0 / 2
0.00% covered (danger)
0.00%
0 / 1
6
 assertEquals
0.00% covered (danger)
0.00%
0 / 11
0.00% covered (danger)
0.00%
0 / 1
30
 printDiff
0.00% covered (danger)
0.00%
0 / 6
0.00% covered (danger)
0.00%
0 / 1
6
 getGroupName
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
1<?php
2
3namespace MediaWiki\Extension\Math;
4
5use ExtensionRegistry;
6use MediaWiki\Extension\Math\Render\RendererFactory;
7use MediaWiki\Extension\Math\Widget\MathTestInputForm;
8use MediaWiki\Logger\LoggerFactory;
9use MediaWiki\SpecialPage\SpecialPage;
10use Psr\Log\LoggerInterface;
11
12/**
13 * MediaWiki math extension
14 *
15 * @copyright 2002-2015 Tomasz Wegrzanowski, Brion Vibber, Moritz Schubotz,
16 * and other MediaWiki contributors
17 * @license GPL-2.0-or-later
18 * @author Moritz Schubotz
19 */
20class SpecialMathStatus extends SpecialPage {
21    /** @var LoggerInterface */
22    private $logger;
23
24    /** @var MathConfig */
25    private $mathConfig;
26
27    /** @var RendererFactory */
28    private $rendererFactory;
29
30    public function __construct(
31        MathConfig $mathConfig,
32        RendererFactory $rendererFactory
33    ) {
34        parent::__construct( 'MathStatus', 'purge' );
35
36        $this->mathConfig = $mathConfig;
37        $this->rendererFactory = $rendererFactory;
38        $this->logger = LoggerFactory::getInstance( 'Math' );
39    }
40
41    /**
42     * @param null|string $query
43     */
44    public function execute( $query ) {
45        $this->setHeaders();
46
47        $out = $this->getOutput();
48        $enabledMathModes = $this->mathConfig->getValidRenderingModeNames();
49        $req = $this->getRequest();
50        $tex = $req->getText( 'wptex' );
51
52        if ( $tex === '' ) {
53            $out->addWikiMsg( 'math-status-introduction', count( $enabledMathModes ) );
54
55            foreach ( $enabledMathModes as $modeNr => $modeName ) {
56                $out->wrapWikiMsg( '=== $1 ===', $modeName );
57                switch ( $modeNr ) {
58                    case MathConfig::MODE_MATHML:
59                        $this->runMathMLTest( $modeName );
60                        break;
61                    case MathConfig::MODE_LATEXML:
62                        $this->runMathLaTeXMLTest( $modeName );
63                        break;
64                    case MathConfig::MODE_NATIVE_MML:
65                        $this->runNativeTest( $modeName );
66                }
67            }
68        }
69
70        $form = new MathTestInputForm( $this, $enabledMathModes, $this->rendererFactory );
71        $form->show();
72    }
73
74    private function runNativeTest( $modeName ) {
75        $this->getOutput()->addWikiMsgArray( 'math-test-start', [ $modeName ] );
76        $renderer = $this->rendererFactory->getRenderer( "a+b", [], MathConfig::MODE_NATIVE_MML );
77        $this->assertTrue( $renderer->render(), "Rendering of a+b in $modeName" );
78        $real = str_replace( "\n", '', $renderer->getHtmlOutput() );
79        $expected = '<mo>+</mo>';
80        $this->assertContains( $expected, $real, "Checking the presence of '+' in the MathML output" );
81        $this->getOutput()->addWikiMsgArray( 'math-test-end', [ $modeName ] );
82    }
83
84    private function runMathMLTest( $modeName ) {
85        $this->getOutput()->addWikiMsgArray( 'math-test-start', [ $modeName ] );
86        $this->testSpecialCaseText();
87        $this->testMathMLIntegration();
88        $this->testPmmlInput();
89        $this->getOutput()->addWikiMsgArray( 'math-test-end', [ $modeName ] );
90    }
91
92    private function runMathLaTeXMLTest( $modeName ) {
93        $this->getOutput()->addWikiMsgArray( 'math-test-start', [ $modeName ] );
94        $this->testLaTeXMLIntegration();
95        $this->testLaTeXMLLinebreak();
96        $this->getOutput()->addWikiMsgArray( 'math-test-end', [ $modeName ] );
97    }
98
99    public function testSpecialCaseText() {
100        $renderer = $this->rendererFactory->getRenderer( 'x^2+\text{a sample Text}', [], MathConfig::MODE_MATHML );
101        $expected = 'a sample Text</mtext>';
102        $this->assertTrue( $renderer->render(), 'Rendering the input "x^2+\text{a sample Text}"' );
103        $this->assertContains(
104            $expected, $renderer->getHtmlOutput(), 'Comparing to the reference rendering'
105        );
106    }
107
108    /**
109     * Checks the basic functionality
110     * i.e. if the span element is generated right.
111     */
112    public function testMathMLIntegration() {
113        $renderer = $this->rendererFactory->getRenderer( "a+b", [], MathConfig::MODE_MATHML );
114        $this->assertTrue( $renderer->render(), "Rendering of a+b in plain MathML mode" );
115        $real = str_replace( "\n", '', $renderer->getHtmlOutput() );
116        $expected = '<mo>+</mo>';
117        $this->assertContains( $expected, $real, "Checking the presence of '+' in the MathML output" );
118        $this->assertContains(
119            '<svg xmlns:xlink="http://www.w3.org/1999/xlink" ',
120            $renderer->getSvg(),
121            "Check that the generated SVG image contains the xlink namespace"
122        );
123    }
124
125    /**
126     * Checks the experimental option to 'render' MathML input
127     */
128    public function testPmmlInput() {
129        // sample from 'Navajo Coal Combustion and Respiratory Health Near Shiprock,
130        // New Mexico' in ''Journal of Environmental and Public Health'' , vol. 2010p.
131        // authors  Joseph E. Bunnell;  Linda V. Garcia;  Jill M. Furst;
132        // Harry Lerch;  Ricardo A. Olea;  Stephen E. Suitt;  Allan Kolker
133        // phpcs:ignore Generic.Files.LineLength.TooLong
134        $inputSample = '<msub>  <mrow>  <mi> P</mi> </mrow>  <mrow>  <mi> i</mi>  <mi> j</mi> </mrow> </msub>  <mo> =</mo>  <mfrac>  <mrow>  <mn> 100</mn>  <msub>  <mrow>  <mi> d</mi> </mrow>  <mrow>  <mi> i</mi>  <mi> j</mi> </mrow> </msub> </mrow>  <mrow>  <mn> 6.75</mn>  <msub>  <mrow>  <mi> r</mi> </mrow>  <mrow>  <mi> j</mi> </mrow> </msub> </mrow> </mfrac>  <mo> ,</mo> </math>';
135        $attribs = [ 'type' => 'pmml' ];
136        $renderer = $this->rendererFactory->getRenderer( $inputSample, $attribs, MathConfig::MODE_MATHML );
137        $this->assertEquals( 'pmml', $renderer->getInputType(), 'Checking if MathML input is supported' );
138        $this->assertTrue( $renderer->render(), 'Rendering Presentation MathML sample' );
139        $real = $renderer->getHtmlOutput();
140        $this->assertContains( 'mode=mathml', $real, 'Checking if the link to SVG image is in correct mode' );
141    }
142
143    /**
144     * Checks the basic functionality
145     * i.e. if the span element is generated right.
146     */
147    public function testLaTeXMLIntegration() {
148        $renderer = $this->rendererFactory->getRenderer( "a+b", [], MathConfig::MODE_LATEXML );
149        $this->assertTrue( $renderer->render(), "Rendering of a+b in LaTeXML mode" );
150        // phpcs:ignore Generic.Files.LineLength.TooLong
151        $expected = '<math xmlns="http://www.w3.org/1998/Math/MathML" ';
152        $real = preg_replace( "/\n\\s*/", '', $renderer->getHtmlOutput() );
153        $this->assertContains( $expected, $real,
154            "Comparing the output to the MathML reference rendering" .
155              $renderer->getLastError() );
156    }
157
158    /**
159     * Checks LaTeXML line break functionality
160     * i.e. if a long line contains a mtr element.
161     * http://www.w3.org/TR/REC-MathML/chap3_5.html#sec3.5.2
162     */
163    public function testLaTeXMLLinebreak() {
164        global $wgMathDefaultLaTeXMLSetting;
165        $tex = '';
166        $testMax = ceil( $wgMathDefaultLaTeXMLSetting[ 'linelength' ] / 2 );
167        for ( $i = 0; $i < $testMax; $i++ ) {
168            $tex .= "$i+";
169        }
170        $tex .= $testMax;
171        $renderer = new MathLaTeXML( $tex, [ 'display' => 'linebreak' ] );
172        $renderer->setPurge();
173        $this->assertTrue( $renderer->render(), "Rendering of linebreak test in LaTeXML mode" );
174        $expected = 'mtr';
175        $real = preg_replace( "/\n\\s*/", '', $renderer->getHtmlOutput() );
176        $this->assertContains( $expected, $real, "Checking for linebreak" .
177              $renderer->getLastError() );
178    }
179
180    private function assertTrue( $expression, $message = '' ) {
181        if ( $expression ) {
182            $this->getOutput()->addWikiMsgArray( 'math-test-success', $message );
183            return true;
184        } else {
185            $this->getOutput()->addWikiMsgArray( 'math-test-fail', $message );
186            return false;
187        }
188    }
189
190    private function assertContains( $expected, $real, $message = '' ) {
191        if ( !$this->assertTrue( strpos( $real, $expected ) !== false, $message ) ) {
192            $this->printDiff( $expected, $real, 'math-test-contains-diff' );
193        }
194    }
195
196    private function assertEquals( $expected, $real, $message = '' ) {
197        if ( is_array( $expected ) ) {
198            foreach ( $expected as $alternative ) {
199                if ( $alternative === $real ) {
200                    $this->getOutput()->addWikiMsgArray( 'math-test-success', $message );
201                    return true;
202                }
203            }
204            // non of the alternatives matched
205            $this->getOutput()->addWikiMsgArray( 'math-test-fail', $message );
206            return false;
207        }
208        if ( !$this->assertTrue( $expected === $real, $message ) ) {
209            $this->printDiff( $expected, $real, 'math-test-equals-diff' );
210            return false;
211        }
212        return true;
213    }
214
215    private function printDiff( $expected, $real, $message = '' ) {
216        if ( ExtensionRegistry::getInstance()->isLoaded( "SyntaxHighlight" ) ) {
217            $expected = "<syntaxhighlight lang=\"xml\">$expected</syntaxhighlight>";
218            $real = "<syntaxhighlight lang=\"xml\">$real</syntaxhighlight>";
219            $this->getOutput()->addWikiMsgArray( $message, [ $real, $expected ] );
220        } else {
221            $this->logger->warning( 'Can not display expected and real value.' .
222                'SyntaxHighlight is not installed.' );
223        }
224    }
225
226    protected function getGroupName() {
227        return 'other';
228    }
229}