From d1c8bf85f86e3a535bcb1c484855631ae4930036 Mon Sep 17 00:00:00 2001 From: Timo Tijhof Date: Mon, 8 Oct 2012 12:59:55 +0200 Subject: [PATCH] Fix HtmlTest, XmlTest; Add setMwGlobals method to base TestCase. The HTML and XML test now set the globals they depend on (instead of relying on the default settings). Tests for the "other" scenarios still exist, globals are overridden inline. They are automatically restored after each test## function by PHPUnit from MediaWikiTestCase::tearDown. Also fixed 2 other problems with the test suite: * HtmlTest::testDropDefaults forgot to pass $message to the assertion from the provider ($case[3]). * The data provider for HtmlTest::testDropDefaults was calling Html::element directly (instead of calling it within the test) which is problematic because data providers are expected to be static. PHPUnit calls them outside the setUp/tearDown flow. (also fixed the function to be public static, as PHPUnit expects). That last one was crucial to make the test still pass correctly. Updated the expected strings to what they are with these fixed non-leakage settings. Took wgHtml5 without xmlform as default. And added tests for variations where it made sense. Change-Id: Iccf6ea81f4bc2639273ab2ad101c58788ee49d45 --- tests/phpunit/MediaWikiTestCase.php | 86 +++++- tests/phpunit/includes/HtmlTest.php | 394 +++++++++++++++------------- tests/phpunit/includes/XmlTest.php | 21 +- 3 files changed, 305 insertions(+), 196 deletions(-) diff --git a/tests/phpunit/MediaWikiTestCase.php b/tests/phpunit/MediaWikiTestCase.php index 54a3f7c31d..6b580a5c4e 100644 --- a/tests/phpunit/MediaWikiTestCase.php +++ b/tests/phpunit/MediaWikiTestCase.php @@ -29,6 +29,13 @@ abstract class MediaWikiTestCase extends PHPUnit_Framework_TestCase { */ private $tmpfiles = array(); + /** + * Holds original values of MediaWiki configuration settings + * to be restored in tearDown(). + * See also setMwGlobal(). + * @var array + */ + private $mwGlobals = array(); /** * Table name prefixes. Oracle likes it shorter. @@ -119,6 +126,30 @@ abstract class MediaWikiTestCase extends PHPUnit_Framework_TestCase { return $fname; } + /** + * setUp and tearDown should (where significant) + * happen in reverse order. + */ + protected function setUp() { + parent::setUp(); + + // Cleaning up temporary files + foreach ( $this->tmpfiles as $fname ) { + if ( is_file( $fname ) || ( is_link( $fname ) ) ) { + unlink( $fname ); + } elseif ( is_dir( $fname ) ) { + wfRecursiveRemoveDir( $fname ); + } + } + + // Clean up open transactions + if ( $this->needsDB() && $this->db ) { + while( $this->db->trxLevel() > 0 ) { + $this->db->rollback(); + } + } + } + protected function tearDown() { // Cleaning up temporary files foreach ( $this->tmpfiles as $fname ) { @@ -129,16 +160,67 @@ abstract class MediaWikiTestCase extends PHPUnit_Framework_TestCase { } } - // clean up open transactions - if( $this->needsDB() && $this->db ) { + // Clean up open transactions + if ( $this->needsDB() && $this->db ) { while( $this->db->trxLevel() > 0 ) { $this->db->rollback(); } } + // Restore mw globals + foreach ( $this->mwGlobals as $key => $value ) { + $GLOBALS[$key] = $value; + } + $this->mwGlobals = array(); + parent::tearDown(); } + /** + * Individual test functions may override globals (either directly or through this + * setMwGlobals() function), however one must call this method at least once for + * each key within the setUp(). + * That way the key is added to the array of globals that will be reset afterwards + * in the tearDown(). And, equally important, that way all other tests are executed + * with the same settings (instead of using the unreliable local settings for most + * tests and fix it only for some tests). + * + * @example + * + * protected function setUp() { + * $this->setMwGlobals( 'wgRestrictStuff', true ); + * } + * + * function testFoo() {} + * + * function testBar() {} + * $this->assertTrue( self::getX()->doStuff() ); + * + * $this->setMwGlobals( 'wgRestrictStuff', false ); + * $this->assertTrue( self::getX()->doStuff() ); + * } + * + * function testQuux() {} + * + * + * @param array|string $pairs Key to the global variable, or an array + * of key/value pairs. + * @param mixed $value Value to set the global to (ignored + * if an array is given as first argument). + */ + protected function setMwGlobals( $pairs, $value = null ) { + if ( !is_array( $pairs ) ) { + $key = $pairs; + $this->mwGlobals[$key] = $GLOBALS[$key]; + $GLOBALS[$key] = $value; + } else { + foreach ( $pairs as $key => $value ) { + $this->mwGlobals[$key] = $GLOBALS[$key]; + $GLOBALS[$key] = $value; + } + } + } + function dbPrefix() { return $this->db->getType() == 'oracle' ? self::ORA_DB_PREFIX : self::DB_PREFIX; } diff --git a/tests/phpunit/includes/HtmlTest.php b/tests/phpunit/includes/HtmlTest.php index 05dea35d81..95a6cb0a04 100644 --- a/tests/phpunit/includes/HtmlTest.php +++ b/tests/phpunit/includes/HtmlTest.php @@ -2,29 +2,17 @@ /** tests for includes/Html.php */ class HtmlTest extends MediaWikiTestCase { - private static $oldLang; - private static $oldContLang; - private static $oldLanguageCode; - private static $oldNamespaces; - private static $oldHTML5; - public function setUp() { - global $wgLang, $wgContLang, $wgLanguageCode, $wgHtml5; + protected function setUp() { + parent::setUp(); - // Save globals - self::$oldLang = $wgLang; - self::$oldContLang = $wgContLang; - self::$oldNamespaces = $wgContLang->getNamespaces(); - self::$oldLanguageCode = $wgLanguageCode; - self::$oldHTML5 = $wgHtml5; - - $wgLanguageCode = 'en'; - $wgContLang = $wgLang = Language::factory( $wgLanguageCode ); + $langCode = 'en'; + $langObj = Language::factory( $langCode ); // Hardcode namespaces during test runs, // so that html output based on existing namespaces // can be properly evaluated. - $wgContLang->setNamespaces( array( + $langObj->setNamespaces( array( -2 => 'Media', -1 => 'Special', 0 => '', @@ -44,77 +32,102 @@ class HtmlTest extends MediaWikiTestCase { 100 => 'Custom', 101 => 'Custom_talk', ) ); + + $this->setMwGlobals( array( + 'wgLanguageCode' => $langCode, + 'wgContLang' => $langObj, + 'wgLang' => $langObj, + 'wgHtml5' => true, + 'wgWellFormedXml' => false, + ) ); } - public function tearDown() { - global $wgLang, $wgContLang, $wgLanguageCode, $wgHtml5; + public function testElementBasics() { + global $wgWellFormedXml; - // Restore globals - $wgContLang->setNamespaces( self::$oldNamespaces ); - $wgLang = self::$oldLang; - $wgContLang = self::$oldContLang; - $wgLanguageCode = self::$oldLanguageCode; - $wgHtml5 = self::$oldHTML5; - } + $this->assertEquals( + '', + Html::element( 'img', null, '' ), + 'No close tag for short-tag elements' + ); - /** - * Wrapper to easily set $wgHtml5 = true. - * Original value will be restored after test completion. - * @todo Move to MediaWikiTestCase - */ - public function enableHTML5() { - global $wgHtml5; - $wgHtml5 = true; - } - /** - * Wrapper to easily set $wgHtml5 = false - * Original value will be restored after test completion. - * @todo Move to MediaWikiTestCase - */ - public function disableHTML5() { - global $wgHtml5; - $wgHtml5 = false; + $this->assertEquals( + '', + Html::element( 'element', null, null ), + 'Close tag for empty element (null, null)' + ); + + $this->assertEquals( + '', + Html::element( 'element', array(), '' ), + 'Close tag for empty element (array, string)' + ); + + $wgWellFormedXml = true; + + $this->assertEquals( + '', + Html::element( 'img', null, '' ), + 'Self-closing tag for short-tag elements (wgWellFormedXml = true)' + ); } public function testExpandAttributesSkipsNullAndFalse() { ### EMPTY ######## - $this->AssertEmpty( + $this->assertEmpty( Html::expandAttributes( array( 'foo' => null ) ), 'skip keys with null value' ); - $this->AssertEmpty( + $this->assertEmpty( Html::expandAttributes( array( 'foo' => false ) ), 'skip keys with false value' ); - $this->AssertNotEmpty( + $this->assertNotEmpty( Html::expandAttributes( array( 'foo' => '' ) ), 'keep keys with an empty string' ); } public function testExpandAttributesForBooleans() { - global $wgHtml5; - $this->AssertEquals( + global $wgHtml5, $wgWellFormedXml; + + $this->assertEquals( '', Html::expandAttributes( array( 'selected' => false ) ), 'Boolean attributes do not generates output when value is false' ); - $this->AssertEquals( + $this->assertEquals( '', Html::expandAttributes( array( 'selected' => null ) ), 'Boolean attributes do not generates output when value is null' ); - $this->AssertEquals( - $wgHtml5 ? ' selected=""' : ' selected="selected"', + $this->assertEquals( + ' selected', Html::expandAttributes( array( 'selected' => true ) ), - 'Boolean attributes skip value output' + 'Boolean attributes have no value when value is true' ); - $this->AssertEquals( - $wgHtml5 ? ' selected=""' : ' selected="selected"', + $this->assertEquals( + ' selected', Html::expandAttributes( array( 'selected' ) ), - 'Boolean attributes (ex: selected) do not need a value' + 'Boolean attributes have no value when value is true (passed as numerical array)' + ); + + $wgWellFormedXml = true; + + $this->assertEquals( + ' selected=""', + Html::expandAttributes( array( 'selected' => true ) ), + 'Boolean attributes have empty string value when value is true (wgWellFormedXml)' + ); + + $wgHtml5 = false; + + $this->assertEquals( + ' selected="selected"', + Html::expandAttributes( array( 'selected' => true ) ), + 'Boolean attributes have their key as value when value is true (wgWellFormedXml, wgHTML5 = false)' ); } @@ -123,26 +136,51 @@ class HtmlTest extends MediaWikiTestCase { * Please note it output a string prefixed with a space! */ public function testExpandAttributesVariousExpansions() { + global $wgWellFormedXml; + ### NOT EMPTY #### - $this->AssertEquals( + $this->assertEquals( + ' empty_string=""', + Html::expandAttributes( array( 'empty_string' => '' ) ), + 'Empty string is always quoted' + ); + $this->assertEquals( + ' key=value', + Html::expandAttributes( array( 'key' => 'value' ) ), + 'Simple string value needs no quotes' + ); + $this->assertEquals( + ' one=1', + Html::expandAttributes( array( 'one' => 1 ) ), + 'Number 1 value needs no quotes' + ); + $this->assertEquals( + ' zero=0', + Html::expandAttributes( array( 'zero' => 0 ) ), + 'Number 0 value needs no quotes' + ); + + $wgWellFormedXml = true; + + $this->assertEquals( ' empty_string=""', Html::expandAttributes( array( 'empty_string' => '' ) ), - 'Value with an empty string' + 'Attribtue values are always quoted (wgWellFormedXml): Empty string' ); - $this->AssertEquals( + $this->assertEquals( ' key="value"', Html::expandAttributes( array( 'key' => 'value' ) ), - 'Value is a string' + 'Attribtue values are always quoted (wgWellFormedXml): Simple string' ); - $this->AssertEquals( + $this->assertEquals( ' one="1"', Html::expandAttributes( array( 'one' => 1 ) ), - 'Value is a numeric one' + 'Attribtue values are always quoted (wgWellFormedXml): Number 1' ); - $this->AssertEquals( + $this->assertEquals( ' zero="0"', Html::expandAttributes( array( 'zero' => 0 ) ), - 'Value is a numeric zero' + 'Attribtue values are always quoted (wgWellFormedXml): Number 0' ); } @@ -153,29 +191,29 @@ class HtmlTest extends MediaWikiTestCase { */ public function testExpandAttributesListValueAttributes() { ### STRING VALUES - $this->AssertEquals( + $this->assertEquals( ' class="redundant spaces here"', Html::expandAttributes( array( 'class' => ' redundant spaces here ' ) ), 'Normalization should strip redundant spaces' ); - $this->AssertEquals( + $this->assertEquals( ' class="foo bar"', Html::expandAttributes( array( 'class' => 'foo bar foo bar bar' ) ), 'Normalization should remove duplicates in string-lists' ); ### "EMPTY" ARRAY VALUES - $this->AssertEquals( + $this->assertEquals( ' class=""', Html::expandAttributes( array( 'class' => array() ) ), 'Value with an empty array' ); - $this->AssertEquals( + $this->assertEquals( ' class=""', Html::expandAttributes( array( 'class' => array( null, '', ' ', ' ' ) ) ), 'Array with null, empty string and spaces' ); ### NON-EMPTY ARRAY VALUES - $this->AssertEquals( + $this->assertEquals( ' class="foo bar"', Html::expandAttributes( array( 'class' => array( 'foo', @@ -186,7 +224,7 @@ class HtmlTest extends MediaWikiTestCase { ) ) ), 'Normalization should remove duplicates in the array' ); - $this->AssertEquals( + $this->assertEquals( ' class="foo bar"', Html::expandAttributes( array( 'class' => array( 'foo bar', @@ -239,48 +277,48 @@ class HtmlTest extends MediaWikiTestCase { function testNamespaceSelector() { $this->assertEquals( - '' . "\n" . +'' . "\n" . +'' . "\n" . +'' . "\n" . +'' . "\n" . +'' . "\n" . +'' . "\n" . +'' . "\n" . +'' . "\n" . +'' . "\n" . +'' . "\n" . +'' . "\n" . +'' . "\n" . +'' . "\n" . +'' . "\n" . +'' . "\n" . +'' . "\n" . '', Html::namespaceSelector(), 'Basic namespace selector without custom options' ); $this->assertEquals( - ' ' . -'' . "\n" . +'' . "\n" . +'' . "\n" . +'' . "\n" . +'' . "\n" . +'' . "\n" . +'' . "\n" . +'' . "\n" . +'' . "\n" . +'' . "\n" . +'' . "\n" . +'' . "\n" . +'' . "\n" . +'' . "\n" . +'' . "\n" . +'' . "\n" . +'' . "\n" . +'' . "\n" . '', Html::namespaceSelector( array( 'selected' => '2', 'all' => 'all', 'label' => 'Select a namespace:' ), @@ -290,24 +328,24 @@ class HtmlTest extends MediaWikiTestCase { ); $this->assertEquals( - ' ' . -'' . "\n" . +'' . "\n" . +'' . "\n" . +'' . "\n" . +'' . "\n" . +'' . "\n" . +'' . "\n" . +'' . "\n" . +'' . "\n" . +'' . "\n" . +'' . "\n" . +'' . "\n" . +'' . "\n" . +'' . "\n" . +'' . "\n" . +'' . "\n" . +'' . "\n" . '', Html::namespaceSelector( array( 'label' => 'Select a namespace:' ) @@ -318,18 +356,18 @@ class HtmlTest extends MediaWikiTestCase { function testCanFilterOutNamespaces() { $this->assertEquals( -'' . "\n" . +'' . "\n" . +'' . "\n" . +'' . "\n" . +'' . "\n" . +'' . "\n" . +'' . "\n" . +'' . "\n" . +'' . "\n" . +'' . "\n" . +'' . "\n" . +'' . "\n" . '', Html::namespaceSelector( array( 'exclude' => array( 0, 1, 3, 100, 101 ) ) @@ -340,23 +378,23 @@ class HtmlTest extends MediaWikiTestCase { function testCanDisableANamespaces() { $this->assertEquals( -'' . "\n" . +'' . "\n" . +'' . "\n" . +'' . "\n" . +'' . "\n" . +'' . "\n" . +'' . "\n" . +'' . "\n" . +'' . "\n" . +'' . "\n" . +'' . "\n" . +'' . "\n" . +'' . "\n" . +'' . "\n" . +'' . "\n" . +'' . "\n" . +'' . "\n" . '', Html::namespaceSelector( array( 'disable' => array( 0, 1, 2, 3, 4 ) @@ -366,12 +404,11 @@ class HtmlTest extends MediaWikiTestCase { } /** - * @dataProvider providesHtml5InputTypes + * @dataProvider provideHtml5InputTypes */ function testHtmlElementAcceptsNewHtml5TypesInHtml5Mode( $HTML5InputType ) { - $this->enableHTML5(); $this->assertEquals( - '', + '', Html::element( 'input', array( 'type' => $HTML5InputType ) ), 'In HTML5, HTML::element() should accept type="' . $HTML5InputType . '"' ); @@ -381,7 +418,7 @@ class HtmlTest extends MediaWikiTestCase { * List of input element types values introduced by HTML5 * Full list at http://www.w3.org/TR/html-markup/input.html */ - function providesHtml5InputTypes() { + function provideHtml5InputTypes() { $types = array( 'datetime', 'datetime-local', @@ -409,19 +446,18 @@ class HtmlTest extends MediaWikiTestCase { * @cover Html::dropDefaults * @dataProvider provideElementsWithAttributesHavingDefaultValues */ - function testDropDefaults( $expected, $element, $message = '' ) { - $this->enableHTML5(); - $this->assertEquals( $expected, $element, $message ); + function testDropDefaults( $expected, $element, $attribs, $message = '' ) { + $this->assertEquals( $expected, Html::element( $element, $attribs ), $message ); } - function provideElementsWithAttributesHavingDefaultValues() { + public static function provideElementsWithAttributesHavingDefaultValues() { # Use cases in a concise format: # , , [, ] # Will be mapped to Html::element() $cases = array(); ### Generic cases, match $attribDefault static array - $cases[] = array( '', + $cases[] = array( '', 'area', array( 'shape' => 'rect' ) ); @@ -449,7 +485,7 @@ class HtmlTest extends MediaWikiTestCase { 'canvas', array( 'width' => 300 ) ); - $cases[] = array( '', + $cases[] = array( '', 'command', array( 'type' => 'command' ) ); @@ -463,18 +499,18 @@ class HtmlTest extends MediaWikiTestCase { 'form', array( 'enctype' => 'application/x-www-form-urlencoded' ) ); - $cases[] = array( '', + $cases[] = array( '', 'input', array( 'formaction' => 'GET' ) ); - $cases[] = array( '', + $cases[] = array( '', 'input', array( 'type' => 'text' ) ); - $cases[] = array( '', + $cases[] = array( '', 'keygen', array( 'keytype' => 'rsa' ) ); - $cases[] = array( '', + $cases[] = array( '', 'link', array( 'media' => 'all' ) ); @@ -499,37 +535,37 @@ class HtmlTest extends MediaWikiTestCase { ### SPECIFIC CASES - # - $cases[] = array( '', + # + $cases[] = array( '', 'link', array( 'type' => 'text/css' ) ); - # specific handling - $cases[] = array( '', + # specific handling + $cases[] = array( '', 'input', array( 'type' => 'checkbox', 'value' => 'on' ), 'Default value "on" is stripped of checkboxes', ); - $cases[] = array( '', + $cases[] = array( '', 'input', array( 'type' => 'radio', 'value' => 'on' ), 'Default value "on" is stripped of radio buttons', ); - $cases[] = array( '', + $cases[] = array( '', 'input', array( 'type' => 'submit', 'value' => 'Submit' ), 'Default value "Submit" is kept on submit buttons (for possible l10n issues)', ); - $cases[] = array( '', + $cases[] = array( '', 'input', array( 'type' => 'color', 'value' => '' ), ); - $cases[] = array( '', + $cases[] = array( '', 'input', array( 'type' => 'range', 'value' => '' ), ); - # ', + # ', 'select', array( 'size' => '4', 'multiple' => true ), ); # .. with numeric value - $cases[] = array( '', + $cases[] = array( '', 'select', array( 'size' => 4, 'multiple' => true ), ); $cases[] = array( '', @@ -553,13 +589,13 @@ class HtmlTest extends MediaWikiTestCase { "dropDefaults accepts values given as an array" ); - # Craft the Html elements $ret = array(); foreach( $cases as $case ) { $ret[] = array( $case[0], - Html::element( $case[1], $case[2] ) + $case[1], $case[2], + isset( $case[3] ) ? $case[3] : '' ); } return $ret; diff --git a/tests/phpunit/includes/XmlTest.php b/tests/phpunit/includes/XmlTest.php index 7f25f58147..74c48f212b 100644 --- a/tests/phpunit/includes/XmlTest.php +++ b/tests/phpunit/includes/XmlTest.php @@ -5,16 +5,10 @@ class XmlTest extends MediaWikiTestCase { private static $oldNamespaces; public function setUp() { - global $wgLang, $wgContLang; + parent::setUp(); - self::$oldLang = $wgLang; - $wgLang = Language::factory( 'en' ); - - // Hardcode namespaces during test runs, - // so that html output based on existing namespaces - // can be properly evaluated. - self::$oldNamespaces = $wgContLang->getNamespaces(); - $wgContLang->setNamespaces( array( + $langObj = Language::factory( 'en' ); + $langObj->setNamespaces( array( -2 => 'Media', -1 => 'Special', 0 => '', @@ -32,13 +26,10 @@ class XmlTest extends MediaWikiTestCase { 100 => 'Custom', 101 => 'Custom_talk', ) ); - } - public function tearDown() { - global $wgLang, $wgContLang; - $wgLang = self::$oldLang; - - $wgContLang->setNamespaces( self::$oldNamespaces ); + $this->setMwGlobals( array( + 'wgLang' => $langObj, + ) ); } public function testExpandAttributes() { -- 2.20.1