From 927340d3457d315477c61f9157bce657082aeccb Mon Sep 17 00:00:00 2001 From: physikerwelt Date: Tue, 14 May 2013 23:49:06 +0200 Subject: [PATCH] New rendering option LaTeXML Introduces a new rending option to render TeX to MathML via LaTeXML. Bug: 43222 Change-Id: I5d29e219c0d3b907e22ea0bb3b30f000d8a7a9f8 --- Math.hooks.php | 6 +- Math.i18n.php | 48 +++++-- Math.php | 30 ++++- MathLaTeXML.php | 247 +++++++++++++++++++++++++++++++++++++ MathRenderer.php | 87 +++++++++---- README | 10 ++ db/math.sql | 2 +- tests/MathDatabaseTest.php | 4 +- tests/MathLaTeXMLTest.php | 146 ++++++++++++++++++++++ tests/MathRendererTest.php | 10 ++ 10 files changed, 552 insertions(+), 38 deletions(-) create mode 100644 MathLaTeXML.php create mode 100644 tests/MathLaTeXMLTest.php diff --git a/Math.hooks.php b/Math.hooks.php index a1d9fead7..3489da542 100644 --- a/Math.hooks.php +++ b/Math.hooks.php @@ -81,7 +81,7 @@ class MathHooks { * @return array of strings */ private static function getMathNames() { - global $wgUseMathJax; + global $wgUseMathJax, $wgUseLaTeXML; $names = array( MW_MATH_PNG => wfMessage( 'mw_math_png' )->escaped(), MW_MATH_SOURCE => wfMessage( 'mw_math_source' )->escaped(), @@ -89,7 +89,9 @@ class MathHooks { if ( $wgUseMathJax ) { $names[MW_MATH_MATHJAX] = wfMessage( 'mw_math_mathjax' )->escaped(); } - + if ( $wgUseLaTeXML ) { + $names[MW_MATH_LATEXML] = wfMessage( 'mw_math_latexml' )->escaped(); + } return $names; } diff --git a/Math.i18n.php b/Math.i18n.php index 1b42f56f8..9f5fd34b8 100644 --- a/Math.i18n.php +++ b/Math.i18n.php @@ -22,6 +22,7 @@ $messages['en'] = array( 'mw_math_png' => 'Always render PNG', 'mw_math_source' => 'Leave it as TeX (for text browsers)', 'mw_math_mathjax' => 'MathJax (experimental; best for most browsers)', + 'mw_math_latexml' => 'LaTeXML (experimental; uses MathML)', // Math errors 'math_failure' => 'Failed to parse', @@ -34,6 +35,10 @@ $messages['en'] = array( 'math_bad_output' => 'Cannot write to or create math output directory', 'math_notexvc' => 'Missing texvc executable; please see math/README to configure.', 'math_output_error' => 'Cannot store math image on filesystem.', + 'math_latexml_timeout' => 'LaTeXML Timeout from \'$1\'', + 'math_latexml_invalidresponse' => 'LaTeXML Invalid response from server \'$1\':', + 'math_latexml_invalidxml' => 'LaTeXML MathML is invalid XML.', + 'math_latexml_invalidjson' => 'LaTeXML Server response is invalid JSON.', ); /** Message documentation (Message documentation) @@ -41,6 +46,7 @@ $messages['en'] = array( * @author Kizito * @author Shirayuki * @author Siebrand + * @author Physikerwelt */ $messages['qqq'] = array( 'math-desc' => '{{desc|name=Math|url=http://www.mediawiki.org/wiki/Extension:Math}}', @@ -53,19 +59,28 @@ Used as label for radio button. See also: * {{msg-mw|Mw math source}} -* {{msg-mw|Mw math mathjax}}', +* {{msg-mw|Mw math mathjax}} +* {{msg-mw|Mw math latexml}}', 'mw_math_source' => 'In user preferences (math). All mw_math_* messages MUST be different, things will break otherwise! -Used as label for radio button. +Used as label for source radio button. See also: * {{msg-mw|Mw math png}} +* {{msg-mw|Mw math mathjax}} +* {{msg-mw|Mw math latexml}}', + 'mw_math_mathjax' => 'Used as label for mathjax radio button. + +See also: +* {{msg-mw|Mw math png}} +* {{msg-mw|Mw math source}} +* {{msg-mw|Mw math latexml}}', + 'mw_math_latexml' => 'Used as label for latexml radio button. + +See also: +* {{msg-mw|Mw math png}} +* {{msg-mw|Mw math source}} * {{msg-mw|Mw math mathjax}}', - 'mw_math_mathjax' => 'Used as label for radio button. - -See also: -* {{msg-mw|Mw math png}} -* {{msg-mw|Mw math source}}', 'math_failure' => 'Used as error message. This message is followed by "(", Error message(*1), Additional message, "): " and Source code. @@ -79,7 +94,12 @@ This message is followed by "(", Error message(*1), Additional message, "): " a * {{msg-mw|Math bad tmpdir}} * {{msg-mw|Math bad output}} * {{msg-mw|Math notexvc}} -* {{msg-mw|Math output error}}', +* {{msg-mw|Math output error}} +* {{msg-mw|Math latexml timeout}} +* {{msg-mw|Math latexml noresponse}} +* {{msg-mw|Math latexml invalidxml}} +* {{msg-mw|Math latexml invalidjson}} +', 'math_unknown_error' => 'Used as error message for unknown texvc error. This message follows the message {{msg-mw|Math failure}}. @@ -113,6 +133,18 @@ This message follows the message {{msg-mw|Math failure}}.', 'math_output_error' => 'Used as error message if the texvc output file could not be stored. This message follows the message {{msg-mw|Math failure}}.', + 'math_latexml_timeout' => 'Used as error message. + +This message follows the message {{msg-mw|Math failure}}. ', + 'math_latexml_invalidresponse' => 'Used as error message. + +This message follows the message {{msg-mw|Math failure}}. ', + 'math_latexml_invalidxml' => 'Used as error message. + +This message follows the message {{msg-mw|Math failure}}. ', + 'math_latexml_invalidjson' => 'Used as error message. + +This message follows the message {{msg-mw|Math failure}}. ', ); /** Achinese (Acèh) diff --git a/Math.php b/Math.php index 83bd58a4f..9314cb721 100644 --- a/Math.php +++ b/Math.php @@ -22,7 +22,7 @@ if ( !defined( 'MEDIAWIKI' ) ) { $wgExtensionCredits['parserhook'][] = array( 'path' => __FILE__, 'name' => 'Math', - 'version' => '1.0', + 'version' => '1.1', 'author' => array( 'Tomasz Wegrzanowski', 'Brion Vibber', '...' ), 'descriptionmsg' => 'math-desc', 'url' => 'https://www.mediawiki.org/wiki/Extension:Math', @@ -38,6 +38,7 @@ define( 'MW_MATH_SOURCE', 3 ); define( 'MW_MATH_MODERN', 4 ); /// @deprecated define( 'MW_MATH_MATHML', 5 ); /// @deprecated define( 'MW_MATH_MATHJAX', 6 ); /// new in 1.19/1.20 +define( 'MW_MATH_LATEXML', 7 ); /// new in 1.22 /**@}*/ /** For back-compat */ @@ -101,6 +102,32 @@ $wgMathDirectory = false; */ $wgUseMathJax = false; +/** + * Use of LaTeXML for details see + * + * + * If you want or need to run your own server, follow these installation + * instructions and override $wgLaTeXMLUrl: + * + * + * If you expect heavy load you can specify multiple servers. In that case one + * server is randomly chosen for each rendering process. Specify the list of + * servers in an array e.g $wgLaTeXMLUrl = array ( 'http://latexml.example.com/convert', + * 'http://latexml2.example.com/convert'); + */ +$wgLaTeXMLUrl = 'http://latexml.mathweb.org/convert'; + +/** + * Allows to use LaTeXML as renderer for mathematical equation. + */ +$wgUseLaTeXML = false; + +/** + * The timeout for the HTTP-Request sent to the LaTeXML to render an equation, + * in seconds. + */ +$wgLaTeXMLTimeout = 240; + ////////// end of config settings. $wgDefaultUserOptions['math'] = MW_MATH_PNG; @@ -119,6 +146,7 @@ $wgAutoloadClasses['MathRenderer'] = $dir . 'MathRenderer.php'; $wgAutoloadClasses['MathTexvc'] = $dir . 'MathTexvc.php'; $wgAutoloadClasses['MathSource'] = $dir . 'MathSource.php'; $wgAutoloadClasses['MathMathJax'] = $dir . 'MathMathJax.php'; +$wgAutoloadClasses['MathLaTeXML'] = $dir . 'MathLaTeXML.php'; $wgExtensionMessagesFiles['Math'] = $dir . 'Math.i18n.php'; $wgParserTestFiles[] = $dir . 'mathParserTests.txt'; diff --git a/MathLaTeXML.php b/MathLaTeXML.php new file mode 100644 index 000000000..ee38317e8 --- /dev/null +++ b/MathLaTeXML.php @@ -0,0 +1,247 @@ +LaTeXMLSettings ) { + return $this->LaTeXMLSettings; + } else { + return self::DEFAULT_LATEXML_SETTING; + } + } + + /** + * Sets the settings for the LaTeXML daemon. + * The settings affect only the current instance of the class. + * For a list of possible settings see: + * http://dlmf.nist.gov/LaTeXML/manual/commands/latexmlpost.xhtml + * An empty value indicates to use the default settings. + * @param string $settings + */ + public function setLaTeXMLSettings( $settings ) { + $this->LaTeXMLSettings = $settings; + } + + /* (non-PHPdoc) + * @see MathRenderer::render() + */ + public function render( $forceReRendering = false ) { + if ( $forceReRendering ) { + $this->setPurge( true ); + } + if ( $this->renderingRequired() ) { + $res = $this->doRender( ); + if ( ! $res ) { + return $this->getLastError(); + } + } + return $this->getMathMLTag(); + } + + /** + * Helper function to checks if the math tag must be rendered. + * @return boolean + */ + private function renderingRequired() { + if ( $this->isPurge() ) { + wfDebugLog( "Math", "Rerendering was requested." ); + return true; + } else { + $dbres = $this->readFromDatabase(); + if ( $dbres ) { + if ( self::isValidMathML( $this->getMathml() ) ) { + wfDebugLog( "Math", "Valid entry found in database." ); + return false; + } else { + wfDebugLog( "Math", "Malformatted entry found in database" ); + return true; + } + } else { + wfDebugLog( "Math", "No entry found in database." ); + return true; + } + } + } + + /** + * Performs a HTTP Post request to the given host. + * Uses $wgLaTeXMLTimeout as timeout. + * Generates error messages on failure + * @see Http::post() + * + * @param string $host + * @param string $post the encoded post request + * @param mixed $res the result + * @param mixed $error the formatted error message or null + * @param String $httpRequestClass class name of MWHttpRequest (needed for testing only) + * @return boolean success + */ + public function makeRequest( $host, $post, &$res, &$error = '', $httpRequestClass = 'MWHttpRequest' ) { + global $wgLaTeXMLTimeout; + $error = ''; + $res = null; + $options = array( 'method' => 'POST', 'postData' => $post, 'timeout' => $wgLaTeXMLTimeout ); + $req = $httpRequestClass::factory( $host, $options ); + $status = $req->execute(); + if ( $status->isGood() ) { + $res = $req->getContent(); + return true; + } else { + if ( $status->hasMessage( 'http-timed-out' ) ) { + $error = $this->getError( 'math_latexml_timeout', $host ); + $res = false; + wfDebugLog( "Math", "\nLaTeXML Timeout:" + . var_export( array( 'post' => $post, 'host' => $host + , 'wgLaTeXMLTimeout' => $wgLaTeXMLTimeout ), true ) . "\n\n" ); + } else { + // for any other unkonwn http error + $errormsg = $status->getHtml(); + $error = $this->getError( 'math_latexml_invalidresponse', $host, $errormsg ); + wfDebugLog( "Math", "\nLaTeXML NoResponse:" + . var_export( array( 'post' => $post, 'host' => $host + , 'errormsg' => $errormsg ), true ) . "\n\n" ); + } + return false; + } + } + + /* (non-PHPdoc) + * @see MathRenderer::writeCache() + */ + public function writeCache() { + if ( $this->isChanged() ) { + $this->writeToDatabase(); + } + } + + /** + * Picks a LaTeXML daemon. + * If more than one demon are availible one is chosen from the + * $wgLaTeXMLUrl array. + * @return string + */ + private static function pickHost() { + global $wgLaTeXMLUrl; + if ( is_array( $wgLaTeXMLUrl ) ) { + $host = array_rand( $wgLaTeXMLUrl ); + } else { + $host = $wgLaTeXMLUrl; + } + wfDebugLog( "Math", "picking host " . $host ); + return $host; + } + + /** + * Does the actual web request to convert TeX to MathML. + * @return boolean + */ + private function doRender( ) { + $host = self::pickHost(); + $texcmd = urlencode( $this->tex ); + $post = $this->getLaTeXMLSettings(); + $post .= '&tex=' . $texcmd; + $this->lastError = ''; + if ( $this->makeRequest( $host, $post, $res, $this->lastError ) ) { + $result = json_decode( $res ); + if ( json_last_error() === JSON_ERROR_NONE ) { + if ( self::isValidMathML( $result->result ) ) { + $this->setMathml( $result->result ); + return true; + } else { + // Do not print bad mathml. It's probably too verbose and might + // mess up the browser output. + $this->lastError = $this->getError( 'math_latexml_invalidxml', $host ); + wfDebugLog( "Math", "\nLaTeXML InvalidMathML:" + . var_export( array( 'post' => $post, 'host' => $host + , 'result' => $result ), true ) . "\n\n" ); + return false; + } + } else { + $this->lastError = $this->getError( 'math_latexml_invalidjson', $host ); + wfDebugLog( "Math", "\nLaTeXML InvalidJSON:" + . var_export( array( 'post' => $post, 'host' => $host + , 'res' => $res ), true ) . "\n\n" ); + return false; + } + } else { + // Error message has already been set. + return false; + } + } + + /** + * Checks if the input is valid MathML, + * and if the root element has the name math + * @param string $XML + * @return boolean + */ + static public function isValidMathML( $XML ) { + $out = false; + $prevInternalErrors = libxml_use_internal_errors( true ); + $xmlObject = simplexml_load_string( $XML ); + if ( !$xmlObject ) { + wfDebugLog( "Math", "XML validation error:\n " . var_export( $XML, true ) . "\n" ); + foreach ( libxml_get_errors() as $error ) { + wfDebugLog( "Math", "\t" . $error->message ); + } + libxml_clear_errors(); + } else { + $name = $xmlObject->getName(); + if ( $name == "math" or $name == "table" or $name == "div" ) { + $out = true; + } else { + wfDebugLog( "Math", "got wrong root element " . $name ); + } + } + libxml_use_internal_errors( $prevInternalErrors ); + return $out; + } + + /** + * Internal version of @link self::embedMathML + * @return string + * @return html element with rendered math + */ + private function getMathMLTag() { + return self::embedMathML( $this->getMathml(), urldecode( $this->getTex() ) ); + } + + /** + * Embeds the MathML-XML element in a HTML span element with class tex + * @param string $mml: the MathML string + * @param string $tagId: optional tagID for references like (pagename#equation2) + * @return html element with rendered math + */ + public static function embedMathML( $mml, $tagId = '', $attribs = false ) { + $mml = str_replace( "\n", " ", $mml ); + if ( ! $attribs ) { + $attribs = array( 'class' => 'tex', 'dir' => 'ltr' ); + if ( $tagId ) { + $attribs['id'] = $tagId; + } + $attribs = Sanitizer::validateTagAttributes( $attribs, 'span' ); + } + return Xml::tags( 'span', $attribs, $mml ); + } + +} \ No newline at end of file diff --git a/MathRenderer.php b/MathRenderer.php index 868a4ddb3..b2fc50cb6 100644 --- a/MathRenderer.php +++ b/MathRenderer.php @@ -9,8 +9,8 @@ */ /** - * Abstract base class with static methods for rendering the tags using - * different technologies. These static methods create a new instance of the + * Abstract base class with static methods for rendering the tags using + * different technologies. These static methods create a new instance of the * extending classes and render the math tags based on the mode setting of the user. * Furthermore this class handles the caching of the rendered output and provides * debug information, @@ -25,19 +25,24 @@ abstract class MathRenderer { * The following variables should made private, as soon it can be verified * that they are not being directly accessed by other extensions. */ - var $mode = MW_MATH_PNG; - var $tex = ''; + protected $mode = MW_MATH_PNG; + protected $tex = ''; /** * is calculated by texvc. * @var string */ - var $hash = ''; - var $html = ''; - var $mathml = ''; - var $conservativeness = 0; - var $params = ''; - var $changed = false; + protected $hash = ''; + protected $html = ''; + protected $mathml = ''; + protected $conservativeness = 0; + protected $params = ''; + protected $changed = false; + /** + * @var boolean forces rerendering if set to true + */ + protected $purge = false; protected $recall; + protected $lastError = ''; /** * Constructs a base MathRenderer @@ -45,7 +50,7 @@ abstract class MathRenderer { * @param string $tex (optional) LaTeX markup * @param array $params (optional) HTML attributes */ - public function __construct( $tex='', $params = array() ) { + public function __construct( $tex = '', $params = array() ) { $this->tex = $tex; $this->params = $params; } @@ -73,7 +78,7 @@ abstract class MathRenderer { */ public static function getRenderer( $tex, $params = array(), $mode = MW_MATH_PNG ) { global $wgDefaultUserOptions; - $validModes = array( MW_MATH_PNG, MW_MATH_SOURCE, MW_MATH_MATHJAX ); + $validModes = array( MW_MATH_PNG, MW_MATH_SOURCE, MW_MATH_MATHJAX, MW_MATH_LATEXML ); if ( !in_array( $mode, $validModes ) ) $mode = $wgDefaultUserOptions['math']; switch ( $mode ) { @@ -83,11 +88,14 @@ abstract class MathRenderer { case MW_MATH_MATHJAX: $renderer = new MathMathJax( $tex, $params ); break; + case MW_MATH_LATEXML: + $renderer = new MathLaTeXML( $tex, $params ); + break; case MW_MATH_PNG: default: $renderer = new MathTexvc( $tex, $params ); } - wfDebugLog ( "Math", 'start rendering $' . $renderer->tex . '$' ); + wfDebugLog ( "Math", 'start rendering $' . $renderer->tex . '$ in mode ' . $mode ); return $renderer; } @@ -152,9 +160,11 @@ abstract class MathRenderer { $this->hash = $xhash['md5']; $this->conservativeness = $rpage->math_html_conservativeness; $this->html = $rpage->math_html; - $this->mathml = $rpage->math_mathml; - $this->recall = true; - return true; + $this->mathml = utf8_decode( $rpage->math_mathml); + if ( StringUtils::isUtf8( $this->mathml ) ) { + $this->recall = true; + return true; + } } # Missing from the database and/or the render cache @@ -189,7 +199,7 @@ abstract class MathRenderer { 'math_outputhash' => $outmd5_sql , 'math_html_conservativeness' => $this->conservativeness, 'math_html' => $this->html, - 'math_mathml' => $this->mathml, + 'math_mathml' => utf8_encode( $this->mathml ), ), __METHOD__ ); @@ -210,6 +220,8 @@ abstract class MathRenderer { $attribs = Sanitizer::mergeAttributes( $attribs, $overrides ); return $attribs; } + + /** * Writes cache. Does nothing by default */ @@ -255,7 +267,7 @@ abstract class MathRenderer { /** * Get the hash calculated by texvc - * + * * @return string hash */ public function getHash() { @@ -321,7 +333,7 @@ abstract class MathRenderer { /** * Get the attributes of the math tag - * + * * @return array() */ public function getParams() { @@ -332,11 +344,11 @@ abstract class MathRenderer { * @param array() $params */ public function setParams( $params ) { - //$changed is not set to true here, because the attributes do not affect - //the rendering in the current implementation. - //If this behavior will change in the future $this->tex is no longer a - //primary key and the input hash cannot be calculate form $this->tex - //only. See the discussion 'Tag extensions in Block mode' on wikitech-l. + // $changed is not set to true here, because the attributes do not affect + // the rendering in the current implementation. + // If this behavior will change in the future $this->tex is no longer a + // primary key and the input hash cannot be calculate form $this->tex + // only. See the discussion 'Tag extensions in Block mode' on wikitech-l. $this->params = $params; } @@ -349,5 +361,32 @@ abstract class MathRenderer { return $this->changed; } + /** + * Checks if there is an explicit user request to rerender the math-tag. + * @return boolean + */ + function isPurge( ) { + if ( $this->purge ) { + return true; + } + // TODO: Figure out if ?action=purge + // until this issue is resolved we use ?mathpurge=true instead + global $wgRequest; + return ( $wgRequest->getVal( 'mathpurge' ) === "true" ); + } + + /** + * Sets purge. If set to true the render is forced to rerender and must not + * use a cached version. + * @return boolean + */ + function setPurge( $purge = true ) { + $this->changed = true; + $this->purge = $purge; + } + + function getLastError(){ + return $this->lastError; + } } diff --git a/README b/README index 8846d8448..f0038602b 100644 --- a/README +++ b/README @@ -7,6 +7,16 @@ This version (for MediaWiki 1.19) has some changes since previous versions: See the README in the math subdirectory for more info on setting up the low-level conversion tools. +MathML support: +If you prefer MathML rather than images you can use LaTeXML to convert the +math tags to MathML. To use that feature you have to enable LaTeXML by setting +$wgUseLaTeXML = true; +It is possible to choose LaTeXML as default option (for anonymous user) by setting +$wgDefaultUserOptions['math'] = MW_MATH_LATEXML; +in the LocalSettings.php file. +The LaTeXML option requires php5-curl to be installed. Without php5-curl no proper +error handling can be guaranteed. + MathJax configuration: Client-side configuration of MathJax can be done by specifying a mathJax.config table, which takes a table as described in: diff --git a/db/math.sql b/db/math.sql index e89991c5c..f50955535 100644 --- a/db/math.sql +++ b/db/math.sql @@ -16,7 +16,7 @@ CREATE TABLE /*_*/math ( -- HTML output from texvc, if any math_html text, - -- MathML output from texvc, if any + -- MathML output from texvc, or from LaTeXML math_mathml text ) /*$wgDBTableOptions*/; diff --git a/tests/MathDatabaseTest.php b/tests/MathDatabaseTest.php index f909f7f20..99fb00eff 100644 --- a/tests/MathDatabaseTest.php +++ b/tests/MathDatabaseTest.php @@ -72,8 +72,8 @@ class MathDatabaseTest extends MediaWikiTestCase { // comparing the class object does now work due to null values etc. // $this->assertEquals($this->renderer,$renderer2); $this->assertEquals( $this->renderer->getTex(), $renderer2->getTex(), "test if tex is the same" ); - $this->assertEquals( $this->renderer->mathml, $renderer2->mathml, "Check MathML encoding" ); - $this->assertEquals( $this->renderer->html, $renderer2->html ); + $this->assertEquals( $this->renderer->getMathml(), $renderer2->getMathml(), "Check MathML encoding" ); + $this->assertEquals( $this->renderer->getHtml(), $renderer2->getHtml() ); } diff --git a/tests/MathLaTeXMLTest.php b/tests/MathLaTeXMLTest.php new file mode 100644 index 000000000..30adf0d9b --- /dev/null +++ b/tests/MathLaTeXMLTest.php @@ -0,0 +1,146 @@ +getMockBuilder( 'MathLaTeXML' ) + ->setMethods( NULL ) + ->disableOriginalConstructor() + ->getMock(); + $requestReturn = $renderer->makeRequest( $url, 'a+b', $res, $error + , 'LaTeXMLHttpRequestTester' ); + $this->assertEquals( false, $requestReturn + , "requestReturn is false if HTTP::post returns false." ); + $this->assertEquals( false, $res + , "res is false if HTTP:post returns false." ); + $errmsg = wfMessage( 'math_latexml_invalidresponse' , $url ) + ->inContentLanguage()->escaped(); + $this->assertContains( $errmsg, $error + , "return an error if HTTP::post returns false" ); + } + + /** + * Tests behavior of makeRequest() that communicates with the host. + * Testcase: Valid request. + * @covers MathTexvc::makeRequest + */ + public function testMakeRequestSuccess() { + self::setMockValues( true, true, false ); + $url = 'http://example.com/valid'; + $renderer = $this->getMockBuilder( 'MathLaTeXML' ) + ->setMethods( NULL ) + ->disableOriginalConstructor() + ->getMock(); + $requestReturn = $renderer->makeRequest( $url, 'a+b', $res, $error + , 'LaTeXMLHttpRequestTester' ); + $this->assertEquals( true, $requestReturn, "successful call return" ); + $this->isTrue( $res, "successfull call" ); + $this->assertEquals( $error,'', "successfull call errormessage" ); + } + + /** + * Tests behavior of makeRequest() that communicates with the host. + * Testcase: Timeout. + * @covers MathTexvc::makeRequest + */ + public function testMakeRequestTimeout() { + self::setMockValues( false, true, true ); + $url = 'http://example.com/timeout'; + $renderer = $this->getMockBuilder( 'MathLaTeXML' ) + ->setMethods( NULL ) + ->disableOriginalConstructor() + ->getMock(); + $requestReturn = $renderer->makeRequest( $url, '$\longcommand$', $res + , $error, 'LaTeXMLHttpRequestTester' ); + $this->assertEquals( false, $requestReturn, "timeout call return" ); + $this->assertEquals( false, $res, "timeout call return" ); + $errmsg = wfMessage( 'math_latexml_timeout', $url ) + ->inContentLanguage()->escaped(); + $this->assertContains( $errmsg, $error, "timeout call errormessage" ); + } + + /** + * Checks the basic functionallity + * i.e. if the span element is generated right. + */ + public function testIntegration() { + global $wgLaTeXMLTimeout; + $wgLaTeXMLTimeout = 20; + $renderer = MathRenderer::getRenderer( "a+b", array(), MW_MATH_LATEXML ); + $real = $renderer->render( true ); + $expected = ' a + b a b a+b '; + $this->assertEquals( $expected, $real + , "Rendering of a+b in plain Text mode" ); + } +} + +/** + * Helper class for testing + * @author physikerwelt + * @see MWHttpRequestTester + * + */ +class LaTeXMLHttpRequestTester { + public static function factory() { + return new LaTeXMLHttpRequestTester(); + } + public static function execute() { + return new LaTeXMLTestStatus(); + } + public static function getContent() { + return MathLaTeXMLTest::$content; + } +} + +/** + * Helper class for testing + * @author physikerwelt + * @see Status + */ +class LaTeXMLTestStatus { + static function isGood() { + return MathLaTeXMLTest::$good; + } + + static function hasMessage( $s ) { + if ( $s == 'http-timed-out' ) { + return MathLaTeXMLTest::$timeout; + } else { + return false; + } + } + static function getHtml() { + return MathLaTeXMLTest::$html; + } +} \ No newline at end of file diff --git a/tests/MathRendererTest.php b/tests/MathRendererTest.php index ea6c96b9a..325fcac63 100644 --- a/tests/MathRendererTest.php +++ b/tests/MathRendererTest.php @@ -63,4 +63,14 @@ class MathRendererTest extends MediaWikiTestCase { $this->assertEquals( $renderer->isChanged(), true , "assumes that changing a hash sets changed to true"); } + + public function testSetPurge(){ + $renderer = $this->getMockBuilder( 'MathRenderer' ) + ->setMethods( array( 'render' ) ) + ->disableOriginalConstructor() + ->getMock(); + $renderer->setPurge(); + $this->assertEquals( $renderer->isPurge(), true, "Test purge." ); + + } } \ No newline at end of file