mediawiki-extensions-Math/tests/phpunit/unit/WikiTexVC/ApiTest.php
physikerwelt ff1536d1f9
Ignore length specifications after linebreaks
In LaTeX line breaks in matrix elements
have an optional argument that controls
the vertical spacing between the current
and the next line. For example,

{\begin{aligned}a\\[6pt] b\end{aligned}}

generates a 6pt space.

This was never implemented and ignored by
mathoid.
The native MathML implementation is unaware
of the optional argument and just prints it
to the next line.

* This commit ignores the input on the grammar
side.

Bug: T375295
Change-Id: I08996129beca9ad852d23f28d7136c982707aea1
2024-09-22 20:22:52 +02:00

274 lines
8 KiB
PHP

<?php
namespace MediaWiki\Extension\Math\Tests\WikiTexVC;
use MediaWiki\Extension\Math\WikiTexVC\SyntaxError;
use MediaWiki\Extension\Math\WikiTexVC\TexVC;
use MediaWikiUnitTestCase;
/**
* @covers \MediaWiki\Extension\Math\WikiTexVC\TexVC
* @covers \MediaWiki\Extension\Math\WikiTexVC\Parser
* @covers \MediaWiki\Extension\Math\WikiTexVC\TexUtil
* @covers \MediaWiki\Extension\Math\WikiTexVC\ParserUtil
*/
class ApiTest extends MediaWikiUnitTestCase {
/** @var TexVC */
private $texVC;
protected function setUp(): void {
parent::setUp();
$this->texVC = new TexVC();
}
public function provideTestCases() {
$testCases = [
[
'in' => '\\newcommand{\\text{do evil things}}',
'status' => 'F',
'details' => '\\newcommand'
],
[
'in' => '\\sin\\left(\\frac12x\\right)',
'output' => '\\sin \\left({\\frac {1}{2}}x\\right)'
],
[
'in' => '\\reals',
'output' => '\mathbb {R} ',
'ams_required' => true
],
[
'in' => '\\lbrack',
'output' => '\\lbrack '
],
[
'in' => '\\figureEightIntegral',
'status' => 'F',
'details' => '\\figureEightIntegral'
],
[
'in' => '\diamondsuit '
],
[
'in' => '\\sinh x'
],
[
'in' => '\\begin{foo}\\end{foo}',
'status' => 'F',
'details' => '\\begin{foo}'
],
[
'in' => '\\hasOwnProperty',
'status' => 'F',
'details' => '\\hasOwnProperty'
],
[
'in' => '\\hline',
'status' => 'S'
],
[
'in' => '\\begin{array}{c}\\hline a \\\\ \\hline\\hline b \\end{array}',
'output' => '{\\begin{array}{c}\\hline a\\\\\\hline \\hline b\\end{array}}'
],
[
'in' => '\\Diamond ',
'ams_required' => true
],
[
'in' => '{\\begin{matrix}a\\ b\\end{matrix}}',
'ams_required' => true
],
[
'in' => '{\\cancel {x}}',
'cancel_required' => true
],
[
'in' => '\\color {red}',
'color_required' => true
],
[
'in' => '\\euro',
'output' => '\\mbox{\\euro} ',
'euro_required' => true
],
[
'in' => '\\coppa',
'output' => '\\mbox{\\coppa} ',
'teubner_required' => true
],
[
'in' => '{\\color [rgb]{1,0,0}{\\mbox{This text is red.}}}',
'color_required' => true
],
[
'in' => '{\\color[rgb]{1.5,0,0}{\\mbox{This text is bright red.}}}',
'status' => 'S'
],
[
'in' => '{\\color [RGB]{51,0,0}{\\mbox{This text is dim red.}}}',
'output' => '{\\color [rgb]{0.2,0,0}{\\mbox{This text is dim red.}}}',
'color_required' => true
],
[
'in' => '{\\color[RGB]{256,0,0}{\\mbox{This text is bright red.}}}',
'status' => 'S'
],
[
'in' => '\\ce{ H2O }',
'mhchem_required' => true,
'status' => 'C'
],
[
'in' => '\\ce{[Zn(OH)4]^2-}',
'mhchem_required' => true,
'status' => 'C'
],
[
'in' => '{\\begin{aligned}a\\\\b\\end{aligned}}',
'ams_required' => true,
],
[
'in' => '{\\begin{aligned}a\\\\[6pt] b\\end{aligned}}',
'output' => '{\\begin{aligned}a\\\\b\\end{aligned}}',
'ams_required' => true,
],
[
'in' => '{\\begin{aligned}a\\\\[-3.4mm] b\\end{aligned}}',
'output' => '{\\begin{aligned}a\\\\b\\end{aligned}}',
'ams_required' => true,
],
];
foreach ( $testCases as $case ) {
yield $case['in'] => [ $case ];
}
}
/**
* @dataProvider provideTestCases
*/
public function testDefinedCases( array $case ) {
$result = $this->texVC->check( $case['in'], [ 'report_required' => true ] );
$resultStatus = $result['status'] ?? '';
$caseStatus = $case['status'] ?? '+';
$this->assertSame( $caseStatus, $resultStatus, 'Status incorrect' );
if ( $resultStatus === '+' ) {
$this->assertSame( $case['output'] ?? $case['in'], $result['output'] ?? '', 'Output incorrect' );
foreach ( array_keys( $result ) as $f ) {
if ( preg_match( '/_required/', $f ) ) {
$this->assertSame( array_key_exists( $f, $case ), $result[$f],
'A required field does not match in result ' . $f );
}
$this->assertCount( 0, $result['warnings'], 'No warnings expected here.' );
}
}
if ( $resultStatus === 'F' ) {
$this->assertSame( $case['details'], $result['details'] ?? '', 'Details incorrect' );
$this->assertCount( 0, $result['warnings'], 'No warnings expected here.' );
}
}
public function testSuccess1() {
$message = 'should return success (1)';
$result = $this->texVC->check( '\\sin(x)+{}{}\\cos(x)^2 newcommand' );
$this->assertEquals( '+', $result['status'], $message );
$this->assertEquals( '\\sin(x)+{}{}\\cos(x)^{2}newcommand', $result['output'], $message );
}
public function testSuccess2() {
$message = 'should return success (2)';
$result = $this->texVC->check( 'y=x+2' );
$this->assertEquals( '+', $result['status'], $message );
$this->assertEquals( 'y=x+2', $result['output'], $message );
}
public function testReport1() {
$message = 'should report undefined functions (1)';
$result = $this->texVC->check( '\\foo' );
$this->assertEquals( 'F', $result['status'], $message );
$this->assertEquals( '\\foo', $result['details'], $message );
}
public function testReport2() {
$message = 'should report undefined functions (2)';
$result = $this->texVC->check( '\\write18' );
$this->assertEquals( 'F', $result['status'], $message );
$this->assertEquals( '\\write', $result['details'], $message );
}
public function testUndefined() {
$message = 'should report undefined parser errors';
$result = $this->texVC->check( '^' );
$this->assertEquals( 'S', $result['status'], $message );
}
public function testDebugException() {
// should throw an exception in debug mode
$this->expectException( SyntaxError::class );
$this->texVC->check( '^', [ 'debug' => true ] );
}
public function testParsedInput() {
$message = 'should accept parsed input';
$parsed = $this->texVC->parse( 'y=x+2' );
$result = $this->texVC->check( $parsed );
$this->assertEquals( '+', $result['status'], $message );
$this->assertEquals( 'y=x+2', $result['output'], $message );
}
public function testRetryParsing() {
$message = 'should retry parsing if oldmhchem is not set';
$result = $this->texVC->check( '\\ce {A\\;+\\;B\\;->\\;C}', [ 'usemhchem' => true ] );
$this->assertEquals( '+', $result['status'], $message );
$this->assertEquals( 'mhchem-deprecation', $result['warnings'][0]['type'], $message );
$this->assertEquals( 'S', $result['warnings'][0]['details']['status'], $message );
}
public function testDeprecationWarning() {
// This test is not complete yet
$message = 'should show a deprecation warning for \\and';
$result = $this->texVC->check( '\\and' );
$this->assertEquals( '+', $result['status'], $message );
$this->assertEquals( 'texvc-deprecation', $result['warnings'][0]['type'], $message );
$this->assertEquals( 'S', $result['warnings'][0]['details']['status'], $message );
}
public function testDeprecationWarning2() {
$message = 'should not show a deprecation warning for \\land';
$result = $this->texVC->check( '\\land' );
$this->assertEquals( '+', $result['status'], $message );
$this->assertCount( 0, $result['warnings'], $message );
}
public function testNoRetry() {
$message = 'should not retry parsing if oldmhchem is set';
$result = $this->texVC->check( '\\ce {A\\;+\\;B\\;->\\notvalidcommand}',
[ 'usemhchem' => true, 'oldmhchem' => true ] );
$this->assertEquals( 'F', $result['status'], $message );
}
public function testSquareDq() {
$result = $this->texVC->check( ']_x',
[ 'usemhchem' => true, 'oldmhchem' => true ] );
$this->assertEquals( ']_{x}', $result['output'] );
}
public function testSquareFq() {
$result = $this->texVC->check( ']_x^2',
[ 'usemhchem' => true, 'oldmhchem' => true ] );
$this->assertEquals( ']_{x}^{2}', $result['output'] );
}
public function mhchemtexifiedTest() {
$result = $this->texVC->check( '\\longleftrightarrows',
[ 'usemhchemtexified' => true ] );
$this->assertEquals( '\\longleftrightarrows', $result['output'] );
}
public function mhchemtexifiedTestFail() {
$result = $this->texVC->check( '\\longleftrightarrows' );
$this->assertEquals( 'C', $result['status'] );
$this->assertFalse( $result['success'] );
}
}