From: Amir Sarabadani Date: Sat, 13 Jul 2019 20:50:28 +0000 (+0200) Subject: Load GlobalFunctions.php to tests/phpunit/bootstrap.php X-Git-Tag: 1.34.0-rc.0~1007^2 X-Git-Url: https://git.cyclocoop.org/%7B%24www_url%7Dadmin/compta/exercices/journal.php?a=commitdiff_plain;h=06f645c453ebd1a3bbd065abd4f29e909f5656c7;p=lhc%2Fweb%2Fwiklou.git Load GlobalFunctions.php to tests/phpunit/bootstrap.php That mostly enables testing global functions Bug: T87781 Change-Id: Ib42c56a67926ebcdba53f4c6c54a5bff98cb77a3 --- diff --git a/.phpcs.xml b/.phpcs.xml index 8f3bd8c527..9f11ebcf5c 100644 --- a/.phpcs.xml +++ b/.phpcs.xml @@ -179,6 +179,7 @@ */maintenance/storage/recompressTracked\.php */maintenance/storage/trackBlobs\.php + */tests/phpunit/unit/includes/GlobalFunctions/*\.php */tests/phpunit/includes/GlobalFunctions/*\.php */tests/phpunit/maintenance/*\.php */tests/phpunit/integration/includes/GlobalFunctions/*\.php diff --git a/tests/phpunit/bootstrap.php b/tests/phpunit/bootstrap.php index a7f0c09736..4400475bee 100644 --- a/tests/phpunit/bootstrap.php +++ b/tests/phpunit/bootstrap.php @@ -73,6 +73,7 @@ wfRequireOnceInGlobalScope( "$IP/includes/AutoLoader.php" ); wfRequireOnceInGlobalScope( "$IP/tests/common/TestsAutoLoader.php" ); wfRequireOnceInGlobalScope( "$IP/includes/Defines.php" ); wfRequireOnceInGlobalScope( "$IP/includes/DefaultSettings.php" ); +wfRequireOnceInGlobalScope( "$IP/includes/GlobalFunctions.php" ); // Load extensions/skins present in filesystem so that classes can be discovered. $directoryToJsonMap = [ diff --git a/tests/phpunit/includes/GlobalFunctions/wfAppendQueryTest.php b/tests/phpunit/includes/GlobalFunctions/wfAppendQueryTest.php deleted file mode 100644 index bb71610b64..0000000000 --- a/tests/phpunit/includes/GlobalFunctions/wfAppendQueryTest.php +++ /dev/null @@ -1,79 +0,0 @@ -assertEquals( $expected, wfAppendQuery( $url, $query ), $message ); - } - - public static function provideAppendQuery() { - return [ - [ - 'http://www.example.org/index.php', - '', - 'http://www.example.org/index.php', - 'No query' - ], - [ - 'http://www.example.org/index.php', - [ 'foo' => 'bar' ], - 'http://www.example.org/index.php?foo=bar', - 'Set query array' - ], - [ - 'http://www.example.org/index.php?foz=baz', - 'foo=bar', - 'http://www.example.org/index.php?foz=baz&foo=bar', - 'Set query string' - ], - [ - 'http://www.example.org/index.php?foo=bar', - '', - 'http://www.example.org/index.php?foo=bar', - 'Empty string with query' - ], - [ - 'http://www.example.org/index.php?foo=bar', - [ 'baz' => 'quux' ], - 'http://www.example.org/index.php?foo=bar&baz=quux', - 'Add query array' - ], - [ - 'http://www.example.org/index.php?foo=bar', - 'baz=quux', - 'http://www.example.org/index.php?foo=bar&baz=quux', - 'Add query string' - ], - [ - 'http://www.example.org/index.php?foo=bar', - [ 'baz' => 'quux', 'foo' => 'baz' ], - 'http://www.example.org/index.php?foo=bar&baz=quux&foo=baz', - 'Modify query array' - ], - [ - 'http://www.example.org/index.php?foo=bar', - 'baz=quux&foo=baz', - 'http://www.example.org/index.php?foo=bar&baz=quux&foo=baz', - 'Modify query string' - ], - [ - 'http://www.example.org/index.php#baz', - 'foo=bar', - 'http://www.example.org/index.php?foo=bar#baz', - 'URL with fragment' - ], - [ - 'http://www.example.org/index.php?foo=bar#baz', - 'quux=blah', - 'http://www.example.org/index.php?foo=bar&quux=blah#baz', - 'URL with query string and fragment' - ] - ]; - } -} diff --git a/tests/phpunit/includes/GlobalFunctions/wfArrayPlus2dTest.php b/tests/phpunit/includes/GlobalFunctions/wfArrayPlus2dTest.php deleted file mode 100644 index 65b56ef4b3..0000000000 --- a/tests/phpunit/includes/GlobalFunctions/wfArrayPlus2dTest.php +++ /dev/null @@ -1,94 +0,0 @@ -assertEquals( - $expected, - wfArrayPlus2d( $baseArray, $newValues ), - $testName - ); - } - - /** - * Provider for testing wfArrayPlus2d - * - * @return array - */ - public static function provideArrays() { - return [ - // target array, new values array, expected result - [ - [ 0 => '1dArray' ], - [ 1 => '1dArray' ], - [ 0 => '1dArray', 1 => '1dArray' ], - "Test simple union of two arrays with different keys", - ], - [ - [ - 0 => [ 0 => '2dArray' ], - ], - [ - 0 => [ 1 => '2dArray' ], - ], - [ - 0 => [ 0 => '2dArray', 1 => '2dArray' ], - ], - "Test union of 2d arrays with different keys in the value array", - ], - [ - [ - 0 => [ 0 => '2dArray' ], - ], - [ - 0 => [ 0 => '1dArray' ], - ], - [ - 0 => [ 0 => '2dArray' ], - ], - "Test union of 2d arrays with same keys in the value array", - ], - [ - [ - 0 => [ 0 => [ 0 => '3dArray' ] ], - ], - [ - 0 => [ 0 => [ 1 => '2dArray' ] ], - ], - [ - 0 => [ 0 => [ 0 => '3dArray' ] ], - ], - "Test union of 3d array with different keys", - ], - [ - [ - 0 => [ 0 => [ 0 => '3dArray' ] ], - ], - [ - 0 => [ 1 => [ 0 => '2dArray' ] ], - ], - [ - 0 => [ 0 => [ 0 => '3dArray' ], 1 => [ 0 => '2dArray' ] ], - ], - "Test union of 3d array with different keys in the value array", - ], - [ - [ - 0 => [ 0 => [ 0 => '3dArray' ] ], - ], - [ - 0 => [ 0 => [ 0 => '2dArray' ] ], - ], - [ - 0 => [ 0 => [ 0 => '3dArray' ] ], - ], - "Test union of 3d array with same keys in the value array", - ], - ]; - } -} diff --git a/tests/phpunit/includes/GlobalFunctions/wfAssembleUrlTest.php b/tests/phpunit/includes/GlobalFunctions/wfAssembleUrlTest.php deleted file mode 100644 index 7ddad369bd..0000000000 --- a/tests/phpunit/includes/GlobalFunctions/wfAssembleUrlTest.php +++ /dev/null @@ -1,112 +0,0 @@ -assertEquals( - $output, - wfAssembleUrl( $parts ), - "Testing $partsDump assembles to $output" - ); - } - - /** - * Provider of URL parts for testing wfAssembleUrl() - * - * @return array - */ - public static function provideURLParts() { - $schemes = [ - '' => [], - '//' => [ - 'delimiter' => '//', - ], - 'http://' => [ - 'scheme' => 'http', - 'delimiter' => '://', - ], - ]; - - $hosts = [ - '' => [], - 'example.com' => [ - 'host' => 'example.com', - ], - 'example.com:123' => [ - 'host' => 'example.com', - 'port' => 123, - ], - 'id@example.com' => [ - 'user' => 'id', - 'host' => 'example.com', - ], - 'id@example.com:123' => [ - 'user' => 'id', - 'host' => 'example.com', - 'port' => 123, - ], - 'id:key@example.com' => [ - 'user' => 'id', - 'pass' => 'key', - 'host' => 'example.com', - ], - 'id:key@example.com:123' => [ - 'user' => 'id', - 'pass' => 'key', - 'host' => 'example.com', - 'port' => 123, - ], - ]; - - $cases = []; - foreach ( $schemes as $scheme => $schemeParts ) { - foreach ( $hosts as $host => $hostParts ) { - foreach ( [ '', '/path' ] as $path ) { - foreach ( [ '', 'query' ] as $query ) { - foreach ( [ '', 'fragment' ] as $fragment ) { - $parts = array_merge( - $schemeParts, - $hostParts - ); - $url = $scheme . - $host . - $path; - - if ( $path ) { - $parts['path'] = $path; - } - if ( $query ) { - $parts['query'] = $query; - $url .= '?' . $query; - } - if ( $fragment ) { - $parts['fragment'] = $fragment; - $url .= '#' . $fragment; - } - - $cases[] = [ - $parts, - $url, - ]; - } - } - } - } - } - - $complexURL = 'http://id:key@example.org:321' . - '/over/there?name=ferret&foo=bar#nose'; - $cases[] = [ - wfParseUrl( $complexURL ), - $complexURL, - ]; - - return $cases; - } -} diff --git a/tests/phpunit/includes/GlobalFunctions/wfBaseNameTest.php b/tests/phpunit/includes/GlobalFunctions/wfBaseNameTest.php deleted file mode 100644 index 78e09e60ba..0000000000 --- a/tests/phpunit/includes/GlobalFunctions/wfBaseNameTest.php +++ /dev/null @@ -1,40 +0,0 @@ -assertEquals( $basename, wfBaseName( $fullpath ), - "wfBaseName('$fullpath') => '$basename'" ); - } - - public static function providePaths() { - return [ - [ '', '' ], - [ '/', '' ], - [ '\\', '' ], - [ '//', '' ], - [ '\\\\', '' ], - [ 'a', 'a' ], - [ 'aaaa', 'aaaa' ], - [ '/a', 'a' ], - [ '\\a', 'a' ], - [ '/aaaa', 'aaaa' ], - [ '\\aaaa', 'aaaa' ], - [ '/aaaa/', 'aaaa' ], - [ '\\aaaa\\', 'aaaa' ], - [ '\\aaaa\\', 'aaaa' ], - [ - '/mnt/upload3/wikipedia/en/thumb/8/8b/' - . 'Zork_Grand_Inquisitor_box_cover.jpg/93px-Zork_Grand_Inquisitor_box_cover.jpg', - '93px-Zork_Grand_Inquisitor_box_cover.jpg' - ], - [ 'C:\\Progra~1\\Wikime~1\\Wikipe~1\\VIEWER.EXE', 'VIEWER.EXE' ], - [ 'Östergötland_coat_of_arms.png', 'Östergötland_coat_of_arms.png' ], - ]; - } -} diff --git a/tests/phpunit/includes/GlobalFunctions/wfEscapeShellArgTest.php b/tests/phpunit/includes/GlobalFunctions/wfEscapeShellArgTest.php deleted file mode 100644 index 7402054ea4..0000000000 --- a/tests/phpunit/includes/GlobalFunctions/wfEscapeShellArgTest.php +++ /dev/null @@ -1,43 +0,0 @@ -assertEquals( $expected, $actual ); - } - - public function testMultipleArgs() { - if ( wfIsWindows() ) { - $expected = '"foo" "bar" "baz"'; - } else { - $expected = "'foo' 'bar' 'baz'"; - } - - $actual = wfEscapeShellArg( 'foo', 'bar', 'baz' ); - - $this->assertEquals( $expected, $actual ); - } - - public function testMultipleArgsAsArray() { - if ( wfIsWindows() ) { - $expected = '"foo" "bar" "baz"'; - } else { - $expected = "'foo' 'bar' 'baz'"; - } - - $actual = wfEscapeShellArg( [ 'foo', 'bar', 'baz' ] ); - - $this->assertEquals( $expected, $actual ); - } -} diff --git a/tests/phpunit/includes/GlobalFunctions/wfGetCallerTest.php b/tests/phpunit/includes/GlobalFunctions/wfGetCallerTest.php deleted file mode 100644 index 8a7bfa5a8c..0000000000 --- a/tests/phpunit/includes/GlobalFunctions/wfGetCallerTest.php +++ /dev/null @@ -1,46 +0,0 @@ -assertEquals( 'WfGetCallerTest->testZero', wfGetCaller( 1 ) ); - } - - function callerOne() { - return wfGetCaller(); - } - - public function testOne() { - $this->assertEquals( 'WfGetCallerTest->testOne', self::callerOne() ); - } - - static function intermediateFunction( $level = 2, $n = 0 ) { - if ( $n > 0 ) { - return self::intermediateFunction( $level, $n - 1 ); - } - - return wfGetCaller( $level ); - } - - public function testTwo() { - $this->assertEquals( 'WfGetCallerTest->testTwo', self::intermediateFunction() ); - } - - public function testN() { - $this->assertEquals( 'WfGetCallerTest->testN', self::intermediateFunction( 2, 0 ) ); - $this->assertEquals( - 'WfGetCallerTest::intermediateFunction', - self::intermediateFunction( 1, 0 ) - ); - - for ( $i = 0; $i < 10; $i++ ) { - $this->assertEquals( - 'WfGetCallerTest::intermediateFunction', - self::intermediateFunction( $i + 1, $i ) - ); - } - } -} diff --git a/tests/phpunit/includes/GlobalFunctions/wfRemoveDotSegmentsTest.php b/tests/phpunit/includes/GlobalFunctions/wfRemoveDotSegmentsTest.php deleted file mode 100644 index eae5588b94..0000000000 --- a/tests/phpunit/includes/GlobalFunctions/wfRemoveDotSegmentsTest.php +++ /dev/null @@ -1,93 +0,0 @@ -assertEquals( - $outputPath, - wfRemoveDotSegments( $inputPath ), - "Testing $inputPath expands to $outputPath" - ); - } - - /** - * Provider of URL paths for testing wfRemoveDotSegments() - * - * @return array - */ - public static function providePaths() { - return [ - [ '/a/b/c/./../../g', '/a/g' ], - [ 'mid/content=5/../6', 'mid/6' ], - [ '/a//../b', '/a/b' ], - [ '/.../a', '/.../a' ], - [ '.../a', '.../a' ], - [ '', '' ], - [ '/', '/' ], - [ '//', '//' ], - [ '.', '' ], - [ '..', '' ], - [ '...', '...' ], - [ '/.', '/' ], - [ '/..', '/' ], - [ './', '' ], - [ '../', '' ], - [ './a', 'a' ], - [ '../a', 'a' ], - [ '../../a', 'a' ], - [ '.././a', 'a' ], - [ './../a', 'a' ], - [ '././a', 'a' ], - [ '../../', '' ], - [ '.././', '' ], - [ './../', '' ], - [ '././', '' ], - [ '../..', '' ], - [ '../.', '' ], - [ './..', '' ], - [ './.', '' ], - [ '/../../a', '/a' ], - [ '/.././a', '/a' ], - [ '/./../a', '/a' ], - [ '/././a', '/a' ], - [ '/../../', '/' ], - [ '/.././', '/' ], - [ '/./../', '/' ], - [ '/././', '/' ], - [ '/../..', '/' ], - [ '/../.', '/' ], - [ '/./..', '/' ], - [ '/./.', '/' ], - [ 'b/../../a', '/a' ], - [ 'b/.././a', '/a' ], - [ 'b/./../a', '/a' ], - [ 'b/././a', 'b/a' ], - [ 'b/../../', '/' ], - [ 'b/.././', '/' ], - [ 'b/./../', '/' ], - [ 'b/././', 'b/' ], - [ 'b/../..', '/' ], - [ 'b/../.', '/' ], - [ 'b/./..', '/' ], - [ 'b/./.', 'b/' ], - [ '/b/../../a', '/a' ], - [ '/b/.././a', '/a' ], - [ '/b/./../a', '/a' ], - [ '/b/././a', '/b/a' ], - [ '/b/../../', '/' ], - [ '/b/.././', '/' ], - [ '/b/./../', '/' ], - [ '/b/././', '/b/' ], - [ '/b/../..', '/' ], - [ '/b/../.', '/' ], - [ '/b/./..', '/' ], - [ '/b/./.', '/b/' ], - ]; - } -} diff --git a/tests/phpunit/includes/GlobalFunctions/wfShorthandToIntegerTest.php b/tests/phpunit/includes/GlobalFunctions/wfShorthandToIntegerTest.php deleted file mode 100644 index 40b2e636c9..0000000000 --- a/tests/phpunit/includes/GlobalFunctions/wfShorthandToIntegerTest.php +++ /dev/null @@ -1,31 +0,0 @@ -assertEquals( - wfShorthandToInteger( $input ), - $output, - $description - ); - } - - public static function provideABunchOfShorthands() { - return [ - [ '', -1, 'Empty string' ], - [ ' ', -1, 'String of spaces' ], - [ '1G', 1024 * 1024 * 1024, 'One gig uppercased' ], - [ '1g', 1024 * 1024 * 1024, 'One gig lowercased' ], - [ '1M', 1024 * 1024, 'One meg uppercased' ], - [ '1m', 1024 * 1024, 'One meg lowercased' ], - [ '1K', 1024, 'One kb uppercased' ], - [ '1k', 1024, 'One kb lowercased' ], - ]; - } -} diff --git a/tests/phpunit/includes/GlobalFunctions/wfStringToBoolTest.php b/tests/phpunit/includes/GlobalFunctions/wfStringToBoolTest.php deleted file mode 100644 index 7f56b60529..0000000000 --- a/tests/phpunit/includes/GlobalFunctions/wfStringToBoolTest.php +++ /dev/null @@ -1,51 +0,0 @@ -assertTrue( wfStringToBool( $str ) ); - } else { - $this->assertFalse( wfStringToBool( $str ) ); - } - } - -} diff --git a/tests/phpunit/includes/GlobalFunctions/wfTimestampTest.php b/tests/phpunit/includes/GlobalFunctions/wfTimestampTest.php deleted file mode 100644 index a70f136a1c..0000000000 --- a/tests/phpunit/includes/GlobalFunctions/wfTimestampTest.php +++ /dev/null @@ -1,194 +0,0 @@ -assertEquals( $output, wfTimestamp( $format, $input ), $desc ); - } - - public static function provideNormalTimestamps() { - $t = gmmktime( 12, 34, 56, 1, 15, 2001 ); - - return [ - // TS_UNIX - [ $t, TS_MW, '20010115123456', 'TS_UNIX to TS_MW' ], - [ -30281104, TS_MW, '19690115123456', 'Negative TS_UNIX to TS_MW' ], - [ $t, TS_UNIX, 979562096, 'TS_UNIX to TS_UNIX' ], - [ $t, TS_DB, '2001-01-15 12:34:56', 'TS_UNIX to TS_DB' ], - [ $t + 0.01, TS_MW, '20010115123456', 'TS_UNIX float to TS_MW' ], - - [ $t, TS_ISO_8601_BASIC, '20010115T123456Z', 'TS_ISO_8601_BASIC to TS_DB' ], - - // TS_MW - [ '20010115123456', TS_MW, '20010115123456', 'TS_MW to TS_MW' ], - [ '20010115123456', TS_UNIX, 979562096, 'TS_MW to TS_UNIX' ], - [ '20010115123456', TS_DB, '2001-01-15 12:34:56', 'TS_MW to TS_DB' ], - [ '20010115123456', TS_ISO_8601_BASIC, '20010115T123456Z', 'TS_MW to TS_ISO_8601_BASIC' ], - - // TS_DB - [ '2001-01-15 12:34:56', TS_MW, '20010115123456', 'TS_DB to TS_MW' ], - [ '2001-01-15 12:34:56', TS_UNIX, 979562096, 'TS_DB to TS_UNIX' ], - [ '2001-01-15 12:34:56', TS_DB, '2001-01-15 12:34:56', 'TS_DB to TS_DB' ], - [ - '2001-01-15 12:34:56', - TS_ISO_8601_BASIC, - '20010115T123456Z', - 'TS_DB to TS_ISO_8601_BASIC' - ], - - # rfc2822 section 3.3 - [ '20010115123456', TS_RFC2822, 'Mon, 15 Jan 2001 12:34:56 GMT', 'TS_MW to TS_RFC2822' ], - [ 'Mon, 15 Jan 2001 12:34:56 GMT', TS_MW, '20010115123456', 'TS_RFC2822 to TS_MW' ], - [ - ' Mon, 15 Jan 2001 12:34:56 GMT', - TS_MW, - '20010115123456', - 'TS_RFC2822 with leading space to TS_MW' - ], - [ - '15 Jan 2001 12:34:56 GMT', - TS_MW, - '20010115123456', - 'TS_RFC2822 without optional day-of-week to TS_MW' - ], - - # FWS = ([*WSP CRLF] 1*WSP) / obs-FWS ; Folding white space - # obs-FWS = 1*WSP *(CRLF 1*WSP) ; Section 4.2 - [ 'Mon, 15 Jan 2001 12:34:56 GMT', TS_MW, '20010115123456', 'TS_RFC2822 to TS_MW' ], - - # WSP = SP / HTAB ; rfc2234 - [ - "Mon, 15 Jan\x092001 12:34:56 GMT", - TS_MW, - '20010115123456', - 'TS_RFC2822 with HTAB to TS_MW' - ], - [ - "Mon, 15 Jan\x09 \x09 2001 12:34:56 GMT", - TS_MW, - '20010115123456', - 'TS_RFC2822 with HTAB and SP to TS_MW' - ], - [ - 'Sun, 6 Nov 94 08:49:37 GMT', - TS_MW, - '19941106084937', - 'TS_RFC2822 with obsolete year to TS_MW' - ], - ]; - } - - /** - * This test checks wfTimestamp() with values outside. - * It needs PHP 64 bits or PHP > 5.1. - * See r74778 and T27451 - * @dataProvider provideOldTimestamps - */ - public function testOldTimestamps( $input, $outputType, $output, $message ) { - $timestamp = wfTimestamp( $outputType, $input ); - if ( substr( $output, 0, 1 ) === '/' ) { - // T66946: Day of the week calculations for very old - // timestamps varies from system to system. - $this->assertRegExp( $output, $timestamp, $message ); - } else { - $this->assertEquals( $output, $timestamp, $message ); - } - } - - public static function provideOldTimestamps() { - return [ - [ - '19011213204554', - TS_RFC2822, - 'Fri, 13 Dec 1901 20:45:54 GMT', - 'Earliest time according to PHP documentation' - ], - [ '20380119031407', TS_RFC2822, 'Tue, 19 Jan 2038 03:14:07 GMT', 'Latest 32 bit time' ], - [ '19011213204552', TS_UNIX, '-2147483648', 'Earliest 32 bit unix time' ], - [ '20380119031407', TS_UNIX, '2147483647', 'Latest 32 bit unix time' ], - [ '19011213204552', TS_RFC2822, 'Fri, 13 Dec 1901 20:45:52 GMT', 'Earliest 32 bit time' ], - [ - '19011213204551', - TS_RFC2822, - 'Fri, 13 Dec 1901 20:45:51 GMT', 'Earliest 32 bit time - 1' - ], - [ '20380119031408', TS_RFC2822, 'Tue, 19 Jan 2038 03:14:08 GMT', 'Latest 32 bit time + 1' ], - [ '19011212000000', TS_MW, '19011212000000', 'Convert to itself r74778#c10645' ], - [ '19011213204551', TS_UNIX, '-2147483649', 'Earliest 32 bit unix time - 1' ], - [ '20380119031408', TS_UNIX, '2147483648', 'Latest 32 bit unix time + 1' ], - [ '-2147483649', TS_MW, '19011213204551', '1901 negative unix time to MediaWiki' ], - [ '-5331871504', TS_MW, '18010115123456', '1801 negative unix time to MediaWiki' ], - [ - '0117-08-09 12:34:56', - TS_RFC2822, - '/, 09 Aug 0117 12:34:56 GMT$/', - 'Death of Roman Emperor [[Trajan]]' - ], - - /* @todo FIXME: 00 to 101 years are taken as being in [1970-2069] */ - [ '-58979923200', TS_RFC2822, '/, 01 Jan 0101 00:00:00 GMT$/', '1/1/101' ], - [ '-62135596800', TS_RFC2822, 'Mon, 01 Jan 0001 00:00:00 GMT', 'Year 1' ], - - /* It is not clear if we should generate a year 0 or not - * We are completely off RFC2822 requirement of year being - * 1900 or later. - */ - [ - '-62142076800', - TS_RFC2822, - 'Wed, 18 Oct 0000 00:00:00 GMT', - 'ISO 8601:2004 [[year 0]], also called [[1 BC]]' - ], - ]; - } - - /** - * @see http://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html#sec3.3.1 - * @dataProvider provideHttpDates - */ - public function testHttpDate( $input, $output, $desc ) { - $this->assertEquals( $output, wfTimestamp( TS_MW, $input ), $desc ); - } - - public static function provideHttpDates() { - return [ - [ 'Sun, 06 Nov 1994 08:49:37 GMT', '19941106084937', 'RFC 822 date' ], - [ 'Sunday, 06-Nov-94 08:49:37 GMT', '19941106084937', 'RFC 850 date' ], - [ 'Sun Nov 6 08:49:37 1994', '19941106084937', "ANSI C's asctime() format" ], - // See http://www.squid-cache.org/mail-archive/squid-users/200307/0122.html and r77171 - [ - 'Mon, 22 Nov 2010 14:12:42 GMT; length=52626', - '20101122141242', - 'Netscape extension to HTTP/1.0' - ], - ]; - } - - /** - * There are a number of assumptions in our codebase where wfTimestamp() - * should give the current date but it is not given a 0 there. See r71751 CR - */ - public function testTimestampParameter() { - $now = wfTimestamp( TS_UNIX ); - // We check that wfTimestamp doesn't return false (error) and use a LessThan assert - // for the cases where the test is run in a second boundary. - - $zero = wfTimestamp( TS_UNIX, 0 ); - $this->assertNotEquals( false, $zero ); - $this->assertLessThan( 5, $zero - $now ); - - $empty = wfTimestamp( TS_UNIX, '' ); - $this->assertNotEquals( false, $empty ); - $this->assertLessThan( 5, $empty - $now ); - - $null = wfTimestamp( TS_UNIX, null ); - $this->assertNotEquals( false, $null ); - $this->assertLessThan( 5, $null - $now ); - } -} diff --git a/tests/phpunit/includes/GlobalFunctions/wfUrlencodeTest.php b/tests/phpunit/includes/GlobalFunctions/wfUrlencodeTest.php deleted file mode 100644 index f9735c1ab1..0000000000 --- a/tests/phpunit/includes/GlobalFunctions/wfUrlencodeTest.php +++ /dev/null @@ -1,123 +0,0 @@ -verifyEncodingFor( 'Apache', $input, $expected ); - } - - /** - * @dataProvider provideURLS - */ - public function testEncodingUrlWithMicrosoftIis7( $input, $expected ) { - $this->verifyEncodingFor( 'Microsoft-IIS/7', $input, $expected ); - } - - # ### HELPERS ############################################################# - - /** - * Internal helper that actually run the test. - * Called by the public methods testEncodingUrlWith...() - */ - private function verifyEncodingFor( $server, $input, $expectations ) { - $expected = $this->extractExpect( $server, $expectations ); - - // save up global - $old = $_SERVER['SERVER_SOFTWARE'] ?? null; - $_SERVER['SERVER_SOFTWARE'] = $server; - wfUrlencode( null ); - - // do the requested test - $this->assertEquals( - $expected, - wfUrlencode( $input ), - "Encoding '$input' for server '$server' should be '$expected'" - ); - - // restore global - if ( $old === null ) { - unset( $_SERVER['SERVER_SOFTWARE'] ); - } else { - $_SERVER['SERVER_SOFTWARE'] = $old; - } - wfUrlencode( null ); - } - - /** - * Interprets the provider array. Return expected value depending - * the HTTP server name. - */ - private function extractExpect( $server, $expectations ) { - if ( is_string( $expectations ) ) { - return $expectations; - } elseif ( is_array( $expectations ) ) { - if ( !array_key_exists( $server, $expectations ) ) { - throw new MWException( __METHOD__ . " expectation does not have any " - . "value for server name $server. Check the provider array.\n" ); - } else { - return $expectations[$server]; - } - } else { - throw new MWException( __METHOD__ . " given invalid expectation for " - . "'$server'. Should be a string or an array [ => ].\n" ); - } - } - - # ### PROVIDERS ########################################################### - - /** - * Format is either: - * [ 'input', 'expected' ]; - * Or: - * [ 'input', - * [ 'Apache', 'expected' ], - * [ 'Microsoft-IIS/7', 'expected' ], - * ], - * If you want to add other HTTP server name, you will have to add a new - * testing method much like the testEncodingUrlWith() method above. - */ - public static function provideURLS() { - return [ - # ## RFC 1738 chars - // + is not safe - [ '+', '%2B' ], - // & and = not safe in queries - [ '&', '%26' ], - [ '=', '%3D' ], - - [ ':', [ - 'Apache' => ':', - 'Microsoft-IIS/7' => '%3A', - ] ], - - // remaining chars do not need encoding - [ - ';@$-_.!*', - ';@$-_.!*', - ], - - # ## Other tests - // slash remain unchanged. %2F seems to break things - [ '/', '/' ], - // T105265 - [ '~', '~' ], - - // Other 'funnies' chars - [ '[]', '%5B%5D' ], - [ '<>', '%3C%3E' ], - - // Apostrophe is encoded - [ '\'', '%27' ], - ]; - } -} diff --git a/tests/phpunit/includes/PathRouterTest.php b/tests/phpunit/includes/PathRouterTest.php deleted file mode 100644 index d8916751c0..0000000000 --- a/tests/phpunit/includes/PathRouterTest.php +++ /dev/null @@ -1,325 +0,0 @@ -add( "/wiki/$1" ); - $this->basicRouter = $router; - } - - public static function provideParse() { - $tests = [ - // Basic path parsing - 'Basic path parsing' => [ - "/wiki/$1", - "/wiki/Foo", - [ 'title' => "Foo" ] - ], - // - 'Loose path auto-$1: /$1' => [ - "/", - "/Foo", - [ 'title' => "Foo" ] - ], - 'Loose path auto-$1: /wiki' => [ - "/wiki", - "/wiki/Foo", - [ 'title' => "Foo" ] - ], - 'Loose path auto-$1: /wiki/' => [ - "/wiki/", - "/wiki/Foo", - [ 'title' => "Foo" ] - ], - // Ensure that path is based on specificity, not order - 'Order, /$1 added first' => [ - [ "/$1", "/a/$1", "/b/$1" ], - "/a/Foo", - [ 'title' => "Foo" ] - ], - 'Order, /$1 added last' => [ - [ "/b/$1", "/a/$1", "/$1" ], - "/a/Foo", - [ 'title' => "Foo" ] - ], - // Handling of key based arrays with a url parameter - 'Key based array' => [ - [ [ - 'path' => [ 'edit' => "/edit/$1" ], - 'params' => [ 'action' => '$key' ], - ] ], - "/edit/Foo", - [ 'title' => "Foo", 'action' => 'edit' ] - ], - // Additional parameter - 'Basic $2' => [ - [ [ - 'path' => '/$2/$1', - 'params' => [ 'test' => '$2' ] - ] ], - "/asdf/Foo", - [ 'title' => "Foo", 'test' => 'asdf' ] - ], - ]; - // Shared patterns for restricted value parameter tests - $restrictedPatterns = [ - [ - 'path' => '/$2/$1', - 'params' => [ 'test' => '$2' ], - 'options' => [ '$2' => [ 'a', 'b' ] ] - ], - [ - 'path' => '/$2/$1', - 'params' => [ 'test2' => '$2' ], - 'options' => [ '$2' => 'c' ] - ], - '/$1' - ]; - $tests += [ - // Restricted value parameter tests - 'Restricted 1' => [ - $restrictedPatterns, - "/asdf/Foo", - [ 'title' => "asdf/Foo" ] - ], - 'Restricted 2' => [ - $restrictedPatterns, - "/a/Foo", - [ 'title' => "Foo", 'test' => 'a' ] - ], - 'Restricted 3' => [ - $restrictedPatterns, - "/c/Foo", - [ 'title' => "Foo", 'test2' => 'c' ] - ], - - // Callback test - 'Callback' => [ - [ [ - 'path' => "/$1", - 'params' => [ 'a' => 'b', 'data:foo' => 'bar' ], - 'options' => [ 'callback' => [ __CLASS__, 'callbackForTest' ] ] - ] ], - '/Foo', - [ - 'title' => "Foo", - 'x' => 'Foo', - 'a' => 'b', - 'foo' => 'bar' - ] - ], - - // Test to ensure that matches are not made if a parameter expects nonexistent input - 'Fail' => [ - [ [ - 'path' => "/wiki/$1", - 'params' => [ 'title' => "$1$2" ], - ] ], - "/wiki/A", - [] - ], - - // Make sure the router handles titles like Special:Recentchanges correctly - 'Special title' => [ - "/wiki/$1", - "/wiki/Special:Recentchanges", - [ 'title' => "Special:Recentchanges" ] - ], - - // Make sure the router decodes urlencoding properly - 'URL encoding' => [ - "/wiki/$1", - "/wiki/Title_With%20Space", - [ 'title' => "Title_With Space" ] - ], - - // Double slash and dot expansion - 'Double slash in prefix' => [ - '/wiki/$1', - '//wiki/Foo', - [ 'title' => 'Foo' ] - ], - 'Double slash at start of $1' => [ - '/wiki/$1', - '/wiki//Foo', - [ 'title' => '/Foo' ] - ], - 'Double slash in middle of $1' => [ - '/wiki/$1', - '/wiki/.hack//SIGN', - [ 'title' => '.hack//SIGN' ] - ], - 'Dots removed 1' => [ - '/wiki/$1', - '/x/../wiki/Foo', - [ 'title' => 'Foo' ] - ], - 'Dots removed 2' => [ - '/wiki/$1', - '/./wiki/Foo', - [ 'title' => 'Foo' ] - ], - 'Dots retained 1' => [ - '/wiki/$1', - '/wiki/../wiki/Foo', - [ 'title' => '../wiki/Foo' ] - ], - 'Dots retained 2' => [ - '/wiki/$1', - '/wiki/./Foo', - [ 'title' => './Foo' ] - ], - 'Triple slash' => [ - '/wiki/$1', - '///wiki/Foo', - [ 'title' => 'Foo' ] - ], - // '..' only traverses one slash, see e.g. RFC 3986 - 'Dots traversing double slash 1' => [ - '/wiki/$1', - '/a//b/../../wiki/Foo', - [] - ], - 'Dots traversing double slash 2' => [ - '/wiki/$1', - '/a//b/../../../wiki/Foo', - [ 'title' => 'Foo' ] - ], - ]; - - // Make sure the router doesn't break on special characters like $ used in regexp replacements - foreach ( [ "$", "$1", "\\", "\\$1" ] as $char ) { - $tests["Regexp character $char"] = [ - "/wiki/$1", - "/wiki/$char", - [ 'title' => "$char" ] - ]; - } - - $tests += [ - // Make sure the router handles characters like +&() properly - "Special characters" => [ - "/wiki/$1", - "/wiki/Plus+And&Dollar\\Stuff();[]{}*", - [ 'title' => "Plus+And&Dollar\\Stuff();[]{}*" ], - ], - - // Make sure the router handles unicode characters correctly - "Unicode 1" => [ - "/wiki/$1", - "/wiki/Spécial:Modifications_récentes" , - [ 'title' => "Spécial:Modifications_récentes" ], - ], - - "Unicode 2" => [ - "/wiki/$1", - "/wiki/Sp%C3%A9cial:Modifications_r%C3%A9centes", - [ 'title' => "Spécial:Modifications_récentes" ], - ] - ]; - - // Ensure the router doesn't choke on long paths. - $lorem = "Lorem_ipsum_dolor_sit_amet,_consectetur_adipisicing_elit,_sed_do_eiusmod_" . - "tempor_incididunt_ut_labore_et_dolore_magna_aliqua._Ut_enim_ad_minim_veniam,_quis_" . - "nostrud_exercitation_ullamco_laboris_nisi_ut_aliquip_ex_ea_commodo_consequat._" . - "Duis_aute_irure_dolor_in_reprehenderit_in_voluptate_velit_esse_cillum_dolore_" . - "eu_fugiat_nulla_pariatur._Excepteur_sint_occaecat_cupidatat_non_proident,_sunt_" . - "in_culpa_qui_officia_deserunt_mollit_anim_id_est_laborum."; - - $tests += [ - "Long path" => [ - "/wiki/$1", - "/wiki/$lorem", - [ 'title' => $lorem ] - ], - - // Ensure that the php passed site of parameter values are not urldecoded - "Pattern urlencoding" => [ - [ [ 'path' => "/wiki/$1", 'params' => [ 'title' => '%20:$1' ] ] ], - "/wiki/Foo", - [ 'title' => '%20:Foo' ] - ], - - // Ensure that raw parameter values do not have any variable replacements or urldecoding - "Raw param value" => [ - [ [ 'path' => "/wiki/$1", 'params' => [ 'title' => [ 'value' => 'bar%20$1' ] ] ] ], - "/wiki/Foo", - [ 'title' => 'bar%20$1' ] - ] - ]; - - return $tests; - } - - /** - * Test path parsing - * @dataProvider provideParse - */ - public function testParse( $patterns, $path, $expected ) { - $patterns = (array)$patterns; - - $router = new PathRouter; - foreach ( $patterns as $pattern ) { - if ( is_array( $pattern ) ) { - $router->add( $pattern['path'], $pattern['params'] ?? [], - $pattern['options'] ?? [] ); - } else { - $router->add( $pattern ); - } - } - $matches = $router->parse( $path ); - $this->assertEquals( $matches, $expected ); - } - - public static function callbackForTest( &$matches, $data ) { - $matches['x'] = $data['$1']; - $matches['foo'] = $data['foo']; - } - - public static function provideWeight() { - return [ - [ '/Foo', [ 'title' => 'Foo' ] ], - [ '/Bar', [ 'ping' => 'pong' ] ], - [ '/Baz', [ 'marco' => 'polo' ] ], - [ '/asdf-foo', [ 'title' => 'qwerty-foo' ] ], - [ '/qwerty-bar', [ 'title' => 'asdf-bar' ] ], - [ '/a/Foo', [ 'title' => 'Foo' ] ], - [ '/asdf/Foo', [ 'title' => 'Foo' ] ], - [ '/qwerty/Foo', [ 'title' => 'Foo', 'qwerty' => 'qwerty' ] ], - [ '/baz/Foo', [ 'title' => 'Foo', 'unrestricted' => 'baz' ] ], - [ '/y/Foo', [ 'title' => 'Foo', 'restricted-to-y' => 'y' ] ], - ]; - } - - /** - * Test to ensure weight of paths is handled correctly - * @dataProvider provideWeight - */ - public function testWeight( $path, $expected ) { - $router = new PathRouter; - $router->addStrict( "/Bar", [ 'ping' => 'pong' ] ); - $router->add( "/asdf-$1", [ 'title' => 'qwerty-$1' ] ); - $router->add( "/$1" ); - $router->add( "/qwerty-$1", [ 'title' => 'asdf-$1' ] ); - $router->addStrict( "/Baz", [ 'marco' => 'polo' ] ); - $router->add( "/a/$1" ); - $router->add( "/asdf/$1" ); - $router->add( "/$2/$1", [ 'unrestricted' => '$2' ] ); - $router->add( [ 'qwerty' => "/qwerty/$1" ], [ 'qwerty' => '$key' ] ); - $router->add( "/$2/$1", [ 'restricted-to-y' => '$2' ], [ '$2' => 'y' ] ); - - $this->assertEquals( $router->parse( $path ), $expected ); - } -} diff --git a/tests/phpunit/includes/Rest/ResponseFactoryTest.php b/tests/phpunit/includes/Rest/ResponseFactoryTest.php deleted file mode 100644 index ae71272f6b..0000000000 --- a/tests/phpunit/includes/Rest/ResponseFactoryTest.php +++ /dev/null @@ -1,146 +0,0 @@ -assertSame( $expected, $rf->encodeJson( $input ) ); - } - - public function testCreateJson() { - $rf = new ResponseFactory; - $response = $rf->createJson( [] ); - $response->getBody()->rewind(); - $this->assertSame( 'application/json', $response->getHeaderLine( 'Content-Type' ) ); - $this->assertSame( '[]', $response->getBody()->getContents() ); - // Make sure getSize() is functional, since testCreateNoContent() depends on it - $this->assertSame( 2, $response->getBody()->getSize() ); - } - - public function testCreateNoContent() { - $rf = new ResponseFactory; - $response = $rf->createNoContent(); - $this->assertSame( [], $response->getHeader( 'Content-Type' ) ); - $this->assertSame( 0, $response->getBody()->getSize() ); - $this->assertSame( 204, $response->getStatusCode() ); - } - - public function testCreatePermanentRedirect() { - $rf = new ResponseFactory; - $response = $rf->createPermanentRedirect( 'http://www.example.com/' ); - $this->assertSame( [ 'http://www.example.com/' ], $response->getHeader( 'Location' ) ); - $this->assertSame( 301, $response->getStatusCode() ); - } - - public function testCreateLegacyTemporaryRedirect() { - $rf = new ResponseFactory; - $response = $rf->createLegacyTemporaryRedirect( 'http://www.example.com/' ); - $this->assertSame( [ 'http://www.example.com/' ], $response->getHeader( 'Location' ) ); - $this->assertSame( 302, $response->getStatusCode() ); - } - - public function testCreateTemporaryRedirect() { - $rf = new ResponseFactory; - $response = $rf->createTemporaryRedirect( 'http://www.example.com/' ); - $this->assertSame( [ 'http://www.example.com/' ], $response->getHeader( 'Location' ) ); - $this->assertSame( 307, $response->getStatusCode() ); - } - - public function testCreateSeeOther() { - $rf = new ResponseFactory; - $response = $rf->createSeeOther( 'http://www.example.com/' ); - $this->assertSame( [ 'http://www.example.com/' ], $response->getHeader( 'Location' ) ); - $this->assertSame( 303, $response->getStatusCode() ); - } - - public function testCreateNotModified() { - $rf = new ResponseFactory; - $response = $rf->createNotModified(); - $this->assertSame( 0, $response->getBody()->getSize() ); - $this->assertSame( 304, $response->getStatusCode() ); - } - - /** @expectedException \InvalidArgumentException */ - public function testCreateHttpErrorInvalid() { - $rf = new ResponseFactory; - $rf->createHttpError( 200 ); - } - - public function testCreateHttpError() { - $rf = new ResponseFactory; - $response = $rf->createHttpError( 415, [ 'message' => '...' ] ); - $this->assertSame( 415, $response->getStatusCode() ); - $body = $response->getBody(); - $body->rewind(); - $data = json_decode( $body->getContents(), true ); - $this->assertSame( 415, $data['httpCode'] ); - $this->assertSame( '...', $data['message'] ); - } - - public function testCreateFromExceptionUnlogged() { - $rf = new ResponseFactory; - $response = $rf->createFromException( new HttpException( 'hello', 415 ) ); - $this->assertSame( 415, $response->getStatusCode() ); - $body = $response->getBody(); - $body->rewind(); - $data = json_decode( $body->getContents(), true ); - $this->assertSame( 415, $data['httpCode'] ); - $this->assertSame( 'hello', $data['message'] ); - } - - public function testCreateFromExceptionLogged() { - $rf = new ResponseFactory; - $response = $rf->createFromException( new \Exception( "hello", 415 ) ); - $this->assertSame( 500, $response->getStatusCode() ); - $body = $response->getBody(); - $body->rewind(); - $data = json_decode( $body->getContents(), true ); - $this->assertSame( 500, $data['httpCode'] ); - $this->assertSame( 'Error: exception of type Exception', $data['message'] ); - } - - public static function provideCreateFromReturnValue() { - return [ - [ 'hello', '{"value":"hello"}' ], - [ true, '{"value":true}' ], - [ [ 'x' => 'y' ], '{"x":"y"}' ], - [ [ 'x', 'y' ], '["x","y"]' ], - [ [ 'a', 'x' => 'y' ], '{"0":"a","x":"y"}' ], - [ (object)[ 'a', 'x' => 'y' ], '{"0":"a","x":"y"}' ], - [ [], '[]' ], - [ (object)[], '{}' ], - ]; - } - - /** @dataProvider provideCreateFromReturnValue */ - public function testCreateFromReturnValue( $input, $expected ) { - $rf = new ResponseFactory; - $response = $rf->createFromReturnValue( $input ); - $body = $response->getBody(); - $body->rewind(); - $this->assertSame( $expected, $body->getContents() ); - } - - /** @expectedException \InvalidArgumentException */ - public function testCreateFromReturnValueInvalid() { - $rf = new ResponseFactory; - $rf->createFromReturnValue( new ArrayIterator ); - } -} diff --git a/tests/phpunit/includes/Revision/MainSlotRoleHandlerTest.php b/tests/phpunit/includes/Revision/MainSlotRoleHandlerTest.php deleted file mode 100644 index 5e32574d40..0000000000 --- a/tests/phpunit/includes/Revision/MainSlotRoleHandlerTest.php +++ /dev/null @@ -1,79 +0,0 @@ -getMockBuilder( Title::class ) - ->disableOriginalConstructor() - ->getMock(); - - $title->method( 'getNamespace' ) - ->willReturn( $ns ); - - return $title; - } - - /** - * @covers \MediaWiki\Revision\MainSlotRoleHandler::__construct - * @covers \MediaWiki\Revision\MainSlotRoleHandler::getRole() - * @covers \MediaWiki\Revision\MainSlotRoleHandler::getNameMessageKey() - * @covers \MediaWiki\Revision\MainSlotRoleHandler::getOutputLayoutHints() - */ - public function testConstruction() { - $handler = new MainSlotRoleHandler( [] ); - $this->assertSame( 'main', $handler->getRole() ); - $this->assertSame( 'slot-name-main', $handler->getNameMessageKey() ); - - $hints = $handler->getOutputLayoutHints(); - $this->assertArrayHasKey( 'display', $hints ); - $this->assertArrayHasKey( 'region', $hints ); - $this->assertArrayHasKey( 'placement', $hints ); - } - - /** - * @covers \MediaWiki\Revision\MainSlotRoleHandler::getDefaultModel() - */ - public function testFetDefaultModel() { - $handler = new MainSlotRoleHandler( [ 100 => CONTENT_MODEL_TEXT ] ); - - // For the main handler, the namespace determins the default model - $titleMain = $this->makeTitleObject( NS_MAIN ); - $this->assertSame( CONTENT_MODEL_WIKITEXT, $handler->getDefaultModel( $titleMain ) ); - - $title100 = $this->makeTitleObject( 100 ); - $this->assertSame( CONTENT_MODEL_TEXT, $handler->getDefaultModel( $title100 ) ); - } - - /** - * @covers \MediaWiki\Revision\MainSlotRoleHandler::isAllowedModel() - */ - public function testIsAllowedModel() { - $handler = new MainSlotRoleHandler( [] ); - - // For the main handler, (nearly) all models are allowed - $title = $this->makeTitleObject( NS_MAIN ); - $this->assertTrue( $handler->isAllowedModel( CONTENT_MODEL_WIKITEXT, $title ) ); - $this->assertTrue( $handler->isAllowedModel( CONTENT_MODEL_TEXT, $title ) ); - } - - /** - * @covers \MediaWiki\Revision\MainSlotRoleHandler::supportsArticleCount() - */ - public function testSupportsArticleCount() { - $handler = new MainSlotRoleHandler( [] ); - - $this->assertTrue( $handler->supportsArticleCount() ); - } - -} diff --git a/tests/phpunit/includes/Revision/SlotRecordTest.php b/tests/phpunit/includes/Revision/SlotRecordTest.php deleted file mode 100644 index 6495967ff4..0000000000 --- a/tests/phpunit/includes/Revision/SlotRecordTest.php +++ /dev/null @@ -1,416 +0,0 @@ - 1234, - 'slot_content_id' => 33, - 'content_size' => '5', - 'content_sha1' => 'someHash', - 'content_address' => 'tt:456', - 'model_name' => CONTENT_MODEL_WIKITEXT, - 'format_name' => CONTENT_FORMAT_WIKITEXT, - 'slot_revision_id' => '2', - 'slot_origin' => '1', - 'role_name' => 'myRole', - ]; - return (object)$data; - } - - public function testCompleteConstruction() { - $row = $this->makeRow(); - $record = new SlotRecord( $row, new WikitextContent( 'A' ) ); - - $this->assertTrue( $record->hasAddress() ); - $this->assertTrue( $record->hasContentId() ); - $this->assertTrue( $record->hasRevision() ); - $this->assertTrue( $record->isInherited() ); - $this->assertSame( 'A', $record->getContent()->getText() ); - $this->assertSame( 5, $record->getSize() ); - $this->assertSame( 'someHash', $record->getSha1() ); - $this->assertSame( CONTENT_MODEL_WIKITEXT, $record->getModel() ); - $this->assertSame( 2, $record->getRevision() ); - $this->assertSame( 1, $record->getOrigin() ); - $this->assertSame( 'tt:456', $record->getAddress() ); - $this->assertSame( 33, $record->getContentId() ); - $this->assertSame( CONTENT_FORMAT_WIKITEXT, $record->getFormat() ); - $this->assertSame( 'myRole', $record->getRole() ); - } - - public function testConstructionDeferred() { - $row = $this->makeRow( [ - 'content_size' => null, // to be computed - 'content_sha1' => null, // to be computed - 'format_name' => function () { - return CONTENT_FORMAT_WIKITEXT; - }, - 'slot_revision_id' => '2', - 'slot_origin' => '2', - 'slot_content_id' => function () { - return null; - }, - ] ); - - $content = function () { - return new WikitextContent( 'A' ); - }; - - $record = new SlotRecord( $row, $content ); - - $this->assertTrue( $record->hasAddress() ); - $this->assertTrue( $record->hasRevision() ); - $this->assertFalse( $record->hasContentId() ); - $this->assertFalse( $record->isInherited() ); - $this->assertSame( 'A', $record->getContent()->getText() ); - $this->assertSame( 1, $record->getSize() ); - $this->assertNotEmpty( $record->getSha1() ); - $this->assertSame( CONTENT_MODEL_WIKITEXT, $record->getModel() ); - $this->assertSame( 2, $record->getRevision() ); - $this->assertSame( 2, $record->getRevision() ); - $this->assertSame( 'tt:456', $record->getAddress() ); - $this->assertSame( CONTENT_FORMAT_WIKITEXT, $record->getFormat() ); - $this->assertSame( 'myRole', $record->getRole() ); - } - - public function testNewUnsaved() { - $record = SlotRecord::newUnsaved( 'myRole', new WikitextContent( 'A' ) ); - - $this->assertFalse( $record->hasAddress() ); - $this->assertFalse( $record->hasContentId() ); - $this->assertFalse( $record->hasRevision() ); - $this->assertFalse( $record->isInherited() ); - $this->assertFalse( $record->hasOrigin() ); - $this->assertSame( 'A', $record->getContent()->getText() ); - $this->assertSame( 1, $record->getSize() ); - $this->assertNotEmpty( $record->getSha1() ); - $this->assertSame( CONTENT_MODEL_WIKITEXT, $record->getModel() ); - $this->assertSame( 'myRole', $record->getRole() ); - } - - public function provideInvalidConstruction() { - yield 'both null' => [ null, null ]; - yield 'null row' => [ null, new WikitextContent( 'A' ) ]; - yield 'array row' => [ [], new WikitextContent( 'A' ) ]; - yield 'empty row' => [ (object)[], new WikitextContent( 'A' ) ]; - yield 'null content' => [ (object)[], null ]; - } - - /** - * @dataProvider provideInvalidConstruction - */ - public function testInvalidConstruction( $row, $content ) { - $this->setExpectedException( InvalidArgumentException::class ); - new SlotRecord( $row, $content ); - } - - public function testGetContentId_fails() { - $record = SlotRecord::newUnsaved( SlotRecord::MAIN, new WikitextContent( 'A' ) ); - $this->setExpectedException( IncompleteRevisionException::class ); - - $record->getContentId(); - } - - public function testGetAddress_fails() { - $record = SlotRecord::newUnsaved( SlotRecord::MAIN, new WikitextContent( 'A' ) ); - $this->setExpectedException( IncompleteRevisionException::class ); - - $record->getAddress(); - } - - public function provideIncomplete() { - $unsaved = SlotRecord::newUnsaved( SlotRecord::MAIN, new WikitextContent( 'A' ) ); - yield 'unsaved' => [ $unsaved ]; - - $parent = new SlotRecord( $this->makeRow(), new WikitextContent( 'A' ) ); - $inherited = SlotRecord::newInherited( $parent ); - yield 'inherited' => [ $inherited ]; - } - - /** - * @dataProvider provideIncomplete - */ - public function testGetRevision_fails( SlotRecord $record ) { - $record = SlotRecord::newUnsaved( SlotRecord::MAIN, new WikitextContent( 'A' ) ); - $this->setExpectedException( IncompleteRevisionException::class ); - - $record->getRevision(); - } - - /** - * @dataProvider provideIncomplete - */ - public function testGetOrigin_fails( SlotRecord $record ) { - $record = SlotRecord::newUnsaved( SlotRecord::MAIN, new WikitextContent( 'A' ) ); - $this->setExpectedException( IncompleteRevisionException::class ); - - $record->getOrigin(); - } - - public function provideHashStability() { - yield [ '', 'phoiac9h4m842xq45sp7s6u21eteeq1' ]; - yield [ 'Lorem ipsum', 'hcr5u40uxr81d3nx89nvwzclfz6r9c5' ]; - } - - /** - * @dataProvider provideHashStability - */ - public function testHashStability( $text, $hash ) { - // Changing the output of the hash function will break things horribly! - - $this->assertSame( $hash, SlotRecord::base36Sha1( $text ) ); - - $record = SlotRecord::newUnsaved( SlotRecord::MAIN, new WikitextContent( $text ) ); - $this->assertSame( $hash, $record->getSha1() ); - } - - public function testHashComputed() { - $row = $this->makeRow(); - $row->content_sha1 = ''; - - $rec = new SlotRecord( $row, new WikitextContent( 'A' ) ); - $this->assertNotEmpty( $rec->getSha1() ); - } - - public function testNewWithSuppressedContent() { - $input = new SlotRecord( $this->makeRow(), new WikitextContent( 'A' ) ); - $output = SlotRecord::newWithSuppressedContent( $input ); - - $this->setExpectedException( SuppressedDataException::class ); - $output->getContent(); - } - - public function testNewInherited() { - $row = $this->makeRow( [ 'slot_revision_id' => 7, 'slot_origin' => 7 ] ); - $parent = new SlotRecord( $row, new WikitextContent( 'A' ) ); - - // This would happen while doing an edit, before saving revision meta-data. - $inherited = SlotRecord::newInherited( $parent ); - - $this->assertSame( $parent->getContentId(), $inherited->getContentId() ); - $this->assertSame( $parent->getAddress(), $inherited->getAddress() ); - $this->assertSame( $parent->getContent(), $inherited->getContent() ); - $this->assertTrue( $inherited->isInherited() ); - $this->assertTrue( $inherited->hasOrigin() ); - $this->assertFalse( $inherited->hasRevision() ); - - // make sure we didn't mess with the internal state of $parent - $this->assertFalse( $parent->isInherited() ); - $this->assertSame( 7, $parent->getRevision() ); - - // This would happen while doing an edit, after saving the revision meta-data - // and content meta-data. - $saved = SlotRecord::newSaved( - 10, - $inherited->getContentId(), - $inherited->getAddress(), - $inherited - ); - $this->assertSame( $parent->getContentId(), $saved->getContentId() ); - $this->assertSame( $parent->getAddress(), $saved->getAddress() ); - $this->assertSame( $parent->getContent(), $saved->getContent() ); - $this->assertTrue( $saved->isInherited() ); - $this->assertTrue( $saved->hasRevision() ); - $this->assertSame( 10, $saved->getRevision() ); - - // make sure we didn't mess with the internal state of $parent or $inherited - $this->assertSame( 7, $parent->getRevision() ); - $this->assertFalse( $inherited->hasRevision() ); - } - - public function testNewSaved() { - // This would happen while doing an edit, before saving revision meta-data. - $unsaved = SlotRecord::newUnsaved( SlotRecord::MAIN, new WikitextContent( 'A' ) ); - - // This would happen while doing an edit, after saving the revision meta-data - // and content meta-data. - $saved = SlotRecord::newSaved( 10, 20, 'theNewAddress', $unsaved ); - $this->assertFalse( $saved->isInherited() ); - $this->assertTrue( $saved->hasOrigin() ); - $this->assertTrue( $saved->hasRevision() ); - $this->assertTrue( $saved->hasAddress() ); - $this->assertTrue( $saved->hasContentId() ); - $this->assertSame( 'theNewAddress', $saved->getAddress() ); - $this->assertSame( 20, $saved->getContentId() ); - $this->assertSame( 'A', $saved->getContent()->getText() ); - $this->assertSame( 10, $saved->getRevision() ); - $this->assertSame( 10, $saved->getOrigin() ); - - // make sure we didn't mess with the internal state of $unsaved - $this->assertFalse( $unsaved->hasAddress() ); - $this->assertFalse( $unsaved->hasContentId() ); - $this->assertFalse( $unsaved->hasRevision() ); - } - - public function provideNewSaved_LogicException() { - $freshRow = $this->makeRow( [ - 'content_id' => 10, - 'content_address' => 'address:1', - 'slot_origin' => 1, - 'slot_revision_id' => 1, - ] ); - - $freshSlot = new SlotRecord( $freshRow, new WikitextContent( 'A' ) ); - yield 'mismatching address' => [ 1, 10, 'address:BAD', $freshSlot ]; - yield 'mismatching revision' => [ 5, 10, 'address:1', $freshSlot ]; - yield 'mismatching content ID' => [ 1, 17, 'address:1', $freshSlot ]; - - $inheritedRow = $this->makeRow( [ - 'content_id' => null, - 'content_address' => null, - 'slot_origin' => 0, - 'slot_revision_id' => 1, - ] ); - - $inheritedSlot = new SlotRecord( $inheritedRow, new WikitextContent( 'A' ) ); - yield 'inherited, but no address' => [ 1, 10, 'address:2', $inheritedSlot ]; - } - - /** - * @dataProvider provideNewSaved_LogicException - */ - public function testNewSaved_LogicException( - $revisionId, - $contentId, - $contentAddress, - SlotRecord $protoSlot - ) { - $this->setExpectedException( LogicException::class ); - SlotRecord::newSaved( $revisionId, $contentId, $contentAddress, $protoSlot ); - } - - public function provideNewSaved_InvalidArgumentException() { - $unsaved = SlotRecord::newUnsaved( SlotRecord::MAIN, new WikitextContent( 'A' ) ); - - yield 'bad revision id' => [ 'xyzzy', 5, 'address', $unsaved ]; - yield 'bad content id' => [ 7, 'xyzzy', 'address', $unsaved ]; - yield 'bad content address' => [ 7, 5, 77, $unsaved ]; - } - - /** - * @dataProvider provideNewSaved_InvalidArgumentException - */ - public function testNewSaved_InvalidArgumentException( - $revisionId, - $contentId, - $contentAddress, - SlotRecord $protoSlot - ) { - $this->setExpectedException( InvalidArgumentException::class ); - SlotRecord::newSaved( $revisionId, $contentId, $contentAddress, $protoSlot ); - } - - public function provideHasSameContent() { - $fail = function () { - self::fail( 'There should be no need to actually load the content.' ); - }; - - $a100a1 = new SlotRecord( - $this->makeRow( - [ - 'model_name' => 'A', - 'content_size' => 100, - 'content_sha1' => 'hash-a', - 'content_address' => 'xxx:a1', - ] - ), - $fail - ); - $a100a1b = new SlotRecord( - $this->makeRow( - [ - 'model_name' => 'A', - 'content_size' => 100, - 'content_sha1' => 'hash-a', - 'content_address' => 'xxx:a1', - ] - ), - $fail - ); - $a100null = new SlotRecord( - $this->makeRow( - [ - 'model_name' => 'A', - 'content_size' => 100, - 'content_sha1' => 'hash-a', - 'content_address' => null, - ] - ), - $fail - ); - $a100a2 = new SlotRecord( - $this->makeRow( - [ - 'model_name' => 'A', - 'content_size' => 100, - 'content_sha1' => 'hash-a', - 'content_address' => 'xxx:a2', - ] - ), - $fail - ); - $b100a1 = new SlotRecord( - $this->makeRow( - [ - 'model_name' => 'B', - 'content_size' => 100, - 'content_sha1' => 'hash-a', - 'content_address' => 'xxx:a1', - ] - ), - $fail - ); - $a200a1 = new SlotRecord( - $this->makeRow( - [ - 'model_name' => 'A', - 'content_size' => 200, - 'content_sha1' => 'hash-a', - 'content_address' => 'xxx:a2', - ] - ), - $fail - ); - $a100x1 = new SlotRecord( - $this->makeRow( - [ - 'model_name' => 'A', - 'content_size' => 100, - 'content_sha1' => 'hash-x', - 'content_address' => 'xxx:x1', - ] - ), - $fail - ); - - yield 'same instance' => [ $a100a1, $a100a1, true ]; - yield 'no address' => [ $a100a1, $a100null, true ]; - yield 'same address' => [ $a100a1, $a100a1b, true ]; - yield 'different address' => [ $a100a1, $a100a2, true ]; - yield 'different model' => [ $a100a1, $b100a1, false ]; - yield 'different size' => [ $a100a1, $a200a1, false ]; - yield 'different hash' => [ $a100a1, $a100x1, false ]; - } - - /** - * @dataProvider provideHasSameContent - */ - public function testHasSameContent( SlotRecord $a, SlotRecord $b, $sameContent ) { - $this->assertSame( $sameContent, $a->hasSameContent( $b ) ); - $this->assertSame( $sameContent, $b->hasSameContent( $a ) ); - } - -} diff --git a/tests/phpunit/includes/TitleArrayFromResultTest.php b/tests/phpunit/includes/TitleArrayFromResultTest.php deleted file mode 100644 index 32c757101a..0000000000 --- a/tests/phpunit/includes/TitleArrayFromResultTest.php +++ /dev/null @@ -1,117 +0,0 @@ -getMockBuilder( Wikimedia\Rdbms\ResultWrapper::class ) - ->disableOriginalConstructor(); - - $resultWrapper = $resultWrapper->getMock(); - $resultWrapper->expects( $this->atLeastOnce() ) - ->method( 'current' ) - ->will( $this->returnValue( $row ) ); - $resultWrapper->expects( $this->any() ) - ->method( 'numRows' ) - ->will( $this->returnValue( $numRows ) ); - - return $resultWrapper; - } - - private function getRowWithTitle( $namespace = 3, $title = 'foo' ) { - $row = new stdClass(); - $row->page_namespace = $namespace; - $row->page_title = $title; - return $row; - } - - /** - * @covers TitleArrayFromResult::__construct - */ - public function testConstructionWithFalseRow() { - $row = false; - $resultWrapper = $this->getMockResultWrapper( $row ); - - $object = new TitleArrayFromResult( $resultWrapper ); - - $this->assertEquals( $resultWrapper, $object->res ); - $this->assertSame( 0, $object->key ); - $this->assertEquals( $row, $object->current ); - } - - /** - * @covers TitleArrayFromResult::__construct - */ - public function testConstructionWithRow() { - $namespace = 0; - $title = 'foo'; - $row = $this->getRowWithTitle( $namespace, $title ); - $resultWrapper = $this->getMockResultWrapper( $row ); - - $object = new TitleArrayFromResult( $resultWrapper ); - - $this->assertEquals( $resultWrapper, $object->res ); - $this->assertSame( 0, $object->key ); - $this->assertInstanceOf( Title::class, $object->current ); - $this->assertEquals( $namespace, $object->current->mNamespace ); - $this->assertEquals( $title, $object->current->mTextform ); - } - - public static function provideNumberOfRows() { - return [ - [ 0 ], - [ 1 ], - [ 122 ], - ]; - } - - /** - * @dataProvider provideNumberOfRows - * @covers TitleArrayFromResult::count - */ - public function testCountWithVaryingValues( $numRows ) { - $object = new TitleArrayFromResult( $this->getMockResultWrapper( - $this->getRowWithTitle(), - $numRows - ) ); - $this->assertEquals( $numRows, $object->count() ); - } - - /** - * @covers TitleArrayFromResult::current - */ - public function testCurrentAfterConstruction() { - $namespace = 0; - $title = 'foo'; - $row = $this->getRowWithTitle( $namespace, $title ); - $object = new TitleArrayFromResult( $this->getMockResultWrapper( $row ) ); - $this->assertInstanceOf( Title::class, $object->current() ); - $this->assertEquals( $namespace, $object->current->mNamespace ); - $this->assertEquals( $title, $object->current->mTextform ); - } - - public function provideTestValid() { - return [ - [ $this->getRowWithTitle(), true ], - [ false, false ], - ]; - } - - /** - * @dataProvider provideTestValid - * @covers TitleArrayFromResult::valid - */ - public function testValid( $input, $expected ) { - $object = new TitleArrayFromResult( $this->getMockResultWrapper( $input ) ); - $this->assertEquals( $expected, $object->valid() ); - } - - // @todo unit test for key() - // @todo unit test for next() - // @todo unit test for rewind() -} diff --git a/tests/phpunit/includes/WikiReferenceTest.php b/tests/phpunit/includes/WikiReferenceTest.php deleted file mode 100644 index e4b21ce5ac..0000000000 --- a/tests/phpunit/includes/WikiReferenceTest.php +++ /dev/null @@ -1,166 +0,0 @@ - [ 'foo.bar', 'http://foo.bar' ], - 'https' => [ 'foo.bar', 'http://foo.bar' ], - - // apparently, this is the expected behavior - 'invalid' => [ 'purple kittens', 'purple kittens' ], - ]; - } - - /** - * @dataProvider provideGetDisplayName - */ - public function testGetDisplayName( $expected, $canonicalServer ) { - $reference = new WikiReference( $canonicalServer, '/wiki/$1' ); - $this->assertEquals( $expected, $reference->getDisplayName() ); - } - - public function testGetCanonicalServer() { - $reference = new WikiReference( 'https://acme.com', '/wiki/$1', '//acme.com' ); - $this->assertEquals( 'https://acme.com', $reference->getCanonicalServer() ); - } - - public function provideGetCanonicalUrl() { - return [ - 'no fragment' => [ - 'https://acme.com/wiki/Foo', - 'https://acme.com', - '//acme.com', - '/wiki/$1', - 'Foo', - null - ], - 'empty fragment' => [ - 'https://acme.com/wiki/Foo', - 'https://acme.com', - '//acme.com', - '/wiki/$1', - 'Foo', - '' - ], - 'fragment' => [ - 'https://acme.com/wiki/Foo#Bar', - 'https://acme.com', - '//acme.com', - '/wiki/$1', - 'Foo', - 'Bar' - ], - 'double fragment' => [ - 'https://acme.com/wiki/Foo#Bar%23Xus', - 'https://acme.com', - '//acme.com', - '/wiki/$1', - 'Foo', - 'Bar#Xus' - ], - 'escaped fragment' => [ - 'https://acme.com/wiki/Foo%23Bar', - 'https://acme.com', - '//acme.com', - '/wiki/$1', - 'Foo#Bar', - null - ], - 'empty path' => [ - 'https://acme.com/Foo', - 'https://acme.com', - '//acme.com', - '/$1', - 'Foo', - null - ], - ]; - } - - /** - * @dataProvider provideGetCanonicalUrl - */ - public function testGetCanonicalUrl( - $expected, $canonicalServer, $server, $path, $page, $fragmentId - ) { - $reference = new WikiReference( $canonicalServer, $path, $server ); - $this->assertEquals( $expected, $reference->getCanonicalUrl( $page, $fragmentId ) ); - } - - /** - * @dataProvider provideGetCanonicalUrl - * @note getUrl is an alias for getCanonicalUrl - */ - public function testGetUrl( $expected, $canonicalServer, $server, $path, $page, $fragmentId ) { - $reference = new WikiReference( $canonicalServer, $path, $server ); - $this->assertEquals( $expected, $reference->getUrl( $page, $fragmentId ) ); - } - - public function provideGetFullUrl() { - return [ - 'no fragment' => [ - '//acme.com/wiki/Foo', - 'https://acme.com', - '//acme.com', - '/wiki/$1', - 'Foo', - null - ], - 'empty fragment' => [ - '//acme.com/wiki/Foo', - 'https://acme.com', - '//acme.com', - '/wiki/$1', - 'Foo', - '' - ], - 'fragment' => [ - '//acme.com/wiki/Foo#Bar', - 'https://acme.com', - '//acme.com', - '/wiki/$1', - 'Foo', - 'Bar' - ], - 'double fragment' => [ - '//acme.com/wiki/Foo#Bar%23Xus', - 'https://acme.com', - '//acme.com', - '/wiki/$1', - 'Foo', - 'Bar#Xus' - ], - 'escaped fragment' => [ - '//acme.com/wiki/Foo%23Bar', - 'https://acme.com', - '//acme.com', - '/wiki/$1', - 'Foo#Bar', - null - ], - 'empty path' => [ - '//acme.com/Foo', - 'https://acme.com', - '//acme.com', - '/$1', - 'Foo', - null - ], - ]; - } - - /** - * @dataProvider provideGetFullUrl - */ - public function testGetFullUrl( $expected, $canonicalServer, $server, $path, $page, $fragmentId ) { - $reference = new WikiReference( $canonicalServer, $path, $server ); - $this->assertEquals( $expected, $reference->getFullUrl( $page, $fragmentId ) ); - } - -} diff --git a/tests/phpunit/includes/debug/logger/monolog/CeeFormatterTest.php b/tests/phpunit/includes/debug/logger/monolog/CeeFormatterTest.php deleted file mode 100644 index b30c7a4c92..0000000000 --- a/tests/phpunit/includes/debug/logger/monolog/CeeFormatterTest.php +++ /dev/null @@ -1,20 +0,0 @@ - [ 'url' => 1 ], 'context' => [ 'url' => 2 ] ]; - $this->assertSame( - $cee_formatter->format( $record ), - "@cee: " . $ls_formatter->format( $record ) ); - } -} diff --git a/tests/phpunit/includes/diff/DifferenceEngineSlotDiffRendererTest.php b/tests/phpunit/includes/diff/DifferenceEngineSlotDiffRendererTest.php deleted file mode 100644 index fe129b751a..0000000000 --- a/tests/phpunit/includes/diff/DifferenceEngineSlotDiffRendererTest.php +++ /dev/null @@ -1,44 +0,0 @@ -getDiff( $oldContent, $newContent ); - $this->assertEquals( 'xxx|yyy', $diff ); - - $diff = $slotDiffRenderer->getDiff( null, $newContent ); - $this->assertEquals( '|yyy', $diff ); - - $diff = $slotDiffRenderer->getDiff( $oldContent, null ); - $this->assertEquals( 'xxx|', $diff ); - } - - public function testAddModules() { - $output = $this->getMockBuilder( OutputPage::class ) - ->disableOriginalConstructor() - ->setMethods( [ 'addModules' ] ) - ->getMock(); - $output->expects( $this->once() ) - ->method( 'addModules' ) - ->with( 'foo' ); - $differenceEngine = new CustomDifferenceEngine(); - $slotDiffRenderer = new DifferenceEngineSlotDiffRenderer( $differenceEngine ); - $slotDiffRenderer->addModules( $output ); - } - - public function testGetExtraCacheKeys() { - $differenceEngine = new CustomDifferenceEngine(); - $slotDiffRenderer = new DifferenceEngineSlotDiffRenderer( $differenceEngine ); - $extraCacheKeys = $slotDiffRenderer->getExtraCacheKeys(); - $this->assertSame( [ 'foo' ], $extraCacheKeys ); - } - -} diff --git a/tests/phpunit/includes/diff/SlotDiffRendererTest.php b/tests/phpunit/includes/diff/SlotDiffRendererTest.php deleted file mode 100644 index a03280ddb2..0000000000 --- a/tests/phpunit/includes/diff/SlotDiffRendererTest.php +++ /dev/null @@ -1,78 +0,0 @@ -getMockBuilder( SlotDiffRenderer::class ) - ->getMock(); - try { - // __call needs help deciding which parameter to take by reference - call_user_func_array( [ TestingAccessWrapper::newFromObject( $slotDiffRenderer ), - 'normalizeContents' ], [ &$oldContent, &$newContent, $allowedClasses ] ); - $this->assertEquals( $expectedOldContent, $oldContent ); - $this->assertEquals( $expectedNewContent, $newContent ); - } catch ( Exception $e ) { - if ( !$expectedExceptionClass ) { - throw $e; - } - $this->assertInstanceOf( $expectedExceptionClass, $e ); - } - } - - public function provideNormalizeContents() { - return [ - 'both null' => [ null, null, null, null, null, InvalidArgumentException::class ], - 'left null' => [ - null, new WikitextContent( 'abc' ), null, - new WikitextContent( '' ), new WikitextContent( 'abc' ), null, - ], - 'right null' => [ - new WikitextContent( 'def' ), null, null, - new WikitextContent( 'def' ), new WikitextContent( '' ), null, - ], - 'type filter' => [ - new WikitextContent( 'abc' ), new WikitextContent( 'def' ), WikitextContent::class, - new WikitextContent( 'abc' ), new WikitextContent( 'def' ), null, - ], - 'type filter (subclass)' => [ - new WikitextContent( 'abc' ), new WikitextContent( 'def' ), TextContent::class, - new WikitextContent( 'abc' ), new WikitextContent( 'def' ), null, - ], - 'type filter (null)' => [ - new WikitextContent( 'abc' ), null, TextContent::class, - new WikitextContent( 'abc' ), new WikitextContent( '' ), null, - ], - 'type filter failure (left)' => [ - new TextContent( 'abc' ), new WikitextContent( 'def' ), WikitextContent::class, - null, null, ParameterTypeException::class, - ], - 'type filter failure (right)' => [ - new WikitextContent( 'abc' ), new TextContent( 'def' ), WikitextContent::class, - null, null, ParameterTypeException::class, - ], - 'type filter (array syntax)' => [ - new WikitextContent( 'abc' ), new JsonContent( 'def' ), - [ JsonContent::class, WikitextContent::class ], - new WikitextContent( 'abc' ), new JsonContent( 'def' ), null, - ], - 'type filter failure (array syntax)' => [ - new WikitextContent( 'abc' ), new CssContent( 'def' ), - [ JsonContent::class, WikitextContent::class ], - null, null, ParameterTypeException::class, - ], - ]; - } - -} diff --git a/tests/phpunit/includes/filerepo/FileBackendDBRepoWrapperTest.php b/tests/phpunit/includes/filerepo/FileBackendDBRepoWrapperTest.php deleted file mode 100644 index 346be7afa3..0000000000 --- a/tests/phpunit/includes/filerepo/FileBackendDBRepoWrapperTest.php +++ /dev/null @@ -1,140 +0,0 @@ -expects( $dbReadsExpected ) - ->method( 'selectField' ) - ->will( $this->returnValue( $dbReturnValue ) ); - - $newPaths = $wrapperMock->getBackendPaths( [ $originalPath ], $latest ); - - $this->assertEquals( - $expectedBackendPath, - $newPaths[0], - $message ); - } - - public function getBackendPathsProvider() { - $prefix = 'mwstore://' . $this->backendName . '/' . $this->repoName; - $mocksForCaching = $this->getMocks(); - - return [ - [ - $mocksForCaching, - false, - $this->once(), - '96246614d75ba1703bdfd5d7660bb57407aaf5d9', - $prefix . '-public/f/o/foobar.jpg', - $prefix . '-original/9/6/2/96246614d75ba1703bdfd5d7660bb57407aaf5d9', - 'Public path translated correctly', - ], - [ - $mocksForCaching, - false, - $this->never(), - '96246614d75ba1703bdfd5d7660bb57407aaf5d9', - $prefix . '-public/f/o/foobar.jpg', - $prefix . '-original/9/6/2/96246614d75ba1703bdfd5d7660bb57407aaf5d9', - 'LRU cache leveraged', - ], - [ - $this->getMocks(), - true, - $this->once(), - '96246614d75ba1703bdfd5d7660bb57407aaf5d9', - $prefix . '-public/f/o/foobar.jpg', - $prefix . '-original/9/6/2/96246614d75ba1703bdfd5d7660bb57407aaf5d9', - 'Latest obtained', - ], - [ - $this->getMocks(), - true, - $this->never(), - '96246614d75ba1703bdfd5d7660bb57407aaf5d9', - $prefix . '-deleted/f/o/foobar.jpg', - $prefix . '-original/f/o/o/foobar', - 'Deleted path translated correctly', - ], - [ - $this->getMocks(), - true, - $this->once(), - null, - $prefix . '-public/b/a/baz.jpg', - $prefix . '-public/b/a/baz.jpg', - 'Path left untouched if no sha1 can be found', - ], - ]; - } - - /** - * @covers FileBackendDBRepoWrapper::getFileContentsMulti - */ - public function testGetFileContentsMulti() { - list( $dbMock, $backendMock, $wrapperMock ) = $this->getMocks(); - - $sha1Path = 'mwstore://' . $this->backendName . '/' . $this->repoName - . '-original/9/6/2/96246614d75ba1703bdfd5d7660bb57407aaf5d9'; - $filenamePath = 'mwstore://' . $this->backendName . '/' . $this->repoName - . '-public/f/o/foobar.jpg'; - - $dbMock->expects( $this->once() ) - ->method( 'selectField' ) - ->will( $this->returnValue( '96246614d75ba1703bdfd5d7660bb57407aaf5d9' ) ); - - $backendMock->expects( $this->once() ) - ->method( 'getFileContentsMulti' ) - ->will( $this->returnValue( [ $sha1Path => 'foo' ] ) ); - - $result = $wrapperMock->getFileContentsMulti( [ 'srcs' => [ $filenamePath ] ] ); - - $this->assertEquals( - [ $filenamePath => 'foo' ], - $result, - 'File contents paths translated properly' - ); - } - - protected function getMocks() { - $dbMock = $this->getMockBuilder( Wikimedia\Rdbms\IDatabase::class ) - ->disableOriginalClone() - ->disableOriginalConstructor() - ->getMock(); - - $backendMock = $this->getMockBuilder( FSFileBackend::class ) - ->setConstructorArgs( [ [ - 'name' => $this->backendName, - 'wikiId' => wfWikiID() - ] ] ) - ->getMock(); - - $wrapperMock = $this->getMockBuilder( FileBackendDBRepoWrapper::class ) - ->setMethods( [ 'getDB' ] ) - ->setConstructorArgs( [ [ - 'backend' => $backendMock, - 'repoName' => $this->repoName, - 'dbHandleFactory' => null - ] ] ) - ->getMock(); - - $wrapperMock->expects( $this->any() )->method( 'getDB' )->will( $this->returnValue( $dbMock ) ); - - return [ $dbMock, $backendMock, $wrapperMock ]; - } -} diff --git a/tests/phpunit/includes/filerepo/file/ForeignDBFileTest.php b/tests/phpunit/includes/filerepo/file/ForeignDBFileTest.php deleted file mode 100644 index 3c92ecb293..0000000000 --- a/tests/phpunit/includes/filerepo/file/ForeignDBFileTest.php +++ /dev/null @@ -1,16 +0,0 @@ -createMock( LocalRepo::class ); - - $file = ForeignDBFile::newFromTitle( $title, $repoMock ); - - $this->assertInstanceOf( ForeignDBFile::class, $file ); - } -} diff --git a/tests/phpunit/includes/htmlform/HTMLCheckMatrixTest.php b/tests/phpunit/includes/htmlform/HTMLCheckMatrixTest.php deleted file mode 100644 index 05c567df75..0000000000 --- a/tests/phpunit/includes/htmlform/HTMLCheckMatrixTest.php +++ /dev/null @@ -1,104 +0,0 @@ - [ 'r1', 'r2' ], - 'columns' => [ 'c1', 'c2' ], - 'fieldname' => 'test', - ]; - - public function testPlainInstantiation() { - try { - new HTMLCheckMatrix( [] ); - } catch ( MWException $e ) { - $this->assertInstanceOf( HTMLFormFieldRequiredOptionsException::class, $e ); - return; - } - - $this->fail( 'Expected MWException indicating missing parameters but none was thrown.' ); - } - - public function testInstantiationWithMinimumRequiredParameters() { - new HTMLCheckMatrix( self::$defaultOptions ); - $this->assertTrue( true ); // form instantiation must throw exception on failure - } - - public function testValidateCallsUserDefinedValidationCallback() { - $called = false; - $field = new HTMLCheckMatrix( self::$defaultOptions + [ - 'validation-callback' => function () use ( &$called ) { - $called = true; - - return false; - }, - ] ); - $this->assertEquals( false, $this->validate( $field, [] ) ); - $this->assertTrue( $called ); - } - - public function testValidateRequiresArrayInput() { - $field = new HTMLCheckMatrix( self::$defaultOptions ); - $this->assertEquals( false, $this->validate( $field, null ) ); - $this->assertEquals( false, $this->validate( $field, true ) ); - $this->assertEquals( false, $this->validate( $field, 'abc' ) ); - $this->assertEquals( false, $this->validate( $field, new stdClass ) ); - $this->assertEquals( true, $this->validate( $field, [] ) ); - } - - public function testValidateAllowsOnlyKnownTags() { - $field = new HTMLCheckMatrix( self::$defaultOptions ); - $this->assertInstanceOf( Message::class, $this->validate( $field, [ 'foo' ] ) ); - } - - public function testValidateAcceptsPartialTagList() { - $field = new HTMLCheckMatrix( self::$defaultOptions ); - $this->assertTrue( $this->validate( $field, [] ) ); - $this->assertTrue( $this->validate( $field, [ 'c1-r1' ] ) ); - $this->assertTrue( $this->validate( $field, [ 'c1-r1', 'c1-r2', 'c2-r1', 'c2-r2' ] ) ); - } - - /** - * This form object actually has no visibility into what happens later on, but essentially - * if the data submitted by the user passes validate the following is run: - * foreach ( $field->filterDataForSubmit( $data ) as $k => $v ) { - * $user->setOption( $k, $v ); - * } - */ - public function testValuesForcedOnRemainOn() { - $field = new HTMLCheckMatrix( self::$defaultOptions + [ - 'force-options-on' => [ 'c2-r1' ], - ] ); - $expected = [ - 'c1-r1' => false, - 'c1-r2' => false, - 'c2-r1' => true, - 'c2-r2' => false, - ]; - $this->assertEquals( $expected, $field->filterDataForSubmit( [] ) ); - } - - public function testValuesForcedOffRemainOff() { - $field = new HTMLCheckMatrix( self::$defaultOptions + [ - 'force-options-off' => [ 'c1-r2', 'c2-r2' ], - ] ); - $expected = [ - 'c1-r1' => true, - 'c1-r2' => false, - 'c2-r1' => true, - 'c2-r2' => false, - ]; - // array_keys on the result simulates submitting all fields checked - $this->assertEquals( $expected, $field->filterDataForSubmit( array_keys( $expected ) ) ); - } - - protected function validate( HTMLFormField $field, $submitted ) { - return $field->validate( - $submitted, - [ self::$defaultOptions['fieldname'] => $submitted ] - ); - } - -} diff --git a/tests/phpunit/includes/json/FormatJsonTest.php b/tests/phpunit/includes/json/FormatJsonTest.php deleted file mode 100644 index 1a99775833..0000000000 --- a/tests/phpunit/includes/json/FormatJsonTest.php +++ /dev/null @@ -1,82 +0,0 @@ -assertInstanceOf( Status::class, $st ); - if ( $expected === false ) { - $this->assertFalse( $st->isOK(), 'Expected isOK() == false' ); - } else { - $this->assertSame( $expectedGoodStatus, $st->isGood(), - 'Expected isGood() == ' . ( $expectedGoodStatus ? 'true' : 'false' ) - ); - $this->assertTrue( $st->isOK(), 'Expected isOK == true' ); - $val = FormatJson::encode( $st->getValue(), false, FormatJson::ALL_OK ); - $this->assertEquals( $expected, $val ); - } - } - -} diff --git a/tests/phpunit/includes/media/JpegMetadataExtractorTest.php b/tests/phpunit/includes/media/JpegMetadataExtractorTest.php deleted file mode 100644 index c943cef906..0000000000 --- a/tests/phpunit/includes/media/JpegMetadataExtractorTest.php +++ /dev/null @@ -1,128 +0,0 @@ -filePath = __DIR__ . '/../../data/media/'; - } - - /** - * We also use this test to test padding bytes don't - * screw stuff up - * - * @param string $file Filename - * - * @dataProvider provideUtf8Comment - */ - public function testUtf8Comment( $file ) { - $res = JpegMetadataExtractor::segmentSplitter( $this->filePath . $file ); - $this->assertEquals( [ 'UTF-8 JPEG Comment — ¼' ], $res['COM'] ); - } - - public static function provideUtf8Comment() { - return [ - [ 'jpeg-comment-utf.jpg' ], - [ 'jpeg-padding-even.jpg' ], - [ 'jpeg-padding-odd.jpg' ], - ]; - } - - /** The file is iso-8859-1, but it should get auto converted */ - public function testIso88591Comment() { - $res = JpegMetadataExtractor::segmentSplitter( $this->filePath . 'jpeg-comment-iso8859-1.jpg' ); - $this->assertEquals( [ 'ISO-8859-1 JPEG Comment - ¼' ], $res['COM'] ); - } - - /** Comment values that are non-textual (random binary junk) should not be shown. - * The example test file has a comment with a 0x5 byte in it which is a control character - * and considered binary junk for our purposes. - */ - public function testBinaryCommentStripped() { - $res = JpegMetadataExtractor::segmentSplitter( $this->filePath . 'jpeg-comment-binary.jpg' ); - $this->assertEmpty( $res['COM'] ); - } - - /* Very rarely a file can have multiple comments. - * Order of comments is based on order inside the file. - */ - public function testMultipleComment() { - $res = JpegMetadataExtractor::segmentSplitter( $this->filePath . 'jpeg-comment-multiple.jpg' ); - $this->assertEquals( [ 'foo', 'bar' ], $res['COM'] ); - } - - public function testXMPExtraction() { - $res = JpegMetadataExtractor::segmentSplitter( $this->filePath . 'jpeg-xmp-psir.jpg' ); - $expected = file_get_contents( $this->filePath . 'jpeg-xmp-psir.xmp' ); - $this->assertEquals( $expected, $res['XMP'] ); - } - - public function testPSIRExtraction() { - $res = JpegMetadataExtractor::segmentSplitter( $this->filePath . 'jpeg-xmp-psir.jpg' ); - $expected = '50686f746f73686f7020332e30003842494d04040000000' - . '000181c02190004746573741c02190003666f6f1c020000020004'; - $this->assertEquals( $expected, bin2hex( $res['PSIR'][0] ) ); - } - - public function testXMPExtractionAltAppId() { - $res = JpegMetadataExtractor::segmentSplitter( $this->filePath . 'jpeg-xmp-alt.jpg' ); - $expected = file_get_contents( $this->filePath . 'jpeg-xmp-psir.xmp' ); - $this->assertEquals( $expected, $res['XMP'] ); - } - - public function testIPTCHashComparisionNoHash() { - $segments = JpegMetadataExtractor::segmentSplitter( $this->filePath . 'jpeg-xmp-psir.jpg' ); - $res = JpegMetadataExtractor::doPSIR( $segments['PSIR'][0] ); - - $this->assertEquals( 'iptc-no-hash', $res ); - } - - public function testIPTCHashComparisionBadHash() { - $segments = JpegMetadataExtractor::segmentSplitter( $this->filePath . 'jpeg-iptc-bad-hash.jpg' ); - $res = JpegMetadataExtractor::doPSIR( $segments['PSIR'][0] ); - - $this->assertEquals( 'iptc-bad-hash', $res ); - } - - public function testIPTCHashComparisionGoodHash() { - $segments = JpegMetadataExtractor::segmentSplitter( $this->filePath . 'jpeg-iptc-good-hash.jpg' ); - $res = JpegMetadataExtractor::doPSIR( $segments['PSIR'][0] ); - - $this->assertEquals( 'iptc-good-hash', $res ); - } - - public function testExifByteOrder() { - $res = JpegMetadataExtractor::segmentSplitter( $this->filePath . 'exif-user-comment.jpg' ); - $expected = 'BE'; - $this->assertEquals( $expected, $res['byteOrder'] ); - } - - public function testInfiniteRead() { - // test file truncated right after a segment, which previously - // caused an infinite loop looking for the next segment byte. - // Should get past infinite loop and throw in wfUnpack() - $this->setExpectedException( 'MWException' ); - $res = JpegMetadataExtractor::segmentSplitter( $this->filePath . 'jpeg-segment-loop1.jpg' ); - } - - public function testInfiniteRead2() { - // test file truncated after a segment's marker and size, which - // would cause a seek past end of file. Seek past end of file - // doesn't actually fail, but prevents further reading and was - // devolving into the previous case (testInfiniteRead). - $this->setExpectedException( 'MWException' ); - $res = JpegMetadataExtractor::segmentSplitter( $this->filePath . 'jpeg-segment-loop2.jpg' ); - } -} diff --git a/tests/phpunit/includes/page/ArticleTest.php b/tests/phpunit/includes/page/ArticleTest.php deleted file mode 100644 index df4a281701..0000000000 --- a/tests/phpunit/includes/page/ArticleTest.php +++ /dev/null @@ -1,57 +0,0 @@ -title = Title::makeTitle( NS_MAIN, 'SomePage' ); - $this->article = new Article( $this->title ); - } - - /** cleanup title object and its article object */ - protected function tearDown() { - parent::tearDown(); - $this->title = null; - $this->article = null; - } - - /** - * @covers Article::__get - */ - public function testImplementsGetMagic() { - $this->assertEquals( false, $this->article->mLatest, "Article __get magic" ); - } - - /** - * @depends testImplementsGetMagic - * @covers Article::__set - */ - public function testImplementsSetMagic() { - $this->article->mLatest = 2; - $this->assertEquals( 2, $this->article->mLatest, "Article __set magic" ); - } - - /** - * @covers Article::__get - * @covers Article::__set - */ - public function testGetOrSetOnNewProperty() { - $this->article->ext_someNewProperty = 12; - $this->assertEquals( 12, $this->article->ext_someNewProperty, - "Article get/set magic on new field" ); - - $this->article->ext_someNewProperty = -8; - $this->assertEquals( -8, $this->article->ext_someNewProperty, - "Article get/set magic on update to new field" ); - } -} diff --git a/tests/phpunit/includes/session/TokenTest.php b/tests/phpunit/includes/session/TokenTest.php deleted file mode 100644 index 47976527ca..0000000000 --- a/tests/phpunit/includes/session/TokenTest.php +++ /dev/null @@ -1,67 +0,0 @@ -getMockBuilder( Token::class ) - ->setMethods( [ 'toStringAtTimestamp' ] ) - ->setConstructorArgs( [ 'sekret', 'salty', true ] ) - ->getMock(); - $token->expects( $this->any() )->method( 'toStringAtTimestamp' ) - ->will( $this->returnValue( 'faketoken+\\' ) ); - - $this->assertSame( 'faketoken+\\', $token->toString() ); - $this->assertSame( 'faketoken+\\', (string)$token ); - $this->assertTrue( $token->wasNew() ); - - $token = new Token( 'sekret', 'salty', false ); - $this->assertFalse( $token->wasNew() ); - } - - public function testToStringAtTimestamp() { - $token = TestingAccessWrapper::newFromObject( new Token( 'sekret', 'salty', false ) ); - - $this->assertSame( - 'd9ade0c7d4349e9df9094e61c33a5a0d5644fde2+\\', - $token->toStringAtTimestamp( 1447362018 ) - ); - $this->assertSame( - 'ee2f7a2488dea9176c224cfb400d43be5644fdea+\\', - $token->toStringAtTimestamp( 1447362026 ) - ); - } - - public function testGetTimestamp() { - $this->assertSame( - 1447362018, Token::getTimestamp( 'd9ade0c7d4349e9df9094e61c33a5a0d5644fde2+\\' ) - ); - $this->assertSame( - 1447362026, Token::getTimestamp( 'ee2f7a2488dea9176c224cfb400d43be5644fdea+\\' ) - ); - $this->assertNull( Token::getTimestamp( 'ee2f7a2488dea9176c224cfb400d43be5644fdea-\\' ) ); - $this->assertNull( Token::getTimestamp( 'ee2f7a2488dea9176c224cfb400d43be+\\' ) ); - - $this->assertNull( Token::getTimestamp( 'ee2f7a2488dea9x76c224cfb400d43be5644fdea+\\' ) ); - } - - public function testMatch() { - $token = TestingAccessWrapper::newFromObject( new Token( 'sekret', 'salty', false ) ); - - $test = $token->toStringAtTimestamp( time() - 10 ); - $this->assertTrue( $token->match( $test ) ); - $this->assertTrue( $token->match( $test, 12 ) ); - $this->assertFalse( $token->match( $test, 8 ) ); - - $this->assertFalse( $token->match( 'ee2f7a2488dea9176c224cfb400d43be5644fdea-\\' ) ); - } - -} diff --git a/tests/phpunit/includes/shell/FirejailCommandTest.php b/tests/phpunit/includes/shell/FirejailCommandTest.php deleted file mode 100644 index 681c3dcda0..0000000000 --- a/tests/phpunit/includes/shell/FirejailCommandTest.php +++ /dev/null @@ -1,85 +0,0 @@ - - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * - */ - -use MediaWiki\Shell\FirejailCommand; -use MediaWiki\Shell\Shell; -use Wikimedia\TestingAccessWrapper; - -class FirejailCommandTest extends PHPUnit\Framework\TestCase { - - use MediaWikiCoversValidator; - - public function provideBuildFinalCommand() { - global $IP; - // phpcs:ignore Generic.Files.LineLength - $env = "'MW_INCLUDE_STDERR=;MW_CPU_LIMIT=180; MW_CGROUP='\'''\''; MW_MEM_LIMIT=307200; MW_FILE_SIZE_LIMIT=102400; MW_WALL_CLOCK_LIMIT=180; MW_USE_LOG_PIPE=yes'"; - $limit = "/bin/bash '$IP/includes/shell/limit.sh'"; - $profile = "--profile=$IP/includes/shell/firejail.profile"; - $blacklist = '--blacklist=' . realpath( MW_CONFIG_FILE ); - $default = "$blacklist --noroot --seccomp --private-dev"; - return [ - [ - 'No restrictions', - 'ls', 0, "$limit ''\''ls'\''' $env" - ], - [ - 'default restriction', - 'ls', Shell::RESTRICT_DEFAULT, - "$limit 'firejail --quiet $profile $default -- '\''ls'\''' $env" - ], - [ - 'no network', - 'ls', Shell::NO_NETWORK, - "$limit 'firejail --quiet $profile --net=none -- '\''ls'\''' $env" - ], - [ - 'default restriction & no network', - 'ls', Shell::RESTRICT_DEFAULT | Shell::NO_NETWORK, - "$limit 'firejail --quiet $profile $default --net=none -- '\''ls'\''' $env" - ], - [ - 'seccomp', - 'ls', Shell::SECCOMP, - "$limit 'firejail --quiet $profile --seccomp -- '\''ls'\''' $env" - ], - [ - 'seccomp & no execve', - 'ls', Shell::SECCOMP | Shell::NO_EXECVE, - "$limit 'firejail --quiet $profile --shell=none --seccomp=execve -- '\''ls'\''' $env" - ], - ]; - } - - /** - * @covers \MediaWiki\Shell\FirejailCommand::buildFinalCommand() - * @dataProvider provideBuildFinalCommand - */ - public function testBuildFinalCommand( $desc, $params, $flags, $expected ) { - $command = new FirejailCommand( 'firejail' ); - $command - ->params( $params ) - ->restrict( $flags ); - $wrapper = TestingAccessWrapper::newFromObject( $command ); - $output = $wrapper->buildFinalCommand( $wrapper->command ); - $this->assertEquals( $expected, $output[0], $desc ); - } - -} diff --git a/tests/phpunit/includes/site/MediaWikiPageNameNormalizerTest.php b/tests/phpunit/includes/site/MediaWikiPageNameNormalizerTest.php deleted file mode 100644 index 15894a3d9a..0000000000 --- a/tests/phpunit/includes/site/MediaWikiPageNameNormalizerTest.php +++ /dev/null @@ -1,116 +0,0 @@ -assertSame( - $expected, - $normalizer->normalizePageName( $pageName, 'https://www.wikidata.org/w/api.php' ) - ); - } - - public function normalizePageTitleProvider() { - // Response are taken from wikidata and kkwiki using the following API request - // api.php?action=query&prop=info&redirects=1&converttitles=1&format=json&titles=… - return [ - 'universe (Q1)' => [ - 'Q1', - 'Q1', - '{"batchcomplete":"","query":{"pages":{"129":{"pageid":129,"ns":0,' - . '"title":"Q1","contentmodel":"wikibase-item","pagelanguage":"en",' - . '"pagelanguagehtmlcode":"en","pagelanguagedir":"ltr",' - . '"touched":"2016-06-23T05:11:21Z","lastrevid":350004448,"length":58001}}}}' - ], - 'Q404 redirects to Q395' => [ - 'Q395', - 'Q404', - '{"batchcomplete":"","query":{"redirects":[{"from":"Q404","to":"Q395"}],"pages"' - . ':{"601":{"pageid":601,"ns":0,"title":"Q395","contentmodel":"wikibase-item",' - . '"pagelanguage":"en","pagelanguagehtmlcode":"en","pagelanguagedir":"ltr",' - . '"touched":"2016-06-23T08:00:20Z","lastrevid":350021914,"length":60108}}}}' - ], - 'D converted to Д (Latin to Cyrillic) (taken from kkwiki)' => [ - 'Д', - 'D', - '{"batchcomplete":"","query":{"converted":[{"from":"D","to":"\u0414"}],' - . '"pages":{"510541":{"pageid":510541,"ns":0,"title":"\u0414",' - . '"contentmodel":"wikitext","pagelanguage":"kk","pagelanguagehtmlcode":"kk",' - . '"pagelanguagedir":"ltr","touched":"2015-11-22T09:16:18Z",' - . '"lastrevid":2373618,"length":3501}}}}' - ], - 'there is no Q0' => [ - false, - 'Q0', - '{"batchcomplete":"","query":{"pages":{"-1":{"ns":0,"title":"Q0",' - . '"missing":"","contentmodel":"wikibase-item","pagelanguage":"en",' - . '"pagelanguagehtmlcode":"en","pagelanguagedir":"ltr"}}}}' - ], - 'invalid title' => [ - false, - '{{', - '{"batchcomplete":"","query":{"pages":{"-1":{"title":"{{",' - . '"invalidreason":"The requested page title contains invalid ' - . 'characters: \"{\".","invalid":""}}}}' - ], - 'error on get' => [ false, 'ABC', false ] - ]; - } - -} - -/** - * @private - * @see Http - */ -class MediaWikiPageNameNormalizerTestMockHttp extends Http { - - /** - * @var mixed - */ - public static $response; - - public static function get( $url, array $options = [], $caller = __METHOD__ ) { - PHPUnit_Framework_Assert::assertInternalType( 'string', $url ); - PHPUnit_Framework_Assert::assertInternalType( 'string', $caller ); - - return self::$response; - } -} diff --git a/tests/phpunit/includes/utils/ZipDirectoryReaderTest.php b/tests/phpunit/includes/utils/ZipDirectoryReaderTest.php deleted file mode 100644 index a1a3fd7309..0000000000 --- a/tests/phpunit/includes/utils/ZipDirectoryReaderTest.php +++ /dev/null @@ -1,88 +0,0 @@ -zipDir = __DIR__ . '/../../data/zip'; - } - - function zipCallback( $entry ) { - $this->entries[] = $entry; - } - - function readZipAssertError( $file, $error, $assertMessage ) { - $this->entries = []; - $status = ZipDirectoryReader::read( "{$this->zipDir}/$file", [ $this, 'zipCallback' ] ); - $this->assertTrue( $status->hasMessage( $error ), $assertMessage ); - } - - function readZipAssertSuccess( $file, $assertMessage ) { - $this->entries = []; - $status = ZipDirectoryReader::read( "{$this->zipDir}/$file", [ $this, 'zipCallback' ] ); - $this->assertTrue( $status->isOK(), $assertMessage ); - } - - public function testEmpty() { - $this->readZipAssertSuccess( 'empty.zip', 'Empty zip' ); - } - - public function testMultiDisk0() { - $this->readZipAssertError( 'split.zip', 'zip-unsupported', - 'Split zip error' ); - } - - public function testNoSignature() { - $this->readZipAssertError( 'nosig.zip', 'zip-wrong-format', - 'No signature should give "wrong format" error' ); - } - - public function testSimple() { - $this->readZipAssertSuccess( 'class.zip', 'Simple ZIP' ); - $this->assertEquals( $this->entries, [ [ - 'name' => 'Class.class', - 'mtime' => '20010115000000', - 'size' => 1, - ] ] ); - } - - public function testBadCentralEntrySignature() { - $this->readZipAssertError( 'wrong-central-entry-sig.zip', 'zip-bad', - 'Bad central entry error' ); - } - - public function testTrailingBytes() { - // Due to T40432 this is now zip-wrong-format instead of zip-bad - $this->readZipAssertError( 'trail.zip', 'zip-wrong-format', - 'Trailing bytes error' ); - } - - public function testWrongCDStart() { - $this->readZipAssertError( 'wrong-cd-start-disk.zip', 'zip-unsupported', - 'Wrong CD start disk error' ); - } - - public function testCentralDirectoryGap() { - $this->readZipAssertError( 'cd-gap.zip', 'zip-bad', - 'CD gap error' ); - } - - public function testCentralDirectoryTruncated() { - $this->readZipAssertError( 'cd-truncated.zip', 'zip-bad', - 'CD truncated error (should hit unpack() overrun)' ); - } - - public function testLooksLikeZip64() { - $this->readZipAssertError( 'looks-like-zip64.zip', 'zip-unsupported', - 'A file which looks like ZIP64 but isn\'t, should give error' ); - } -} diff --git a/tests/phpunit/unit/includes/GlobalFunctions/wfAppendQueryTest.php b/tests/phpunit/unit/includes/GlobalFunctions/wfAppendQueryTest.php new file mode 100644 index 0000000000..11d44758c6 --- /dev/null +++ b/tests/phpunit/unit/includes/GlobalFunctions/wfAppendQueryTest.php @@ -0,0 +1,79 @@ +assertEquals( $expected, wfAppendQuery( $url, $query ), $message ); + } + + public static function provideAppendQuery() { + return [ + [ + 'http://www.example.org/index.php', + '', + 'http://www.example.org/index.php', + 'No query' + ], + [ + 'http://www.example.org/index.php', + [ 'foo' => 'bar' ], + 'http://www.example.org/index.php?foo=bar', + 'Set query array' + ], + [ + 'http://www.example.org/index.php?foz=baz', + 'foo=bar', + 'http://www.example.org/index.php?foz=baz&foo=bar', + 'Set query string' + ], + [ + 'http://www.example.org/index.php?foo=bar', + '', + 'http://www.example.org/index.php?foo=bar', + 'Empty string with query' + ], + [ + 'http://www.example.org/index.php?foo=bar', + [ 'baz' => 'quux' ], + 'http://www.example.org/index.php?foo=bar&baz=quux', + 'Add query array' + ], + [ + 'http://www.example.org/index.php?foo=bar', + 'baz=quux', + 'http://www.example.org/index.php?foo=bar&baz=quux', + 'Add query string' + ], + [ + 'http://www.example.org/index.php?foo=bar', + [ 'baz' => 'quux', 'foo' => 'baz' ], + 'http://www.example.org/index.php?foo=bar&baz=quux&foo=baz', + 'Modify query array' + ], + [ + 'http://www.example.org/index.php?foo=bar', + 'baz=quux&foo=baz', + 'http://www.example.org/index.php?foo=bar&baz=quux&foo=baz', + 'Modify query string' + ], + [ + 'http://www.example.org/index.php#baz', + 'foo=bar', + 'http://www.example.org/index.php?foo=bar#baz', + 'URL with fragment' + ], + [ + 'http://www.example.org/index.php?foo=bar#baz', + 'quux=blah', + 'http://www.example.org/index.php?foo=bar&quux=blah#baz', + 'URL with query string and fragment' + ] + ]; + } +} diff --git a/tests/phpunit/unit/includes/GlobalFunctions/wfArrayPlus2dTest.php b/tests/phpunit/unit/includes/GlobalFunctions/wfArrayPlus2dTest.php new file mode 100644 index 0000000000..9c3e56c0e8 --- /dev/null +++ b/tests/phpunit/unit/includes/GlobalFunctions/wfArrayPlus2dTest.php @@ -0,0 +1,94 @@ +assertEquals( + $expected, + wfArrayPlus2d( $baseArray, $newValues ), + $testName + ); + } + + /** + * Provider for testing wfArrayPlus2d + * + * @return array + */ + public static function provideArrays() { + return [ + // target array, new values array, expected result + [ + [ 0 => '1dArray' ], + [ 1 => '1dArray' ], + [ 0 => '1dArray', 1 => '1dArray' ], + "Test simple union of two arrays with different keys", + ], + [ + [ + 0 => [ 0 => '2dArray' ], + ], + [ + 0 => [ 1 => '2dArray' ], + ], + [ + 0 => [ 0 => '2dArray', 1 => '2dArray' ], + ], + "Test union of 2d arrays with different keys in the value array", + ], + [ + [ + 0 => [ 0 => '2dArray' ], + ], + [ + 0 => [ 0 => '1dArray' ], + ], + [ + 0 => [ 0 => '2dArray' ], + ], + "Test union of 2d arrays with same keys in the value array", + ], + [ + [ + 0 => [ 0 => [ 0 => '3dArray' ] ], + ], + [ + 0 => [ 0 => [ 1 => '2dArray' ] ], + ], + [ + 0 => [ 0 => [ 0 => '3dArray' ] ], + ], + "Test union of 3d array with different keys", + ], + [ + [ + 0 => [ 0 => [ 0 => '3dArray' ] ], + ], + [ + 0 => [ 1 => [ 0 => '2dArray' ] ], + ], + [ + 0 => [ 0 => [ 0 => '3dArray' ], 1 => [ 0 => '2dArray' ] ], + ], + "Test union of 3d array with different keys in the value array", + ], + [ + [ + 0 => [ 0 => [ 0 => '3dArray' ] ], + ], + [ + 0 => [ 0 => [ 0 => '2dArray' ] ], + ], + [ + 0 => [ 0 => [ 0 => '3dArray' ] ], + ], + "Test union of 3d array with same keys in the value array", + ], + ]; + } +} diff --git a/tests/phpunit/unit/includes/GlobalFunctions/wfAssembleUrlTest.php b/tests/phpunit/unit/includes/GlobalFunctions/wfAssembleUrlTest.php new file mode 100644 index 0000000000..f1f1986e5b --- /dev/null +++ b/tests/phpunit/unit/includes/GlobalFunctions/wfAssembleUrlTest.php @@ -0,0 +1,112 @@ +assertEquals( + $output, + wfAssembleUrl( $parts ), + "Testing $partsDump assembles to $output" + ); + } + + /** + * Provider of URL parts for testing wfAssembleUrl() + * + * @return array + */ + public static function provideURLParts() { + $schemes = [ + '' => [], + '//' => [ + 'delimiter' => '//', + ], + 'http://' => [ + 'scheme' => 'http', + 'delimiter' => '://', + ], + ]; + + $hosts = [ + '' => [], + 'example.com' => [ + 'host' => 'example.com', + ], + 'example.com:123' => [ + 'host' => 'example.com', + 'port' => 123, + ], + 'id@example.com' => [ + 'user' => 'id', + 'host' => 'example.com', + ], + 'id@example.com:123' => [ + 'user' => 'id', + 'host' => 'example.com', + 'port' => 123, + ], + 'id:key@example.com' => [ + 'user' => 'id', + 'pass' => 'key', + 'host' => 'example.com', + ], + 'id:key@example.com:123' => [ + 'user' => 'id', + 'pass' => 'key', + 'host' => 'example.com', + 'port' => 123, + ], + ]; + + $cases = []; + foreach ( $schemes as $scheme => $schemeParts ) { + foreach ( $hosts as $host => $hostParts ) { + foreach ( [ '', '/path' ] as $path ) { + foreach ( [ '', 'query' ] as $query ) { + foreach ( [ '', 'fragment' ] as $fragment ) { + $parts = array_merge( + $schemeParts, + $hostParts + ); + $url = $scheme . + $host . + $path; + + if ( $path ) { + $parts['path'] = $path; + } + if ( $query ) { + $parts['query'] = $query; + $url .= '?' . $query; + } + if ( $fragment ) { + $parts['fragment'] = $fragment; + $url .= '#' . $fragment; + } + + $cases[] = [ + $parts, + $url, + ]; + } + } + } + } + } + + $complexURL = 'http://id:key@example.org:321' . + '/over/there?name=ferret&foo=bar#nose'; + $cases[] = [ + wfParseUrl( $complexURL ), + $complexURL, + ]; + + return $cases; + } +} diff --git a/tests/phpunit/unit/includes/GlobalFunctions/wfBaseNameTest.php b/tests/phpunit/unit/includes/GlobalFunctions/wfBaseNameTest.php new file mode 100644 index 0000000000..4d55fb77af --- /dev/null +++ b/tests/phpunit/unit/includes/GlobalFunctions/wfBaseNameTest.php @@ -0,0 +1,40 @@ +assertEquals( $basename, wfBaseName( $fullpath ), + "wfBaseName('$fullpath') => '$basename'" ); + } + + public static function providePaths() { + return [ + [ '', '' ], + [ '/', '' ], + [ '\\', '' ], + [ '//', '' ], + [ '\\\\', '' ], + [ 'a', 'a' ], + [ 'aaaa', 'aaaa' ], + [ '/a', 'a' ], + [ '\\a', 'a' ], + [ '/aaaa', 'aaaa' ], + [ '\\aaaa', 'aaaa' ], + [ '/aaaa/', 'aaaa' ], + [ '\\aaaa\\', 'aaaa' ], + [ '\\aaaa\\', 'aaaa' ], + [ + '/mnt/upload3/wikipedia/en/thumb/8/8b/' + . 'Zork_Grand_Inquisitor_box_cover.jpg/93px-Zork_Grand_Inquisitor_box_cover.jpg', + '93px-Zork_Grand_Inquisitor_box_cover.jpg' + ], + [ 'C:\\Progra~1\\Wikime~1\\Wikipe~1\\VIEWER.EXE', 'VIEWER.EXE' ], + [ 'Östergötland_coat_of_arms.png', 'Östergötland_coat_of_arms.png' ], + ]; + } +} diff --git a/tests/phpunit/unit/includes/GlobalFunctions/wfEscapeShellArgTest.php b/tests/phpunit/unit/includes/GlobalFunctions/wfEscapeShellArgTest.php new file mode 100644 index 0000000000..d86b3979b5 --- /dev/null +++ b/tests/phpunit/unit/includes/GlobalFunctions/wfEscapeShellArgTest.php @@ -0,0 +1,43 @@ +assertEquals( $expected, $actual ); + } + + public function testMultipleArgs() { + if ( wfIsWindows() ) { + $expected = '"foo" "bar" "baz"'; + } else { + $expected = "'foo' 'bar' 'baz'"; + } + + $actual = wfEscapeShellArg( 'foo', 'bar', 'baz' ); + + $this->assertEquals( $expected, $actual ); + } + + public function testMultipleArgsAsArray() { + if ( wfIsWindows() ) { + $expected = '"foo" "bar" "baz"'; + } else { + $expected = "'foo' 'bar' 'baz'"; + } + + $actual = wfEscapeShellArg( [ 'foo', 'bar', 'baz' ] ); + + $this->assertEquals( $expected, $actual ); + } +} diff --git a/tests/phpunit/unit/includes/GlobalFunctions/wfGetCallerTest.php b/tests/phpunit/unit/includes/GlobalFunctions/wfGetCallerTest.php new file mode 100644 index 0000000000..ae397d532b --- /dev/null +++ b/tests/phpunit/unit/includes/GlobalFunctions/wfGetCallerTest.php @@ -0,0 +1,46 @@ +assertEquals( 'WfGetCallerTest->testZero', wfGetCaller( 1 ) ); + } + + function callerOne() { + return wfGetCaller(); + } + + public function testOne() { + $this->assertEquals( 'WfGetCallerTest->testOne', self::callerOne() ); + } + + static function intermediateFunction( $level = 2, $n = 0 ) { + if ( $n > 0 ) { + return self::intermediateFunction( $level, $n - 1 ); + } + + return wfGetCaller( $level ); + } + + public function testTwo() { + $this->assertEquals( 'WfGetCallerTest->testTwo', self::intermediateFunction() ); + } + + public function testN() { + $this->assertEquals( 'WfGetCallerTest->testN', self::intermediateFunction( 2, 0 ) ); + $this->assertEquals( + 'WfGetCallerTest::intermediateFunction', + self::intermediateFunction( 1, 0 ) + ); + + for ( $i = 0; $i < 10; $i++ ) { + $this->assertEquals( + 'WfGetCallerTest::intermediateFunction', + self::intermediateFunction( $i + 1, $i ) + ); + } + } +} diff --git a/tests/phpunit/unit/includes/GlobalFunctions/wfRemoveDotSegmentsTest.php b/tests/phpunit/unit/includes/GlobalFunctions/wfRemoveDotSegmentsTest.php new file mode 100644 index 0000000000..199fa21eca --- /dev/null +++ b/tests/phpunit/unit/includes/GlobalFunctions/wfRemoveDotSegmentsTest.php @@ -0,0 +1,93 @@ +assertEquals( + $outputPath, + wfRemoveDotSegments( $inputPath ), + "Testing $inputPath expands to $outputPath" + ); + } + + /** + * Provider of URL paths for testing wfRemoveDotSegments() + * + * @return array + */ + public static function providePaths() { + return [ + [ '/a/b/c/./../../g', '/a/g' ], + [ 'mid/content=5/../6', 'mid/6' ], + [ '/a//../b', '/a/b' ], + [ '/.../a', '/.../a' ], + [ '.../a', '.../a' ], + [ '', '' ], + [ '/', '/' ], + [ '//', '//' ], + [ '.', '' ], + [ '..', '' ], + [ '...', '...' ], + [ '/.', '/' ], + [ '/..', '/' ], + [ './', '' ], + [ '../', '' ], + [ './a', 'a' ], + [ '../a', 'a' ], + [ '../../a', 'a' ], + [ '.././a', 'a' ], + [ './../a', 'a' ], + [ '././a', 'a' ], + [ '../../', '' ], + [ '.././', '' ], + [ './../', '' ], + [ '././', '' ], + [ '../..', '' ], + [ '../.', '' ], + [ './..', '' ], + [ './.', '' ], + [ '/../../a', '/a' ], + [ '/.././a', '/a' ], + [ '/./../a', '/a' ], + [ '/././a', '/a' ], + [ '/../../', '/' ], + [ '/.././', '/' ], + [ '/./../', '/' ], + [ '/././', '/' ], + [ '/../..', '/' ], + [ '/../.', '/' ], + [ '/./..', '/' ], + [ '/./.', '/' ], + [ 'b/../../a', '/a' ], + [ 'b/.././a', '/a' ], + [ 'b/./../a', '/a' ], + [ 'b/././a', 'b/a' ], + [ 'b/../../', '/' ], + [ 'b/.././', '/' ], + [ 'b/./../', '/' ], + [ 'b/././', 'b/' ], + [ 'b/../..', '/' ], + [ 'b/../.', '/' ], + [ 'b/./..', '/' ], + [ 'b/./.', 'b/' ], + [ '/b/../../a', '/a' ], + [ '/b/.././a', '/a' ], + [ '/b/./../a', '/a' ], + [ '/b/././a', '/b/a' ], + [ '/b/../../', '/' ], + [ '/b/.././', '/' ], + [ '/b/./../', '/' ], + [ '/b/././', '/b/' ], + [ '/b/../..', '/' ], + [ '/b/../.', '/' ], + [ '/b/./..', '/' ], + [ '/b/./.', '/b/' ], + ]; + } +} diff --git a/tests/phpunit/unit/includes/GlobalFunctions/wfShorthandToIntegerTest.php b/tests/phpunit/unit/includes/GlobalFunctions/wfShorthandToIntegerTest.php new file mode 100644 index 0000000000..c2d1f4ab94 --- /dev/null +++ b/tests/phpunit/unit/includes/GlobalFunctions/wfShorthandToIntegerTest.php @@ -0,0 +1,31 @@ +assertEquals( + wfShorthandToInteger( $input ), + $output, + $description + ); + } + + public static function provideABunchOfShorthands() { + return [ + [ '', -1, 'Empty string' ], + [ ' ', -1, 'String of spaces' ], + [ '1G', 1024 * 1024 * 1024, 'One gig uppercased' ], + [ '1g', 1024 * 1024 * 1024, 'One gig lowercased' ], + [ '1M', 1024 * 1024, 'One meg uppercased' ], + [ '1m', 1024 * 1024, 'One meg lowercased' ], + [ '1K', 1024, 'One kb uppercased' ], + [ '1k', 1024, 'One kb lowercased' ], + ]; + } +} diff --git a/tests/phpunit/unit/includes/GlobalFunctions/wfStringToBoolTest.php b/tests/phpunit/unit/includes/GlobalFunctions/wfStringToBoolTest.php new file mode 100644 index 0000000000..b99d695ffc --- /dev/null +++ b/tests/phpunit/unit/includes/GlobalFunctions/wfStringToBoolTest.php @@ -0,0 +1,51 @@ +assertTrue( wfStringToBool( $str ) ); + } else { + $this->assertFalse( wfStringToBool( $str ) ); + } + } + +} diff --git a/tests/phpunit/unit/includes/GlobalFunctions/wfTimestampTest.php b/tests/phpunit/unit/includes/GlobalFunctions/wfTimestampTest.php new file mode 100644 index 0000000000..94347bd19a --- /dev/null +++ b/tests/phpunit/unit/includes/GlobalFunctions/wfTimestampTest.php @@ -0,0 +1,194 @@ +assertEquals( $output, wfTimestamp( $format, $input ), $desc ); + } + + public static function provideNormalTimestamps() { + $t = gmmktime( 12, 34, 56, 1, 15, 2001 ); + + return [ + // TS_UNIX + [ $t, TS_MW, '20010115123456', 'TS_UNIX to TS_MW' ], + [ -30281104, TS_MW, '19690115123456', 'Negative TS_UNIX to TS_MW' ], + [ $t, TS_UNIX, 979562096, 'TS_UNIX to TS_UNIX' ], + [ $t, TS_DB, '2001-01-15 12:34:56', 'TS_UNIX to TS_DB' ], + [ $t + 0.01, TS_MW, '20010115123456', 'TS_UNIX float to TS_MW' ], + + [ $t, TS_ISO_8601_BASIC, '20010115T123456Z', 'TS_ISO_8601_BASIC to TS_DB' ], + + // TS_MW + [ '20010115123456', TS_MW, '20010115123456', 'TS_MW to TS_MW' ], + [ '20010115123456', TS_UNIX, 979562096, 'TS_MW to TS_UNIX' ], + [ '20010115123456', TS_DB, '2001-01-15 12:34:56', 'TS_MW to TS_DB' ], + [ '20010115123456', TS_ISO_8601_BASIC, '20010115T123456Z', 'TS_MW to TS_ISO_8601_BASIC' ], + + // TS_DB + [ '2001-01-15 12:34:56', TS_MW, '20010115123456', 'TS_DB to TS_MW' ], + [ '2001-01-15 12:34:56', TS_UNIX, 979562096, 'TS_DB to TS_UNIX' ], + [ '2001-01-15 12:34:56', TS_DB, '2001-01-15 12:34:56', 'TS_DB to TS_DB' ], + [ + '2001-01-15 12:34:56', + TS_ISO_8601_BASIC, + '20010115T123456Z', + 'TS_DB to TS_ISO_8601_BASIC' + ], + + # rfc2822 section 3.3 + [ '20010115123456', TS_RFC2822, 'Mon, 15 Jan 2001 12:34:56 GMT', 'TS_MW to TS_RFC2822' ], + [ 'Mon, 15 Jan 2001 12:34:56 GMT', TS_MW, '20010115123456', 'TS_RFC2822 to TS_MW' ], + [ + ' Mon, 15 Jan 2001 12:34:56 GMT', + TS_MW, + '20010115123456', + 'TS_RFC2822 with leading space to TS_MW' + ], + [ + '15 Jan 2001 12:34:56 GMT', + TS_MW, + '20010115123456', + 'TS_RFC2822 without optional day-of-week to TS_MW' + ], + + # FWS = ([*WSP CRLF] 1*WSP) / obs-FWS ; Folding white space + # obs-FWS = 1*WSP *(CRLF 1*WSP) ; Section 4.2 + [ 'Mon, 15 Jan 2001 12:34:56 GMT', TS_MW, '20010115123456', 'TS_RFC2822 to TS_MW' ], + + # WSP = SP / HTAB ; rfc2234 + [ + "Mon, 15 Jan\x092001 12:34:56 GMT", + TS_MW, + '20010115123456', + 'TS_RFC2822 with HTAB to TS_MW' + ], + [ + "Mon, 15 Jan\x09 \x09 2001 12:34:56 GMT", + TS_MW, + '20010115123456', + 'TS_RFC2822 with HTAB and SP to TS_MW' + ], + [ + 'Sun, 6 Nov 94 08:49:37 GMT', + TS_MW, + '19941106084937', + 'TS_RFC2822 with obsolete year to TS_MW' + ], + ]; + } + + /** + * This test checks wfTimestamp() with values outside. + * It needs PHP 64 bits or PHP > 5.1. + * See r74778 and T27451 + * @dataProvider provideOldTimestamps + */ + public function testOldTimestamps( $input, $outputType, $output, $message ) { + $timestamp = wfTimestamp( $outputType, $input ); + if ( substr( $output, 0, 1 ) === '/' ) { + // T66946: Day of the week calculations for very old + // timestamps varies from system to system. + $this->assertRegExp( $output, $timestamp, $message ); + } else { + $this->assertEquals( $output, $timestamp, $message ); + } + } + + public static function provideOldTimestamps() { + return [ + [ + '19011213204554', + TS_RFC2822, + 'Fri, 13 Dec 1901 20:45:54 GMT', + 'Earliest time according to PHP documentation' + ], + [ '20380119031407', TS_RFC2822, 'Tue, 19 Jan 2038 03:14:07 GMT', 'Latest 32 bit time' ], + [ '19011213204552', TS_UNIX, '-2147483648', 'Earliest 32 bit unix time' ], + [ '20380119031407', TS_UNIX, '2147483647', 'Latest 32 bit unix time' ], + [ '19011213204552', TS_RFC2822, 'Fri, 13 Dec 1901 20:45:52 GMT', 'Earliest 32 bit time' ], + [ + '19011213204551', + TS_RFC2822, + 'Fri, 13 Dec 1901 20:45:51 GMT', 'Earliest 32 bit time - 1' + ], + [ '20380119031408', TS_RFC2822, 'Tue, 19 Jan 2038 03:14:08 GMT', 'Latest 32 bit time + 1' ], + [ '19011212000000', TS_MW, '19011212000000', 'Convert to itself r74778#c10645' ], + [ '19011213204551', TS_UNIX, '-2147483649', 'Earliest 32 bit unix time - 1' ], + [ '20380119031408', TS_UNIX, '2147483648', 'Latest 32 bit unix time + 1' ], + [ '-2147483649', TS_MW, '19011213204551', '1901 negative unix time to MediaWiki' ], + [ '-5331871504', TS_MW, '18010115123456', '1801 negative unix time to MediaWiki' ], + [ + '0117-08-09 12:34:56', + TS_RFC2822, + '/, 09 Aug 0117 12:34:56 GMT$/', + 'Death of Roman Emperor [[Trajan]]' + ], + + /* @todo FIXME: 00 to 101 years are taken as being in [1970-2069] */ + [ '-58979923200', TS_RFC2822, '/, 01 Jan 0101 00:00:00 GMT$/', '1/1/101' ], + [ '-62135596800', TS_RFC2822, 'Mon, 01 Jan 0001 00:00:00 GMT', 'Year 1' ], + + /* It is not clear if we should generate a year 0 or not + * We are completely off RFC2822 requirement of year being + * 1900 or later. + */ + [ + '-62142076800', + TS_RFC2822, + 'Wed, 18 Oct 0000 00:00:00 GMT', + 'ISO 8601:2004 [[year 0]], also called [[1 BC]]' + ], + ]; + } + + /** + * @see http://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html#sec3.3.1 + * @dataProvider provideHttpDates + */ + public function testHttpDate( $input, $output, $desc ) { + $this->assertEquals( $output, wfTimestamp( TS_MW, $input ), $desc ); + } + + public static function provideHttpDates() { + return [ + [ 'Sun, 06 Nov 1994 08:49:37 GMT', '19941106084937', 'RFC 822 date' ], + [ 'Sunday, 06-Nov-94 08:49:37 GMT', '19941106084937', 'RFC 850 date' ], + [ 'Sun Nov 6 08:49:37 1994', '19941106084937', "ANSI C's asctime() format" ], + // See http://www.squid-cache.org/mail-archive/squid-users/200307/0122.html and r77171 + [ + 'Mon, 22 Nov 2010 14:12:42 GMT; length=52626', + '20101122141242', + 'Netscape extension to HTTP/1.0' + ], + ]; + } + + /** + * There are a number of assumptions in our codebase where wfTimestamp() + * should give the current date but it is not given a 0 there. See r71751 CR + */ + public function testTimestampParameter() { + $now = wfTimestamp( TS_UNIX ); + // We check that wfTimestamp doesn't return false (error) and use a LessThan assert + // for the cases where the test is run in a second boundary. + + $zero = wfTimestamp( TS_UNIX, 0 ); + $this->assertNotEquals( false, $zero ); + $this->assertLessThan( 5, $zero - $now ); + + $empty = wfTimestamp( TS_UNIX, '' ); + $this->assertNotEquals( false, $empty ); + $this->assertLessThan( 5, $empty - $now ); + + $null = wfTimestamp( TS_UNIX, null ); + $this->assertNotEquals( false, $null ); + $this->assertLessThan( 5, $null - $now ); + } +} diff --git a/tests/phpunit/unit/includes/GlobalFunctions/wfUrlencodeTest.php b/tests/phpunit/unit/includes/GlobalFunctions/wfUrlencodeTest.php new file mode 100644 index 0000000000..2fc038c268 --- /dev/null +++ b/tests/phpunit/unit/includes/GlobalFunctions/wfUrlencodeTest.php @@ -0,0 +1,123 @@ +verifyEncodingFor( 'Apache', $input, $expected ); + } + + /** + * @dataProvider provideURLS + */ + public function testEncodingUrlWithMicrosoftIis7( $input, $expected ) { + $this->verifyEncodingFor( 'Microsoft-IIS/7', $input, $expected ); + } + + # ### HELPERS ############################################################# + + /** + * Internal helper that actually run the test. + * Called by the public methods testEncodingUrlWith...() + */ + private function verifyEncodingFor( $server, $input, $expectations ) { + $expected = $this->extractExpect( $server, $expectations ); + + // save up global + $old = $_SERVER['SERVER_SOFTWARE'] ?? null; + $_SERVER['SERVER_SOFTWARE'] = $server; + wfUrlencode( null ); + + // do the requested test + $this->assertEquals( + $expected, + wfUrlencode( $input ), + "Encoding '$input' for server '$server' should be '$expected'" + ); + + // restore global + if ( $old === null ) { + unset( $_SERVER['SERVER_SOFTWARE'] ); + } else { + $_SERVER['SERVER_SOFTWARE'] = $old; + } + wfUrlencode( null ); + } + + /** + * Interprets the provider array. Return expected value depending + * the HTTP server name. + */ + private function extractExpect( $server, $expectations ) { + if ( is_string( $expectations ) ) { + return $expectations; + } elseif ( is_array( $expectations ) ) { + if ( !array_key_exists( $server, $expectations ) ) { + throw new MWException( __METHOD__ . " expectation does not have any " + . "value for server name $server. Check the provider array.\n" ); + } else { + return $expectations[$server]; + } + } else { + throw new MWException( __METHOD__ . " given invalid expectation for " + . "'$server'. Should be a string or an array [ => ].\n" ); + } + } + + # ### PROVIDERS ########################################################### + + /** + * Format is either: + * [ 'input', 'expected' ]; + * Or: + * [ 'input', + * [ 'Apache', 'expected' ], + * [ 'Microsoft-IIS/7', 'expected' ], + * ], + * If you want to add other HTTP server name, you will have to add a new + * testing method much like the testEncodingUrlWith() method above. + */ + public static function provideURLS() { + return [ + # ## RFC 1738 chars + // + is not safe + [ '+', '%2B' ], + // & and = not safe in queries + [ '&', '%26' ], + [ '=', '%3D' ], + + [ ':', [ + 'Apache' => ':', + 'Microsoft-IIS/7' => '%3A', + ] ], + + // remaining chars do not need encoding + [ + ';@$-_.!*', + ';@$-_.!*', + ], + + # ## Other tests + // slash remain unchanged. %2F seems to break things + [ '/', '/' ], + // T105265 + [ '~', '~' ], + + // Other 'funnies' chars + [ '[]', '%5B%5D' ], + [ '<>', '%3C%3E' ], + + // Apostrophe is encoded + [ '\'', '%27' ], + ]; + } +} diff --git a/tests/phpunit/unit/includes/PathRouterTest.php b/tests/phpunit/unit/includes/PathRouterTest.php new file mode 100644 index 0000000000..fb23706150 --- /dev/null +++ b/tests/phpunit/unit/includes/PathRouterTest.php @@ -0,0 +1,325 @@ +add( "/wiki/$1" ); + $this->basicRouter = $router; + } + + public static function provideParse() { + $tests = [ + // Basic path parsing + 'Basic path parsing' => [ + "/wiki/$1", + "/wiki/Foo", + [ 'title' => "Foo" ] + ], + // + 'Loose path auto-$1: /$1' => [ + "/", + "/Foo", + [ 'title' => "Foo" ] + ], + 'Loose path auto-$1: /wiki' => [ + "/wiki", + "/wiki/Foo", + [ 'title' => "Foo" ] + ], + 'Loose path auto-$1: /wiki/' => [ + "/wiki/", + "/wiki/Foo", + [ 'title' => "Foo" ] + ], + // Ensure that path is based on specificity, not order + 'Order, /$1 added first' => [ + [ "/$1", "/a/$1", "/b/$1" ], + "/a/Foo", + [ 'title' => "Foo" ] + ], + 'Order, /$1 added last' => [ + [ "/b/$1", "/a/$1", "/$1" ], + "/a/Foo", + [ 'title' => "Foo" ] + ], + // Handling of key based arrays with a url parameter + 'Key based array' => [ + [ [ + 'path' => [ 'edit' => "/edit/$1" ], + 'params' => [ 'action' => '$key' ], + ] ], + "/edit/Foo", + [ 'title' => "Foo", 'action' => 'edit' ] + ], + // Additional parameter + 'Basic $2' => [ + [ [ + 'path' => '/$2/$1', + 'params' => [ 'test' => '$2' ] + ] ], + "/asdf/Foo", + [ 'title' => "Foo", 'test' => 'asdf' ] + ], + ]; + // Shared patterns for restricted value parameter tests + $restrictedPatterns = [ + [ + 'path' => '/$2/$1', + 'params' => [ 'test' => '$2' ], + 'options' => [ '$2' => [ 'a', 'b' ] ] + ], + [ + 'path' => '/$2/$1', + 'params' => [ 'test2' => '$2' ], + 'options' => [ '$2' => 'c' ] + ], + '/$1' + ]; + $tests += [ + // Restricted value parameter tests + 'Restricted 1' => [ + $restrictedPatterns, + "/asdf/Foo", + [ 'title' => "asdf/Foo" ] + ], + 'Restricted 2' => [ + $restrictedPatterns, + "/a/Foo", + [ 'title' => "Foo", 'test' => 'a' ] + ], + 'Restricted 3' => [ + $restrictedPatterns, + "/c/Foo", + [ 'title' => "Foo", 'test2' => 'c' ] + ], + + // Callback test + 'Callback' => [ + [ [ + 'path' => "/$1", + 'params' => [ 'a' => 'b', 'data:foo' => 'bar' ], + 'options' => [ 'callback' => [ __CLASS__, 'callbackForTest' ] ] + ] ], + '/Foo', + [ + 'title' => "Foo", + 'x' => 'Foo', + 'a' => 'b', + 'foo' => 'bar' + ] + ], + + // Test to ensure that matches are not made if a parameter expects nonexistent input + 'Fail' => [ + [ [ + 'path' => "/wiki/$1", + 'params' => [ 'title' => "$1$2" ], + ] ], + "/wiki/A", + [] + ], + + // Make sure the router handles titles like Special:Recentchanges correctly + 'Special title' => [ + "/wiki/$1", + "/wiki/Special:Recentchanges", + [ 'title' => "Special:Recentchanges" ] + ], + + // Make sure the router decodes urlencoding properly + 'URL encoding' => [ + "/wiki/$1", + "/wiki/Title_With%20Space", + [ 'title' => "Title_With Space" ] + ], + + // Double slash and dot expansion + 'Double slash in prefix' => [ + '/wiki/$1', + '//wiki/Foo', + [ 'title' => 'Foo' ] + ], + 'Double slash at start of $1' => [ + '/wiki/$1', + '/wiki//Foo', + [ 'title' => '/Foo' ] + ], + 'Double slash in middle of $1' => [ + '/wiki/$1', + '/wiki/.hack//SIGN', + [ 'title' => '.hack//SIGN' ] + ], + 'Dots removed 1' => [ + '/wiki/$1', + '/x/../wiki/Foo', + [ 'title' => 'Foo' ] + ], + 'Dots removed 2' => [ + '/wiki/$1', + '/./wiki/Foo', + [ 'title' => 'Foo' ] + ], + 'Dots retained 1' => [ + '/wiki/$1', + '/wiki/../wiki/Foo', + [ 'title' => '../wiki/Foo' ] + ], + 'Dots retained 2' => [ + '/wiki/$1', + '/wiki/./Foo', + [ 'title' => './Foo' ] + ], + 'Triple slash' => [ + '/wiki/$1', + '///wiki/Foo', + [ 'title' => 'Foo' ] + ], + // '..' only traverses one slash, see e.g. RFC 3986 + 'Dots traversing double slash 1' => [ + '/wiki/$1', + '/a//b/../../wiki/Foo', + [] + ], + 'Dots traversing double slash 2' => [ + '/wiki/$1', + '/a//b/../../../wiki/Foo', + [ 'title' => 'Foo' ] + ], + ]; + + // Make sure the router doesn't break on special characters like $ used in regexp replacements + foreach ( [ "$", "$1", "\\", "\\$1" ] as $char ) { + $tests["Regexp character $char"] = [ + "/wiki/$1", + "/wiki/$char", + [ 'title' => "$char" ] + ]; + } + + $tests += [ + // Make sure the router handles characters like +&() properly + "Special characters" => [ + "/wiki/$1", + "/wiki/Plus+And&Dollar\\Stuff();[]{}*", + [ 'title' => "Plus+And&Dollar\\Stuff();[]{}*" ], + ], + + // Make sure the router handles unicode characters correctly + "Unicode 1" => [ + "/wiki/$1", + "/wiki/Spécial:Modifications_récentes" , + [ 'title' => "Spécial:Modifications_récentes" ], + ], + + "Unicode 2" => [ + "/wiki/$1", + "/wiki/Sp%C3%A9cial:Modifications_r%C3%A9centes", + [ 'title' => "Spécial:Modifications_récentes" ], + ] + ]; + + // Ensure the router doesn't choke on long paths. + $lorem = "Lorem_ipsum_dolor_sit_amet,_consectetur_adipisicing_elit,_sed_do_eiusmod_" . + "tempor_incididunt_ut_labore_et_dolore_magna_aliqua._Ut_enim_ad_minim_veniam,_quis_" . + "nostrud_exercitation_ullamco_laboris_nisi_ut_aliquip_ex_ea_commodo_consequat._" . + "Duis_aute_irure_dolor_in_reprehenderit_in_voluptate_velit_esse_cillum_dolore_" . + "eu_fugiat_nulla_pariatur._Excepteur_sint_occaecat_cupidatat_non_proident,_sunt_" . + "in_culpa_qui_officia_deserunt_mollit_anim_id_est_laborum."; + + $tests += [ + "Long path" => [ + "/wiki/$1", + "/wiki/$lorem", + [ 'title' => $lorem ] + ], + + // Ensure that the php passed site of parameter values are not urldecoded + "Pattern urlencoding" => [ + [ [ 'path' => "/wiki/$1", 'params' => [ 'title' => '%20:$1' ] ] ], + "/wiki/Foo", + [ 'title' => '%20:Foo' ] + ], + + // Ensure that raw parameter values do not have any variable replacements or urldecoding + "Raw param value" => [ + [ [ 'path' => "/wiki/$1", 'params' => [ 'title' => [ 'value' => 'bar%20$1' ] ] ] ], + "/wiki/Foo", + [ 'title' => 'bar%20$1' ] + ] + ]; + + return $tests; + } + + /** + * Test path parsing + * @dataProvider provideParse + */ + public function testParse( $patterns, $path, $expected ) { + $patterns = (array)$patterns; + + $router = new PathRouter; + foreach ( $patterns as $pattern ) { + if ( is_array( $pattern ) ) { + $router->add( $pattern['path'], $pattern['params'] ?? [], + $pattern['options'] ?? [] ); + } else { + $router->add( $pattern ); + } + } + $matches = $router->parse( $path ); + $this->assertEquals( $matches, $expected ); + } + + public static function callbackForTest( &$matches, $data ) { + $matches['x'] = $data['$1']; + $matches['foo'] = $data['foo']; + } + + public static function provideWeight() { + return [ + [ '/Foo', [ 'title' => 'Foo' ] ], + [ '/Bar', [ 'ping' => 'pong' ] ], + [ '/Baz', [ 'marco' => 'polo' ] ], + [ '/asdf-foo', [ 'title' => 'qwerty-foo' ] ], + [ '/qwerty-bar', [ 'title' => 'asdf-bar' ] ], + [ '/a/Foo', [ 'title' => 'Foo' ] ], + [ '/asdf/Foo', [ 'title' => 'Foo' ] ], + [ '/qwerty/Foo', [ 'title' => 'Foo', 'qwerty' => 'qwerty' ] ], + [ '/baz/Foo', [ 'title' => 'Foo', 'unrestricted' => 'baz' ] ], + [ '/y/Foo', [ 'title' => 'Foo', 'restricted-to-y' => 'y' ] ], + ]; + } + + /** + * Test to ensure weight of paths is handled correctly + * @dataProvider provideWeight + */ + public function testWeight( $path, $expected ) { + $router = new PathRouter; + $router->addStrict( "/Bar", [ 'ping' => 'pong' ] ); + $router->add( "/asdf-$1", [ 'title' => 'qwerty-$1' ] ); + $router->add( "/$1" ); + $router->add( "/qwerty-$1", [ 'title' => 'asdf-$1' ] ); + $router->addStrict( "/Baz", [ 'marco' => 'polo' ] ); + $router->add( "/a/$1" ); + $router->add( "/asdf/$1" ); + $router->add( "/$2/$1", [ 'unrestricted' => '$2' ] ); + $router->add( [ 'qwerty' => "/qwerty/$1" ], [ 'qwerty' => '$key' ] ); + $router->add( "/$2/$1", [ 'restricted-to-y' => '$2' ], [ '$2' => 'y' ] ); + + $this->assertEquals( $router->parse( $path ), $expected ); + } +} diff --git a/tests/phpunit/unit/includes/Rest/ResponseFactoryTest.php b/tests/phpunit/unit/includes/Rest/ResponseFactoryTest.php new file mode 100644 index 0000000000..04d54de899 --- /dev/null +++ b/tests/phpunit/unit/includes/Rest/ResponseFactoryTest.php @@ -0,0 +1,146 @@ +assertSame( $expected, $rf->encodeJson( $input ) ); + } + + public function testCreateJson() { + $rf = new ResponseFactory; + $response = $rf->createJson( [] ); + $response->getBody()->rewind(); + $this->assertSame( 'application/json', $response->getHeaderLine( 'Content-Type' ) ); + $this->assertSame( '[]', $response->getBody()->getContents() ); + // Make sure getSize() is functional, since testCreateNoContent() depends on it + $this->assertSame( 2, $response->getBody()->getSize() ); + } + + public function testCreateNoContent() { + $rf = new ResponseFactory; + $response = $rf->createNoContent(); + $this->assertSame( [], $response->getHeader( 'Content-Type' ) ); + $this->assertSame( 0, $response->getBody()->getSize() ); + $this->assertSame( 204, $response->getStatusCode() ); + } + + public function testCreatePermanentRedirect() { + $rf = new ResponseFactory; + $response = $rf->createPermanentRedirect( 'http://www.example.com/' ); + $this->assertSame( [ 'http://www.example.com/' ], $response->getHeader( 'Location' ) ); + $this->assertSame( 301, $response->getStatusCode() ); + } + + public function testCreateLegacyTemporaryRedirect() { + $rf = new ResponseFactory; + $response = $rf->createLegacyTemporaryRedirect( 'http://www.example.com/' ); + $this->assertSame( [ 'http://www.example.com/' ], $response->getHeader( 'Location' ) ); + $this->assertSame( 302, $response->getStatusCode() ); + } + + public function testCreateTemporaryRedirect() { + $rf = new ResponseFactory; + $response = $rf->createTemporaryRedirect( 'http://www.example.com/' ); + $this->assertSame( [ 'http://www.example.com/' ], $response->getHeader( 'Location' ) ); + $this->assertSame( 307, $response->getStatusCode() ); + } + + public function testCreateSeeOther() { + $rf = new ResponseFactory; + $response = $rf->createSeeOther( 'http://www.example.com/' ); + $this->assertSame( [ 'http://www.example.com/' ], $response->getHeader( 'Location' ) ); + $this->assertSame( 303, $response->getStatusCode() ); + } + + public function testCreateNotModified() { + $rf = new ResponseFactory; + $response = $rf->createNotModified(); + $this->assertSame( 0, $response->getBody()->getSize() ); + $this->assertSame( 304, $response->getStatusCode() ); + } + + /** @expectedException \InvalidArgumentException */ + public function testCreateHttpErrorInvalid() { + $rf = new ResponseFactory; + $rf->createHttpError( 200 ); + } + + public function testCreateHttpError() { + $rf = new ResponseFactory; + $response = $rf->createHttpError( 415, [ 'message' => '...' ] ); + $this->assertSame( 415, $response->getStatusCode() ); + $body = $response->getBody(); + $body->rewind(); + $data = json_decode( $body->getContents(), true ); + $this->assertSame( 415, $data['httpCode'] ); + $this->assertSame( '...', $data['message'] ); + } + + public function testCreateFromExceptionUnlogged() { + $rf = new ResponseFactory; + $response = $rf->createFromException( new HttpException( 'hello', 415 ) ); + $this->assertSame( 415, $response->getStatusCode() ); + $body = $response->getBody(); + $body->rewind(); + $data = json_decode( $body->getContents(), true ); + $this->assertSame( 415, $data['httpCode'] ); + $this->assertSame( 'hello', $data['message'] ); + } + + public function testCreateFromExceptionLogged() { + $rf = new ResponseFactory; + $response = $rf->createFromException( new \Exception( "hello", 415 ) ); + $this->assertSame( 500, $response->getStatusCode() ); + $body = $response->getBody(); + $body->rewind(); + $data = json_decode( $body->getContents(), true ); + $this->assertSame( 500, $data['httpCode'] ); + $this->assertSame( 'Error: exception of type Exception', $data['message'] ); + } + + public static function provideCreateFromReturnValue() { + return [ + [ 'hello', '{"value":"hello"}' ], + [ true, '{"value":true}' ], + [ [ 'x' => 'y' ], '{"x":"y"}' ], + [ [ 'x', 'y' ], '["x","y"]' ], + [ [ 'a', 'x' => 'y' ], '{"0":"a","x":"y"}' ], + [ (object)[ 'a', 'x' => 'y' ], '{"0":"a","x":"y"}' ], + [ [], '[]' ], + [ (object)[], '{}' ], + ]; + } + + /** @dataProvider provideCreateFromReturnValue */ + public function testCreateFromReturnValue( $input, $expected ) { + $rf = new ResponseFactory; + $response = $rf->createFromReturnValue( $input ); + $body = $response->getBody(); + $body->rewind(); + $this->assertSame( $expected, $body->getContents() ); + } + + /** @expectedException \InvalidArgumentException */ + public function testCreateFromReturnValueInvalid() { + $rf = new ResponseFactory; + $rf->createFromReturnValue( new ArrayIterator ); + } +} diff --git a/tests/phpunit/unit/includes/Revision/MainSlotRoleHandlerTest.php b/tests/phpunit/unit/includes/Revision/MainSlotRoleHandlerTest.php new file mode 100644 index 0000000000..9dff2cc500 --- /dev/null +++ b/tests/phpunit/unit/includes/Revision/MainSlotRoleHandlerTest.php @@ -0,0 +1,79 @@ +getMockBuilder( Title::class ) + ->disableOriginalConstructor() + ->getMock(); + + $title->method( 'getNamespace' ) + ->willReturn( $ns ); + + return $title; + } + + /** + * @covers \MediaWiki\Revision\MainSlotRoleHandler::__construct + * @covers \MediaWiki\Revision\MainSlotRoleHandler::getRole() + * @covers \MediaWiki\Revision\MainSlotRoleHandler::getNameMessageKey() + * @covers \MediaWiki\Revision\MainSlotRoleHandler::getOutputLayoutHints() + */ + public function testConstruction() { + $handler = new MainSlotRoleHandler( [] ); + $this->assertSame( 'main', $handler->getRole() ); + $this->assertSame( 'slot-name-main', $handler->getNameMessageKey() ); + + $hints = $handler->getOutputLayoutHints(); + $this->assertArrayHasKey( 'display', $hints ); + $this->assertArrayHasKey( 'region', $hints ); + $this->assertArrayHasKey( 'placement', $hints ); + } + + /** + * @covers \MediaWiki\Revision\MainSlotRoleHandler::getDefaultModel() + */ + public function testFetDefaultModel() { + $handler = new MainSlotRoleHandler( [ 100 => CONTENT_MODEL_TEXT ] ); + + // For the main handler, the namespace determins the default model + $titleMain = $this->makeTitleObject( NS_MAIN ); + $this->assertSame( CONTENT_MODEL_WIKITEXT, $handler->getDefaultModel( $titleMain ) ); + + $title100 = $this->makeTitleObject( 100 ); + $this->assertSame( CONTENT_MODEL_TEXT, $handler->getDefaultModel( $title100 ) ); + } + + /** + * @covers \MediaWiki\Revision\MainSlotRoleHandler::isAllowedModel() + */ + public function testIsAllowedModel() { + $handler = new MainSlotRoleHandler( [] ); + + // For the main handler, (nearly) all models are allowed + $title = $this->makeTitleObject( NS_MAIN ); + $this->assertTrue( $handler->isAllowedModel( CONTENT_MODEL_WIKITEXT, $title ) ); + $this->assertTrue( $handler->isAllowedModel( CONTENT_MODEL_TEXT, $title ) ); + } + + /** + * @covers \MediaWiki\Revision\MainSlotRoleHandler::supportsArticleCount() + */ + public function testSupportsArticleCount() { + $handler = new MainSlotRoleHandler( [] ); + + $this->assertTrue( $handler->supportsArticleCount() ); + } + +} diff --git a/tests/phpunit/unit/includes/Revision/SlotRecordTest.php b/tests/phpunit/unit/includes/Revision/SlotRecordTest.php new file mode 100644 index 0000000000..aab430aaa6 --- /dev/null +++ b/tests/phpunit/unit/includes/Revision/SlotRecordTest.php @@ -0,0 +1,416 @@ + 1234, + 'slot_content_id' => 33, + 'content_size' => '5', + 'content_sha1' => 'someHash', + 'content_address' => 'tt:456', + 'model_name' => CONTENT_MODEL_WIKITEXT, + 'format_name' => CONTENT_FORMAT_WIKITEXT, + 'slot_revision_id' => '2', + 'slot_origin' => '1', + 'role_name' => 'myRole', + ]; + return (object)$data; + } + + public function testCompleteConstruction() { + $row = $this->makeRow(); + $record = new SlotRecord( $row, new WikitextContent( 'A' ) ); + + $this->assertTrue( $record->hasAddress() ); + $this->assertTrue( $record->hasContentId() ); + $this->assertTrue( $record->hasRevision() ); + $this->assertTrue( $record->isInherited() ); + $this->assertSame( 'A', $record->getContent()->getText() ); + $this->assertSame( 5, $record->getSize() ); + $this->assertSame( 'someHash', $record->getSha1() ); + $this->assertSame( CONTENT_MODEL_WIKITEXT, $record->getModel() ); + $this->assertSame( 2, $record->getRevision() ); + $this->assertSame( 1, $record->getOrigin() ); + $this->assertSame( 'tt:456', $record->getAddress() ); + $this->assertSame( 33, $record->getContentId() ); + $this->assertSame( CONTENT_FORMAT_WIKITEXT, $record->getFormat() ); + $this->assertSame( 'myRole', $record->getRole() ); + } + + public function testConstructionDeferred() { + $row = $this->makeRow( [ + 'content_size' => null, // to be computed + 'content_sha1' => null, // to be computed + 'format_name' => function () { + return CONTENT_FORMAT_WIKITEXT; + }, + 'slot_revision_id' => '2', + 'slot_origin' => '2', + 'slot_content_id' => function () { + return null; + }, + ] ); + + $content = function () { + return new WikitextContent( 'A' ); + }; + + $record = new SlotRecord( $row, $content ); + + $this->assertTrue( $record->hasAddress() ); + $this->assertTrue( $record->hasRevision() ); + $this->assertFalse( $record->hasContentId() ); + $this->assertFalse( $record->isInherited() ); + $this->assertSame( 'A', $record->getContent()->getText() ); + $this->assertSame( 1, $record->getSize() ); + $this->assertNotEmpty( $record->getSha1() ); + $this->assertSame( CONTENT_MODEL_WIKITEXT, $record->getModel() ); + $this->assertSame( 2, $record->getRevision() ); + $this->assertSame( 2, $record->getRevision() ); + $this->assertSame( 'tt:456', $record->getAddress() ); + $this->assertSame( CONTENT_FORMAT_WIKITEXT, $record->getFormat() ); + $this->assertSame( 'myRole', $record->getRole() ); + } + + public function testNewUnsaved() { + $record = SlotRecord::newUnsaved( 'myRole', new WikitextContent( 'A' ) ); + + $this->assertFalse( $record->hasAddress() ); + $this->assertFalse( $record->hasContentId() ); + $this->assertFalse( $record->hasRevision() ); + $this->assertFalse( $record->isInherited() ); + $this->assertFalse( $record->hasOrigin() ); + $this->assertSame( 'A', $record->getContent()->getText() ); + $this->assertSame( 1, $record->getSize() ); + $this->assertNotEmpty( $record->getSha1() ); + $this->assertSame( CONTENT_MODEL_WIKITEXT, $record->getModel() ); + $this->assertSame( 'myRole', $record->getRole() ); + } + + public function provideInvalidConstruction() { + yield 'both null' => [ null, null ]; + yield 'null row' => [ null, new WikitextContent( 'A' ) ]; + yield 'array row' => [ [], new WikitextContent( 'A' ) ]; + yield 'empty row' => [ (object)[], new WikitextContent( 'A' ) ]; + yield 'null content' => [ (object)[], null ]; + } + + /** + * @dataProvider provideInvalidConstruction + */ + public function testInvalidConstruction( $row, $content ) { + $this->setExpectedException( InvalidArgumentException::class ); + new SlotRecord( $row, $content ); + } + + public function testGetContentId_fails() { + $record = SlotRecord::newUnsaved( SlotRecord::MAIN, new WikitextContent( 'A' ) ); + $this->setExpectedException( IncompleteRevisionException::class ); + + $record->getContentId(); + } + + public function testGetAddress_fails() { + $record = SlotRecord::newUnsaved( SlotRecord::MAIN, new WikitextContent( 'A' ) ); + $this->setExpectedException( IncompleteRevisionException::class ); + + $record->getAddress(); + } + + public function provideIncomplete() { + $unsaved = SlotRecord::newUnsaved( SlotRecord::MAIN, new WikitextContent( 'A' ) ); + yield 'unsaved' => [ $unsaved ]; + + $parent = new SlotRecord( $this->makeRow(), new WikitextContent( 'A' ) ); + $inherited = SlotRecord::newInherited( $parent ); + yield 'inherited' => [ $inherited ]; + } + + /** + * @dataProvider provideIncomplete + */ + public function testGetRevision_fails( SlotRecord $record ) { + $record = SlotRecord::newUnsaved( SlotRecord::MAIN, new WikitextContent( 'A' ) ); + $this->setExpectedException( IncompleteRevisionException::class ); + + $record->getRevision(); + } + + /** + * @dataProvider provideIncomplete + */ + public function testGetOrigin_fails( SlotRecord $record ) { + $record = SlotRecord::newUnsaved( SlotRecord::MAIN, new WikitextContent( 'A' ) ); + $this->setExpectedException( IncompleteRevisionException::class ); + + $record->getOrigin(); + } + + public function provideHashStability() { + yield [ '', 'phoiac9h4m842xq45sp7s6u21eteeq1' ]; + yield [ 'Lorem ipsum', 'hcr5u40uxr81d3nx89nvwzclfz6r9c5' ]; + } + + /** + * @dataProvider provideHashStability + */ + public function testHashStability( $text, $hash ) { + // Changing the output of the hash function will break things horribly! + + $this->assertSame( $hash, SlotRecord::base36Sha1( $text ) ); + + $record = SlotRecord::newUnsaved( SlotRecord::MAIN, new WikitextContent( $text ) ); + $this->assertSame( $hash, $record->getSha1() ); + } + + public function testHashComputed() { + $row = $this->makeRow(); + $row->content_sha1 = ''; + + $rec = new SlotRecord( $row, new WikitextContent( 'A' ) ); + $this->assertNotEmpty( $rec->getSha1() ); + } + + public function testNewWithSuppressedContent() { + $input = new SlotRecord( $this->makeRow(), new WikitextContent( 'A' ) ); + $output = SlotRecord::newWithSuppressedContent( $input ); + + $this->setExpectedException( SuppressedDataException::class ); + $output->getContent(); + } + + public function testNewInherited() { + $row = $this->makeRow( [ 'slot_revision_id' => 7, 'slot_origin' => 7 ] ); + $parent = new SlotRecord( $row, new WikitextContent( 'A' ) ); + + // This would happen while doing an edit, before saving revision meta-data. + $inherited = SlotRecord::newInherited( $parent ); + + $this->assertSame( $parent->getContentId(), $inherited->getContentId() ); + $this->assertSame( $parent->getAddress(), $inherited->getAddress() ); + $this->assertSame( $parent->getContent(), $inherited->getContent() ); + $this->assertTrue( $inherited->isInherited() ); + $this->assertTrue( $inherited->hasOrigin() ); + $this->assertFalse( $inherited->hasRevision() ); + + // make sure we didn't mess with the internal state of $parent + $this->assertFalse( $parent->isInherited() ); + $this->assertSame( 7, $parent->getRevision() ); + + // This would happen while doing an edit, after saving the revision meta-data + // and content meta-data. + $saved = SlotRecord::newSaved( + 10, + $inherited->getContentId(), + $inherited->getAddress(), + $inherited + ); + $this->assertSame( $parent->getContentId(), $saved->getContentId() ); + $this->assertSame( $parent->getAddress(), $saved->getAddress() ); + $this->assertSame( $parent->getContent(), $saved->getContent() ); + $this->assertTrue( $saved->isInherited() ); + $this->assertTrue( $saved->hasRevision() ); + $this->assertSame( 10, $saved->getRevision() ); + + // make sure we didn't mess with the internal state of $parent or $inherited + $this->assertSame( 7, $parent->getRevision() ); + $this->assertFalse( $inherited->hasRevision() ); + } + + public function testNewSaved() { + // This would happen while doing an edit, before saving revision meta-data. + $unsaved = SlotRecord::newUnsaved( SlotRecord::MAIN, new WikitextContent( 'A' ) ); + + // This would happen while doing an edit, after saving the revision meta-data + // and content meta-data. + $saved = SlotRecord::newSaved( 10, 20, 'theNewAddress', $unsaved ); + $this->assertFalse( $saved->isInherited() ); + $this->assertTrue( $saved->hasOrigin() ); + $this->assertTrue( $saved->hasRevision() ); + $this->assertTrue( $saved->hasAddress() ); + $this->assertTrue( $saved->hasContentId() ); + $this->assertSame( 'theNewAddress', $saved->getAddress() ); + $this->assertSame( 20, $saved->getContentId() ); + $this->assertSame( 'A', $saved->getContent()->getText() ); + $this->assertSame( 10, $saved->getRevision() ); + $this->assertSame( 10, $saved->getOrigin() ); + + // make sure we didn't mess with the internal state of $unsaved + $this->assertFalse( $unsaved->hasAddress() ); + $this->assertFalse( $unsaved->hasContentId() ); + $this->assertFalse( $unsaved->hasRevision() ); + } + + public function provideNewSaved_LogicException() { + $freshRow = $this->makeRow( [ + 'content_id' => 10, + 'content_address' => 'address:1', + 'slot_origin' => 1, + 'slot_revision_id' => 1, + ] ); + + $freshSlot = new SlotRecord( $freshRow, new WikitextContent( 'A' ) ); + yield 'mismatching address' => [ 1, 10, 'address:BAD', $freshSlot ]; + yield 'mismatching revision' => [ 5, 10, 'address:1', $freshSlot ]; + yield 'mismatching content ID' => [ 1, 17, 'address:1', $freshSlot ]; + + $inheritedRow = $this->makeRow( [ + 'content_id' => null, + 'content_address' => null, + 'slot_origin' => 0, + 'slot_revision_id' => 1, + ] ); + + $inheritedSlot = new SlotRecord( $inheritedRow, new WikitextContent( 'A' ) ); + yield 'inherited, but no address' => [ 1, 10, 'address:2', $inheritedSlot ]; + } + + /** + * @dataProvider provideNewSaved_LogicException + */ + public function testNewSaved_LogicException( + $revisionId, + $contentId, + $contentAddress, + SlotRecord $protoSlot + ) { + $this->setExpectedException( LogicException::class ); + SlotRecord::newSaved( $revisionId, $contentId, $contentAddress, $protoSlot ); + } + + public function provideNewSaved_InvalidArgumentException() { + $unsaved = SlotRecord::newUnsaved( SlotRecord::MAIN, new WikitextContent( 'A' ) ); + + yield 'bad revision id' => [ 'xyzzy', 5, 'address', $unsaved ]; + yield 'bad content id' => [ 7, 'xyzzy', 'address', $unsaved ]; + yield 'bad content address' => [ 7, 5, 77, $unsaved ]; + } + + /** + * @dataProvider provideNewSaved_InvalidArgumentException + */ + public function testNewSaved_InvalidArgumentException( + $revisionId, + $contentId, + $contentAddress, + SlotRecord $protoSlot + ) { + $this->setExpectedException( InvalidArgumentException::class ); + SlotRecord::newSaved( $revisionId, $contentId, $contentAddress, $protoSlot ); + } + + public function provideHasSameContent() { + $fail = function () { + self::fail( 'There should be no need to actually load the content.' ); + }; + + $a100a1 = new SlotRecord( + $this->makeRow( + [ + 'model_name' => 'A', + 'content_size' => 100, + 'content_sha1' => 'hash-a', + 'content_address' => 'xxx:a1', + ] + ), + $fail + ); + $a100a1b = new SlotRecord( + $this->makeRow( + [ + 'model_name' => 'A', + 'content_size' => 100, + 'content_sha1' => 'hash-a', + 'content_address' => 'xxx:a1', + ] + ), + $fail + ); + $a100null = new SlotRecord( + $this->makeRow( + [ + 'model_name' => 'A', + 'content_size' => 100, + 'content_sha1' => 'hash-a', + 'content_address' => null, + ] + ), + $fail + ); + $a100a2 = new SlotRecord( + $this->makeRow( + [ + 'model_name' => 'A', + 'content_size' => 100, + 'content_sha1' => 'hash-a', + 'content_address' => 'xxx:a2', + ] + ), + $fail + ); + $b100a1 = new SlotRecord( + $this->makeRow( + [ + 'model_name' => 'B', + 'content_size' => 100, + 'content_sha1' => 'hash-a', + 'content_address' => 'xxx:a1', + ] + ), + $fail + ); + $a200a1 = new SlotRecord( + $this->makeRow( + [ + 'model_name' => 'A', + 'content_size' => 200, + 'content_sha1' => 'hash-a', + 'content_address' => 'xxx:a2', + ] + ), + $fail + ); + $a100x1 = new SlotRecord( + $this->makeRow( + [ + 'model_name' => 'A', + 'content_size' => 100, + 'content_sha1' => 'hash-x', + 'content_address' => 'xxx:x1', + ] + ), + $fail + ); + + yield 'same instance' => [ $a100a1, $a100a1, true ]; + yield 'no address' => [ $a100a1, $a100null, true ]; + yield 'same address' => [ $a100a1, $a100a1b, true ]; + yield 'different address' => [ $a100a1, $a100a2, true ]; + yield 'different model' => [ $a100a1, $b100a1, false ]; + yield 'different size' => [ $a100a1, $a200a1, false ]; + yield 'different hash' => [ $a100a1, $a100x1, false ]; + } + + /** + * @dataProvider provideHasSameContent + */ + public function testHasSameContent( SlotRecord $a, SlotRecord $b, $sameContent ) { + $this->assertSame( $sameContent, $a->hasSameContent( $b ) ); + $this->assertSame( $sameContent, $b->hasSameContent( $a ) ); + } + +} diff --git a/tests/phpunit/unit/includes/TitleArrayFromResultTest.php b/tests/phpunit/unit/includes/TitleArrayFromResultTest.php new file mode 100644 index 0000000000..10b7d36c66 --- /dev/null +++ b/tests/phpunit/unit/includes/TitleArrayFromResultTest.php @@ -0,0 +1,115 @@ +getMockBuilder( Wikimedia\Rdbms\ResultWrapper::class ) + ->disableOriginalConstructor(); + + $resultWrapper = $resultWrapper->getMock(); + $resultWrapper->expects( $this->atLeastOnce() ) + ->method( 'current' ) + ->will( $this->returnValue( $row ) ); + $resultWrapper->expects( $this->any() ) + ->method( 'numRows' ) + ->will( $this->returnValue( $numRows ) ); + + return $resultWrapper; + } + + private function getRowWithTitle( $namespace = 3, $title = 'foo' ) { + $row = new stdClass(); + $row->page_namespace = $namespace; + $row->page_title = $title; + return $row; + } + + /** + * @covers TitleArrayFromResult::__construct + */ + public function testConstructionWithFalseRow() { + $row = false; + $resultWrapper = $this->getMockResultWrapper( $row ); + + $object = new TitleArrayFromResult( $resultWrapper ); + + $this->assertEquals( $resultWrapper, $object->res ); + $this->assertSame( 0, $object->key ); + $this->assertEquals( $row, $object->current ); + } + + /** + * @covers TitleArrayFromResult::__construct + */ + public function testConstructionWithRow() { + $namespace = 0; + $title = 'foo'; + $row = $this->getRowWithTitle( $namespace, $title ); + $resultWrapper = $this->getMockResultWrapper( $row ); + + $object = new TitleArrayFromResult( $resultWrapper ); + + $this->assertEquals( $resultWrapper, $object->res ); + $this->assertSame( 0, $object->key ); + $this->assertInstanceOf( Title::class, $object->current ); + $this->assertEquals( $namespace, $object->current->mNamespace ); + $this->assertEquals( $title, $object->current->mTextform ); + } + + public static function provideNumberOfRows() { + return [ + [ 0 ], + [ 1 ], + [ 122 ], + ]; + } + + /** + * @dataProvider provideNumberOfRows + * @covers TitleArrayFromResult::count + */ + public function testCountWithVaryingValues( $numRows ) { + $object = new TitleArrayFromResult( $this->getMockResultWrapper( + $this->getRowWithTitle(), + $numRows + ) ); + $this->assertEquals( $numRows, $object->count() ); + } + + /** + * @covers TitleArrayFromResult::current + */ + public function testCurrentAfterConstruction() { + $namespace = 0; + $title = 'foo'; + $row = $this->getRowWithTitle( $namespace, $title ); + $object = new TitleArrayFromResult( $this->getMockResultWrapper( $row ) ); + $this->assertInstanceOf( Title::class, $object->current() ); + $this->assertEquals( $namespace, $object->current->mNamespace ); + $this->assertEquals( $title, $object->current->mTextform ); + } + + public function provideTestValid() { + return [ + [ $this->getRowWithTitle(), true ], + [ false, false ], + ]; + } + + /** + * @dataProvider provideTestValid + * @covers TitleArrayFromResult::valid + */ + public function testValid( $input, $expected ) { + $object = new TitleArrayFromResult( $this->getMockResultWrapper( $input ) ); + $this->assertEquals( $expected, $object->valid() ); + } + + // @todo unit test for key() + // @todo unit test for next() + // @todo unit test for rewind() +} diff --git a/tests/phpunit/unit/includes/WikiReferenceTest.php b/tests/phpunit/unit/includes/WikiReferenceTest.php new file mode 100644 index 0000000000..a4aae86c0f --- /dev/null +++ b/tests/phpunit/unit/includes/WikiReferenceTest.php @@ -0,0 +1,164 @@ + [ 'foo.bar', 'http://foo.bar' ], + 'https' => [ 'foo.bar', 'http://foo.bar' ], + + // apparently, this is the expected behavior + 'invalid' => [ 'purple kittens', 'purple kittens' ], + ]; + } + + /** + * @dataProvider provideGetDisplayName + */ + public function testGetDisplayName( $expected, $canonicalServer ) { + $reference = new WikiReference( $canonicalServer, '/wiki/$1' ); + $this->assertEquals( $expected, $reference->getDisplayName() ); + } + + public function testGetCanonicalServer() { + $reference = new WikiReference( 'https://acme.com', '/wiki/$1', '//acme.com' ); + $this->assertEquals( 'https://acme.com', $reference->getCanonicalServer() ); + } + + public function provideGetCanonicalUrl() { + return [ + 'no fragment' => [ + 'https://acme.com/wiki/Foo', + 'https://acme.com', + '//acme.com', + '/wiki/$1', + 'Foo', + null + ], + 'empty fragment' => [ + 'https://acme.com/wiki/Foo', + 'https://acme.com', + '//acme.com', + '/wiki/$1', + 'Foo', + '' + ], + 'fragment' => [ + 'https://acme.com/wiki/Foo#Bar', + 'https://acme.com', + '//acme.com', + '/wiki/$1', + 'Foo', + 'Bar' + ], + 'double fragment' => [ + 'https://acme.com/wiki/Foo#Bar%23Xus', + 'https://acme.com', + '//acme.com', + '/wiki/$1', + 'Foo', + 'Bar#Xus' + ], + 'escaped fragment' => [ + 'https://acme.com/wiki/Foo%23Bar', + 'https://acme.com', + '//acme.com', + '/wiki/$1', + 'Foo#Bar', + null + ], + 'empty path' => [ + 'https://acme.com/Foo', + 'https://acme.com', + '//acme.com', + '/$1', + 'Foo', + null + ], + ]; + } + + /** + * @dataProvider provideGetCanonicalUrl + */ + public function testGetCanonicalUrl( + $expected, $canonicalServer, $server, $path, $page, $fragmentId + ) { + $reference = new WikiReference( $canonicalServer, $path, $server ); + $this->assertEquals( $expected, $reference->getCanonicalUrl( $page, $fragmentId ) ); + } + + /** + * @dataProvider provideGetCanonicalUrl + * @note getUrl is an alias for getCanonicalUrl + */ + public function testGetUrl( $expected, $canonicalServer, $server, $path, $page, $fragmentId ) { + $reference = new WikiReference( $canonicalServer, $path, $server ); + $this->assertEquals( $expected, $reference->getUrl( $page, $fragmentId ) ); + } + + public function provideGetFullUrl() { + return [ + 'no fragment' => [ + '//acme.com/wiki/Foo', + 'https://acme.com', + '//acme.com', + '/wiki/$1', + 'Foo', + null + ], + 'empty fragment' => [ + '//acme.com/wiki/Foo', + 'https://acme.com', + '//acme.com', + '/wiki/$1', + 'Foo', + '' + ], + 'fragment' => [ + '//acme.com/wiki/Foo#Bar', + 'https://acme.com', + '//acme.com', + '/wiki/$1', + 'Foo', + 'Bar' + ], + 'double fragment' => [ + '//acme.com/wiki/Foo#Bar%23Xus', + 'https://acme.com', + '//acme.com', + '/wiki/$1', + 'Foo', + 'Bar#Xus' + ], + 'escaped fragment' => [ + '//acme.com/wiki/Foo%23Bar', + 'https://acme.com', + '//acme.com', + '/wiki/$1', + 'Foo#Bar', + null + ], + 'empty path' => [ + '//acme.com/Foo', + 'https://acme.com', + '//acme.com', + '/$1', + 'Foo', + null + ], + ]; + } + + /** + * @dataProvider provideGetFullUrl + */ + public function testGetFullUrl( $expected, $canonicalServer, $server, $path, $page, $fragmentId ) { + $reference = new WikiReference( $canonicalServer, $path, $server ); + $this->assertEquals( $expected, $reference->getFullUrl( $page, $fragmentId ) ); + } + +} diff --git a/tests/phpunit/unit/includes/debug/logger/monolog/CeeFormatterTest.php b/tests/phpunit/unit/includes/debug/logger/monolog/CeeFormatterTest.php new file mode 100644 index 0000000000..946a1a274d --- /dev/null +++ b/tests/phpunit/unit/includes/debug/logger/monolog/CeeFormatterTest.php @@ -0,0 +1,20 @@ + [ 'url' => 1 ], 'context' => [ 'url' => 2 ] ]; + $this->assertSame( + $cee_formatter->format( $record ), + "@cee: " . $ls_formatter->format( $record ) ); + } +} diff --git a/tests/phpunit/unit/includes/diff/DifferenceEngineSlotDiffRendererTest.php b/tests/phpunit/unit/includes/diff/DifferenceEngineSlotDiffRendererTest.php new file mode 100644 index 0000000000..1a8b58590a --- /dev/null +++ b/tests/phpunit/unit/includes/diff/DifferenceEngineSlotDiffRendererTest.php @@ -0,0 +1,44 @@ +getDiff( $oldContent, $newContent ); + $this->assertEquals( 'xxx|yyy', $diff ); + + $diff = $slotDiffRenderer->getDiff( null, $newContent ); + $this->assertEquals( '|yyy', $diff ); + + $diff = $slotDiffRenderer->getDiff( $oldContent, null ); + $this->assertEquals( 'xxx|', $diff ); + } + + public function testAddModules() { + $output = $this->getMockBuilder( OutputPage::class ) + ->disableOriginalConstructor() + ->setMethods( [ 'addModules' ] ) + ->getMock(); + $output->expects( $this->once() ) + ->method( 'addModules' ) + ->with( 'foo' ); + $differenceEngine = new CustomDifferenceEngine(); + $slotDiffRenderer = new DifferenceEngineSlotDiffRenderer( $differenceEngine ); + $slotDiffRenderer->addModules( $output ); + } + + public function testGetExtraCacheKeys() { + $differenceEngine = new CustomDifferenceEngine(); + $slotDiffRenderer = new DifferenceEngineSlotDiffRenderer( $differenceEngine ); + $extraCacheKeys = $slotDiffRenderer->getExtraCacheKeys(); + $this->assertSame( [ 'foo' ], $extraCacheKeys ); + } + +} diff --git a/tests/phpunit/unit/includes/diff/SlotDiffRendererTest.php b/tests/phpunit/unit/includes/diff/SlotDiffRendererTest.php new file mode 100644 index 0000000000..f778115780 --- /dev/null +++ b/tests/phpunit/unit/includes/diff/SlotDiffRendererTest.php @@ -0,0 +1,78 @@ +getMockBuilder( SlotDiffRenderer::class ) + ->getMock(); + try { + // __call needs help deciding which parameter to take by reference + call_user_func_array( [ TestingAccessWrapper::newFromObject( $slotDiffRenderer ), + 'normalizeContents' ], [ &$oldContent, &$newContent, $allowedClasses ] ); + $this->assertEquals( $expectedOldContent, $oldContent ); + $this->assertEquals( $expectedNewContent, $newContent ); + } catch ( Exception $e ) { + if ( !$expectedExceptionClass ) { + throw $e; + } + $this->assertInstanceOf( $expectedExceptionClass, $e ); + } + } + + public function provideNormalizeContents() { + return [ + 'both null' => [ null, null, null, null, null, InvalidArgumentException::class ], + 'left null' => [ + null, new WikitextContent( 'abc' ), null, + new WikitextContent( '' ), new WikitextContent( 'abc' ), null, + ], + 'right null' => [ + new WikitextContent( 'def' ), null, null, + new WikitextContent( 'def' ), new WikitextContent( '' ), null, + ], + 'type filter' => [ + new WikitextContent( 'abc' ), new WikitextContent( 'def' ), WikitextContent::class, + new WikitextContent( 'abc' ), new WikitextContent( 'def' ), null, + ], + 'type filter (subclass)' => [ + new WikitextContent( 'abc' ), new WikitextContent( 'def' ), TextContent::class, + new WikitextContent( 'abc' ), new WikitextContent( 'def' ), null, + ], + 'type filter (null)' => [ + new WikitextContent( 'abc' ), null, TextContent::class, + new WikitextContent( 'abc' ), new WikitextContent( '' ), null, + ], + 'type filter failure (left)' => [ + new TextContent( 'abc' ), new WikitextContent( 'def' ), WikitextContent::class, + null, null, ParameterTypeException::class, + ], + 'type filter failure (right)' => [ + new WikitextContent( 'abc' ), new TextContent( 'def' ), WikitextContent::class, + null, null, ParameterTypeException::class, + ], + 'type filter (array syntax)' => [ + new WikitextContent( 'abc' ), new JsonContent( 'def' ), + [ JsonContent::class, WikitextContent::class ], + new WikitextContent( 'abc' ), new JsonContent( 'def' ), null, + ], + 'type filter failure (array syntax)' => [ + new WikitextContent( 'abc' ), new CssContent( 'def' ), + [ JsonContent::class, WikitextContent::class ], + null, null, ParameterTypeException::class, + ], + ]; + } + +} diff --git a/tests/phpunit/unit/includes/filerepo/FileBackendDBRepoWrapperTest.php b/tests/phpunit/unit/includes/filerepo/FileBackendDBRepoWrapperTest.php new file mode 100644 index 0000000000..6084601b05 --- /dev/null +++ b/tests/phpunit/unit/includes/filerepo/FileBackendDBRepoWrapperTest.php @@ -0,0 +1,140 @@ +expects( $dbReadsExpected ) + ->method( 'selectField' ) + ->will( $this->returnValue( $dbReturnValue ) ); + + $newPaths = $wrapperMock->getBackendPaths( [ $originalPath ], $latest ); + + $this->assertEquals( + $expectedBackendPath, + $newPaths[0], + $message ); + } + + public function getBackendPathsProvider() { + $prefix = 'mwstore://' . $this->backendName . '/' . $this->repoName; + $mocksForCaching = $this->getMocks(); + + return [ + [ + $mocksForCaching, + false, + $this->once(), + '96246614d75ba1703bdfd5d7660bb57407aaf5d9', + $prefix . '-public/f/o/foobar.jpg', + $prefix . '-original/9/6/2/96246614d75ba1703bdfd5d7660bb57407aaf5d9', + 'Public path translated correctly', + ], + [ + $mocksForCaching, + false, + $this->never(), + '96246614d75ba1703bdfd5d7660bb57407aaf5d9', + $prefix . '-public/f/o/foobar.jpg', + $prefix . '-original/9/6/2/96246614d75ba1703bdfd5d7660bb57407aaf5d9', + 'LRU cache leveraged', + ], + [ + $this->getMocks(), + true, + $this->once(), + '96246614d75ba1703bdfd5d7660bb57407aaf5d9', + $prefix . '-public/f/o/foobar.jpg', + $prefix . '-original/9/6/2/96246614d75ba1703bdfd5d7660bb57407aaf5d9', + 'Latest obtained', + ], + [ + $this->getMocks(), + true, + $this->never(), + '96246614d75ba1703bdfd5d7660bb57407aaf5d9', + $prefix . '-deleted/f/o/foobar.jpg', + $prefix . '-original/f/o/o/foobar', + 'Deleted path translated correctly', + ], + [ + $this->getMocks(), + true, + $this->once(), + null, + $prefix . '-public/b/a/baz.jpg', + $prefix . '-public/b/a/baz.jpg', + 'Path left untouched if no sha1 can be found', + ], + ]; + } + + /** + * @covers FileBackendDBRepoWrapper::getFileContentsMulti + */ + public function testGetFileContentsMulti() { + list( $dbMock, $backendMock, $wrapperMock ) = $this->getMocks(); + + $sha1Path = 'mwstore://' . $this->backendName . '/' . $this->repoName + . '-original/9/6/2/96246614d75ba1703bdfd5d7660bb57407aaf5d9'; + $filenamePath = 'mwstore://' . $this->backendName . '/' . $this->repoName + . '-public/f/o/foobar.jpg'; + + $dbMock->expects( $this->once() ) + ->method( 'selectField' ) + ->will( $this->returnValue( '96246614d75ba1703bdfd5d7660bb57407aaf5d9' ) ); + + $backendMock->expects( $this->once() ) + ->method( 'getFileContentsMulti' ) + ->will( $this->returnValue( [ $sha1Path => 'foo' ] ) ); + + $result = $wrapperMock->getFileContentsMulti( [ 'srcs' => [ $filenamePath ] ] ); + + $this->assertEquals( + [ $filenamePath => 'foo' ], + $result, + 'File contents paths translated properly' + ); + } + + protected function getMocks() { + $dbMock = $this->getMockBuilder( Wikimedia\Rdbms\IDatabase::class ) + ->disableOriginalClone() + ->disableOriginalConstructor() + ->getMock(); + + $backendMock = $this->getMockBuilder( FSFileBackend::class ) + ->setConstructorArgs( [ [ + 'name' => $this->backendName, + 'wikiId' => wfWikiID() + ] ] ) + ->getMock(); + + $wrapperMock = $this->getMockBuilder( FileBackendDBRepoWrapper::class ) + ->setMethods( [ 'getDB' ] ) + ->setConstructorArgs( [ [ + 'backend' => $backendMock, + 'repoName' => $this->repoName, + 'dbHandleFactory' => null + ] ] ) + ->getMock(); + + $wrapperMock->expects( $this->any() )->method( 'getDB' )->will( $this->returnValue( $dbMock ) ); + + return [ $dbMock, $backendMock, $wrapperMock ]; + } +} diff --git a/tests/phpunit/unit/includes/filerepo/file/ForeignDBFileTest.php b/tests/phpunit/unit/includes/filerepo/file/ForeignDBFileTest.php new file mode 100644 index 0000000000..32624aa068 --- /dev/null +++ b/tests/phpunit/unit/includes/filerepo/file/ForeignDBFileTest.php @@ -0,0 +1,14 @@ +createMock( LocalRepo::class ); + + $file = ForeignDBFile::newFromTitle( $title, $repoMock ); + + $this->assertInstanceOf( ForeignDBFile::class, $file ); + } +} diff --git a/tests/phpunit/unit/includes/htmlform/HTMLCheckMatrixTest.php b/tests/phpunit/unit/includes/htmlform/HTMLCheckMatrixTest.php new file mode 100644 index 0000000000..659d48d43d --- /dev/null +++ b/tests/phpunit/unit/includes/htmlform/HTMLCheckMatrixTest.php @@ -0,0 +1,104 @@ + [ 'r1', 'r2' ], + 'columns' => [ 'c1', 'c2' ], + 'fieldname' => 'test', + ]; + + public function testPlainInstantiation() { + try { + new HTMLCheckMatrix( [] ); + } catch ( MWException $e ) { + $this->assertInstanceOf( HTMLFormFieldRequiredOptionsException::class, $e ); + return; + } + + $this->fail( 'Expected MWException indicating missing parameters but none was thrown.' ); + } + + public function testInstantiationWithMinimumRequiredParameters() { + new HTMLCheckMatrix( self::$defaultOptions ); + $this->assertTrue( true ); // form instantiation must throw exception on failure + } + + public function testValidateCallsUserDefinedValidationCallback() { + $called = false; + $field = new HTMLCheckMatrix( self::$defaultOptions + [ + 'validation-callback' => function () use ( &$called ) { + $called = true; + + return false; + }, + ] ); + $this->assertEquals( false, $this->validate( $field, [] ) ); + $this->assertTrue( $called ); + } + + public function testValidateRequiresArrayInput() { + $field = new HTMLCheckMatrix( self::$defaultOptions ); + $this->assertEquals( false, $this->validate( $field, null ) ); + $this->assertEquals( false, $this->validate( $field, true ) ); + $this->assertEquals( false, $this->validate( $field, 'abc' ) ); + $this->assertEquals( false, $this->validate( $field, new stdClass ) ); + $this->assertEquals( true, $this->validate( $field, [] ) ); + } + + public function testValidateAllowsOnlyKnownTags() { + $field = new HTMLCheckMatrix( self::$defaultOptions ); + $this->assertInstanceOf( Message::class, $this->validate( $field, [ 'foo' ] ) ); + } + + public function testValidateAcceptsPartialTagList() { + $field = new HTMLCheckMatrix( self::$defaultOptions ); + $this->assertTrue( $this->validate( $field, [] ) ); + $this->assertTrue( $this->validate( $field, [ 'c1-r1' ] ) ); + $this->assertTrue( $this->validate( $field, [ 'c1-r1', 'c1-r2', 'c2-r1', 'c2-r2' ] ) ); + } + + /** + * This form object actually has no visibility into what happens later on, but essentially + * if the data submitted by the user passes validate the following is run: + * foreach ( $field->filterDataForSubmit( $data ) as $k => $v ) { + * $user->setOption( $k, $v ); + * } + */ + public function testValuesForcedOnRemainOn() { + $field = new HTMLCheckMatrix( self::$defaultOptions + [ + 'force-options-on' => [ 'c2-r1' ], + ] ); + $expected = [ + 'c1-r1' => false, + 'c1-r2' => false, + 'c2-r1' => true, + 'c2-r2' => false, + ]; + $this->assertEquals( $expected, $field->filterDataForSubmit( [] ) ); + } + + public function testValuesForcedOffRemainOff() { + $field = new HTMLCheckMatrix( self::$defaultOptions + [ + 'force-options-off' => [ 'c1-r2', 'c2-r2' ], + ] ); + $expected = [ + 'c1-r1' => true, + 'c1-r2' => false, + 'c2-r1' => true, + 'c2-r2' => false, + ]; + // array_keys on the result simulates submitting all fields checked + $this->assertEquals( $expected, $field->filterDataForSubmit( array_keys( $expected ) ) ); + } + + protected function validate( HTMLFormField $field, $submitted ) { + return $field->validate( + $submitted, + [ self::$defaultOptions['fieldname'] => $submitted ] + ); + } + +} diff --git a/tests/phpunit/unit/includes/json/FormatJsonTest.php b/tests/phpunit/unit/includes/json/FormatJsonTest.php new file mode 100644 index 0000000000..94c7d91457 --- /dev/null +++ b/tests/phpunit/unit/includes/json/FormatJsonTest.php @@ -0,0 +1,82 @@ +assertInstanceOf( Status::class, $st ); + if ( $expected === false ) { + $this->assertFalse( $st->isOK(), 'Expected isOK() == false' ); + } else { + $this->assertSame( $expectedGoodStatus, $st->isGood(), + 'Expected isGood() == ' . ( $expectedGoodStatus ? 'true' : 'false' ) + ); + $this->assertTrue( $st->isOK(), 'Expected isOK == true' ); + $val = FormatJson::encode( $st->getValue(), false, FormatJson::ALL_OK ); + $this->assertEquals( $expected, $val ); + } + } + +} diff --git a/tests/phpunit/unit/includes/media/JpegMetadataExtractorTest.php b/tests/phpunit/unit/includes/media/JpegMetadataExtractorTest.php new file mode 100644 index 0000000000..365c140405 --- /dev/null +++ b/tests/phpunit/unit/includes/media/JpegMetadataExtractorTest.php @@ -0,0 +1,128 @@ +filePath = __DIR__ . '/../../../data/media/'; + } + + /** + * We also use this test to test padding bytes don't + * screw stuff up + * + * @param string $file Filename + * + * @dataProvider provideUtf8Comment + */ + public function testUtf8Comment( $file ) { + $res = JpegMetadataExtractor::segmentSplitter( $this->filePath . $file ); + $this->assertEquals( [ 'UTF-8 JPEG Comment — ¼' ], $res['COM'] ); + } + + public static function provideUtf8Comment() { + return [ + [ 'jpeg-comment-utf.jpg' ], + [ 'jpeg-padding-even.jpg' ], + [ 'jpeg-padding-odd.jpg' ], + ]; + } + + /** The file is iso-8859-1, but it should get auto converted */ + public function testIso88591Comment() { + $res = JpegMetadataExtractor::segmentSplitter( $this->filePath . 'jpeg-comment-iso8859-1.jpg' ); + $this->assertEquals( [ 'ISO-8859-1 JPEG Comment - ¼' ], $res['COM'] ); + } + + /** Comment values that are non-textual (random binary junk) should not be shown. + * The example test file has a comment with a 0x5 byte in it which is a control character + * and considered binary junk for our purposes. + */ + public function testBinaryCommentStripped() { + $res = JpegMetadataExtractor::segmentSplitter( $this->filePath . 'jpeg-comment-binary.jpg' ); + $this->assertEmpty( $res['COM'] ); + } + + /* Very rarely a file can have multiple comments. + * Order of comments is based on order inside the file. + */ + public function testMultipleComment() { + $res = JpegMetadataExtractor::segmentSplitter( $this->filePath . 'jpeg-comment-multiple.jpg' ); + $this->assertEquals( [ 'foo', 'bar' ], $res['COM'] ); + } + + public function testXMPExtraction() { + $res = JpegMetadataExtractor::segmentSplitter( $this->filePath . 'jpeg-xmp-psir.jpg' ); + $expected = file_get_contents( $this->filePath . 'jpeg-xmp-psir.xmp' ); + $this->assertEquals( $expected, $res['XMP'] ); + } + + public function testPSIRExtraction() { + $res = JpegMetadataExtractor::segmentSplitter( $this->filePath . 'jpeg-xmp-psir.jpg' ); + $expected = '50686f746f73686f7020332e30003842494d04040000000' + . '000181c02190004746573741c02190003666f6f1c020000020004'; + $this->assertEquals( $expected, bin2hex( $res['PSIR'][0] ) ); + } + + public function testXMPExtractionAltAppId() { + $res = JpegMetadataExtractor::segmentSplitter( $this->filePath . 'jpeg-xmp-alt.jpg' ); + $expected = file_get_contents( $this->filePath . 'jpeg-xmp-psir.xmp' ); + $this->assertEquals( $expected, $res['XMP'] ); + } + + public function testIPTCHashComparisionNoHash() { + $segments = JpegMetadataExtractor::segmentSplitter( $this->filePath . 'jpeg-xmp-psir.jpg' ); + $res = JpegMetadataExtractor::doPSIR( $segments['PSIR'][0] ); + + $this->assertEquals( 'iptc-no-hash', $res ); + } + + public function testIPTCHashComparisionBadHash() { + $segments = JpegMetadataExtractor::segmentSplitter( $this->filePath . 'jpeg-iptc-bad-hash.jpg' ); + $res = JpegMetadataExtractor::doPSIR( $segments['PSIR'][0] ); + + $this->assertEquals( 'iptc-bad-hash', $res ); + } + + public function testIPTCHashComparisionGoodHash() { + $segments = JpegMetadataExtractor::segmentSplitter( $this->filePath . 'jpeg-iptc-good-hash.jpg' ); + $res = JpegMetadataExtractor::doPSIR( $segments['PSIR'][0] ); + + $this->assertEquals( 'iptc-good-hash', $res ); + } + + public function testExifByteOrder() { + $res = JpegMetadataExtractor::segmentSplitter( $this->filePath . 'exif-user-comment.jpg' ); + $expected = 'BE'; + $this->assertEquals( $expected, $res['byteOrder'] ); + } + + public function testInfiniteRead() { + // test file truncated right after a segment, which previously + // caused an infinite loop looking for the next segment byte. + // Should get past infinite loop and throw in wfUnpack() + $this->setExpectedException( 'MWException' ); + $res = JpegMetadataExtractor::segmentSplitter( $this->filePath . 'jpeg-segment-loop1.jpg' ); + } + + public function testInfiniteRead2() { + // test file truncated after a segment's marker and size, which + // would cause a seek past end of file. Seek past end of file + // doesn't actually fail, but prevents further reading and was + // devolving into the previous case (testInfiniteRead). + $this->setExpectedException( 'MWException' ); + $res = JpegMetadataExtractor::segmentSplitter( $this->filePath . 'jpeg-segment-loop2.jpg' ); + } +} diff --git a/tests/phpunit/unit/includes/page/ArticleTest.php b/tests/phpunit/unit/includes/page/ArticleTest.php new file mode 100644 index 0000000000..9995793806 --- /dev/null +++ b/tests/phpunit/unit/includes/page/ArticleTest.php @@ -0,0 +1,57 @@ +title = Title::makeTitle( NS_MAIN, 'SomePage' ); + $this->article = new Article( $this->title ); + } + + /** cleanup title object and its article object */ + protected function tearDown() { + parent::tearDown(); + $this->title = null; + $this->article = null; + } + + /** + * @covers Article::__get + */ + public function testImplementsGetMagic() { + $this->assertEquals( false, $this->article->mLatest, "Article __get magic" ); + } + + /** + * @depends testImplementsGetMagic + * @covers Article::__set + */ + public function testImplementsSetMagic() { + $this->article->mLatest = 2; + $this->assertEquals( 2, $this->article->mLatest, "Article __set magic" ); + } + + /** + * @covers Article::__get + * @covers Article::__set + */ + public function testGetOrSetOnNewProperty() { + $this->article->ext_someNewProperty = 12; + $this->assertEquals( 12, $this->article->ext_someNewProperty, + "Article get/set magic on new field" ); + + $this->article->ext_someNewProperty = -8; + $this->assertEquals( -8, $this->article->ext_someNewProperty, + "Article get/set magic on update to new field" ); + } +} diff --git a/tests/phpunit/unit/includes/session/TokenTest.php b/tests/phpunit/unit/includes/session/TokenTest.php new file mode 100644 index 0000000000..5546603425 --- /dev/null +++ b/tests/phpunit/unit/includes/session/TokenTest.php @@ -0,0 +1,67 @@ +getMockBuilder( Token::class ) + ->setMethods( [ 'toStringAtTimestamp' ] ) + ->setConstructorArgs( [ 'sekret', 'salty', true ] ) + ->getMock(); + $token->expects( $this->any() )->method( 'toStringAtTimestamp' ) + ->will( $this->returnValue( 'faketoken+\\' ) ); + + $this->assertSame( 'faketoken+\\', $token->toString() ); + $this->assertSame( 'faketoken+\\', (string)$token ); + $this->assertTrue( $token->wasNew() ); + + $token = new Token( 'sekret', 'salty', false ); + $this->assertFalse( $token->wasNew() ); + } + + public function testToStringAtTimestamp() { + $token = TestingAccessWrapper::newFromObject( new Token( 'sekret', 'salty', false ) ); + + $this->assertSame( + 'd9ade0c7d4349e9df9094e61c33a5a0d5644fde2+\\', + $token->toStringAtTimestamp( 1447362018 ) + ); + $this->assertSame( + 'ee2f7a2488dea9176c224cfb400d43be5644fdea+\\', + $token->toStringAtTimestamp( 1447362026 ) + ); + } + + public function testGetTimestamp() { + $this->assertSame( + 1447362018, Token::getTimestamp( 'd9ade0c7d4349e9df9094e61c33a5a0d5644fde2+\\' ) + ); + $this->assertSame( + 1447362026, Token::getTimestamp( 'ee2f7a2488dea9176c224cfb400d43be5644fdea+\\' ) + ); + $this->assertNull( Token::getTimestamp( 'ee2f7a2488dea9176c224cfb400d43be5644fdea-\\' ) ); + $this->assertNull( Token::getTimestamp( 'ee2f7a2488dea9176c224cfb400d43be+\\' ) ); + + $this->assertNull( Token::getTimestamp( 'ee2f7a2488dea9x76c224cfb400d43be5644fdea+\\' ) ); + } + + public function testMatch() { + $token = TestingAccessWrapper::newFromObject( new Token( 'sekret', 'salty', false ) ); + + $test = $token->toStringAtTimestamp( time() - 10 ); + $this->assertTrue( $token->match( $test ) ); + $this->assertTrue( $token->match( $test, 12 ) ); + $this->assertFalse( $token->match( $test, 8 ) ); + + $this->assertFalse( $token->match( 'ee2f7a2488dea9176c224cfb400d43be5644fdea-\\' ) ); + } + +} diff --git a/tests/phpunit/unit/includes/shell/FirejailCommandTest.php b/tests/phpunit/unit/includes/shell/FirejailCommandTest.php new file mode 100644 index 0000000000..e3a3b86b24 --- /dev/null +++ b/tests/phpunit/unit/includes/shell/FirejailCommandTest.php @@ -0,0 +1,83 @@ + + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + +use MediaWiki\Shell\FirejailCommand; +use MediaWiki\Shell\Shell; +use Wikimedia\TestingAccessWrapper; + +class FirejailCommandTest extends MediaWikiUnitTestCase { + + public function provideBuildFinalCommand() { + global $IP; + // phpcs:ignore Generic.Files.LineLength + $env = "'MW_INCLUDE_STDERR=;MW_CPU_LIMIT=180; MW_CGROUP='\'''\''; MW_MEM_LIMIT=307200; MW_FILE_SIZE_LIMIT=102400; MW_WALL_CLOCK_LIMIT=180; MW_USE_LOG_PIPE=yes'"; + $limit = "/bin/bash '$IP/includes/shell/limit.sh'"; + $profile = "--profile=$IP/includes/shell/firejail.profile"; + $blacklist = '--blacklist=' . realpath( MW_CONFIG_FILE ); + $default = "$blacklist --noroot --seccomp --private-dev"; + return [ + [ + 'No restrictions', + 'ls', 0, "$limit ''\''ls'\''' $env" + ], + [ + 'default restriction', + 'ls', Shell::RESTRICT_DEFAULT, + "$limit 'firejail --quiet $profile $default -- '\''ls'\''' $env" + ], + [ + 'no network', + 'ls', Shell::NO_NETWORK, + "$limit 'firejail --quiet $profile --net=none -- '\''ls'\''' $env" + ], + [ + 'default restriction & no network', + 'ls', Shell::RESTRICT_DEFAULT | Shell::NO_NETWORK, + "$limit 'firejail --quiet $profile $default --net=none -- '\''ls'\''' $env" + ], + [ + 'seccomp', + 'ls', Shell::SECCOMP, + "$limit 'firejail --quiet $profile --seccomp -- '\''ls'\''' $env" + ], + [ + 'seccomp & no execve', + 'ls', Shell::SECCOMP | Shell::NO_EXECVE, + "$limit 'firejail --quiet $profile --shell=none --seccomp=execve -- '\''ls'\''' $env" + ], + ]; + } + + /** + * @covers \MediaWiki\Shell\FirejailCommand::buildFinalCommand() + * @dataProvider provideBuildFinalCommand + */ + public function testBuildFinalCommand( $desc, $params, $flags, $expected ) { + $command = new FirejailCommand( 'firejail' ); + $command + ->params( $params ) + ->restrict( $flags ); + $wrapper = TestingAccessWrapper::newFromObject( $command ); + $output = $wrapper->buildFinalCommand( $wrapper->command ); + $this->assertEquals( $expected, $output[0], $desc ); + } + +} diff --git a/tests/phpunit/unit/includes/site/MediaWikiPageNameNormalizerTest.php b/tests/phpunit/unit/includes/site/MediaWikiPageNameNormalizerTest.php new file mode 100644 index 0000000000..d426306e3f --- /dev/null +++ b/tests/phpunit/unit/includes/site/MediaWikiPageNameNormalizerTest.php @@ -0,0 +1,114 @@ +assertSame( + $expected, + $normalizer->normalizePageName( $pageName, 'https://www.wikidata.org/w/api.php' ) + ); + } + + public function normalizePageTitleProvider() { + // Response are taken from wikidata and kkwiki using the following API request + // api.php?action=query&prop=info&redirects=1&converttitles=1&format=json&titles=… + return [ + 'universe (Q1)' => [ + 'Q1', + 'Q1', + '{"batchcomplete":"","query":{"pages":{"129":{"pageid":129,"ns":0,' + . '"title":"Q1","contentmodel":"wikibase-item","pagelanguage":"en",' + . '"pagelanguagehtmlcode":"en","pagelanguagedir":"ltr",' + . '"touched":"2016-06-23T05:11:21Z","lastrevid":350004448,"length":58001}}}}' + ], + 'Q404 redirects to Q395' => [ + 'Q395', + 'Q404', + '{"batchcomplete":"","query":{"redirects":[{"from":"Q404","to":"Q395"}],"pages"' + . ':{"601":{"pageid":601,"ns":0,"title":"Q395","contentmodel":"wikibase-item",' + . '"pagelanguage":"en","pagelanguagehtmlcode":"en","pagelanguagedir":"ltr",' + . '"touched":"2016-06-23T08:00:20Z","lastrevid":350021914,"length":60108}}}}' + ], + 'D converted to Д (Latin to Cyrillic) (taken from kkwiki)' => [ + 'Д', + 'D', + '{"batchcomplete":"","query":{"converted":[{"from":"D","to":"\u0414"}],' + . '"pages":{"510541":{"pageid":510541,"ns":0,"title":"\u0414",' + . '"contentmodel":"wikitext","pagelanguage":"kk","pagelanguagehtmlcode":"kk",' + . '"pagelanguagedir":"ltr","touched":"2015-11-22T09:16:18Z",' + . '"lastrevid":2373618,"length":3501}}}}' + ], + 'there is no Q0' => [ + false, + 'Q0', + '{"batchcomplete":"","query":{"pages":{"-1":{"ns":0,"title":"Q0",' + . '"missing":"","contentmodel":"wikibase-item","pagelanguage":"en",' + . '"pagelanguagehtmlcode":"en","pagelanguagedir":"ltr"}}}}' + ], + 'invalid title' => [ + false, + '{{', + '{"batchcomplete":"","query":{"pages":{"-1":{"title":"{{",' + . '"invalidreason":"The requested page title contains invalid ' + . 'characters: \"{\".","invalid":""}}}}' + ], + 'error on get' => [ false, 'ABC', false ] + ]; + } + +} + +/** + * @private + * @see Http + */ +class MediaWikiPageNameNormalizerTestMockHttp extends Http { + + /** + * @var mixed + */ + public static $response; + + public static function get( $url, array $options = [], $caller = __METHOD__ ) { + PHPUnit_Framework_Assert::assertInternalType( 'string', $url ); + PHPUnit_Framework_Assert::assertInternalType( 'string', $caller ); + + return self::$response; + } +} diff --git a/tests/phpunit/unit/includes/utils/ZipDirectoryReaderTest.php b/tests/phpunit/unit/includes/utils/ZipDirectoryReaderTest.php new file mode 100644 index 0000000000..be7e224e03 --- /dev/null +++ b/tests/phpunit/unit/includes/utils/ZipDirectoryReaderTest.php @@ -0,0 +1,86 @@ +zipDir = __DIR__ . '/../../../data/zip'; + } + + function zipCallback( $entry ) { + $this->entries[] = $entry; + } + + function readZipAssertError( $file, $error, $assertMessage ) { + $this->entries = []; + $status = ZipDirectoryReader::read( "{$this->zipDir}/$file", [ $this, 'zipCallback' ] ); + $this->assertTrue( $status->hasMessage( $error ), $assertMessage ); + } + + function readZipAssertSuccess( $file, $assertMessage ) { + $this->entries = []; + $status = ZipDirectoryReader::read( "{$this->zipDir}/$file", [ $this, 'zipCallback' ] ); + $this->assertTrue( $status->isOK(), $assertMessage ); + } + + public function testEmpty() { + $this->readZipAssertSuccess( 'empty.zip', 'Empty zip' ); + } + + public function testMultiDisk0() { + $this->readZipAssertError( 'split.zip', 'zip-unsupported', + 'Split zip error' ); + } + + public function testNoSignature() { + $this->readZipAssertError( 'nosig.zip', 'zip-wrong-format', + 'No signature should give "wrong format" error' ); + } + + public function testSimple() { + $this->readZipAssertSuccess( 'class.zip', 'Simple ZIP' ); + $this->assertEquals( $this->entries, [ [ + 'name' => 'Class.class', + 'mtime' => '20010115000000', + 'size' => 1, + ] ] ); + } + + public function testBadCentralEntrySignature() { + $this->readZipAssertError( 'wrong-central-entry-sig.zip', 'zip-bad', + 'Bad central entry error' ); + } + + public function testTrailingBytes() { + // Due to T40432 this is now zip-wrong-format instead of zip-bad + $this->readZipAssertError( 'trail.zip', 'zip-wrong-format', + 'Trailing bytes error' ); + } + + public function testWrongCDStart() { + $this->readZipAssertError( 'wrong-cd-start-disk.zip', 'zip-unsupported', + 'Wrong CD start disk error' ); + } + + public function testCentralDirectoryGap() { + $this->readZipAssertError( 'cd-gap.zip', 'zip-bad', + 'CD gap error' ); + } + + public function testCentralDirectoryTruncated() { + $this->readZipAssertError( 'cd-truncated.zip', 'zip-bad', + 'CD truncated error (should hit unpack() overrun)' ); + } + + public function testLooksLikeZip64() { + $this->readZipAssertError( 'looks-like-zip64.zip', 'zip-unsupported', + 'A file which looks like ZIP64 but isn\'t, should give error' ); + } +}