Add Integration for the TexVC-PHP check to Math

Bug: T312528

Change-Id: I0eda7d4213a5f703ab7ae7887aa364af9e576dbe
This commit is contained in:
Stegmujo 2022-10-10 14:09:14 +00:00
parent 8333e2541a
commit c27f7edef9
6 changed files with 290 additions and 23 deletions

View file

@ -6,7 +6,8 @@
"Brion Vibber",
"Moritz Schubotz",
"Derk-Jan Hartman",
"André Greiner-Petter"
"André Greiner-Petter",
"Johannes Stegmüller"
],
"url": "https://www.mediawiki.org/wiki/Extension:Math",
"descriptionmsg": "math-desc",
@ -102,6 +103,10 @@
"description": "Fallback value for wbEntitySelector if wbRepo is not configured. See https://www.mediawiki.org/wiki/Manual:CORS for cross wiki communication.",
"value": "https://www.wikidata.org/w/api.php"
},
"MathTexVCService": {
"description": "Determine which TexVC variant is used, currently available: 'mathoid', 'restbase' (default) and 'local'.",
"value": "restbase"
},
"MathLaTeXMLTimeout": {
"value": 240
},

View file

@ -2,7 +2,10 @@
namespace MediaWiki\Extension\Math\InputCheck;
use Exception;
use InvalidArgumentException;
use MediaWiki\Config\ServiceOptions;
use MediaWiki\Extension\Math\MathRestbaseInterface;
use MediaWiki\Http\HttpRequestFactory;
use Psr\Log\LoggerInterface;
use WANObjectCache;
@ -12,6 +15,7 @@ class InputCheckFactory {
public const CONSTRUCTOR_OPTIONS = [
'MathMathMLUrl',
'MathLaTeXMLTimeout',
'MathTexVCService'
];
/** @var string */
private $url;
@ -23,6 +27,8 @@ class InputCheckFactory {
private $httpFactory;
/** @var LoggerInterface */
private $logger;
/** @var string */
private $texVCmode;
/**
* @param ServiceOptions $options
@ -39,15 +45,16 @@ class InputCheckFactory {
$options->assertRequiredOptions( self::CONSTRUCTOR_OPTIONS );
$this->url = $options->get( 'MathMathMLUrl' );
$this->timeout = $options->get( 'MathLaTeXMLTimeout' );
$this->texVCmode = $options->get( 'MathTexVCService' );
$this->cache = $cache;
$this->httpFactory = $httpFactory;
$this->logger = $logger;
}
/**
* @param string $input
* @param string $type
* @return MathoidChecker
* @param string $input input string to be checked
* @param string $type type of input
* @return MathoidChecker checker based on mathoid
*/
public function newMathoidChecker( string $input, string $type ): MathoidChecker {
return new MathoidChecker(
@ -60,4 +67,55 @@ class InputCheckFactory {
$type
);
}
/**
* @param string $input input string to be checked
* @param string $type type of input
* @param MathRestbaseInterface|null &$restbaseInterface restbase interface which is used for remote communication
* @return RestbaseChecker checker based on communication with restbase interface
*/
public function newRestbaseChecker( string $input, string $type,
MathRestbaseInterface &$restbaseInterface = null ): RestbaseChecker {
return new RestbaseChecker(
$input,
$type,
$restbaseInterface
);
}
/**
* @param string $input input string to be checked
* @param string $type type of input (only 'tex')
* @return LocalChecker checker based on php implementation of TexVC within Math-extension
* @throws InvalidArgumentException|Exception if the type is not correct.
*/
public function newLocalChecker( string $input, string $type ): LocalChecker {
return new LocalChecker(
$input,
$type
);
}
/**
* Creates an instance of BaseChecker based on the configuration parameter for the texVC Service.
* By default, this sets the checker to the local PHP variant of TexVC.
*
* @param string $input input string which is checked
* @param string $type input type, for some configurations this has to be 'tex'
* @param MathRestbaseInterface|null &$restbaseInterface restbase interface,
* only necessary when using 'restbase' configuration
* @return BaseChecker a checker object which has the results of the check.
*/
public function newDefaultChecker( string $input, string $type,
MathRestbaseInterface &$restbaseInterface = null ): BaseChecker {
switch ( $this->texVCmode ) {
case "mathoid":
return $this->newMathoidChecker( $input, $type );
case "local":
return $this->newLocalChecker( $input, $type );
case "restbase":
default:
return $this->newRestbaseChecker( $input, $type, $restbaseInterface );
}
}
}

View file

@ -0,0 +1,63 @@
<?php
namespace MediaWiki\Extension\Math\InputCheck;
use Exception;
use InvalidArgumentException;
use MediaWiki\Extension\Math\TexVC\TexVC;
use Message;
class LocalChecker extends BaseChecker {
/** @var TexVC */
private $texVC;
/** @var array */
private $result;
/**
* @param string $tex the TeX input string to be checked
* @param string $type the input type, only tex allowed
* @throws InvalidArgumentException|Exception if the type is not correct.
*/
public function __construct( $tex = '', $type = 'tex' ) {
parent::__construct( $tex );
if ( $type !== 'tex' ) {
// this type check will be refactored with the introduction of chem-types:
// see: https://phabricator.wikimedia.org/T321262
throw new InvalidArgumentException( "Non supported type passed to LocalChecker: " . $type );
}
$this->texVC = new TexVC();
$this->result = $this->texVC->check( $tex );
}
/**
* @return bool
*/
public function isValid() {
return $this->result["status"] === "+";
}
/**
* Some TeX checking programs may return
* a modified tex string after having checked it.
* You can get the altered tex string with this method
* @return string|null A valid Tex string or null if no output
*/
public function getValidTex() {
return array_key_exists( "output", $this->result ) ? $this->result["output"] : null;
}
/**
* Returns the string of the last error.
* @return ?Message
*/
public function getError(): ?Message {
if ( $this->result["success"] ) {
return null;
}
$errorOb = (object)[ "error" => null ];
$errorOb->error = (object)$this->result["error"];
return $this->errorObjectToMessage( $errorOb, "LocalCheck" );
}
}

View file

@ -0,0 +1,95 @@
<?php
namespace phpunit\InputCheck;
use InvalidArgumentException;
use MediaWiki\Config\ServiceOptions;
use MediaWiki\Extension\Math\InputCheck\InputCheckFactory;
use MediaWiki\Extension\Math\InputCheck\LocalChecker;
use MediaWiki\Extension\Math\InputCheck\MathoidChecker;
use MediaWiki\Extension\Math\InputCheck\RestbaseChecker;
use MediaWiki\Http\HttpRequestFactory;
use MediaWiki\Logger\LoggerFactory;
use MediaWiki\Tests\Unit\MockServiceDependenciesTrait;
use MediaWikiIntegrationTestCase;
use WANObjectCache;
/**
* @method InputCheckFactory newServiceInstance(string $serviceClass, array $parameterOverrides)
* @covers \MediaWiki\Extension\Math\InputCheck\InputCheckFactory
*/
class InputCheckFactoryTest extends MediaWikiIntegrationTestCase {
use MockServiceDependenciesTrait;
private $fakeHTTP;
private $fakeWAN;
protected function setUp(): void {
parent::setUp();
$this->fakeHTTP = $this->createMock( HttpRequestFactory::class );
$this->fakeWAN = $this->createMock( WANObjectCache::class );
}
public function testNewMathoidChecker() {
$checker = $this->newServiceInstance( InputCheckFactory::class, [] )
->newMathoidChecker( 'FORMULA', 'TYPE' );
$this->assertInstanceOf( MathoidChecker::class, $checker );
}
public function testNewRestbaseChecker() {
$checker = $this->newServiceInstance( InputCheckFactory::class, [] )
->newRestbaseChecker( 'FORMULA', 'TYPE' );
$this->assertInstanceOf( RestbaseChecker::class, $checker );
}
public function testNewLocalChecker() {
$checker = $this->newServiceInstance( InputCheckFactory::class, [] )
->newLocalChecker( 'FORMULA', 'tex' );
$this->assertInstanceOf( LocalChecker::class, $checker );
}
public function testInvalidLocalChecker() {
$this->expectException( InvalidArgumentException::class );
$this->newServiceInstance( InputCheckFactory::class, [] )
->newLocalChecker( 'FORMULA', 'invalidtype' );
}
public function testNewDefaultChecker() {
$checker = $this->newServiceInstance( InputCheckFactory::class, [] )
->newDefaultChecker( 'FORMULA', 'TYPE' );
$this->assertInstanceOf( RestbaseChecker::class, $checker );
}
public function testNewMLocalCheckerDefault() {
$myFactory = new InputCheckFactory(
new ServiceOptions( InputCheckFactory::CONSTRUCTOR_OPTIONS, [
'MathMathMLUrl' => 'something',
'MathTexVCService' => 'local',
'MathLaTeXMLTimeout' => 240
] ),
$this->fakeWAN,
$this->fakeHTTP,
LoggerFactory::getInstance( 'Math' )
);
$checker = $myFactory->newDefaultChecker( 'FORMULA', 'tex' );
$this->assertInstanceOf( LocalChecker::class, $checker );
}
public function testMathoidCheckerInDefault() {
$myFactory = new InputCheckFactory(
new ServiceOptions( InputCheckFactory::CONSTRUCTOR_OPTIONS, [
'MathMathMLUrl' => 'something',
'MathTexVCService' => 'mathoid',
'MathLaTeXMLTimeout' => 240
] ),
$this->fakeWAN,
$this->fakeHTTP,
LoggerFactory::getInstance( 'Math' )
);
$checker = $myFactory->newDefaultChecker( 'FORMULA', 'TYPE' );
$this->assertInstanceOf( MathoidChecker::class, $checker );
}
}

View file

@ -0,0 +1,65 @@
<?php
namespace MediaWiki\Extension\Math\InputCheck;
use InvalidArgumentException;
use MediaWikiIntegrationTestCase;
use Message;
use MockHttpTrait;
/**
* @group Math
* @license GPL-2.0-or-later
* tbd move this to unittests
* @covers \MediaWiki\Extension\Math\InputCheck\LocalChecker
*/
class LocalCheckerTest extends MediaWikiIntegrationTestCase {
use MockHttpTrait;
public function testValid() {
$checker = new LocalChecker( '\sin x^2' );
$this->assertNull( $checker->getError() );
$this->assertTrue( $checker->isValid() );
$this->assertNull( $checker->getError() );
$this->assertSame( '\\sin x^{2}', $checker->getValidTex() );
}
public function testValidType() {
$checker = new LocalChecker( '\sin x^2', 'tex' );
$this->assertTrue( $checker->isValid() );
}
public function testInvalidType() {
$this->expectException( InvalidArgumentException::class );
new LocalChecker( '\sin x^2', 'chem' );
}
public function testInvalid() {
$checker = new LocalChecker( '\sin\newcommand' );
$this->assertFalse( $checker->isValid() );
$this->assertStringContainsString(
Message::newFromKey( 'math_unknown_function', '\newcommand' )
->inContentLanguage()
->escaped(),
$checker->getError()
->inContentLanguage()
->escaped()
);
$this->assertNull( $checker->getValidTex() );
}
public function testErrorSyntax() {
$checker = new LocalChecker( '\left(' );
$this->assertFalse( $checker->isValid() );
$this->assertStringContainsString(
Message::newFromKey( 'math_syntax_error' )
->inContentLanguage()
->escaped(),
$checker->getError()
->inContentLanguage()
->escaped()
);
}
}

View file

@ -1,19 +0,0 @@
<?php
use MediaWiki\Extension\Math\InputCheck\InputCheckFactory;
use MediaWiki\Extension\Math\InputCheck\MathoidChecker;
use MediaWiki\Tests\Unit\MockServiceDependenciesTrait;
/**
* @method InputCheckFactory newServiceInstance( string $serviceClass, array $parameterOverrides )
* @covers \MediaWiki\Extension\Math\InputCheck\InputCheckFactory
*/
class InputCheckFactoryTest extends MediaWikiUnitTestCase {
use MockServiceDependenciesTrait;
public function testNewMathoidChecker() {
$checker = $this->newServiceInstance( InputCheckFactory::class, [] )
->newMathoidChecker( 'FORMULA', 'TYPE' );
$this->assertInstanceOf( MathoidChecker::class, $checker );
}
}