From d417de70fcf39e0a7a15ba780b597914d16ca0f7 Mon Sep 17 00:00:00 2001 From: Pierre Schmitz Date: Wed, 12 Mar 2014 18:12:23 +0100 Subject: Update to MediaWiki 1.22.4 --- .../includes/api/ApiAccountCreationTest.php | 159 ---- tests/phpunit/includes/api/ApiBlockTest.php | 95 -- tests/phpunit/includes/api/ApiEditPageTest.php | 417 -------- tests/phpunit/includes/api/ApiOptionsTest.php | 420 -------- tests/phpunit/includes/api/ApiParseTest.php | 29 - tests/phpunit/includes/api/ApiPurgeTest.php | 40 - tests/phpunit/includes/api/ApiTest.php | 259 ----- tests/phpunit/includes/api/ApiTestCase.php | 253 ----- tests/phpunit/includes/api/ApiTestCaseUpload.php | 149 --- tests/phpunit/includes/api/ApiUploadTest.php | 561 ----------- tests/phpunit/includes/api/ApiWatchTest.php | 148 --- .../phpunit/includes/api/PrefixUniquenessTest.php | 25 - .../phpunit/includes/api/RandomImageGenerator.php | 468 --------- .../includes/api/format/ApiFormatPhpTest.php | 17 - .../includes/api/format/ApiFormatTestBase.php | 22 - .../phpunit/includes/api/generateRandomImages.php | 46 - .../includes/api/query/ApiQueryBasicTest.php | 395 -------- .../includes/api/query/ApiQueryContinue2Test.php | 68 -- .../includes/api/query/ApiQueryContinueTest.php | 313 ------ .../api/query/ApiQueryContinueTestBase.php | 209 ---- .../includes/api/query/ApiQueryRevisionsTest.php | 39 - tests/phpunit/includes/api/query/ApiQueryTest.php | 66 -- .../includes/api/query/ApiQueryTestBase.php | 150 --- tests/phpunit/includes/api/words.txt | 1000 -------------------- 24 files changed, 5348 deletions(-) delete mode 100644 tests/phpunit/includes/api/ApiAccountCreationTest.php delete mode 100644 tests/phpunit/includes/api/ApiBlockTest.php delete mode 100644 tests/phpunit/includes/api/ApiEditPageTest.php delete mode 100644 tests/phpunit/includes/api/ApiOptionsTest.php delete mode 100644 tests/phpunit/includes/api/ApiParseTest.php delete mode 100644 tests/phpunit/includes/api/ApiPurgeTest.php delete mode 100644 tests/phpunit/includes/api/ApiTest.php delete mode 100644 tests/phpunit/includes/api/ApiTestCase.php delete mode 100644 tests/phpunit/includes/api/ApiTestCaseUpload.php delete mode 100644 tests/phpunit/includes/api/ApiUploadTest.php delete mode 100644 tests/phpunit/includes/api/ApiWatchTest.php delete mode 100644 tests/phpunit/includes/api/PrefixUniquenessTest.php delete mode 100644 tests/phpunit/includes/api/RandomImageGenerator.php delete mode 100644 tests/phpunit/includes/api/format/ApiFormatPhpTest.php delete mode 100644 tests/phpunit/includes/api/format/ApiFormatTestBase.php delete mode 100644 tests/phpunit/includes/api/generateRandomImages.php delete mode 100644 tests/phpunit/includes/api/query/ApiQueryBasicTest.php delete mode 100644 tests/phpunit/includes/api/query/ApiQueryContinue2Test.php delete mode 100644 tests/phpunit/includes/api/query/ApiQueryContinueTest.php delete mode 100644 tests/phpunit/includes/api/query/ApiQueryContinueTestBase.php delete mode 100644 tests/phpunit/includes/api/query/ApiQueryRevisionsTest.php delete mode 100644 tests/phpunit/includes/api/query/ApiQueryTest.php delete mode 100644 tests/phpunit/includes/api/query/ApiQueryTestBase.php delete mode 100644 tests/phpunit/includes/api/words.txt (limited to 'tests/phpunit/includes/api') diff --git a/tests/phpunit/includes/api/ApiAccountCreationTest.php b/tests/phpunit/includes/api/ApiAccountCreationTest.php deleted file mode 100644 index 68f80ac9..00000000 --- a/tests/phpunit/includes/api/ApiAccountCreationTest.php +++ /dev/null @@ -1,159 +0,0 @@ -setMwGlobals( array( 'wgEnableEmail' => true ) ); - } - - /** - * Test the account creation API with a valid request. Also - * make sure the new account can log in and is valid. - * - * This test does multiple API requests so it might end up being - * a bit slow. Raise the default timeout. - * @group medium - */ - public function testValid() { - global $wgServer; - - if ( !isset( $wgServer ) ) { - $this->markTestIncomplete( 'This test needs $wgServer to be set in LocalSettings.php' ); - } - - $password = User::randomPassword(); - - $ret = $this->doApiRequest( array( - 'action' => 'createaccount', - 'name' => 'Apitestnew', - 'password' => $password, - 'email' => 'test@domain.test', - 'realname' => 'Test Name' - ) ); - - $result = $ret[0]; - $this->assertNotInternalType( 'bool', $result ); - $this->assertNotInternalType( 'null', $result['createaccount'] ); - - // Should first ask for token. - $a = $result['createaccount']; - $this->assertEquals( 'needtoken', $a['result'] ); - $token = $a['token']; - - // Finally create the account - $ret = $this->doApiRequest( - array( - 'action' => 'createaccount', - 'name' => 'Apitestnew', - 'password' => $password, - 'token' => $token, - 'email' => 'test@domain.test', - 'realname' => 'Test Name' - ), - $ret[2] - ); - - $result = $ret[0]; - $this->assertNotInternalType( 'bool', $result ); - $this->assertEquals( 'success', $result['createaccount']['result'] ); - - // Try logging in with the new user. - $ret = $this->doApiRequest( array( - 'action' => 'login', - 'lgname' => 'Apitestnew', - 'lgpassword' => $password, - ) ); - - $result = $ret[0]; - $this->assertNotInternalType( 'bool', $result ); - $this->assertNotInternalType( 'null', $result['login'] ); - - $a = $result['login']['result']; - $this->assertEquals( 'NeedToken', $a ); - $token = $result['login']['token']; - - $ret = $this->doApiRequest( - array( - 'action' => 'login', - 'lgtoken' => $token, - 'lgname' => 'Apitestnew', - 'lgpassword' => $password, - ), - $ret[2] - ); - - $result = $ret[0]; - - $this->assertNotInternalType( 'bool', $result ); - $a = $result['login']['result']; - - $this->assertEquals( 'Success', $a ); - - // log out to destroy the session - $ret = $this->doApiRequest( - array( - 'action' => 'logout', - ), - $ret[2] - ); - $this->assertEquals( array(), $ret[0] ); - } - - /** - * Make sure requests with no names are invalid. - * @expectedException UsageException - */ - public function testNoName() { - $this->doApiRequest( array( - 'action' => 'createaccount', - 'token' => LoginForm::getCreateaccountToken(), - 'password' => 'password', - ) ); - } - - /** - * Make sure requests with no password are invalid. - * @expectedException UsageException - */ - public function testNoPassword() { - $this->doApiRequest( array( - 'action' => 'createaccount', - 'name' => 'testName', - 'token' => LoginForm::getCreateaccountToken(), - ) ); - } - - /** - * Make sure requests with existing users are invalid. - * @expectedException UsageException - */ - public function testExistingUser() { - $this->doApiRequest( array( - 'action' => 'createaccount', - 'name' => 'Apitestsysop', - 'token' => LoginForm::getCreateaccountToken(), - 'password' => 'password', - 'email' => 'test@domain.test', - ) ); - } - - /** - * Make sure requests with invalid emails are invalid. - * @expectedException UsageException - */ - public function testInvalidEmail() { - $this->doApiRequest( array( - 'action' => 'createaccount', - 'name' => 'Test User', - 'token' => LoginForm::getCreateaccountToken(), - 'password' => 'password', - 'email' => 'invalid', - ) ); - } -} diff --git a/tests/phpunit/includes/api/ApiBlockTest.php b/tests/phpunit/includes/api/ApiBlockTest.php deleted file mode 100644 index 8afb748a..00000000 --- a/tests/phpunit/includes/api/ApiBlockTest.php +++ /dev/null @@ -1,95 +0,0 @@ -doLogin(); - } - - function getTokens() { - return $this->getTokenList( self::$users['sysop'] ); - } - - function addDBData() { - $user = User::newFromName( 'UTApiBlockee' ); - - if ( $user->getId() == 0 ) { - $user->addToDatabase(); - $user->setPassword( 'UTApiBlockeePassword' ); - - $user->saveSettings(); - } - } - - /** - * This test has probably always been broken and use an invalid token - * Bug tracking brokenness is https://bugzilla.wikimedia.org/35646 - * - * Root cause is https://gerrit.wikimedia.org/r/3434 - * Which made the Block/Unblock API to actually verify the token - * previously always considered valid (bug 34212). - */ - public function testMakeNormalBlock() { - $tokens = $this->getTokens(); - - $user = User::newFromName( 'UTApiBlockee' ); - - if ( !$user->getId() ) { - $this->markTestIncomplete( "The user UTApiBlockee does not exist" ); - } - - if ( !array_key_exists( 'blocktoken', $tokens ) ) { - $this->markTestIncomplete( "No block token found" ); - } - - $this->doApiRequest( array( - 'action' => 'block', - 'user' => 'UTApiBlockee', - 'reason' => 'Some reason', - 'token' => $tokens['blocktoken'] ), null, false, self::$users['sysop']->user ); - - $block = Block::newFromTarget( 'UTApiBlockee' ); - - $this->assertTrue( !is_null( $block ), 'Block is valid' ); - - $this->assertEquals( 'UTApiBlockee', (string)$block->getTarget() ); - $this->assertEquals( 'Some reason', $block->mReason ); - $this->assertEquals( 'infinity', $block->mExpiry ); - } - - /** - * Attempting to block without a token should give a UsageException with - * error message: - * "The token parameter must be set" - * - * @dataProvider provideBlockUnblockAction - * @expectedException UsageException - */ - public function testBlockingActionWithNoToken( $action ) { - $this->doApiRequest( - array( - 'action' => $action, - 'user' => 'UTApiBlockee', - 'reason' => 'Some reason', - ), - null, - false, - self::$users['sysop']->user - ); - } - - /** - * Just provide the 'block' and 'unblock' action to test both API calls - */ - public static function provideBlockUnblockAction() { - return array( - array( 'block' ), - array( 'unblock' ), - ); - } -} diff --git a/tests/phpunit/includes/api/ApiEditPageTest.php b/tests/phpunit/includes/api/ApiEditPageTest.php deleted file mode 100644 index 0c49b12b..00000000 --- a/tests/phpunit/includes/api/ApiEditPageTest.php +++ /dev/null @@ -1,417 +0,0 @@ -resetNamespaces(); # reset namespace cache - - $this->doLogin(); - } - - public function tearDown() { - global $wgExtraNamespaces, $wgNamespaceContentModels, $wgContentHandlers, $wgContLang; - - unset( $wgExtraNamespaces[12312] ); - unset( $wgExtraNamespaces[12313] ); - - unset( $wgNamespaceContentModels[12312] ); - unset( $wgContentHandlers["testing"] ); - - MWNamespace::getCanonicalNamespaces( true ); # reset namespace cache - $wgContLang->resetNamespaces(); # reset namespace cache - - parent::tearDown(); - } - - public function testEdit() { - $name = 'Help:ApiEditPageTest_testEdit'; // assume Help namespace to default to wikitext - - // -- test new page -------------------------------------------- - $apiResult = $this->doApiRequestWithToken( array( - 'action' => 'edit', - 'title' => $name, - 'text' => 'some text', - ) ); - $apiResult = $apiResult[0]; - - // Validate API result data - $this->assertArrayHasKey( 'edit', $apiResult ); - $this->assertArrayHasKey( 'result', $apiResult['edit'] ); - $this->assertEquals( 'Success', $apiResult['edit']['result'] ); - - $this->assertArrayHasKey( 'new', $apiResult['edit'] ); - $this->assertArrayNotHasKey( 'nochange', $apiResult['edit'] ); - - $this->assertArrayHasKey( 'pageid', $apiResult['edit'] ); - - // -- test existing page, no change ---------------------------- - $data = $this->doApiRequestWithToken( array( - 'action' => 'edit', - 'title' => $name, - 'text' => 'some text', - ) ); - - $this->assertEquals( 'Success', $data[0]['edit']['result'] ); - - $this->assertArrayNotHasKey( 'new', $data[0]['edit'] ); - $this->assertArrayHasKey( 'nochange', $data[0]['edit'] ); - - // -- test existing page, with change -------------------------- - $data = $this->doApiRequestWithToken( array( - 'action' => 'edit', - 'title' => $name, - 'text' => 'different text' - ) ); - - $this->assertEquals( 'Success', $data[0]['edit']['result'] ); - - $this->assertArrayNotHasKey( 'new', $data[0]['edit'] ); - $this->assertArrayNotHasKey( 'nochange', $data[0]['edit'] ); - - $this->assertArrayHasKey( 'oldrevid', $data[0]['edit'] ); - $this->assertArrayHasKey( 'newrevid', $data[0]['edit'] ); - $this->assertNotEquals( - $data[0]['edit']['newrevid'], - $data[0]['edit']['oldrevid'], - "revision id should change after edit" - ); - } - - public function testNonTextEdit() { - $name = 'Dummy:ApiEditPageTest_testNonTextEdit'; - $data = serialize( 'some bla bla text' ); - - // -- test new page -------------------------------------------- - $apiResult = $this->doApiRequestWithToken( array( - 'action' => 'edit', - 'title' => $name, - 'text' => $data, ) ); - $apiResult = $apiResult[0]; - - // Validate API result data - $this->assertArrayHasKey( 'edit', $apiResult ); - $this->assertArrayHasKey( 'result', $apiResult['edit'] ); - $this->assertEquals( 'Success', $apiResult['edit']['result'] ); - - $this->assertArrayHasKey( 'new', $apiResult['edit'] ); - $this->assertArrayNotHasKey( 'nochange', $apiResult['edit'] ); - - $this->assertArrayHasKey( 'pageid', $apiResult['edit'] ); - - // validate resulting revision - $page = WikiPage::factory( Title::newFromText( $name ) ); - $this->assertEquals( "testing", $page->getContentModel() ); - $this->assertEquals( $data, $page->getContent()->serialize() ); - } - - public static function provideEditAppend() { - return array( - array( #0: append - 'foo', 'append', 'bar', "foobar" - ), - array( #1: prepend - 'foo', 'prepend', 'bar', "barfoo" - ), - array( #2: append to empty page - '', 'append', 'foo', "foo" - ), - array( #3: prepend to empty page - '', 'prepend', 'foo', "foo" - ), - array( #4: append to non-existing page - null, 'append', 'foo', "foo" - ), - array( #5: prepend to non-existing page - null, 'prepend', 'foo', "foo" - ), - ); - } - - /** - * @dataProvider provideEditAppend - */ - public function testEditAppend( $text, $op, $append, $expected ) { - static $count = 0; - $count++; - - // assume NS_HELP defaults to wikitext - $name = "Help:ApiEditPageTest_testEditAppend_$count"; - - // -- create page (or not) ----------------------------------------- - if ( $text !== null ) { - if ( $text === '' ) { - // can't create an empty page, so create it with some content - $this->doApiRequestWithToken( array( - 'action' => 'edit', - 'title' => $name, - 'text' => '(dummy)', ) ); - } - - list( $re ) = $this->doApiRequestWithToken( array( - 'action' => 'edit', - 'title' => $name, - 'text' => $text, ) ); - - $this->assertEquals( 'Success', $re['edit']['result'] ); // sanity - } - - // -- try append/prepend -------------------------------------------- - list( $re ) = $this->doApiRequestWithToken( array( - 'action' => 'edit', - 'title' => $name, - $op . 'text' => $append, ) ); - - $this->assertEquals( 'Success', $re['edit']['result'] ); - - // -- validate ----------------------------------------------------- - $page = new WikiPage( Title::newFromText( $name ) ); - $content = $page->getContent(); - $this->assertNotNull( $content, 'Page should have been created' ); - - $text = $content->getNativeData(); - - $this->assertEquals( $expected, $text ); - } - - /** - * Test editing of sections - */ - public function testEditSection() { - $name = 'Help:ApiEditPageTest_testEditSection'; - $page = WikiPage::factory( Title::newFromText( $name ) ); - $text = "==section 1==\ncontent 1\n==section 2==\ncontent2"; - // Preload the page with some text - $page->doEditContent( ContentHandler::makeContent( $text, $page->getTitle() ), 'summary' ); - - list( $re ) = $this->doApiRequestWithToken( array( - 'action' => 'edit', - 'title' => $name, - 'section' => '1', - 'text' => "==section 1==\nnew content 1", - ) ); - $this->assertEquals( 'Success', $re['edit']['result'] ); - $newtext = WikiPage::factory( Title::newFromText( $name) )->getContent( Revision::RAW )->getNativeData(); - $this->assertEquals( $newtext, "==section 1==\nnew content 1\n\n==section 2==\ncontent2" ); - - // Test that we raise a 'nosuchsection' error - try { - $this->doApiRequestWithToken( array( - 'action' => 'edit', - 'title' => $name, - 'section' => '9999', - 'text' => 'text', - ) ); - $this->fail( "Should have raised a UsageException" ); - } catch ( UsageException $e ) { - $this->assertEquals( $e->getCodeString(), 'nosuchsection' ); - } - } - - /** - * Test action=edit§ion=new - * Run it twice so we test adding a new section on a - * page that doesn't exist (bug 52830) and one that - * does exist - */ - public function testEditNewSection() { - $name = 'Help:ApiEditPageTest_testEditNewSection'; - - // Test on a page that does not already exist - $this->assertFalse( Title::newFromText( $name )->exists() ); - list( $re ) = $this->doApiRequestWithToken( array( - 'action' => 'edit', - 'title' => $name, - 'section' => 'new', - 'text' => 'test', - 'summary' => 'header', - )); - - $this->assertEquals( 'Success', $re['edit']['result'] ); - // Check the page text is correct - $text = WikiPage::factory( Title::newFromText( $name ) )->getContent( Revision::RAW )->getNativeData(); - $this->assertEquals( $text, "== header ==\n\ntest" ); - - // Now on one that does - $this->assertTrue( Title::newFromText( $name )->exists() ); - list( $re2 ) = $this->doApiRequestWithToken( array( - 'action' => 'edit', - 'title' => $name, - 'section' => 'new', - 'text' => 'test', - 'summary' => 'header', - )); - - $this->assertEquals( 'Success', $re2['edit']['result'] ); - $text = WikiPage::factory( Title::newFromText( $name ) )->getContent( Revision::RAW )->getNativeData(); - $this->assertEquals( $text, "== header ==\n\ntest\n\n== header ==\n\ntest" ); - } - - public function testEditConflict() { - static $count = 0; - $count++; - - // assume NS_HELP defaults to wikitext - $name = "Help:ApiEditPageTest_testEditConflict_$count"; - $title = Title::newFromText( $name ); - - $page = WikiPage::factory( $title ); - - // base edit - $page->doEditContent( new WikitextContent( "Foo" ), - "testing 1", EDIT_NEW, false, self::$users['sysop']->user ); - $this->forceRevisionDate( $page, '20120101000000' ); - $baseTime = $page->getRevision()->getTimestamp(); - - // conflicting edit - $page->doEditContent( new WikitextContent( "Foo bar" ), - "testing 2", EDIT_UPDATE, $page->getLatest(), self::$users['uploader']->user ); - $this->forceRevisionDate( $page, '20120101020202' ); - - // try to save edit, expect conflict - try { - $this->doApiRequestWithToken( array( - 'action' => 'edit', - 'title' => $name, - 'text' => 'nix bar!', - 'basetimestamp' => $baseTime, - ), null, self::$users['sysop']->user ); - - $this->fail( 'edit conflict expected' ); - } catch ( UsageException $ex ) { - $this->assertEquals( 'editconflict', $ex->getCodeString() ); - } - } - - public function testEditConflict_redirect() { - static $count = 0; - $count++; - - // assume NS_HELP defaults to wikitext - $name = "Help:ApiEditPageTest_testEditConflict_redirect_$count"; - $title = Title::newFromText( $name ); - $page = WikiPage::factory( $title ); - - $rname = "Help:ApiEditPageTest_testEditConflict_redirect_r$count"; - $rtitle = Title::newFromText( $rname ); - $rpage = WikiPage::factory( $rtitle ); - - // base edit for content - $page->doEditContent( new WikitextContent( "Foo" ), - "testing 1", EDIT_NEW, false, self::$users['sysop']->user ); - $this->forceRevisionDate( $page, '20120101000000' ); - $baseTime = $page->getRevision()->getTimestamp(); - - // base edit for redirect - $rpage->doEditContent( new WikitextContent( "#REDIRECT [[$name]]" ), - "testing 1", EDIT_NEW, false, self::$users['sysop']->user ); - $this->forceRevisionDate( $rpage, '20120101000000' ); - - // conflicting edit to redirect - $rpage->doEditContent( new WikitextContent( "#REDIRECT [[$name]]\n\n[[Category:Test]]" ), - "testing 2", EDIT_UPDATE, $page->getLatest(), self::$users['uploader']->user ); - $this->forceRevisionDate( $rpage, '20120101020202' ); - - // try to save edit; should work, because we follow the redirect - list( $re, , ) = $this->doApiRequestWithToken( array( - 'action' => 'edit', - 'title' => $rname, - 'text' => 'nix bar!', - 'basetimestamp' => $baseTime, - 'redirect' => true, - ), null, self::$users['sysop']->user ); - - $this->assertEquals( 'Success', $re['edit']['result'], - "no edit conflict expected when following redirect" ); - - // try again, without following the redirect. Should fail. - try { - $this->doApiRequestWithToken( array( - 'action' => 'edit', - 'title' => $rname, - 'text' => 'nix bar!', - 'basetimestamp' => $baseTime, - ), null, self::$users['sysop']->user ); - - $this->fail( 'edit conflict expected' ); - } catch ( UsageException $ex ) { - $this->assertEquals( 'editconflict', $ex->getCodeString() ); - } - } - - public function testEditConflict_bug41990() { - static $count = 0; - $count++; - - /* - * bug 41990: if the target page has a newer revision than the redirect, then editing the - * redirect while specifying 'redirect' and *not* specifying 'basetimestamp' erroneously - * caused an edit conflict to be detected. - */ - - // assume NS_HELP defaults to wikitext - $name = "Help:ApiEditPageTest_testEditConflict_redirect_bug41990_$count"; - $title = Title::newFromText( $name ); - $page = WikiPage::factory( $title ); - - $rname = "Help:ApiEditPageTest_testEditConflict_redirect_bug41990_r$count"; - $rtitle = Title::newFromText( $rname ); - $rpage = WikiPage::factory( $rtitle ); - - // base edit for content - $page->doEditContent( new WikitextContent( "Foo" ), - "testing 1", EDIT_NEW, false, self::$users['sysop']->user ); - $this->forceRevisionDate( $page, '20120101000000' ); - - // base edit for redirect - $rpage->doEditContent( new WikitextContent( "#REDIRECT [[$name]]" ), - "testing 1", EDIT_NEW, false, self::$users['sysop']->user ); - $this->forceRevisionDate( $rpage, '20120101000000' ); - $baseTime = $rpage->getRevision()->getTimestamp(); - - // new edit to content - $page->doEditContent( new WikitextContent( "Foo bar" ), - "testing 2", EDIT_UPDATE, $page->getLatest(), self::$users['uploader']->user ); - $this->forceRevisionDate( $rpage, '20120101020202' ); - - // try to save edit; should work, following the redirect. - list( $re, , ) = $this->doApiRequestWithToken( array( - 'action' => 'edit', - 'title' => $rname, - 'text' => 'nix bar!', - 'redirect' => true, - ), null, self::$users['sysop']->user ); - - $this->assertEquals( 'Success', $re['edit']['result'], - "no edit conflict expected here" ); - } - - protected function forceRevisionDate( WikiPage $page, $timestamp ) { - $dbw = wfGetDB( DB_MASTER ); - - $dbw->update( 'revision', - array( 'rev_timestamp' => $dbw->timestamp( $timestamp ) ), - array( 'rev_id' => $page->getLatest() ) ); - - $page->clear(); - } -} diff --git a/tests/phpunit/includes/api/ApiOptionsTest.php b/tests/phpunit/includes/api/ApiOptionsTest.php deleted file mode 100644 index ad1e73ab..00000000 --- a/tests/phpunit/includes/api/ApiOptionsTest.php +++ /dev/null @@ -1,420 +0,0 @@ - 'success' ); - - protected function setUp() { - parent::setUp(); - - $this->mUserMock = $this->getMockBuilder( 'User' ) - ->disableOriginalConstructor() - ->getMock(); - - // Set up groups and rights - $this->mUserMock->expects( $this->any() ) - ->method( 'getEffectiveGroups' )->will( $this->returnValue( array( '*', 'user' ) ) ); - $this->mUserMock->expects( $this->any() ) - ->method( 'isAllowed' )->will( $this->returnValue( true ) ); - - // Set up callback for User::getOptionKinds - $this->mUserMock->expects( $this->any() ) - ->method( 'getOptionKinds' )->will( $this->returnCallback( array( $this, 'getOptionKinds' ) ) ); - - // Create a new context - $this->mContext = new DerivativeContext( new RequestContext() ); - $this->mContext->getContext()->setTitle( Title::newFromText( 'Test' ) ); - $this->mContext->setUser( $this->mUserMock ); - - $main = new ApiMain( $this->mContext ); - - // Empty session - $this->mSession = array(); - - $this->mTested = new ApiOptions( $main, 'options' ); - - global $wgHooks; - if ( !isset( $wgHooks['GetPreferences'] ) ) { - $wgHooks['GetPreferences'] = array(); - } - $this->mOldGetPreferencesHooks = $wgHooks['GetPreferences']; - $wgHooks['GetPreferences'][] = array( $this, 'hookGetPreferences' ); - } - - protected function tearDown() { - global $wgHooks; - - if ( $this->mOldGetPreferencesHooks !== false ) { - $wgHooks['GetPreferences'] = $this->mOldGetPreferencesHooks; - $this->mOldGetPreferencesHooks = false; - } - - parent::tearDown(); - } - - public function hookGetPreferences( $user, &$preferences ) { - $preferences = array(); - - foreach ( array( 'name', 'willBeNull', 'willBeEmpty', 'willBeHappy' ) as $k ) { - $preferences[$k] = array( - 'type' => 'text', - 'section' => 'test', - 'label' => ' ', - ); - } - - $preferences['testmultiselect'] = array( - 'type' => 'multiselect', - 'options' => array( - 'Test' => array( - 'Some HTML here for option 1' => 'opt1', - 'Some HTML here for option 2' => 'opt2', - 'Some HTML here for option 3' => 'opt3', - 'Some HTML here for option 4' => 'opt4', - ), - ), - 'section' => 'test', - 'label' => ' ', - 'prefix' => 'testmultiselect-', - 'default' => array(), - ); - - return true; - } - - public function getOptionKinds( IContextSource $context, $options = null ) { - // Match with above. - $kinds = array( - 'name' => 'registered', - 'willBeNull' => 'registered', - 'willBeEmpty' => 'registered', - 'willBeHappy' => 'registered', - 'testmultiselect-opt1' => 'registered-multiselect', - 'testmultiselect-opt2' => 'registered-multiselect', - 'testmultiselect-opt3' => 'registered-multiselect', - 'testmultiselect-opt4' => 'registered-multiselect', - ); - - if ( $options === null ) { - return $kinds; - } - - $mapping = array(); - foreach ( $options as $key => $value ) { - if ( isset( $kinds[$key] ) ) { - $mapping[$key] = $kinds[$key]; - } elseif ( substr( $key, 0, 7 ) === 'userjs-' ) { - $mapping[$key] = 'userjs'; - } else { - $mapping[$key] = 'unused'; - } - } - - return $mapping; - } - - private function getSampleRequest( $custom = array() ) { - $request = array( - 'token' => '123ABC', - 'change' => null, - 'optionname' => null, - 'optionvalue' => null, - ); - - return array_merge( $request, $custom ); - } - - private function executeQuery( $request ) { - $this->mContext->setRequest( new FauxRequest( $request, true, $this->mSession ) ); - $this->mTested->execute(); - - return $this->mTested->getResult()->getData(); - } - - /** - * @expectedException UsageException - */ - public function testNoToken() { - $request = $this->getSampleRequest( array( 'token' => null ) ); - - $this->executeQuery( $request ); - } - - public function testAnon() { - $this->mUserMock->expects( $this->once() ) - ->method( 'isAnon' ) - ->will( $this->returnValue( true ) ); - - try { - $request = $this->getSampleRequest(); - - $this->executeQuery( $request ); - } catch ( UsageException $e ) { - $this->assertEquals( 'notloggedin', $e->getCodeString() ); - $this->assertEquals( 'Anonymous users cannot change preferences', $e->getMessage() ); - - return; - } - $this->fail( "UsageException was not thrown" ); - } - - public function testNoOptionname() { - try { - $request = $this->getSampleRequest( array( 'optionvalue' => '1' ) ); - - $this->executeQuery( $request ); - } catch ( UsageException $e ) { - $this->assertEquals( 'nooptionname', $e->getCodeString() ); - $this->assertEquals( 'The optionname parameter must be set', $e->getMessage() ); - - return; - } - $this->fail( "UsageException was not thrown" ); - } - - public function testNoChanges() { - $this->mUserMock->expects( $this->never() ) - ->method( 'resetOptions' ); - - $this->mUserMock->expects( $this->never() ) - ->method( 'setOption' ); - - $this->mUserMock->expects( $this->never() ) - ->method( 'saveSettings' ); - - try { - $request = $this->getSampleRequest(); - - $this->executeQuery( $request ); - } catch ( UsageException $e ) { - $this->assertEquals( 'nochanges', $e->getCodeString() ); - $this->assertEquals( 'No changes were requested', $e->getMessage() ); - - return; - } - $this->fail( "UsageException was not thrown" ); - } - - public function testReset() { - $this->mUserMock->expects( $this->once() ) - ->method( 'resetOptions' ) - ->with( $this->equalTo( array( 'all' ) ) ); - - $this->mUserMock->expects( $this->never() ) - ->method( 'setOption' ); - - $this->mUserMock->expects( $this->once() ) - ->method( 'saveSettings' ); - - $request = $this->getSampleRequest( array( 'reset' => '' ) ); - - $response = $this->executeQuery( $request ); - - $this->assertEquals( self::$Success, $response ); - } - - public function testResetKinds() { - $this->mUserMock->expects( $this->once() ) - ->method( 'resetOptions' ) - ->with( $this->equalTo( array( 'registered' ) ) ); - - $this->mUserMock->expects( $this->never() ) - ->method( 'setOption' ); - - $this->mUserMock->expects( $this->once() ) - ->method( 'saveSettings' ); - - $request = $this->getSampleRequest( array( 'reset' => '', 'resetkinds' => 'registered' ) ); - - $response = $this->executeQuery( $request ); - - $this->assertEquals( self::$Success, $response ); - } - - public function testOptionWithValue() { - $this->mUserMock->expects( $this->never() ) - ->method( 'resetOptions' ); - - $this->mUserMock->expects( $this->once() ) - ->method( 'setOption' ) - ->with( $this->equalTo( 'name' ), $this->equalTo( 'value' ) ); - - $this->mUserMock->expects( $this->once() ) - ->method( 'saveSettings' ); - - $request = $this->getSampleRequest( array( 'optionname' => 'name', 'optionvalue' => 'value' ) ); - - $response = $this->executeQuery( $request ); - - $this->assertEquals( self::$Success, $response ); - } - - public function testOptionResetValue() { - $this->mUserMock->expects( $this->never() ) - ->method( 'resetOptions' ); - - $this->mUserMock->expects( $this->once() ) - ->method( 'setOption' ) - ->with( $this->equalTo( 'name' ), $this->identicalTo( null ) ); - - $this->mUserMock->expects( $this->once() ) - ->method( 'saveSettings' ); - - $request = $this->getSampleRequest( array( 'optionname' => 'name' ) ); - $response = $this->executeQuery( $request ); - - $this->assertEquals( self::$Success, $response ); - } - - public function testChange() { - $this->mUserMock->expects( $this->never() ) - ->method( 'resetOptions' ); - - $this->mUserMock->expects( $this->at( 2 ) ) - ->method( 'getOptions' ); - - $this->mUserMock->expects( $this->at( 4 ) ) - ->method( 'setOption' ) - ->with( $this->equalTo( 'willBeNull' ), $this->identicalTo( null ) ); - - $this->mUserMock->expects( $this->at( 5 ) ) - ->method( 'getOptions' ); - - $this->mUserMock->expects( $this->at( 6 ) ) - ->method( 'setOption' ) - ->with( $this->equalTo( 'willBeEmpty' ), $this->equalTo( '' ) ); - - $this->mUserMock->expects( $this->at( 7 ) ) - ->method( 'getOptions' ); - - $this->mUserMock->expects( $this->at( 8 ) ) - ->method( 'setOption' ) - ->with( $this->equalTo( 'willBeHappy' ), $this->equalTo( 'Happy' ) ); - - $this->mUserMock->expects( $this->once() ) - ->method( 'saveSettings' ); - - $request = $this->getSampleRequest( array( 'change' => 'willBeNull|willBeEmpty=|willBeHappy=Happy' ) ); - - $response = $this->executeQuery( $request ); - - $this->assertEquals( self::$Success, $response ); - } - - public function testResetChangeOption() { - $this->mUserMock->expects( $this->once() ) - ->method( 'resetOptions' ); - - $this->mUserMock->expects( $this->at( 4 ) ) - ->method( 'getOptions' ); - - $this->mUserMock->expects( $this->at( 5 ) ) - ->method( 'setOption' ) - ->with( $this->equalTo( 'willBeHappy' ), $this->equalTo( 'Happy' ) ); - - $this->mUserMock->expects( $this->at( 6 ) ) - ->method( 'getOptions' ); - - $this->mUserMock->expects( $this->at( 7 ) ) - ->method( 'setOption' ) - ->with( $this->equalTo( 'name' ), $this->equalTo( 'value' ) ); - - $this->mUserMock->expects( $this->once() ) - ->method( 'saveSettings' ); - - $args = array( - 'reset' => '', - 'change' => 'willBeHappy=Happy', - 'optionname' => 'name', - 'optionvalue' => 'value' - ); - - $response = $this->executeQuery( $this->getSampleRequest( $args ) ); - - $this->assertEquals( self::$Success, $response ); - } - - public function testMultiSelect() { - $this->mUserMock->expects( $this->never() ) - ->method( 'resetOptions' ); - - $this->mUserMock->expects( $this->at( 3 ) ) - ->method( 'setOption' ) - ->with( $this->equalTo( 'testmultiselect-opt1' ), $this->identicalTo( true ) ); - - $this->mUserMock->expects( $this->at( 4 ) ) - ->method( 'setOption' ) - ->with( $this->equalTo( 'testmultiselect-opt2' ), $this->identicalTo( null ) ); - - $this->mUserMock->expects( $this->at( 5 ) ) - ->method( 'setOption' ) - ->with( $this->equalTo( 'testmultiselect-opt3' ), $this->identicalTo( false ) ); - - $this->mUserMock->expects( $this->at( 6 ) ) - ->method( 'setOption' ) - ->with( $this->equalTo( 'testmultiselect-opt4' ), $this->identicalTo( false ) ); - - $this->mUserMock->expects( $this->once() ) - ->method( 'saveSettings' ); - - $request = $this->getSampleRequest( array( - 'change' => 'testmultiselect-opt1=1|testmultiselect-opt2|testmultiselect-opt3=|testmultiselect-opt4=0' - ) ); - - $response = $this->executeQuery( $request ); - - $this->assertEquals( self::$Success, $response ); - } - - public function testUnknownOption() { - $this->mUserMock->expects( $this->never() ) - ->method( 'resetOptions' ); - - $this->mUserMock->expects( $this->never() ) - ->method( 'saveSettings' ); - - $request = $this->getSampleRequest( array( - 'change' => 'unknownOption=1' - ) ); - - $response = $this->executeQuery( $request ); - - $this->assertEquals( array( - 'options' => 'success', - 'warnings' => array( - 'options' => array( - '*' => "Validation error for 'unknownOption': not a valid preference" - ) - ) - ), $response ); - } - - public function testUserjsOption() { - $this->mUserMock->expects( $this->never() ) - ->method( 'resetOptions' ); - - $this->mUserMock->expects( $this->at( 3 ) ) - ->method( 'setOption' ) - ->with( $this->equalTo( 'userjs-option' ), $this->equalTo( '1' ) ); - - $this->mUserMock->expects( $this->once() ) - ->method( 'saveSettings' ); - - $request = $this->getSampleRequest( array( - 'change' => 'userjs-option=1' - ) ); - - $response = $this->executeQuery( $request ); - - $this->assertEquals( self::$Success, $response ); - } -} diff --git a/tests/phpunit/includes/api/ApiParseTest.php b/tests/phpunit/includes/api/ApiParseTest.php deleted file mode 100644 index 2d714e65..00000000 --- a/tests/phpunit/includes/api/ApiParseTest.php +++ /dev/null @@ -1,29 +0,0 @@ -doLogin(); - } - - public function testParseNonexistentPage() { - $somePage = mt_rand(); - - try { - $this->doApiRequest( array( - 'action' => 'parse', - 'page' => $somePage ) ); - - $this->fail( "API did not return an error when parsing a nonexistent page" ); - } catch ( UsageException $ex ) { - $this->assertEquals( 'missingtitle', $ex->getCodeString(), - "Parse request for nonexistent page must give 'missingtitle' error: " . var_export( $ex->getMessageArray(), true ) ); - } - } -} diff --git a/tests/phpunit/includes/api/ApiPurgeTest.php b/tests/phpunit/includes/api/ApiPurgeTest.php deleted file mode 100644 index 28b5ff4d..00000000 --- a/tests/phpunit/includes/api/ApiPurgeTest.php +++ /dev/null @@ -1,40 +0,0 @@ -doLogin(); - } - - /** - * @group Broken - */ - public function testPurgeMainPage() { - if ( !Title::newFromText( 'UTPage' )->exists() ) { - $this->markTestIncomplete( "The article [[UTPage]] does not exist" ); - } - - $somePage = mt_rand(); - - $data = $this->doApiRequest( array( - 'action' => 'purge', - 'titles' => 'UTPage|' . $somePage . '|%5D' ) ); - - $this->assertArrayHasKey( 'purge', $data[0], - "Must receive a 'purge' result from API" ); - - $this->assertEquals( 3, count( $data[0]['purge'] ), - "Purge request for three articles should give back three results received: " . var_export( $data[0]['purge'], true ) ); - - $pages = array( 'UTPage' => 'purged', $somePage => 'missing', '%5D' => 'invalid' ); - foreach ( $data[0]['purge'] as $v ) { - $this->assertArrayHasKey( $pages[$v['title']], $v ); - } - } -} diff --git a/tests/phpunit/includes/api/ApiTest.php b/tests/phpunit/includes/api/ApiTest.php deleted file mode 100644 index 472f8c4a..00000000 --- a/tests/phpunit/includes/api/ApiTest.php +++ /dev/null @@ -1,259 +0,0 @@ -assertEquals( - null, $mock->requireOnlyOneParameter( array( "filename" => "foo.txt", - "enablechunks" => false ), "filename", "enablechunks" ) ); - } - - /** - * @expectedException UsageException - */ - public function testRequireOnlyOneParameterZero() { - $mock = new MockApi(); - - $this->assertEquals( - null, $mock->requireOnlyOneParameter( array( "filename" => "foo.txt", - "enablechunks" => 0 ), "filename", "enablechunks" ) ); - } - - /** - * @expectedException UsageException - */ - public function testRequireOnlyOneParameterTrue() { - $mock = new MockApi(); - - $this->assertEquals( - null, $mock->requireOnlyOneParameter( array( "filename" => "foo.txt", - "enablechunks" => true ), "filename", "enablechunks" ) ); - } - - /** - * Test that the API will accept a FauxRequest and execute. The help action - * (default) throws a UsageException. Just validate we're getting proper XML - * - * @expectedException UsageException - */ - public function testApi() { - $api = new ApiMain( - new FauxRequest( array( 'action' => 'help', 'format' => 'xml' ) ) - ); - $api->execute(); - $api->getPrinter()->setBufferResult( true ); - $api->printResult( false ); - $resp = $api->getPrinter()->getBuffer(); - - libxml_use_internal_errors( true ); - $sxe = simplexml_load_string( $resp ); - $this->assertNotInternalType( "bool", $sxe ); - $this->assertThat( $sxe, $this->isInstanceOf( "SimpleXMLElement" ) ); - } - - /** - * Test result of attempted login with an empty username - */ - public function testApiLoginNoName() { - $data = $this->doApiRequest( array( 'action' => 'login', - 'lgname' => '', 'lgpassword' => self::$users['sysop']->password, - ) ); - $this->assertEquals( 'NoName', $data[0]['login']['result'] ); - } - - public function testApiLoginBadPass() { - global $wgServer; - - $user = self::$users['sysop']; - $user->user->logOut(); - - if ( !isset( $wgServer ) ) { - $this->markTestIncomplete( 'This test needs $wgServer to be set in LocalSettings.php' ); - } - $ret = $this->doApiRequest( array( - "action" => "login", - "lgname" => $user->username, - "lgpassword" => "bad", - ) ); - - $result = $ret[0]; - - $this->assertNotInternalType( "bool", $result ); - $a = $result["login"]["result"]; - $this->assertEquals( "NeedToken", $a ); - - $token = $result["login"]["token"]; - - $ret = $this->doApiRequest( - array( - "action" => "login", - "lgtoken" => $token, - "lgname" => $user->username, - "lgpassword" => "badnowayinhell", - ), - $ret[2] - ); - - $result = $ret[0]; - - $this->assertNotInternalType( "bool", $result ); - $a = $result["login"]["result"]; - - $this->assertEquals( "WrongPass", $a ); - } - - public function testApiLoginGoodPass() { - global $wgServer; - - if ( !isset( $wgServer ) ) { - $this->markTestIncomplete( 'This test needs $wgServer to be set in LocalSettings.php' ); - } - - $user = self::$users['sysop']; - $user->user->logOut(); - - $ret = $this->doApiRequest( array( - "action" => "login", - "lgname" => $user->username, - "lgpassword" => $user->password, - ) - ); - - $result = $ret[0]; - $this->assertNotInternalType( "bool", $result ); - $this->assertNotInternalType( "null", $result["login"] ); - - $a = $result["login"]["result"]; - $this->assertEquals( "NeedToken", $a ); - $token = $result["login"]["token"]; - - $ret = $this->doApiRequest( - array( - "action" => "login", - "lgtoken" => $token, - "lgname" => $user->username, - "lgpassword" => $user->password, - ), - $ret[2] - ); - - $result = $ret[0]; - - $this->assertNotInternalType( "bool", $result ); - $a = $result["login"]["result"]; - - $this->assertEquals( "Success", $a ); - } - - /** - * @group Broken - */ - public function testApiGotCookie() { - $this->markTestIncomplete( "The server can't do external HTTP requests, and the internal one won't give cookies" ); - - global $wgServer, $wgScriptPath; - - if ( !isset( $wgServer ) ) { - $this->markTestIncomplete( 'This test needs $wgServer to be set in LocalSettings.php' ); - } - $user = self::$users['sysop']; - - $req = MWHttpRequest::factory( self::$apiUrl . "?action=login&format=xml", - array( "method" => "POST", - "postData" => array( - "lgname" => $user->username, - "lgpassword" => $user->password - ) - ) - ); - $req->execute(); - - libxml_use_internal_errors( true ); - $sxe = simplexml_load_string( $req->getContent() ); - $this->assertNotInternalType( "bool", $sxe ); - $this->assertThat( $sxe, $this->isInstanceOf( "SimpleXMLElement" ) ); - $this->assertNotInternalType( "null", $sxe->login[0] ); - - $a = $sxe->login[0]->attributes()->result[0]; - $this->assertEquals( ' result="NeedToken"', $a->asXML() ); - $token = (string)$sxe->login[0]->attributes()->token; - - $req->setData( array( - "lgtoken" => $token, - "lgname" => $user->username, - "lgpassword" => $user->password ) ); - $req->execute(); - - $cj = $req->getCookieJar(); - $serverName = parse_url( $wgServer, PHP_URL_HOST ); - $this->assertNotEquals( false, $serverName ); - $serializedCookie = $cj->serializeToHttpRequest( $wgScriptPath, $serverName ); - $this->assertNotEquals( '', $serializedCookie ); - $this->assertRegexp( '/_session=[^;]*; .*UserID=[0-9]*; .*UserName=' . $user->userName . '; .*Token=/', $serializedCookie ); - - return $cj; - } - - public function testRunLogin() { - $sysopUser = self::$users['sysop']; - $data = $this->doApiRequest( array( - 'action' => 'login', - 'lgname' => $sysopUser->username, - 'lgpassword' => $sysopUser->password ) ); - - $this->assertArrayHasKey( "login", $data[0] ); - $this->assertArrayHasKey( "result", $data[0]['login'] ); - $this->assertEquals( "NeedToken", $data[0]['login']['result'] ); - $token = $data[0]['login']['token']; - - $data = $this->doApiRequest( array( - 'action' => 'login', - "lgtoken" => $token, - "lgname" => $sysopUser->username, - "lgpassword" => $sysopUser->password ), $data[2] ); - - $this->assertArrayHasKey( "login", $data[0] ); - $this->assertArrayHasKey( "result", $data[0]['login'] ); - $this->assertEquals( "Success", $data[0]['login']['result'] ); - $this->assertArrayHasKey( 'lgtoken', $data[0]['login'] ); - - return $data; - } - - public function testGettingToken() { - foreach ( self::$users as $user ) { - $this->runTokenTest( $user ); - } - } - - function runTokenTest( $user ) { - $tokens = $this->getTokenList( $user ); - - $rights = $user->user->getRights(); - - $this->assertArrayHasKey( 'edittoken', $tokens ); - $this->assertArrayHasKey( 'movetoken', $tokens ); - - if ( isset( $rights['delete'] ) ) { - $this->assertArrayHasKey( 'deletetoken', $tokens ); - } - - if ( isset( $rights['block'] ) ) { - $this->assertArrayHasKey( 'blocktoken', $tokens ); - $this->assertArrayHasKey( 'unblocktoken', $tokens ); - } - - if ( isset( $rights['protect'] ) ) { - $this->assertArrayHasKey( 'protecttoken', $tokens ); - } - - return $tokens; - } -} diff --git a/tests/phpunit/includes/api/ApiTestCase.php b/tests/phpunit/includes/api/ApiTestCase.php deleted file mode 100644 index 94ef9c68..00000000 --- a/tests/phpunit/includes/api/ApiTestCase.php +++ /dev/null @@ -1,253 +0,0 @@ - new TestUser( - 'Apitestsysop', - 'Api Test Sysop', - 'api_test_sysop@example.com', - array( 'sysop' ) - ), - 'uploader' => new TestUser( - 'Apitestuser', - 'Api Test User', - 'api_test_user@example.com', - array() - ) - ); - - $this->setMwGlobals( array( - 'wgMemc' => new EmptyBagOStuff(), - 'wgAuth' => new StubObject( 'wgAuth', 'AuthPlugin' ), - 'wgRequest' => new FauxRequest( array() ), - 'wgUser' => self::$users['sysop']->user, - ) ); - - $this->apiContext = new ApiTestContext(); - } - - /** - * Edits or creates a page/revision - * @param $pageName string page title - * @param $text string content of the page - * @param $summary string optional summary string for the revision - * @param $defaultNs int optional namespace id - * @return array as returned by WikiPage::doEditContent() - */ - protected function editPage( $pageName, $text, $summary = '', $defaultNs = NS_MAIN ) { - $title = Title::newFromText( $pageName, $defaultNs ); - $page = WikiPage::factory( $title ); - - return $page->doEditContent( ContentHandler::makeContent( $text, $title ), $summary ); - } - - /** - * Does the API request and returns the result. - * - * The returned value is an array containing - * - the result data (array) - * - the request (WebRequest) - * - the session data of the request (array) - * - if $appendModule is true, the Api module $module - * - * @param array $params - * @param array|null $session - * @param bool $appendModule - * @param User|null $user - * - * @return array - */ - protected function doApiRequest( array $params, array $session = null, $appendModule = false, User $user = null ) { - global $wgRequest, $wgUser; - - if ( is_null( $session ) ) { - // re-use existing global session by default - $session = $wgRequest->getSessionArray(); - } - - // set up global environment - if ( $user ) { - $wgUser = $user; - } - - $wgRequest = new FauxRequest( $params, true, $session ); - RequestContext::getMain()->setRequest( $wgRequest ); - - // set up local environment - $context = $this->apiContext->newTestContext( $wgRequest, $wgUser ); - - $module = new ApiMain( $context, true ); - - // run it! - $module->execute(); - - // construct result - $results = array( - $module->getResultData(), - $context->getRequest(), - $context->getRequest()->getSessionArray() - ); - - if ( $appendModule ) { - $results[] = $module; - } - - return $results; - } - - /** - * Add an edit token to the API request - * This is cheating a bit -- we grab a token in the correct format and then add it to the pseudo-session and to the - * request, without actually requesting a "real" edit token - * @param $params Array: key-value API params - * @param $session Array|null: session array - * @param $user User|null A User object for the context - * @return result of the API call - * @throws Exception in case wsToken is not set in the session - */ - protected function doApiRequestWithToken( array $params, array $session = null, User $user = null ) { - global $wgRequest; - - if ( $session === null ) { - $session = $wgRequest->getSessionArray(); - } - - if ( $session['wsToken'] ) { - // add edit token to fake session - $session['wsEditToken'] = $session['wsToken']; - // add token to request parameters - $params['token'] = md5( $session['wsToken'] ) . User::EDIT_TOKEN_SUFFIX; - - return $this->doApiRequest( $params, $session, false, $user ); - } else { - throw new Exception( "request data not in right format" ); - } - } - - protected function doLogin( $user = 'sysop' ) { - if ( !array_key_exists( $user, self::$users ) ) { - throw new MWException( "Can not log in to undefined user $user" ); - } - - $data = $this->doApiRequest( array( - 'action' => 'login', - 'lgname' => self::$users[ $user ]->username, - 'lgpassword' => self::$users[ $user ]->password ) ); - - $token = $data[0]['login']['token']; - - $data = $this->doApiRequest( - array( - 'action' => 'login', - 'lgtoken' => $token, - 'lgname' => self::$users[ $user ]->username, - 'lgpassword' => self::$users[ $user ]->password, - ), - $data[2] - ); - - return $data; - } - - protected function getTokenList( $user, $session = null ) { - $data = $this->doApiRequest( array( - 'action' => 'tokens', - 'type' => 'edit|delete|protect|move|block|unblock|watch' - ), $session, false, $user->user ); - - if ( !array_key_exists( 'tokens', $data[0] ) ) { - throw new MWException( 'Api failed to return a token list' ); - } - - return $data[0]['tokens']; - } - - public function testApiTestGroup() { - $groups = PHPUnit_Util_Test::getGroups( get_class( $this ) ); - $constraint = PHPUnit_Framework_Assert::logicalOr( - $this->contains( 'medium' ), - $this->contains( 'large' ) - ); - $this->assertThat( $groups, $constraint, - 'ApiTestCase::setUp can be slow, tests must be "medium" or "large"' - ); - } -} - -class UserWrapper { - public $userName; - public $password; - public $user; - - public function __construct( $userName, $password, $group = '' ) { - $this->userName = $userName; - $this->password = $password; - - $this->user = User::newFromName( $this->userName ); - if ( !$this->user->getID() ) { - $this->user = User::createNew( $this->userName, array( - "email" => "test@example.com", - "real_name" => "Test User" ) ); - } - $this->user->setPassword( $this->password ); - - if ( $group !== '' ) { - $this->user->addGroup( $group ); - } - $this->user->saveSettings(); - } -} - -class MockApi extends ApiBase { - public function execute() { - } - - public function getVersion() { - } - - public function __construct() { - } - - public function getAllowedParams() { - return array( - 'filename' => null, - 'enablechunks' => false, - 'sessionkey' => null, - ); - } -} - -class ApiTestContext extends RequestContext { - - /** - * Returns a DerivativeContext with the request variables in place - * - * @param $request WebRequest request object including parameters and session - * @param $user User or null - * @return DerivativeContext - */ - public function newTestContext( WebRequest $request, User $user = null ) { - $context = new DerivativeContext( $this ); - $context->setRequest( $request ); - if ( $user !== null ) { - $context->setUser( $user ); - } - - return $context; - } -} diff --git a/tests/phpunit/includes/api/ApiTestCaseUpload.php b/tests/phpunit/includes/api/ApiTestCaseUpload.php deleted file mode 100644 index 7e18b6ed..00000000 --- a/tests/phpunit/includes/api/ApiTestCaseUpload.php +++ /dev/null @@ -1,149 +0,0 @@ -setMwGlobals( array( - 'wgEnableUploads' => true, - 'wgEnableAPI' => true, - ) ); - - wfSetupSession(); - - $this->clearFakeUploads(); - } - - protected function tearDown() { - $this->clearTempUpload(); - - parent::tearDown(); - } - - /** - * Helper function -- remove files and associated articles by Title - * @param $title Title: title to be removed - */ - public function deleteFileByTitle( $title ) { - if ( $title->exists() ) { - $file = wfFindFile( $title, array( 'ignoreRedirect' => true ) ); - $noOldArchive = ""; // yes this really needs to be set this way - $comment = "removing for test"; - $restrictDeletedVersions = false; - $status = FileDeleteForm::doDelete( $title, $file, $noOldArchive, $comment, $restrictDeletedVersions ); - if ( !$status->isGood() ) { - return false; - } - $page = WikiPage::factory( $title ); - $page->doDeleteArticle( "removing for test" ); - - // see if it now doesn't exist; reload - $title = Title::newFromText( $title->getText(), NS_FILE ); - } - - return !( $title && $title instanceof Title && $title->exists() ); - } - - /** - * Helper function -- remove files and associated articles with a particular filename - * @param $fileName String: filename to be removed - */ - public function deleteFileByFileName( $fileName ) { - return $this->deleteFileByTitle( Title::newFromText( $fileName, NS_FILE ) ); - } - - /** - * Helper function -- given a file on the filesystem, find matching content in the db (and associated articles) and remove them. - * @param $filePath String: path to file on the filesystem - */ - public function deleteFileByContent( $filePath ) { - $hash = FSFile::getSha1Base36FromPath( $filePath ); - $dupes = RepoGroup::singleton()->findBySha1( $hash ); - $success = true; - foreach ( $dupes as $dupe ) { - $success &= $this->deleteFileByTitle( $dupe->getTitle() ); - } - - return $success; - } - - /** - * Fake an upload by dumping the file into temp space, and adding info to $_FILES. - * (This is what PHP would normally do). - * @param $fieldName String: name this would have in the upload form - * @param $fileName String: name to title this - * @param $type String: mime type - * @param $filePath String: path where to find file contents - */ - function fakeUploadFile( $fieldName, $fileName, $type, $filePath ) { - $tmpName = tempnam( wfTempDir(), "" ); - if ( !file_exists( $filePath ) ) { - throw new Exception( "$filePath doesn't exist!" ); - } - - if ( !copy( $filePath, $tmpName ) ) { - throw new Exception( "couldn't copy $filePath to $tmpName" ); - } - - clearstatcache(); - $size = filesize( $tmpName ); - if ( $size === false ) { - throw new Exception( "couldn't stat $tmpName" ); - } - - $_FILES[$fieldName] = array( - 'name' => $fileName, - 'type' => $type, - 'tmp_name' => $tmpName, - 'size' => $size, - 'error' => null - ); - - return true; - } - - function fakeUploadChunk( $fieldName, $fileName, $type, & $chunkData ) { - $tmpName = tempnam( wfTempDir(), "" ); - // copy the chunk data to temp location: - if ( !file_put_contents( $tmpName, $chunkData ) ) { - throw new Exception( "couldn't copy chunk data to $tmpName" ); - } - - clearstatcache(); - $size = filesize( $tmpName ); - if ( $size === false ) { - throw new Exception( "couldn't stat $tmpName" ); - } - - $_FILES[$fieldName] = array( - 'name' => $fileName, - 'type' => $type, - 'tmp_name' => $tmpName, - 'size' => $size, - 'error' => null - ); - } - - function clearTempUpload() { - if ( isset( $_FILES['file']['tmp_name'] ) ) { - $tmp = $_FILES['file']['tmp_name']; - if ( file_exists( $tmp ) ) { - unlink( $tmp ); - } - } - } - - /** - * Remove traces of previous fake uploads - */ - function clearFakeUploads() { - $_FILES = array(); - } -} diff --git a/tests/phpunit/includes/api/ApiUploadTest.php b/tests/phpunit/includes/api/ApiUploadTest.php deleted file mode 100644 index 1540af55..00000000 --- a/tests/phpunit/includes/api/ApiUploadTest.php +++ /dev/null @@ -1,561 +0,0 @@ - 'login', - 'lgname' => $user->username, - 'lgpassword' => $user->password - ); - list( $result, , $session ) = $this->doApiRequest( $params ); - $this->assertArrayHasKey( "login", $result ); - $this->assertArrayHasKey( "result", $result['login'] ); - $this->assertEquals( "NeedToken", $result['login']['result'] ); - $token = $result['login']['token']; - - $params = array( - 'action' => 'login', - 'lgtoken' => $token, - 'lgname' => $user->username, - 'lgpassword' => $user->password - ); - list( $result, , $session ) = $this->doApiRequest( $params, $session ); - $this->assertArrayHasKey( "login", $result ); - $this->assertArrayHasKey( "result", $result['login'] ); - $this->assertEquals( "Success", $result['login']['result'] ); - $this->assertArrayHasKey( 'lgtoken', $result['login'] ); - - $this->assertNotEmpty( $session, 'API Login must return a session' ); - - return $session; - } - - /** - * @depends testLogin - */ - public function testUploadRequiresToken( $session ) { - $exception = false; - try { - $this->doApiRequest( array( - 'action' => 'upload' - ) ); - } catch ( UsageException $e ) { - $exception = true; - $this->assertEquals( "The token parameter must be set", $e->getMessage() ); - } - $this->assertTrue( $exception, "Got exception" ); - } - - /** - * @depends testLogin - */ - public function testUploadMissingParams( $session ) { - $exception = false; - try { - $this->doApiRequestWithToken( array( - 'action' => 'upload', - ), $session, self::$users['uploader']->user ); - } catch ( UsageException $e ) { - $exception = true; - $this->assertEquals( "One of the parameters filekey, file, url, statuskey is required", - $e->getMessage() ); - } - $this->assertTrue( $exception, "Got exception" ); - } - - - /** - * @depends testLogin - */ - public function testUpload( $session ) { - $extension = 'png'; - $mimeType = 'image/png'; - - try { - $randomImageGenerator = new RandomImageGenerator(); - $filePaths = $randomImageGenerator->writeImages( 1, $extension, wfTempDir() ); - } catch ( Exception $e ) { - $this->markTestIncomplete( $e->getMessage() ); - } - - $filePath = $filePaths[0]; - $fileSize = filesize( $filePath ); - $fileName = basename( $filePath ); - - $this->deleteFileByFileName( $fileName ); - $this->deleteFileByContent( $filePath ); - - if ( !$this->fakeUploadFile( 'file', $fileName, $mimeType, $filePath ) ) { - $this->markTestIncomplete( "Couldn't upload file!\n" ); - } - - $params = array( - 'action' => 'upload', - 'filename' => $fileName, - 'file' => 'dummy content', - 'comment' => 'dummy comment', - 'text' => "This is the page text for $fileName", - ); - - $exception = false; - try { - list( $result, , ) = $this->doApiRequestWithToken( $params, $session, - self::$users['uploader']->user ); - } catch ( UsageException $e ) { - $exception = true; - } - $this->assertTrue( isset( $result['upload'] ) ); - $this->assertEquals( 'Success', $result['upload']['result'] ); - $this->assertEquals( $fileSize, (int)$result['upload']['imageinfo']['size'] ); - $this->assertEquals( $mimeType, $result['upload']['imageinfo']['mime'] ); - $this->assertFalse( $exception ); - - // clean up - $this->deleteFileByFilename( $fileName ); - unlink( $filePath ); - } - - - /** - * @depends testLogin - */ - public function testUploadZeroLength( $session ) { - $mimeType = 'image/png'; - - $filePath = tempnam( wfTempDir(), "" ); - $fileName = "apiTestUploadZeroLength.png"; - - $this->deleteFileByFileName( $fileName ); - - if ( !$this->fakeUploadFile( 'file', $fileName, $mimeType, $filePath ) ) { - $this->markTestIncomplete( "Couldn't upload file!\n" ); - } - - $params = array( - 'action' => 'upload', - 'filename' => $fileName, - 'file' => 'dummy content', - 'comment' => 'dummy comment', - 'text' => "This is the page text for $fileName", - ); - - $exception = false; - try { - $this->doApiRequestWithToken( $params, $session, self::$users['uploader']->user ); - } catch ( UsageException $e ) { - $this->assertContains( 'The file you submitted was empty', $e->getMessage() ); - $exception = true; - } - $this->assertTrue( $exception ); - - // clean up - $this->deleteFileByFilename( $fileName ); - unlink( $filePath ); - } - - - /** - * @depends testLogin - */ - public function testUploadSameFileName( $session ) { - $extension = 'png'; - $mimeType = 'image/png'; - - try { - $randomImageGenerator = new RandomImageGenerator(); - $filePaths = $randomImageGenerator->writeImages( 2, $extension, wfTempDir() ); - } catch ( Exception $e ) { - $this->markTestIncomplete( $e->getMessage() ); - } - - // we'll reuse this filename - $fileName = basename( $filePaths[0] ); - - // clear any other files with the same name - $this->deleteFileByFileName( $fileName ); - - // we reuse these params - $params = array( - 'action' => 'upload', - 'filename' => $fileName, - 'file' => 'dummy content', - 'comment' => 'dummy comment', - 'text' => "This is the page text for $fileName", - ); - - // first upload .... should succeed - - if ( !$this->fakeUploadFile( 'file', $fileName, $mimeType, $filePaths[0] ) ) { - $this->markTestIncomplete( "Couldn't upload file!\n" ); - } - - $exception = false; - try { - list( $result, , $session ) = $this->doApiRequestWithToken( $params, $session, - self::$users['uploader']->user ); - } catch ( UsageException $e ) { - $exception = true; - } - $this->assertTrue( isset( $result['upload'] ) ); - $this->assertEquals( 'Success', $result['upload']['result'] ); - $this->assertFalse( $exception ); - - // second upload with the same name (but different content) - - if ( !$this->fakeUploadFile( 'file', $fileName, $mimeType, $filePaths[1] ) ) { - $this->markTestIncomplete( "Couldn't upload file!\n" ); - } - - $exception = false; - try { - list( $result, , ) = $this->doApiRequestWithToken( $params, $session, - self::$users['uploader']->user ); // FIXME: leaks a temporary file - } catch ( UsageException $e ) { - $exception = true; - } - $this->assertTrue( isset( $result['upload'] ) ); - $this->assertEquals( 'Warning', $result['upload']['result'] ); - $this->assertTrue( isset( $result['upload']['warnings'] ) ); - $this->assertTrue( isset( $result['upload']['warnings']['exists'] ) ); - $this->assertFalse( $exception ); - - // clean up - $this->deleteFileByFilename( $fileName ); - unlink( $filePaths[0] ); - unlink( $filePaths[1] ); - } - - - /** - * @depends testLogin - */ - public function testUploadSameContent( $session ) { - $extension = 'png'; - $mimeType = 'image/png'; - - try { - $randomImageGenerator = new RandomImageGenerator(); - $filePaths = $randomImageGenerator->writeImages( 1, $extension, wfTempDir() ); - } catch ( Exception $e ) { - $this->markTestIncomplete( $e->getMessage() ); - } - - $fileNames[0] = basename( $filePaths[0] ); - $fileNames[1] = "SameContentAs" . $fileNames[0]; - - // clear any other files with the same name or content - $this->deleteFileByContent( $filePaths[0] ); - $this->deleteFileByFileName( $fileNames[0] ); - $this->deleteFileByFileName( $fileNames[1] ); - - // first upload .... should succeed - - $params = array( - 'action' => 'upload', - 'filename' => $fileNames[0], - 'file' => 'dummy content', - 'comment' => 'dummy comment', - 'text' => "This is the page text for " . $fileNames[0], - ); - - if ( !$this->fakeUploadFile( 'file', $fileNames[0], $mimeType, $filePaths[0] ) ) { - $this->markTestIncomplete( "Couldn't upload file!\n" ); - } - - $exception = false; - try { - list( $result, , $session ) = $this->doApiRequestWithToken( $params, $session, - self::$users['uploader']->user ); - } catch ( UsageException $e ) { - $exception = true; - } - $this->assertTrue( isset( $result['upload'] ) ); - $this->assertEquals( 'Success', $result['upload']['result'] ); - $this->assertFalse( $exception ); - - // second upload with the same content (but different name) - - if ( !$this->fakeUploadFile( 'file', $fileNames[1], $mimeType, $filePaths[0] ) ) { - $this->markTestIncomplete( "Couldn't upload file!\n" ); - } - - $params = array( - 'action' => 'upload', - 'filename' => $fileNames[1], - 'file' => 'dummy content', - 'comment' => 'dummy comment', - 'text' => "This is the page text for " . $fileNames[1], - ); - - $exception = false; - try { - list( $result ) = $this->doApiRequestWithToken( $params, $session, - self::$users['uploader']->user ); // FIXME: leaks a temporary file - } catch ( UsageException $e ) { - $exception = true; - } - $this->assertTrue( isset( $result['upload'] ) ); - $this->assertEquals( 'Warning', $result['upload']['result'] ); - $this->assertTrue( isset( $result['upload']['warnings'] ) ); - $this->assertTrue( isset( $result['upload']['warnings']['duplicate'] ) ); - $this->assertFalse( $exception ); - - // clean up - $this->deleteFileByFilename( $fileNames[0] ); - $this->deleteFileByFilename( $fileNames[1] ); - unlink( $filePaths[0] ); - } - - /** - * @depends testLogin - */ - public function testUploadStash( $session ) { - $this->setMwGlobals( array( - 'wgUser' => self::$users['uploader']->user, // @todo FIXME: still used somewhere - ) ); - - $extension = 'png'; - $mimeType = 'image/png'; - - try { - $randomImageGenerator = new RandomImageGenerator(); - $filePaths = $randomImageGenerator->writeImages( 1, $extension, wfTempDir() ); - } catch ( Exception $e ) { - $this->markTestIncomplete( $e->getMessage() ); - } - - $filePath = $filePaths[0]; - $fileSize = filesize( $filePath ); - $fileName = basename( $filePath ); - - $this->deleteFileByFileName( $fileName ); - $this->deleteFileByContent( $filePath ); - - if ( !$this->fakeUploadFile( 'file', $fileName, $mimeType, $filePath ) ) { - $this->markTestIncomplete( "Couldn't upload file!\n" ); - } - - $params = array( - 'action' => 'upload', - 'stash' => 1, - 'filename' => $fileName, - 'file' => 'dummy content', - 'comment' => 'dummy comment', - 'text' => "This is the page text for $fileName", - ); - - $exception = false; - try { - list( $result, , $session ) = $this->doApiRequestWithToken( $params, $session, - self::$users['uploader']->user ); // FIXME: leaks a temporary file - } catch ( UsageException $e ) { - $exception = true; - } - $this->assertFalse( $exception ); - $this->assertTrue( isset( $result['upload'] ) ); - $this->assertEquals( 'Success', $result['upload']['result'] ); - $this->assertEquals( $fileSize, (int)$result['upload']['imageinfo']['size'] ); - $this->assertEquals( $mimeType, $result['upload']['imageinfo']['mime'] ); - $this->assertTrue( isset( $result['upload']['filekey'] ) ); - $this->assertEquals( $result['upload']['sessionkey'], $result['upload']['filekey'] ); - $filekey = $result['upload']['filekey']; - - // it should be visible from Special:UploadStash - // XXX ...but how to test this, with a fake WebRequest with the session? - - // now we should try to release the file from stash - $params = array( - 'action' => 'upload', - 'filekey' => $filekey, - 'filename' => $fileName, - 'comment' => 'dummy comment', - 'text' => "This is the page text for $fileName, altered", - ); - - $this->clearFakeUploads(); - $exception = false; - try { - list( $result ) = $this->doApiRequestWithToken( $params, $session, - self::$users['uploader']->user ); - } catch ( UsageException $e ) { - $exception = true; - } - $this->assertTrue( isset( $result['upload'] ) ); - $this->assertEquals( 'Success', $result['upload']['result'] ); - $this->assertFalse( $exception, "No UsageException exception." ); - - // clean up - $this->deleteFileByFilename( $fileName ); - unlink( $filePath ); - } - - /** - * @depends testLogin - */ - public function testUploadChunks( $session ) { - $this->setMwGlobals( array( - 'wgUser' => self::$users['uploader']->user, // @todo FIXME: still used somewhere - ) ); - - $chunkSize = 1048576; - // Download a large image file - // ( using RandomImageGenerator for large files is not stable ) - $mimeType = 'image/jpeg'; - $url = 'http://upload.wikimedia.org/wikipedia/commons/e/ed/Oberaargletscher_from_Oberaar%2C_2010_07.JPG'; - $filePath = wfTempDir() . '/Oberaargletscher_from_Oberaar.jpg'; - try { - // Only download if the file is not avaliable in the temp location: - if ( !is_file( $filePath ) ) { - copy( $url, $filePath ); - } - } catch ( Exception $e ) { - $this->markTestIncomplete( $e->getMessage() ); - } - - $fileSize = filesize( $filePath ); - $fileName = basename( $filePath ); - - $this->deleteFileByFileName( $fileName ); - $this->deleteFileByContent( $filePath ); - - // Base upload params: - $params = array( - 'action' => 'upload', - 'stash' => 1, - 'filename' => $fileName, - 'filesize' => $fileSize, - 'offset' => 0, - ); - - // Upload chunks - $chunkSessionKey = false; - $resultOffset = 0; - // Open the file: - $handle = @fopen( $filePath, "r" ); - if ( $handle === false ) { - $this->markTestIncomplete( "could not open file: $filePath" ); - } - while ( !feof( $handle ) ) { - // Get the current chunk - $chunkData = @fread( $handle, $chunkSize ); - - // Upload the current chunk into the $_FILE object: - $this->fakeUploadChunk( 'chunk', 'blob', $mimeType, $chunkData ); - - // Check for chunkSessionKey - if ( !$chunkSessionKey ) { - // Upload fist chunk ( and get the session key ) - try { - list( $result, , $session ) = $this->doApiRequestWithToken( $params, $session, - self::$users['uploader']->user ); - } catch ( UsageException $e ) { - $this->markTestIncomplete( $e->getMessage() ); - } - // Make sure we got a valid chunk continue: - $this->assertTrue( isset( $result['upload'] ) ); - $this->assertTrue( isset( $result['upload']['filekey'] ) ); - // If we don't get a session key mark test incomplete. - if ( !isset( $result['upload']['filekey'] ) ) { - $this->markTestIncomplete( "no filekey provided" ); - } - $chunkSessionKey = $result['upload']['filekey']; - $this->assertEquals( 'Continue', $result['upload']['result'] ); - // First chunk should have chunkSize == offset - $this->assertEquals( $chunkSize, $result['upload']['offset'] ); - $resultOffset = $result['upload']['offset']; - continue; - } - // Filekey set to chunk session - $params['filekey'] = $chunkSessionKey; - // Update the offset ( always add chunkSize for subquent chunks should be in-sync with $result['upload']['offset'] ) - $params['offset'] += $chunkSize; - // Make sure param offset is insync with resultOffset: - $this->assertEquals( $resultOffset, $params['offset'] ); - // Upload current chunk - try { - list( $result, , $session ) = $this->doApiRequestWithToken( $params, $session, - self::$users['uploader']->user ); - } catch ( UsageException $e ) { - $this->markTestIncomplete( $e->getMessage() ); - } - // Make sure we got a valid chunk continue: - $this->assertTrue( isset( $result['upload'] ) ); - $this->assertTrue( isset( $result['upload']['filekey'] ) ); - - // Check if we were on the last chunk: - if ( $params['offset'] + $chunkSize >= $fileSize ) { - $this->assertEquals( 'Success', $result['upload']['result'] ); - break; - } else { - $this->assertEquals( 'Continue', $result['upload']['result'] ); - // update $resultOffset - $resultOffset = $result['upload']['offset']; - } - } - fclose( $handle ); - - // Check that we got a valid file result: - wfDebug( __METHOD__ . " hohoh filesize {$fileSize} info {$result['upload']['imageinfo']['size']}\n\n" ); - $this->assertEquals( $fileSize, $result['upload']['imageinfo']['size'] ); - $this->assertEquals( $mimeType, $result['upload']['imageinfo']['mime'] ); - $this->assertTrue( isset( $result['upload']['filekey'] ) ); - $filekey = $result['upload']['filekey']; - - // Now we should try to release the file from stash - $params = array( - 'action' => 'upload', - 'filekey' => $filekey, - 'filename' => $fileName, - 'comment' => 'dummy comment', - 'text' => "This is the page text for $fileName, altered", - ); - $this->clearFakeUploads(); - $exception = false; - try { - list( $result ) = $this->doApiRequestWithToken( $params, $session, - self::$users['uploader']->user ); - } catch ( UsageException $e ) { - $exception = true; - } - $this->assertTrue( isset( $result['upload'] ) ); - $this->assertEquals( 'Success', $result['upload']['result'] ); - $this->assertFalse( $exception ); - - // clean up - $this->deleteFileByFilename( $fileName ); - // don't remove downloaded temporary file for fast subquent tests. - //unlink( $filePath ); - } -} diff --git a/tests/phpunit/includes/api/ApiWatchTest.php b/tests/phpunit/includes/api/ApiWatchTest.php deleted file mode 100644 index 028ea9ff..00000000 --- a/tests/phpunit/includes/api/ApiWatchTest.php +++ /dev/null @@ -1,148 +0,0 @@ -doLogin(); - } - - function getTokens() { - return $this->getTokenList( self::$users['sysop'] ); - } - - /** - */ - public function testWatchEdit() { - $tokens = $this->getTokens(); - - $data = $this->doApiRequest( array( - 'action' => 'edit', - 'title' => 'Help:UTPage', // Help namespace is hopefully wikitext - 'text' => 'new text', - 'token' => $tokens['edittoken'], - 'watchlist' => 'watch' ) ); - $this->assertArrayHasKey( 'edit', $data[0] ); - $this->assertArrayHasKey( 'result', $data[0]['edit'] ); - $this->assertEquals( 'Success', $data[0]['edit']['result'] ); - - return $data; - } - - /** - * @depends testWatchEdit - */ - public function testWatchClear() { - $tokens = $this->getTokens(); - - $data = $this->doApiRequest( array( - 'action' => 'query', - 'list' => 'watchlist' ) ); - - if ( isset( $data[0]['query']['watchlist'] ) ) { - $wl = $data[0]['query']['watchlist']; - - foreach ( $wl as $page ) { - $data = $this->doApiRequest( array( - 'action' => 'watch', - 'title' => $page['title'], - 'unwatch' => true, - 'token' => $tokens['watchtoken'] ) ); - } - } - $data = $this->doApiRequest( array( - 'action' => 'query', - 'list' => 'watchlist' ), $data ); - $this->assertArrayHasKey( 'query', $data[0] ); - $this->assertArrayHasKey( 'watchlist', $data[0]['query'] ); - $this->assertEquals( 0, count( $data[0]['query']['watchlist'] ) ); - - return $data; - } - - /** - */ - public function testWatchProtect() { - $tokens = $this->getTokens(); - - $data = $this->doApiRequest( array( - 'action' => 'protect', - 'token' => $tokens['protecttoken'], - 'title' => 'Help:UTPage', - 'protections' => 'edit=sysop', - 'watchlist' => 'unwatch' ) ); - - $this->assertArrayHasKey( 'protect', $data[0] ); - $this->assertArrayHasKey( 'protections', $data[0]['protect'] ); - $this->assertEquals( 1, count( $data[0]['protect']['protections'] ) ); - $this->assertArrayHasKey( 'edit', $data[0]['protect']['protections'][0] ); - } - - /** - */ - public function testGetRollbackToken() { - $this->getTokens(); - - if ( !Title::newFromText( 'Help:UTPage' )->exists() ) { - $this->markTestSkipped( "The article [[Help:UTPage]] does not exist" ); //TODO: just create it? - } - - $data = $this->doApiRequest( array( - 'action' => 'query', - 'prop' => 'revisions', - 'titles' => 'Help:UTPage', - 'rvtoken' => 'rollback' ) ); - - $this->assertArrayHasKey( 'query', $data[0] ); - $this->assertArrayHasKey( 'pages', $data[0]['query'] ); - $keys = array_keys( $data[0]['query']['pages'] ); - $key = array_pop( $keys ); - - if ( isset( $data[0]['query']['pages'][$key]['missing'] ) ) { - $this->markTestSkipped( "Target page (Help:UTPage) doesn't exist" ); - } - - $this->assertArrayHasKey( 'pageid', $data[0]['query']['pages'][$key] ); - $this->assertArrayHasKey( 'revisions', $data[0]['query']['pages'][$key] ); - $this->assertArrayHasKey( 0, $data[0]['query']['pages'][$key]['revisions'] ); - $this->assertArrayHasKey( 'rollbacktoken', $data[0]['query']['pages'][$key]['revisions'][0] ); - - return $data; - } - - /** - * @group Broken - * Broken because there is currently no revision info in the $pageinfo - * - * @depends testGetRollbackToken - */ - public function testWatchRollback( $data ) { - $keys = array_keys( $data[0]['query']['pages'] ); - $key = array_pop( $keys ); - $pageinfo = $data[0]['query']['pages'][$key]; - $revinfo = $pageinfo['revisions'][0]; - - try { - $data = $this->doApiRequest( array( - 'action' => 'rollback', - 'title' => 'Help:UTPage', - 'user' => $revinfo['user'], - 'token' => $pageinfo['rollbacktoken'], - 'watchlist' => 'watch' ) ); - - $this->assertArrayHasKey( 'rollback', $data[0] ); - $this->assertArrayHasKey( 'title', $data[0]['rollback'] ); - } catch ( UsageException $ue ) { - if ( $ue->getCodeString() == 'onlyauthor' ) { - $this->markTestIncomplete( "Only one author to 'Help:UTPage', cannot test rollback" ); - } else { - $this->fail( "Received error '" . $ue->getCodeString() . "'" ); - } - } - } -} diff --git a/tests/phpunit/includes/api/PrefixUniquenessTest.php b/tests/phpunit/includes/api/PrefixUniquenessTest.php deleted file mode 100644 index d9be85e3..00000000 --- a/tests/phpunit/includes/api/PrefixUniquenessTest.php +++ /dev/null @@ -1,25 +0,0 @@ -getModuleManager()->getNamesWithClasses(); - $prefixes = array(); - - foreach ( $modules as $name => $class ) { - $module = new $class( $main, $name ); - $prefix = $module->getModulePrefix(); - if ( isset( $prefixes[$prefix] ) ) { - $this->fail( "Module prefix '{$prefix}' is shared between {$class} and {$prefixes[$prefix]}" ); - } - $prefixes[$module->getModulePrefix()] = $class; - } - $this->assertTrue( true ); // dummy call to make this test non-incomplete - } -} diff --git a/tests/phpunit/includes/api/RandomImageGenerator.php b/tests/phpunit/includes/api/RandomImageGenerator.php deleted file mode 100644 index 59756b21..00000000 --- a/tests/phpunit/includes/api/RandomImageGenerator.php +++ /dev/null @@ -1,468 +0,0 @@ - - */ - -/** - * RandomImageGenerator: does what it says on the tin. - * Can fetch a random image, or also write a number of them to disk with random filenames. - */ -class RandomImageGenerator { - - private $dictionaryFile; - private $minWidth = 400; - private $maxWidth = 800; - private $minHeight = 400; - private $maxHeight = 800; - private $shapesToDraw = 5; - - /** - * Orientations: 0th row, 0th column, Exif orientation code, rotation 2x2 matrix that is opposite of orientation - * n.b. we do not handle the 'flipped' orientations, which is why there is no entry for 2, 4, 5, or 7. Those - * seem to be rare in real images anyway - * (we also would need a non-symmetric shape for the images to test those, like a letter F) - */ - private static $orientations = array( - array( - '0thRow' => 'top', - '0thCol' => 'left', - 'exifCode' => 1, - 'counterRotation' => array( array( 1, 0 ), array( 0, 1 ) ) - ), - array( - '0thRow' => 'bottom', - '0thCol' => 'right', - 'exifCode' => 3, - 'counterRotation' => array( array( -1, 0 ), array( 0, -1 ) ) - ), - array( - '0thRow' => 'right', - '0thCol' => 'top', - 'exifCode' => 6, - 'counterRotation' => array( array( 0, 1 ), array( 1, 0 ) ) - ), - array( - '0thRow' => 'left', - '0thCol' => 'bottom', - 'exifCode' => 8, - 'counterRotation' => array( array( 0, -1 ), array( -1, 0 ) ) - ) - ); - - - public function __construct( $options = array() ) { - foreach ( array( 'dictionaryFile', 'minWidth', 'minHeight', 'maxWidth', 'maxHeight', 'shapesToDraw' ) as $property ) { - if ( isset( $options[$property] ) ) { - $this->$property = $options[$property]; - } - } - - // find the dictionary file, to generate random names - if ( !isset( $this->dictionaryFile ) ) { - foreach ( - array( - '/usr/share/dict/words', - '/usr/dict/words', - __DIR__ . '/words.txt' - ) as $dictionaryFile - ) { - if ( is_file( $dictionaryFile ) and is_readable( $dictionaryFile ) ) { - $this->dictionaryFile = $dictionaryFile; - break; - } - } - } - if ( !isset( $this->dictionaryFile ) ) { - throw new Exception( "RandomImageGenerator: dictionary file not found or not specified properly" ); - } - } - - /** - * Writes random images with random filenames to disk in the directory you specify, or current working directory - * - * @param $number Integer: number of filenames to write - * @param $format String: optional, must be understood by ImageMagick, such as 'jpg' or 'gif' - * @param $dir String: directory, optional (will default to current working directory) - * @return Array: filenames we just wrote - */ - function writeImages( $number, $format = 'jpg', $dir = null ) { - $filenames = $this->getRandomFilenames( $number, $format, $dir ); - $imageWriteMethod = $this->getImageWriteMethod( $format ); - foreach ( $filenames as $filename ) { - $this->{$imageWriteMethod}( $this->getImageSpec(), $format, $filename ); - } - - return $filenames; - } - - - /** - * Figure out how we write images. This is a factor of both format and the local system - * @param $format (a typical extension like 'svg', 'jpg', etc.) - */ - function getImageWriteMethod( $format ) { - global $wgUseImageMagick, $wgImageMagickConvertCommand; - if ( $format === 'svg' ) { - return 'writeSvg'; - } else { - // figure out how to write images - global $wgExiv2Command; - if ( class_exists( 'Imagick' ) && $wgExiv2Command && is_executable( $wgExiv2Command ) ) { - return 'writeImageWithApi'; - } elseif ( $wgUseImageMagick && $wgImageMagickConvertCommand && is_executable( $wgImageMagickConvertCommand ) ) { - return 'writeImageWithCommandLine'; - } - } - throw new Exception( "RandomImageGenerator: could not find a suitable method to write images in '$format' format" ); - } - - /** - * Return a number of randomly-generated filenames - * Each filename uses two words randomly drawn from the dictionary, like elephantine_spatula.jpg - * - * @param $number Integer: of filenames to generate - * @param $extension String: optional, defaults to 'jpg' - * @param $dir String: optional, defaults to current working directory - * @return Array: of filenames - */ - private function getRandomFilenames( $number, $extension = 'jpg', $dir = null ) { - if ( is_null( $dir ) ) { - $dir = getcwd(); - } - $filenames = array(); - foreach ( $this->getRandomWordPairs( $number ) as $pair ) { - $basename = $pair[0] . '_' . $pair[1]; - if ( !is_null( $extension ) ) { - $basename .= '.' . $extension; - } - $basename = preg_replace( '/\s+/', '', $basename ); - $filenames[] = "$dir/$basename"; - } - - return $filenames; - } - - - /** - * Generate data representing an image of random size (within limits), - * consisting of randomly colored and sized upward pointing triangles against a random background color - * (This data is used in the writeImage* methods). - * @return {Mixed} - */ - public function getImageSpec() { - $spec = array(); - - $spec['width'] = mt_rand( $this->minWidth, $this->maxWidth ); - $spec['height'] = mt_rand( $this->minHeight, $this->maxHeight ); - $spec['fill'] = $this->getRandomColor(); - - $diagonalLength = sqrt( pow( $spec['width'], 2 ) + pow( $spec['height'], 2 ) ); - - $draws = array(); - for ( $i = 0; $i <= $this->shapesToDraw; $i++ ) { - $radius = mt_rand( 0, $diagonalLength / 4 ); - if ( $radius == 0 ) { - continue; - } - $originX = mt_rand( -1 * $radius, $spec['width'] + $radius ); - $originY = mt_rand( -1 * $radius, $spec['height'] + $radius ); - $angle = mt_rand( 0, ( 3.141592 / 2 ) * $radius ) / $radius; - $legDeltaX = round( $radius * sin( $angle ) ); - $legDeltaY = round( $radius * cos( $angle ) ); - - $draw = array(); - $draw['fill'] = $this->getRandomColor(); - $draw['shape'] = array( - array( 'x' => $originX, 'y' => $originY - $radius ), - array( 'x' => $originX + $legDeltaX, 'y' => $originY + $legDeltaY ), - array( 'x' => $originX - $legDeltaX, 'y' => $originY + $legDeltaY ), - array( 'x' => $originX, 'y' => $originY - $radius ) - ); - $draws[] = $draw; - } - - $spec['draws'] = $draws; - - return $spec; - } - - /** - * Given array( array('x' => 10, 'y' => 20), array( 'x' => 30, y=> 5 ) ) - * returns "10,20 30,5" - * Useful for SVG and imagemagick command line arguments - * @param $shape: Array of arrays, each array containing x & y keys mapped to numeric values - * @return string - */ - static function shapePointsToString( $shape ) { - $points = array(); - foreach ( $shape as $point ) { - $points[] = $point['x'] . ',' . $point['y']; - } - - return join( " ", $points ); - } - - /** - * Based on image specification, write a very simple SVG file to disk. - * Ignores the background spec because transparency is cool. :) - * @param $spec: spec describing background and shapes to draw - * @param $format: file format to write (which is obviously always svg here) - * @param $filename: filename to write to - */ - public function writeSvg( $spec, $format, $filename ) { - $svg = new SimpleXmlElement( '' ); - $svg->addAttribute( 'xmlns', 'http://www.w3.org/2000/svg' ); - $svg->addAttribute( 'version', '1.1' ); - $svg->addAttribute( 'width', $spec['width'] ); - $svg->addAttribute( 'height', $spec['height'] ); - $g = $svg->addChild( 'g' ); - foreach ( $spec['draws'] as $drawSpec ) { - $shape = $g->addChild( 'polygon' ); - $shape->addAttribute( 'fill', $drawSpec['fill'] ); - $shape->addAttribute( 'points', self::shapePointsToString( $drawSpec['shape'] ) ); - } - - if ( !$fh = fopen( $filename, 'w' ) ) { - throw new Exception( "couldn't open $filename for writing" ); - } - fwrite( $fh, $svg->asXML() ); - if ( !fclose( $fh ) ) { - throw new Exception( "couldn't close $filename" ); - } - } - - /** - * Based on an image specification, write such an image to disk, using Imagick PHP extension - * @param $spec: spec describing background and circles to draw - * @param $format: file format to write - * @param $filename: filename to write to - */ - public function writeImageWithApi( $spec, $format, $filename ) { - // this is a hack because I can't get setImageOrientation() to work. See below. - global $wgExiv2Command; - - $image = new Imagick(); - /** - * If the format is 'jpg', will also add a random orientation -- the image will be drawn rotated with triangle points - * facing in some direction (0, 90, 180 or 270 degrees) and a countering rotation should turn the triangle points upward again - */ - $orientation = self::$orientations[0]; // default is normal orientation - if ( $format == 'jpg' ) { - $orientation = self::$orientations[array_rand( self::$orientations )]; - $spec = self::rotateImageSpec( $spec, $orientation['counterRotation'] ); - } - - $image->newImage( $spec['width'], $spec['height'], new ImagickPixel( $spec['fill'] ) ); - - foreach ( $spec['draws'] as $drawSpec ) { - $draw = new ImagickDraw(); - $draw->setFillColor( $drawSpec['fill'] ); - $draw->polygon( $drawSpec['shape'] ); - $image->drawImage( $draw ); - } - - $image->setImageFormat( $format ); - - // this doesn't work, even though it's documented to do so... - // $image->setImageOrientation( $orientation['exifCode'] ); - - $image->writeImage( $filename ); - - // because the above setImageOrientation call doesn't work... nor can I get an external imagemagick binary to do this either... - // hacking this for now (only works if you have exiv2 installed, a program to read and manipulate exif) - if ( $wgExiv2Command ) { - $cmd = wfEscapeShellArg( $wgExiv2Command ) - . " -M " - . wfEscapeShellArg( "set Exif.Image.Orientation " . $orientation['exifCode'] ) - . " " - . wfEscapeShellArg( $filename ); - - $retval = 0; - $err = wfShellExec( $cmd, $retval ); - if ( $retval !== 0 ) { - print "Error with $cmd: $retval, $err\n"; - } - } - } - - /** - * Given an image specification, produce rotated version - * This is used when simulating a rotated image capture with Exif orientation - * @param $spec Object returned by getImageSpec - * @param $matrix 2x2 transformation matrix - * @return transformed Spec - */ - private static function rotateImageSpec( &$spec, $matrix ) { - $tSpec = array(); - $dims = self::matrixMultiply2x2( $matrix, $spec['width'], $spec['height'] ); - $correctionX = 0; - $correctionY = 0; - if ( $dims['x'] < 0 ) { - $correctionX = abs( $dims['x'] ); - } - if ( $dims['y'] < 0 ) { - $correctionY = abs( $dims['y'] ); - } - $tSpec['width'] = abs( $dims['x'] ); - $tSpec['height'] = abs( $dims['y'] ); - $tSpec['fill'] = $spec['fill']; - $tSpec['draws'] = array(); - foreach ( $spec['draws'] as $draw ) { - $tDraw = array( - 'fill' => $draw['fill'], - 'shape' => array() - ); - foreach ( $draw['shape'] as $point ) { - $tPoint = self::matrixMultiply2x2( $matrix, $point['x'], $point['y'] ); - $tPoint['x'] += $correctionX; - $tPoint['y'] += $correctionY; - $tDraw['shape'][] = $tPoint; - } - $tSpec['draws'][] = $tDraw; - } - - return $tSpec; - } - - /** - * Given a matrix and a pair of images, return new position - * @param $matrix: 2x2 rotation matrix - * @param $x: x-coordinate number - * @param $y: y-coordinate number - * @return Array transformed with properties x, y - */ - private static function matrixMultiply2x2( $matrix, $x, $y ) { - return array( - 'x' => $x * $matrix[0][0] + $y * $matrix[0][1], - 'y' => $x * $matrix[1][0] + $y * $matrix[1][1] - ); - } - - - /** - * Based on an image specification, write such an image to disk, using the command line ImageMagick program ('convert'). - * - * Sample command line: - * $ convert -size 100x60 xc:rgb(90,87,45) \ - * -draw 'fill rgb(12,34,56) polygon 41,39 44,57 50,57 41,39' \ - * -draw 'fill rgb(99,123,231) circle 59,39 56,57' \ - * -draw 'fill rgb(240,12,32) circle 50,21 50,3' filename.png - * - * @param $spec: spec describing background and shapes to draw - * @param $format: file format to write (unused by this method but kept so it has the same signature as writeImageWithApi) - * @param $filename: filename to write to - */ - public function writeImageWithCommandLine( $spec, $format, $filename ) { - global $wgImageMagickConvertCommand; - $args = array(); - $args[] = "-size " . wfEscapeShellArg( $spec['width'] . 'x' . $spec['height'] ); - $args[] = wfEscapeShellArg( "xc:" . $spec['fill'] ); - foreach ( $spec['draws'] as $draw ) { - $fill = $draw['fill']; - $polygon = self::shapePointsToString( $draw['shape'] ); - $drawCommand = "fill $fill polygon $polygon"; - $args[] = '-draw ' . wfEscapeShellArg( $drawCommand ); - } - $args[] = wfEscapeShellArg( $filename ); - - $command = wfEscapeShellArg( $wgImageMagickConvertCommand ) . " " . implode( " ", $args ); - $retval = null; - wfShellExec( $command, $retval ); - - return ( $retval === 0 ); - } - - /** - * Generate a string of random colors for ImageMagick or SVG, like "rgb(12, 37, 98)" - * - * @return {String} - */ - public function getRandomColor() { - $components = array(); - for ( $i = 0; $i <= 2; $i++ ) { - $components[] = mt_rand( 0, 255 ); - } - - return 'rgb(' . join( ', ', $components ) . ')'; - } - - /** - * Get an array of random pairs of random words, like array( array( 'foo', 'bar' ), array( 'quux', 'baz' ) ); - * - * @param $number Integer: number of pairs - * @return Array: of two-element arrays - */ - private function getRandomWordPairs( $number ) { - $lines = $this->getRandomLines( $number * 2 ); - // construct pairs of words - $pairs = array(); - $count = count( $lines ); - for ( $i = 0; $i < $count; $i += 2 ) { - $pairs[] = array( $lines[$i], $lines[$i + 1] ); - } - - return $pairs; - } - - /** - * Return N random lines from a file - * - * Will throw exception if the file could not be read or if it had fewer lines than requested. - * - * @param $number_desired Integer: number of lines desired - * @return Array: of exactly n elements, drawn randomly from lines the file - */ - private function getRandomLines( $number_desired ) { - $filepath = $this->dictionaryFile; - - // initialize array of lines - $lines = array(); - for ( $i = 0; $i < $number_desired; $i++ ) { - $lines[] = null; - } - - /* - * This algorithm obtains N random lines from a file in one single pass. It does this by replacing elements of - * a fixed-size array of lines, less and less frequently as it reads the file. - */ - $fh = fopen( $filepath, "r" ); - if ( !$fh ) { - throw new Exception( "couldn't open $filepath" ); - } - $line_number = 0; - $max_index = $number_desired - 1; - while ( !feof( $fh ) ) { - $line = fgets( $fh ); - if ( $line !== false ) { - $line_number++; - $line = trim( $line ); - if ( mt_rand( 0, $line_number ) <= $max_index ) { - $lines[mt_rand( 0, $max_index )] = $line; - } - } - } - fclose( $fh ); - if ( $line_number < $number_desired ) { - throw new Exception( "not enough lines in $filepath" ); - } - - return $lines; - } -} diff --git a/tests/phpunit/includes/api/format/ApiFormatPhpTest.php b/tests/phpunit/includes/api/format/ApiFormatPhpTest.php deleted file mode 100644 index a0bbb2dc..00000000 --- a/tests/phpunit/includes/api/format/ApiFormatPhpTest.php +++ /dev/null @@ -1,17 +0,0 @@ -apiRequest( 'php', array( 'action' => 'query', 'meta' => 'siteinfo' ) ); - - $this->assertInternalType( 'array', unserialize( $data ) ); - $this->assertGreaterThan( 0, count( (array)$data ) ); - } -} diff --git a/tests/phpunit/includes/api/format/ApiFormatTestBase.php b/tests/phpunit/includes/api/format/ApiFormatTestBase.php deleted file mode 100644 index 153f2cf4..00000000 --- a/tests/phpunit/includes/api/format/ApiFormatTestBase.php +++ /dev/null @@ -1,22 +0,0 @@ -createPrinterByName( $format ); - $printer->setUnescapeAmps( false ); - - $printer->initPrinter( false ); - - ob_start(); - $printer->execute(); - $out = ob_get_clean(); - - $printer->closePrinter(); - - return $out; - } -} diff --git a/tests/phpunit/includes/api/generateRandomImages.php b/tests/phpunit/includes/api/generateRandomImages.php deleted file mode 100644 index 87f5c4c0..00000000 --- a/tests/phpunit/includes/api/generateRandomImages.php +++ /dev/null @@ -1,46 +0,0 @@ -writeImages( $number, $format ); - } -} - -$maintClass = 'GenerateRandomImages'; -require RUN_MAINTENANCE_IF_MAIN; diff --git a/tests/phpunit/includes/api/query/ApiQueryBasicTest.php b/tests/phpunit/includes/api/query/ApiQueryBasicTest.php deleted file mode 100644 index 1a2aa832..00000000 --- a/tests/phpunit/includes/api/query/ApiQueryBasicTest.php +++ /dev/null @@ -1,395 +0,0 @@ -@gmail.com" - * - * 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. - * http://www.gnu.org/copyleft/gpl.html - * - * @file - */ - -require_once 'ApiQueryTestBase.php'; - -/** These tests validate basic functionality of the api query module - * - * @group API - * @group Database - * @group medium - */ -class ApiQueryBasicTest extends ApiQueryTestBase { - /** - * Create a set of pages. These must not change, otherwise the tests might give wrong results. - * @see MediaWikiTestCase::addDBData() - */ - function addDBData() { - try { - if ( Title::newFromText( 'AQBT-All' )->exists() ) { - return; - } - - // Ordering is important, as it will be returned in the same order as stored in the index - $this->editPage( 'AQBT-All', '[[Category:AQBT-Cat]] [[AQBT-Links]] {{AQBT-T}}' ); - $this->editPage( 'AQBT-Categories', '[[Category:AQBT-Cat]]' ); - $this->editPage( 'AQBT-Links', '[[AQBT-All]] [[AQBT-Categories]] [[AQBT-Templates]]' ); - $this->editPage( 'AQBT-Templates', '{{AQBT-T}}' ); - $this->editPage( 'AQBT-T', 'Content', '', NS_TEMPLATE ); - - // Refresh due to the bug with listing transclusions as links if they don't exist - $this->editPage( 'AQBT-All', '[[Category:AQBT-Cat]] [[AQBT-Links]] {{AQBT-T}}' ); - $this->editPage( 'AQBT-Templates', '{{AQBT-T}}' ); - } catch ( Exception $e ) { - $this->exceptionFromAddDBData = $e; - } - } - - private static $links = array( - array( 'prop' => 'links', 'titles' => 'AQBT-All' ), - array( 'pages' => array( - '1' => array( - 'pageid' => 1, - 'ns' => 0, - 'title' => 'AQBT-All', - 'links' => array( - array( 'ns' => 0, 'title' => 'AQBT-Links' ), - ) - ) - ) ) - ); - - private static $templates = array( - array( 'prop' => 'templates', 'titles' => 'AQBT-All' ), - array( 'pages' => array( - '1' => array( - 'pageid' => 1, - 'ns' => 0, - 'title' => 'AQBT-All', - 'templates' => array( - array( 'ns' => 10, 'title' => 'Template:AQBT-T' ), - ) - ) - ) ) - ); - - private static $categories = array( - array( 'prop' => 'categories', 'titles' => 'AQBT-All' ), - array( 'pages' => array( - '1' => array( - 'pageid' => 1, - 'ns' => 0, - 'title' => 'AQBT-All', - 'categories' => array( - array( 'ns' => 14, 'title' => 'Category:AQBT-Cat' ), - ) - ) - ) ) - ); - - private static $allpages = array( - array( 'list' => 'allpages', 'apprefix' => 'AQBT-' ), - array( 'allpages' => array( - array( 'pageid' => 1, 'ns' => 0, 'title' => 'AQBT-All' ), - array( 'pageid' => 2, 'ns' => 0, 'title' => 'AQBT-Categories' ), - array( 'pageid' => 3, 'ns' => 0, 'title' => 'AQBT-Links' ), - array( 'pageid' => 4, 'ns' => 0, 'title' => 'AQBT-Templates' ), - ) ) - ); - - private static $alllinks = array( - array( 'list' => 'alllinks', 'alprefix' => 'AQBT-' ), - array( 'alllinks' => array( - array( 'ns' => 0, 'title' => 'AQBT-All' ), - array( 'ns' => 0, 'title' => 'AQBT-Categories' ), - array( 'ns' => 0, 'title' => 'AQBT-Links' ), - array( 'ns' => 0, 'title' => 'AQBT-Templates' ), - ) ) - ); - - private static $alltransclusions = array( - array( 'list' => 'alltransclusions', 'atprefix' => 'AQBT-' ), - array( 'alltransclusions' => array( - array( 'ns' => 10, 'title' => 'Template:AQBT-T' ), - array( 'ns' => 10, 'title' => 'Template:AQBT-T' ), - ) ) - ); - - private static $allcategories = array( - array( 'list' => 'allcategories', 'acprefix' => 'AQBT-' ), - array( 'allcategories' => array( - array( '*' => 'AQBT-Cat' ), - ) ) - ); - - private static $backlinks = array( - array( 'list' => 'backlinks', 'bltitle' => 'AQBT-Links' ), - array( 'backlinks' => array( - array( 'pageid' => 1, 'ns' => 0, 'title' => 'AQBT-All' ), - ) ) - ); - - private static $embeddedin = array( - array( 'list' => 'embeddedin', 'eititle' => 'Template:AQBT-T' ), - array( 'embeddedin' => array( - array( 'pageid' => 1, 'ns' => 0, 'title' => 'AQBT-All' ), - array( 'pageid' => 4, 'ns' => 0, 'title' => 'AQBT-Templates' ), - ) ) - ); - - private static $categorymembers = array( - array( 'list' => 'categorymembers', 'cmtitle' => 'Category:AQBT-Cat' ), - array( 'categorymembers' => array( - array( 'pageid' => 1, 'ns' => 0, 'title' => 'AQBT-All' ), - array( 'pageid' => 2, 'ns' => 0, 'title' => 'AQBT-Categories' ), - ) ) - ); - - private static $generatorAllpages = array( - array( 'generator' => 'allpages', 'gapprefix' => 'AQBT-' ), - array( 'pages' => array( - '1' => array( - 'pageid' => 1, - 'ns' => 0, - 'title' => 'AQBT-All' ), - '2' => array( - 'pageid' => 2, - 'ns' => 0, - 'title' => 'AQBT-Categories' ), - '3' => array( - 'pageid' => 3, - 'ns' => 0, - 'title' => 'AQBT-Links' ), - '4' => array( - 'pageid' => 4, - 'ns' => 0, - 'title' => 'AQBT-Templates' ), - ) ) - ); - - private static $generatorLinks = array( - array( 'generator' => 'links', 'titles' => 'AQBT-Links' ), - array( 'pages' => array( - '1' => array( - 'pageid' => 1, - 'ns' => 0, - 'title' => 'AQBT-All' ), - '2' => array( - 'pageid' => 2, - 'ns' => 0, - 'title' => 'AQBT-Categories' ), - '4' => array( - 'pageid' => 4, - 'ns' => 0, - 'title' => 'AQBT-Templates' ), - ) ) - ); - - private static $generatorLinksPropLinks = array( - array( 'prop' => 'links' ), - array( 'pages' => array( - '1' => array( 'links' => array( - array( 'ns' => 0, 'title' => 'AQBT-Links' ), - ) ) - ) ) - ); - - private static $generatorLinksPropTemplates = array( - array( 'prop' => 'templates' ), - array( 'pages' => array( - '1' => array( 'templates' => array( - array( 'ns' => 10, 'title' => 'Template:AQBT-T' ) ) ), - '4' => array( 'templates' => array( - array( 'ns' => 10, 'title' => 'Template:AQBT-T' ) ) ), - ) ) - ); - - /** - * Test basic props - */ - public function testProps() { - $this->check( self::$links ); - $this->check( self::$templates ); - $this->check( self::$categories ); - } - - /** - * Test basic lists - */ - public function testLists() { - $this->check( self::$allpages ); - $this->check( self::$alllinks ); - $this->check( self::$alltransclusions ); - // This test is temporarily disabled until a sqlite bug is fixed - // $this->check( self::$allcategories ); - $this->check( self::$backlinks ); - $this->check( self::$embeddedin ); - $this->check( self::$categorymembers ); - } - - /** - * Test basic lists - */ - public function testAllTogether() { - - // All props together - $this->check( $this->merge( - self::$links, - self::$templates, - self::$categories - ) ); - - // All lists together - $this->check( $this->merge( - self::$allpages, - self::$alllinks, - self::$alltransclusions, - // This test is temporarily disabled until a sqlite bug is fixed - // self::$allcategories, - self::$backlinks, - self::$embeddedin, - self::$categorymembers - ) ); - - // All props+lists together - $this->check( $this->merge( - self::$links, - self::$templates, - self::$categories, - self::$allpages, - self::$alllinks, - self::$alltransclusions, - // This test is temporarily disabled until a sqlite bug is fixed - // self::$allcategories, - self::$backlinks, - self::$embeddedin, - self::$categorymembers - ) ); - } - - /** - * Test basic lists - */ - public function testGenerator() { - // generator=allpages - $this->check( self::$generatorAllpages ); - // generator=allpages & list=allpages - $this->check( $this->merge( - self::$generatorAllpages, - self::$allpages ) ); - // generator=links - $this->check( self::$generatorLinks ); - // generator=links & prop=links - $this->check( $this->merge( - self::$generatorLinks, - self::$generatorLinksPropLinks ) ); - // generator=links & prop=templates - $this->check( $this->merge( - self::$generatorLinks, - self::$generatorLinksPropTemplates ) ); - // generator=links & prop=links|templates - $this->check( $this->merge( - self::$generatorLinks, - self::$generatorLinksPropLinks, - self::$generatorLinksPropTemplates ) ); - // generator=links & prop=links|templates & list=allpages|... - $this->check( $this->merge( - self::$generatorLinks, - self::$generatorLinksPropLinks, - self::$generatorLinksPropTemplates, - self::$allpages, - self::$alllinks, - self::$alltransclusions, - // This test is temporarily disabled until a sqlite bug is fixed - // self::$allcategories, - self::$backlinks, - self::$embeddedin, - self::$categorymembers ) ); - } - - /** - * Test bug 51821 - */ - public function testGeneratorRedirects() { - $this->editPage( 'AQBT-Target', 'test' ); - $this->editPage( 'AQBT-Redir', '#REDIRECT [[AQBT-Target]]' ); - $this->check( array( - array( 'generator' => 'backlinks', 'gbltitle' => 'AQBT-Target', 'redirects' => '1' ), - array( - 'redirects' => array( - array( - 'from' => 'AQBT-Redir', - 'to' => 'AQBT-Target', - ) - ), - 'pages' => array( - '6' => array( - 'pageid' => 6, - 'ns' => 0, - 'title' => 'AQBT-Target', - ) - ), - ) - ) ); - } - - /** - * Recursively merges the expected values in the $item into the $all - */ - private function mergeExpected( &$all, $item ) { - foreach ( $item as $k => $v ) { - if ( array_key_exists( $k, $all ) ) { - if ( is_array( $all[$k] ) ) { - $this->mergeExpected( $all[$k], $v ); - } else { - $this->assertEquals( $all[$k], $v ); - } - } else { - $all[$k] = $v; - } - } - } - - /** - * Recursively compare arrays, ignoring mismatches in numeric key and pageids. - * @param $expected array expected values - * @param $result array returned values - */ - private function assertQueryResults( $expected, $result ) { - reset( $expected ); - reset( $result ); - while ( true ) { - $e = each( $expected ); - $r = each( $result ); - // If either of the arrays is shorter, abort. If both are done, success. - $this->assertEquals( (bool)$e, (bool)$r ); - if ( !$e ) { - break; // done - } - // continue only if keys are identical or both keys are numeric - $this->assertTrue( $e['key'] === $r['key'] || ( is_numeric( $e['key'] ) && is_numeric( $r['key'] ) ) ); - // don't compare pageids - if ( $e['key'] !== 'pageid' ) { - // If values are arrays, compare recursively, otherwise compare with === - if ( is_array( $e['value'] ) && is_array( $r['value'] ) ) { - $this->assertQueryResults( $e['value'], $r['value'] ); - } else { - $this->assertEquals( $e['value'], $r['value'] ); - } - } - } - } -} diff --git a/tests/phpunit/includes/api/query/ApiQueryContinue2Test.php b/tests/phpunit/includes/api/query/ApiQueryContinue2Test.php deleted file mode 100644 index 4d5ddbae..00000000 --- a/tests/phpunit/includes/api/query/ApiQueryContinue2Test.php +++ /dev/null @@ -1,68 +0,0 @@ -@gmail.com" - * - * 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 3 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. - * http://www.gnu.org/copyleft/gpl.html - */ - -require_once 'ApiQueryContinueTestBase.php'; - -/** - * @group API - * @group Database - * @group medium - */ -class ApiQueryContinue2Test extends ApiQueryContinueTestBase { - /** - * Create a set of pages. These must not change, otherwise the tests might give wrong results. - * @see MediaWikiTestCase::addDBData() - */ - function addDBData() { - try { - $this->editPage( 'AQCT73462-A', '**AQCT73462-A** [[AQCT73462-B]] [[AQCT73462-C]]' ); - $this->editPage( 'AQCT73462-B', '[[AQCT73462-A]] **AQCT73462-B** [[AQCT73462-C]]' ); - $this->editPage( 'AQCT73462-C', '[[AQCT73462-A]] [[AQCT73462-B]] **AQCT73462-C**' ); - $this->editPage( 'AQCT73462-A', '**AQCT73462-A** [[AQCT73462-B]] [[AQCT73462-C]]' ); - $this->editPage( 'AQCT73462-B', '[[AQCT73462-A]] **AQCT73462-B** [[AQCT73462-C]]' ); - $this->editPage( 'AQCT73462-C', '[[AQCT73462-A]] [[AQCT73462-B]] **AQCT73462-C**' ); - } catch ( Exception $e ) { - $this->exceptionFromAddDBData = $e; - } - } - - /** - * @medium - */ - public function testA() { - $this->mVerbose = false; - $mk = function ( $g, $p, $gDir ) { - return array( - 'generator' => 'allpages', - 'gapprefix' => 'AQCT73462-', - 'prop' => 'links', - 'gaplimit' => "$g", - 'pllimit' => "$p", - 'gapdir' => $gDir ? "ascending" : "descending", - ); - }; - // generator + 1 prop + 1 list - $data = $this->query( $mk( 99, 99, true ), 1, 'g1p', false ); - $this->checkC( $data, $mk( 1, 1, true ), 6, 'g1p-11t' ); - $this->checkC( $data, $mk( 2, 2, true ), 3, 'g1p-22t' ); - $this->checkC( $data, $mk( 1, 1, false ), 6, 'g1p-11f' ); - $this->checkC( $data, $mk( 2, 2, false ), 3, 'g1p-22f' ); - } -} diff --git a/tests/phpunit/includes/api/query/ApiQueryContinueTest.php b/tests/phpunit/includes/api/query/ApiQueryContinueTest.php deleted file mode 100644 index f494e9ca..00000000 --- a/tests/phpunit/includes/api/query/ApiQueryContinueTest.php +++ /dev/null @@ -1,313 +0,0 @@ -@gmail.com" - * - * 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. - * http://www.gnu.org/copyleft/gpl.html - */ - -require_once 'ApiQueryContinueTestBase.php'; - -/** - * These tests validate the new continue functionality of the api query module by - * doing multiple requests with varying parameters, merging the results, and checking - * that the result matches the full data received in one no-limits call. - * - * @group API - * @group Database - * @group medium - */ -class ApiQueryContinueTest extends ApiQueryContinueTestBase { - /** - * Create a set of pages. These must not change, otherwise the tests might give wrong results. - * @see MediaWikiTestCase::addDBData() - */ - function addDBData() { - try { - $this->editPage( 'Template:AQCT-T1', '**Template:AQCT-T1**' ); - $this->editPage( 'Template:AQCT-T2', '**Template:AQCT-T2**' ); - $this->editPage( 'Template:AQCT-T3', '**Template:AQCT-T3**' ); - $this->editPage( 'Template:AQCT-T4', '**Template:AQCT-T4**' ); - $this->editPage( 'Template:AQCT-T5', '**Template:AQCT-T5**' ); - - $this->editPage( 'AQCT-1', '**AQCT-1** {{AQCT-T2}} {{AQCT-T3}} {{AQCT-T4}} {{AQCT-T5}}' ); - $this->editPage( 'AQCT-2', '[[AQCT-1]] **AQCT-2** {{AQCT-T3}} {{AQCT-T4}} {{AQCT-T5}}' ); - $this->editPage( 'AQCT-3', '[[AQCT-1]] [[AQCT-2]] **AQCT-3** {{AQCT-T4}} {{AQCT-T5}}' ); - $this->editPage( 'AQCT-4', '[[AQCT-1]] [[AQCT-2]] [[AQCT-3]] **AQCT-4** {{AQCT-T5}}' ); - $this->editPage( 'AQCT-5', '[[AQCT-1]] [[AQCT-2]] [[AQCT-3]] [[AQCT-4]] **AQCT-5**' ); - } catch ( Exception $e ) { - $this->exceptionFromAddDBData = $e; - } - } - - /** - * Test smart continue - list=allpages - * @medium - */ - public function test1List() { - $this->mVerbose = false; - $mk = function ( $l ) { - return array( - 'list' => 'allpages', - 'apprefix' => 'AQCT-', - 'aplimit' => "$l", - ); - }; - $data = $this->query( $mk( 99 ), 1, '1L', false ); - - // 1 list - $this->checkC( $data, $mk( 1 ), 5, '1L-1' ); - $this->checkC( $data, $mk( 2 ), 3, '1L-2' ); - $this->checkC( $data, $mk( 3 ), 2, '1L-3' ); - $this->checkC( $data, $mk( 4 ), 2, '1L-4' ); - $this->checkC( $data, $mk( 5 ), 1, '1L-5' ); - } - - /** - * Test smart continue - list=allpages|alltransclusions - * @medium - */ - public function test2Lists() { - $this->mVerbose = false; - $mk = function ( $l1, $l2 ) { - return array( - 'list' => 'allpages|alltransclusions', - 'apprefix' => 'AQCT-', - 'atprefix' => 'AQCT-', - 'atunique' => '', - 'aplimit' => "$l1", - 'atlimit' => "$l2", - ); - }; - // 2 lists - $data = $this->query( $mk( 99, 99 ), 1, '2L', false ); - $this->checkC( $data, $mk( 1, 1 ), 5, '2L-11' ); - $this->checkC( $data, $mk( 2, 2 ), 3, '2L-22' ); - $this->checkC( $data, $mk( 3, 3 ), 2, '2L-33' ); - $this->checkC( $data, $mk( 4, 4 ), 2, '2L-44' ); - $this->checkC( $data, $mk( 5, 5 ), 1, '2L-55' ); - } - - /** - * Test smart continue - generator=allpages, prop=links - * @medium - */ - public function testGen1Prop() { - $this->mVerbose = false; - $mk = function ( $g, $p ) { - return array( - 'generator' => 'allpages', - 'gapprefix' => 'AQCT-', - 'gaplimit' => "$g", - 'prop' => 'links', - 'pllimit' => "$p", - ); - }; - // generator + 1 prop - $data = $this->query( $mk( 99, 99 ), 1, 'G1P', false ); - $this->checkC( $data, $mk( 1, 1 ), 11, 'G1P-11' ); - $this->checkC( $data, $mk( 2, 2 ), 6, 'G1P-22' ); - $this->checkC( $data, $mk( 3, 3 ), 4, 'G1P-33' ); - $this->checkC( $data, $mk( 4, 4 ), 3, 'G1P-44' ); - $this->checkC( $data, $mk( 5, 5 ), 2, 'G1P-55' ); - } - - /** - * Test smart continue - generator=allpages, prop=links|templates - * @medium - */ - public function testGen2Prop() { - $this->mVerbose = false; - $mk = function ( $g, $p1, $p2 ) { - return array( - 'generator' => 'allpages', - 'gapprefix' => 'AQCT-', - 'gaplimit' => "$g", - 'prop' => 'links|templates', - 'pllimit' => "$p1", - 'tllimit' => "$p2", - ); - }; - // generator + 2 props - $data = $this->query( $mk( 99, 99, 99 ), 1, 'G2P', false ); - $this->checkC( $data, $mk( 1, 1, 1 ), 16, 'G2P-111' ); - $this->checkC( $data, $mk( 2, 2, 2 ), 9, 'G2P-222' ); - $this->checkC( $data, $mk( 3, 3, 3 ), 6, 'G2P-333' ); - $this->checkC( $data, $mk( 4, 4, 4 ), 4, 'G2P-444' ); - $this->checkC( $data, $mk( 5, 5, 5 ), 2, 'G2P-555' ); - $this->checkC( $data, $mk( 5, 1, 1 ), 10, 'G2P-511' ); - $this->checkC( $data, $mk( 4, 2, 2 ), 7, 'G2P-422' ); - $this->checkC( $data, $mk( 2, 3, 3 ), 7, 'G2P-233' ); - $this->checkC( $data, $mk( 2, 4, 4 ), 5, 'G2P-244' ); - $this->checkC( $data, $mk( 1, 5, 5 ), 5, 'G2P-155' ); - } - - /** - * Test smart continue - generator=allpages, prop=links, list=alltransclusions - * @medium - */ - public function testGen1Prop1List() { - $this->mVerbose = false; - $mk = function ( $g, $p, $l ) { - return array( - 'generator' => 'allpages', - 'gapprefix' => 'AQCT-', - 'gaplimit' => "$g", - 'prop' => 'links', - 'pllimit' => "$p", - 'list' => 'alltransclusions', - 'atprefix' => 'AQCT-', - 'atunique' => '', - 'atlimit' => "$l", - ); - }; - // generator + 1 prop + 1 list - $data = $this->query( $mk( 99, 99, 99 ), 1, 'G1P1L', false ); - $this->checkC( $data, $mk( 1, 1, 1 ), 11, 'G1P1L-111' ); - $this->checkC( $data, $mk( 2, 2, 2 ), 6, 'G1P1L-222' ); - $this->checkC( $data, $mk( 3, 3, 3 ), 4, 'G1P1L-333' ); - $this->checkC( $data, $mk( 4, 4, 4 ), 3, 'G1P1L-444' ); - $this->checkC( $data, $mk( 5, 5, 5 ), 2, 'G1P1L-555' ); - $this->checkC( $data, $mk( 5, 5, 1 ), 4, 'G1P1L-551' ); - $this->checkC( $data, $mk( 5, 5, 2 ), 2, 'G1P1L-552' ); - } - - /** - * Test smart continue - generator=allpages, prop=links|templates, - * list=alllinks|alltransclusions, meta=siteinfo - * @medium - */ - public function testGen2Prop2List1Meta() { - $this->mVerbose = false; - $mk = function ( $g, $p1, $p2, $l1, $l2 ) { - return array( - 'generator' => 'allpages', - 'gapprefix' => 'AQCT-', - 'gaplimit' => "$g", - 'prop' => 'links|templates', - 'pllimit' => "$p1", - 'tllimit' => "$p2", - 'list' => 'alllinks|alltransclusions', - 'alprefix' => 'AQCT-', - 'alunique' => '', - 'allimit' => "$l1", - 'atprefix' => 'AQCT-', - 'atunique' => '', - 'atlimit' => "$l2", - 'meta' => 'siteinfo', - 'siprop' => 'namespaces', - ); - }; - // generator + 1 prop + 1 list - $data = $this->query( $mk( 99, 99, 99, 99, 99 ), 1, 'G2P2L1M', false ); - $this->checkC( $data, $mk( 1, 1, 1, 1, 1 ), 16, 'G2P2L1M-11111' ); - $this->checkC( $data, $mk( 2, 2, 2, 2, 2 ), 9, 'G2P2L1M-22222' ); - $this->checkC( $data, $mk( 3, 3, 3, 3, 3 ), 6, 'G2P2L1M-33333' ); - $this->checkC( $data, $mk( 4, 4, 4, 4, 4 ), 4, 'G2P2L1M-44444' ); - $this->checkC( $data, $mk( 5, 5, 5, 5, 5 ), 2, 'G2P2L1M-55555' ); - $this->checkC( $data, $mk( 5, 5, 5, 1, 1 ), 4, 'G2P2L1M-55511' ); - $this->checkC( $data, $mk( 5, 5, 5, 2, 2 ), 2, 'G2P2L1M-55522' ); - $this->checkC( $data, $mk( 5, 1, 1, 5, 5 ), 10, 'G2P2L1M-51155' ); - $this->checkC( $data, $mk( 5, 2, 2, 5, 5 ), 5, 'G2P2L1M-52255' ); - } - - /** - * Test smart continue - generator=templates, prop=templates - * @medium - */ - public function testSameGenAndProp() { - $this->mVerbose = false; - $mk = function ( $g, $gDir, $p, $pDir ) { - return array( - 'titles' => 'AQCT-1', - 'generator' => 'templates', - 'gtllimit' => "$g", - 'gtldir' => $gDir ? 'ascending' : 'descending', - 'prop' => 'templates', - 'tllimit' => "$p", - 'tldir' => $pDir ? 'ascending' : 'descending', - ); - }; - // generator + 1 prop - $data = $this->query( $mk( 99, true, 99, true ), 1, 'G=P', false ); - - $this->checkC( $data, $mk( 1, true, 1, true ), 4, 'G=P-1t1t' ); - $this->checkC( $data, $mk( 2, true, 2, true ), 2, 'G=P-2t2t' ); - $this->checkC( $data, $mk( 3, true, 3, true ), 2, 'G=P-3t3t' ); - $this->checkC( $data, $mk( 1, true, 3, true ), 4, 'G=P-1t3t' ); - $this->checkC( $data, $mk( 3, true, 1, true ), 2, 'G=P-3t1t' ); - - $this->checkC( $data, $mk( 1, true, 1, false ), 4, 'G=P-1t1f' ); - $this->checkC( $data, $mk( 2, true, 2, false ), 2, 'G=P-2t2f' ); - $this->checkC( $data, $mk( 3, true, 3, false ), 2, 'G=P-3t3f' ); - $this->checkC( $data, $mk( 1, true, 3, false ), 4, 'G=P-1t3f' ); - $this->checkC( $data, $mk( 3, true, 1, false ), 2, 'G=P-3t1f' ); - - $this->checkC( $data, $mk( 1, false, 1, true ), 4, 'G=P-1f1t' ); - $this->checkC( $data, $mk( 2, false, 2, true ), 2, 'G=P-2f2t' ); - $this->checkC( $data, $mk( 3, false, 3, true ), 2, 'G=P-3f3t' ); - $this->checkC( $data, $mk( 1, false, 3, true ), 4, 'G=P-1f3t' ); - $this->checkC( $data, $mk( 3, false, 1, true ), 2, 'G=P-3f1t' ); - - $this->checkC( $data, $mk( 1, false, 1, false ), 4, 'G=P-1f1f' ); - $this->checkC( $data, $mk( 2, false, 2, false ), 2, 'G=P-2f2f' ); - $this->checkC( $data, $mk( 3, false, 3, false ), 2, 'G=P-3f3f' ); - $this->checkC( $data, $mk( 1, false, 3, false ), 4, 'G=P-1f3f' ); - $this->checkC( $data, $mk( 3, false, 1, false ), 2, 'G=P-3f1f' ); - } - - /** - * Test smart continue - generator=allpages, list=allpages - * @medium - */ - public function testSameGenList() { - $this->mVerbose = false; - $mk = function ( $g, $gDir, $l, $pDir ) { - return array( - 'generator' => 'allpages', - 'gapprefix' => 'AQCT-', - 'gaplimit' => "$g", - 'gapdir' => $gDir ? 'ascending' : 'descending', - 'list' => 'allpages', - 'apprefix' => 'AQCT-', - 'aplimit' => "$l", - 'apdir' => $pDir ? 'ascending' : 'descending', - ); - }; - // generator + 1 list - $data = $this->query( $mk( 99, true, 99, true ), 1, 'G=L', false ); - - $this->checkC( $data, $mk( 1, true, 1, true ), 5, 'G=L-1t1t' ); - $this->checkC( $data, $mk( 2, true, 2, true ), 3, 'G=L-2t2t' ); - $this->checkC( $data, $mk( 3, true, 3, true ), 2, 'G=L-3t3t' ); - $this->checkC( $data, $mk( 1, true, 3, true ), 5, 'G=L-1t3t' ); - $this->checkC( $data, $mk( 3, true, 1, true ), 5, 'G=L-3t1t' ); - $this->checkC( $data, $mk( 1, true, 1, false ), 5, 'G=L-1t1f' ); - $this->checkC( $data, $mk( 2, true, 2, false ), 3, 'G=L-2t2f' ); - $this->checkC( $data, $mk( 3, true, 3, false ), 2, 'G=L-3t3f' ); - $this->checkC( $data, $mk( 1, true, 3, false ), 5, 'G=L-1t3f' ); - $this->checkC( $data, $mk( 3, true, 1, false ), 5, 'G=L-3t1f' ); - $this->checkC( $data, $mk( 1, false, 1, true ), 5, 'G=L-1f1t' ); - $this->checkC( $data, $mk( 2, false, 2, true ), 3, 'G=L-2f2t' ); - $this->checkC( $data, $mk( 3, false, 3, true ), 2, 'G=L-3f3t' ); - $this->checkC( $data, $mk( 1, false, 3, true ), 5, 'G=L-1f3t' ); - $this->checkC( $data, $mk( 3, false, 1, true ), 5, 'G=L-3f1t' ); - $this->checkC( $data, $mk( 1, false, 1, false ), 5, 'G=L-1f1f' ); - $this->checkC( $data, $mk( 2, false, 2, false ), 3, 'G=L-2f2f' ); - $this->checkC( $data, $mk( 3, false, 3, false ), 2, 'G=L-3f3f' ); - $this->checkC( $data, $mk( 1, false, 3, false ), 5, 'G=L-1f3f' ); - $this->checkC( $data, $mk( 3, false, 1, false ), 5, 'G=L-3f1f' ); - } -} diff --git a/tests/phpunit/includes/api/query/ApiQueryContinueTestBase.php b/tests/phpunit/includes/api/query/ApiQueryContinueTestBase.php deleted file mode 100644 index fbb1e640..00000000 --- a/tests/phpunit/includes/api/query/ApiQueryContinueTestBase.php +++ /dev/null @@ -1,209 +0,0 @@ -@gmail.com" - * - * 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. - * http://www.gnu.org/copyleft/gpl.html - * - * @file - */ - -require_once 'ApiQueryTestBase.php'; - -abstract class ApiQueryContinueTestBase extends ApiQueryTestBase { - - /** - * Enable to print in-depth debugging info during the test run - */ - protected $mVerbose = false; - - /** - * Run query() and compare against expected values - */ - protected function checkC( $expected, $params, $expectedCount, $id, $continue = true ) { - $result = $this->query( $params, $expectedCount, $id, $continue ); - $this->assertResult( $expected, $result, $id ); - } - - /** - * Run query in a loop until no more values are available - * @param array $params api parameters - * @param int $expectedCount max number of iterations - * @param string $id unit test id - * @param boolean $useContinue true to use smart continue - * @return mixed: merged results data array - * @throws Exception - */ - protected function query( $params, $expectedCount, $id, $useContinue = true ) { - if ( isset( $params['action'] ) ) { - $this->assertEquals( 'query', $params['action'], 'Invalid query action' ); - } else { - $params['action'] = 'query'; - } - if ( $useContinue && !isset( $params['continue'] ) ) { - $params['continue'] = ''; - } - $count = 0; - $result = array(); - $continue = array(); - do { - $request = array_merge( $params, $continue ); - uksort( $request, function ( $a, $b ) { - // put 'continue' params at the end - lazy method - $a = strpos( $a, 'continue' ) !== false ? 'zzz ' . $a : $a; - $b = strpos( $b, 'continue' ) !== false ? 'zzz ' . $b : $b; - - return strcmp( $a, $b ); - } ); - $reqStr = http_build_query( $request ); - //$reqStr = str_replace( '&', ' & ', $reqStr ); - $this->assertLessThan( $expectedCount, $count, "$id more data: $reqStr" ); - if ( $this->mVerbose ) { - print "$id (#$count): $reqStr\n"; - } - try { - $data = $this->doApiRequest( $request ); - } catch ( Exception $e ) { - throw new Exception( "$id on $count", 0, $e ); - } - $data = $data[0]; - if ( isset( $data['warnings'] ) ) { - $warnings = json_encode( $data['warnings'] ); - $this->fail( "$id Warnings on #$count in $reqStr\n$warnings" ); - } - $this->assertArrayHasKey( 'query', $data, "$id no 'query' on #$count in $reqStr" ); - if ( isset( $data['continue'] ) ) { - $continue = $data['continue']; - unset( $data['continue'] ); - } else { - $continue = array(); - } - if ( $this->mVerbose ) { - $this->printResult( $data ); - } - $this->mergeResult( $result, $data ); - $count++; - if ( empty( $continue ) ) { - // $this->assertEquals( $expectedCount, $count, "$id finished early" ); - if ( $expectedCount > $count ) { - print "***** $id Finished early in $count turns. $expectedCount was expected\n"; - } - - return $result; - } elseif ( !$useContinue ) { - $this->assertFalse( 'Non-smart query must be requested all at once' ); - } - } while ( true ); - } - - private function printResult( $data ) { - $q = $data['query']; - $print = array(); - if ( isset( $q['pages'] ) ) { - foreach ( $q['pages'] as $p ) { - $m = $p['title']; - if ( isset( $p['links'] ) ) { - $m .= '/[' . implode( ',', array_map( - function ( $v ) { - return $v['title']; - }, - $p['links'] ) ) . ']'; - } - if ( isset( $p['categories'] ) ) { - $m .= '/(' . implode( ',', array_map( - function ( $v ) { - return str_replace( 'Category:', '', $v['title'] ); - }, - $p['categories'] ) ) . ')'; - } - $print[] = $m; - } - } - if ( isset( $q['allcategories'] ) ) { - $print[] = '*Cats/(' . implode( ',', array_map( - function ( $v ) { - return $v['*']; - }, - $q['allcategories'] ) ) . ')'; - } - self::GetItems( $q, 'allpages', 'Pages', $print ); - self::GetItems( $q, 'alllinks', 'Links', $print ); - self::GetItems( $q, 'alltransclusions', 'Trnscl', $print ); - print ' ' . implode( ' ', $print ) . "\n"; - } - - private static function GetItems( $q, $moduleName, $name, &$print ) { - if ( isset( $q[$moduleName] ) ) { - $print[] = "*$name/[" . implode( ',', - array_map( function ( $v ) { - return $v['title']; - }, - $q[$moduleName] ) ) . ']'; - } - } - - /** - * Recursively merge the new result returned from the query to the previous results. - * @param mixed $results - * @param mixed $newResult - * @param bool $numericIds If true, treat keys as ids to be merged instead of appending - */ - protected function mergeResult( &$results, $newResult, $numericIds = false ) { - $this->assertEquals( is_array( $results ), is_array( $newResult ), 'Type of result and data do not match' ); - if ( !is_array( $results ) ) { - $this->assertEquals( $results, $newResult, 'Repeated result must be the same as before' ); - } else { - $sort = null; - foreach ( $newResult as $key => $value ) { - if ( !$numericIds && $sort === null ) { - if ( !is_array( $value ) ) { - $sort = false; - } elseif ( array_key_exists( 'title', $value ) ) { - $sort = function ( $a, $b ) { - return strcmp( $a['title'], $b['title'] ); - }; - } else { - $sort = false; - } - } - $keyExists = array_key_exists( $key, $results ); - if ( is_numeric( $key ) ) { - if ( $numericIds ) { - if ( !$keyExists ) { - $results[$key] = $value; - } else { - $this->mergeResult( $results[$key], $value ); - } - } else { - $results[] = $value; - } - } elseif ( !$keyExists ) { - $results[$key] = $value; - } else { - $this->mergeResult( $results[$key], $value, $key === 'pages' ); - } - } - if ( $numericIds ) { - ksort( $results, SORT_NUMERIC ); - } elseif ( $sort !== null && $sort !== false ) { - uasort( $results, $sort ); - } - } - } -} diff --git a/tests/phpunit/includes/api/query/ApiQueryRevisionsTest.php b/tests/phpunit/includes/api/query/ApiQueryRevisionsTest.php deleted file mode 100644 index 1bca2256..00000000 --- a/tests/phpunit/includes/api/query/ApiQueryRevisionsTest.php +++ /dev/null @@ -1,39 +0,0 @@ -doEdit( 'Some text', 'inserting content' ); - - $apiResult = $this->doApiRequest( array( - 'action' => 'query', - 'prop' => 'revisions', - 'titles' => $pageName, - 'rvprop' => 'content', - ) ); - $this->assertArrayHasKey( 'query', $apiResult[0] ); - $this->assertArrayHasKey( 'pages', $apiResult[0]['query'] ); - foreach ( $apiResult[0]['query']['pages'] as $page ) { - $this->assertArrayHasKey( 'revisions', $page ); - foreach ( $page['revisions'] as $revision ) { - $this->assertArrayHasKey( 'contentformat', $revision, - 'contentformat should be included when asking content so client knows how to interpret it' - ); - $this->assertArrayHasKey( 'contentmodel', $revision, - 'contentmodel should be included when asking content so client knows how to interpret it' - ); - } - } - } -} diff --git a/tests/phpunit/includes/api/query/ApiQueryTest.php b/tests/phpunit/includes/api/query/ApiQueryTest.php deleted file mode 100644 index f5645555..00000000 --- a/tests/phpunit/includes/api/query/ApiQueryTest.php +++ /dev/null @@ -1,66 +0,0 @@ -doLogin(); - } - - public function testTitlesGetNormalized() { - - global $wgMetaNamespace; - - $data = $this->doApiRequest( array( - 'action' => 'query', - 'titles' => 'Project:articleA|article_B' ) ); - - $this->assertArrayHasKey( 'query', $data[0] ); - $this->assertArrayHasKey( 'normalized', $data[0]['query'] ); - - // Forge a normalized title - $to = Title::newFromText( $wgMetaNamespace . ':ArticleA' ); - - $this->assertEquals( - array( - 'from' => 'Project:articleA', - 'to' => $to->getPrefixedText(), - ), - $data[0]['query']['normalized'][0] - ); - - $this->assertEquals( - array( - 'from' => 'article_B', - 'to' => 'Article B' - ), - $data[0]['query']['normalized'][1] - ); - } - - public function testTitlesAreRejectedIfInvalid() { - $title = false; - while ( !$title || Title::newFromText( $title )->exists() ) { - $title = md5( mt_rand( 0, 10000 ) + rand( 0, 999000 ) ); - } - - $data = $this->doApiRequest( array( - 'action' => 'query', - 'titles' => $title . '|Talk:' ) ); - - $this->assertArrayHasKey( 'query', $data[0] ); - $this->assertArrayHasKey( 'pages', $data[0]['query'] ); - $this->assertEquals( 2, count( $data[0]['query']['pages'] ) ); - - $this->assertArrayHasKey( -2, $data[0]['query']['pages'] ); - $this->assertArrayHasKey( -1, $data[0]['query']['pages'] ); - - $this->assertArrayHasKey( 'missing', $data[0]['query']['pages'][-2] ); - $this->assertArrayHasKey( 'invalid', $data[0]['query']['pages'][-1] ); - } -} diff --git a/tests/phpunit/includes/api/query/ApiQueryTestBase.php b/tests/phpunit/includes/api/query/ApiQueryTestBase.php deleted file mode 100644 index 8ee8ea96..00000000 --- a/tests/phpunit/includes/api/query/ApiQueryTestBase.php +++ /dev/null @@ -1,150 +0,0 @@ -@gmail.com" - * - * 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 3 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. - * http://www.gnu.org/copyleft/gpl.html - * - * @file - */ - -/** This class has some common functionality for testing query module - */ -abstract class ApiQueryTestBase extends ApiTestCase { - - const PARAM_ASSERT = <<validateRequestExpectedPair( $v ); - $request = array_merge_recursive( $request, $req ); - $this->mergeExpected( $expected, $exp ); - } - - return array( $request, $expected ); - } - - /** - * Check that the parameter is a valid two element array, - * with the first element being API request and the second - expected result - */ - private function validateRequestExpectedPair( $v ) { - $this->assertType( 'array', $v, self::PARAM_ASSERT ); - $this->assertEquals( 2, count( $v ), self::PARAM_ASSERT ); - $this->assertArrayHasKey( 0, $v, self::PARAM_ASSERT ); - $this->assertArrayHasKey( 1, $v, self::PARAM_ASSERT ); - $this->assertType( 'array', $v[0], self::PARAM_ASSERT ); - $this->assertType( 'array', $v[1], self::PARAM_ASSERT ); - - return $v; - } - - /** - * Recursively merges the expected values in the $item into the $all - */ - private function mergeExpected( &$all, $item ) { - foreach ( $item as $k => $v ) { - if ( array_key_exists( $k, $all ) ) { - if ( is_array( $all[$k] ) ) { - $this->mergeExpected( $all[$k], $v ); - } else { - $this->assertEquals( $all[$k], $v ); - } - } else { - $all[$k] = $v; - } - } - } - - /** - * Checks that the request's result matches the expected results. - * @param $values array is a two element array( request, expected_results ) - * @throws Exception - */ - protected function check( $values ) { - list( $req, $exp ) = $this->validateRequestExpectedPair( $values ); - if ( !array_key_exists( 'action', $req ) ) { - $req['action'] = 'query'; - } - foreach ( $req as &$val ) { - if ( is_array( $val ) ) { - $val = implode( '|', array_unique( $val ) ); - } - } - $result = $this->doApiRequest( $req ); - $this->assertResult( array( 'query' => $exp ), $result[0], $req ); - } - - protected function assertResult( $exp, $result, $message = '' ) { - try { - $this->assertResultRecursive( $exp, $result ); - } catch ( Exception $e ) { - if ( is_array( $message ) ) { - $message = http_build_query( $message ); - } - print "\nRequest: $message\n"; - print "\nExpected:\n"; - print_r( $exp ); - print "\nResult:\n"; - print_r( $result ); - throw $e; // rethrow it - } - } - - /** - * Recursively compare arrays, ignoring mismatches in numeric key and pageids. - * @param $expected array expected values - * @param $result array returned values - */ - private function assertResultRecursive( $expected, $result ) { - reset( $expected ); - reset( $result ); - while ( true ) { - $e = each( $expected ); - $r = each( $result ); - // If either of the arrays is shorter, abort. If both are done, success. - $this->assertEquals( (bool)$e, (bool)$r ); - if ( !$e ) { - break; // done - } - // continue only if keys are identical or both keys are numeric - $this->assertTrue( $e['key'] === $r['key'] || ( is_numeric( $e['key'] ) && is_numeric( $r['key'] ) ) ); - // don't compare pageids - if ( $e['key'] !== 'pageid' ) { - // If values are arrays, compare recursively, otherwise compare with === - if ( is_array( $e['value'] ) && is_array( $r['value'] ) ) { - $this->assertResultRecursive( $e['value'], $r['value'] ); - } else { - $this->assertEquals( $e['value'], $r['value'] ); - } - } - } - } -} diff --git a/tests/phpunit/includes/api/words.txt b/tests/phpunit/includes/api/words.txt deleted file mode 100644 index 7ce23ee3..00000000 --- a/tests/phpunit/includes/api/words.txt +++ /dev/null @@ -1,1000 +0,0 @@ -Andaquian -Anoplanthus -Araquaju -Astrophyton -Avarish -Batonga -Bdellidae -Betoyan -Bismarck -Britishness -Carmen -Chatillon -Clement -Coryphaena -Croton -Cyrillianism -Dagomba -Decimus -Dichorisandra -Duculinae -Empusa -Escallonia -Fathometer -Fon -Fundulinae -Gadswoons -Gederathite -Gemini -Gerbera -Gregarinida -Gyracanthus -Halopsychidae -Hasidim -Hemerobius -Ichthyosauridae -Iscariot -Jeames -Jesuitry -Jovian -Judaization -Katie -Ladin -Langhian -Lapithaean -Lisette -Macrochira -Malaxis -Malvastrum -Maranhao -Marxian -Maurist -Metrosideros -Micky -Microsporon -Odacidae -Ophiuchid -Osmorhiza -Paguma -Palesman -Papayaceae -Pastinaca -Philoxenian -Pleurostigma -Rarotongan -Rhodoraceae -Rong -Saho -Sanyakoan -Sardanapalian -Sauropoda -Sedentaria -Shambu -Shukulumbwe -Solonian -Spaniardization -Spirochaetaceae -Stomatopoda -Stratiotes -Taiwanhemp -Titanically -Venetianed -Victrola -Yuman -abatis -abaton -abjoint -acanthoma -acari -acceptance -actinography -acuteness -addiment -adelite -adelomorphic -adelphogamy -adipocele -aelurophobia -affined -aflaunt -agathokakological -aischrolatreia -alarmedly -alebench -aleurone -allelotropic -allerion -alloplastic -allowable -alternacy -alternariose -altricial -ambitionist -amendment -amiableness -amicableness -ammo -amortizable -anchorate -anemometrically -angelocracy -angelological -anodal -anomalure -antedate -antiagglutinin -antirationalist -antiscorbutic -antisplasher -antithesize -antiunionist -antoecian -apolegamic -appropriation -archididascalian -archival -arteriophlebotomy -articulable -asseveration -assignation -atelo -atrienses -atrophy -atterminement -atypic -automower -aveloz -awrist -azteca -bairnteam -balsamweed -bannerman -beardy -becry -beek -beggarwise -bescab -bestness -bethel -bewildering -bibliophilism -bitterblain -blakeberyed -boccarella -bocedization -boobyalla -bourbon -bowbent -bowerbird -brachygnathous -brail -branchiferous -brelaw -brew -brideweed -bridgeable -brombenzamide -buddler -burbankian -burr -buskin -cacochymical -calefactory -caliper -canaliculus -candidature -canellaceous -canniness -canning -cantilene -carbonatation -carthamic -caseum -caudated -causationist -ceruleite -chalder -chalta -charmel -chekan -chillness -chirogymnast -chirpling -chlorinous -cholanthrene -chondroblast -chromatography -chromophilous -chronical -cicatrice -cinchonine -city -clubbing -coastal -coaxially -coercible -coeternity -coff -coinventor -collyba -combinator -complanation -comprehensibility -conchuela -congenital -context -contranatural -corallum -cordately -cornupete -corolliferous -coroneted -corticosterone -coseat -cottage -crocetin -crossleted -crottels -curvedness -cycadeous -cyclism -cylindrically -cynanche -cyrtoceratitic -cystospasm -danceress -dancette -dawny -daydreamy -debar -decarburization -decorousness -decrepitness -delirious -deozonizer -dermatosis -desma -deutencephalic -diacetate -diarthrodial -diathermy -dicolic -dimastigate -dimidiation -dipetto -disavowable -disintrench -disman -dismay -disorder -disoxygenation -dithionous -dogman -dragonfly -dramatical -drawspan -drubbly -drunk -duskly -ecderonic -ectocuniform -ectocyst -ehrwaldite -electrocute -elemicin -embracing -emotionality -enactment -enamor -enclave -endameba -endochylous -endocrinologist -endolymph -endothecal -entasia -epigeous -episcopicide -epitrichial -erminee -erraticalness -eruptivity -erythrocytoschisis -esperance -estuous -eucrystalline -eugeny -evacuant -everbloomer -evocation -exarchateship -exasperate -excorticate -excrementary -exile -expandedly -exponency -expressionist -expulsion -extemporary -extollation -extortive -extrabulbar -extraprostatic -facticide -fairer -fakery -fasibitikite -fatiscent -fearless -febrifuge -ferie -fibrousness -fingered -fisheye -flagpole -flagrantness -fleche -fluidism -folliculin -footbreadth -forceps -forecontrive -forthbring -foveated -fuchsin -fungicidal -funori -gamelang -gametically -garvanzo -gasoliner -gastrophile -germproof -gerontism -gigantical -glaciology -godmotherhood -gooseherd -gordunite -gove -gracilis -greathead -grieveship -guidable -gyromancy -gyrostat -habitus -hailweed -handhole -hangalai -haznadar -heliced -hemihypertrophy -hemimorphic -hemistrumectomy -heptavalent -heptite -herbalist -herpetology -hesperid -hexacarbon -hieromnemon -hobbyless -holodactylic -homoeoarchy -hopperings -hospitable -houseboat -huh -huntedly -hydroponics -hydrosomal -hyperdactylia -hyperperistalsis -hypogeocarpous -ideogram -idiopathical -illegitimate -imambarah -impotently -improvise -impuberal -inaccurately -incarnant -inchoation -incliner -incredulous -indiscriminateness -indulgenced -inebriation -inexpressiveness -infibulate -inflectedness -iniome -ink -inquietly -insaturable -insinuative -instiller -institutive -insultproof -interactionist -intercensal -interpenetrable -intertranspicuous -intrinsicality -inwards -iridiocyte -iridoparalysis -irreportable -isoprene -isosmotic -izard -jacuaru -jaculative -jerkined -joe -joyous -julienne -justicehood -kali -kalidium -katha -kathal -keelage -keratomycosis -khaki -khedival -kinkily -knife -kolo -kraken -kwarta -labba -labber -laboress -lacunar -latch -lauric -lawter -lectotype -leeches -legible -lepidosteoid -leucobasalt -leverer -libellate -limnimeter -lithography -lithotypic -locomotor -logarithmetically -logistician -lyncine -lysogenesis -machan -macromyelon -maharana -mandibulate -manganapatite -marchpane -mas -masochistic -mastaba -matching -meditatively -megalopolitan -melaniline -mentum -mercaptides -mestome -metasomatism -meterless -micronuclear -micropetalous -microreaction -microsporophore -mileway -milliarium -millisecond -misbind -miscollocation -misreader -modernicide -modification -modulant -monkfish -monoamino -monocarbide -monographical -morphinomaniac -mullein -munge -mutilate -mycophagist -myelosarcoma -myospasm -myriadly -nagaika -naphthionate -natant -naviculaeform -nayward -neallotype -necrophilia -nectared -neigher -neogamous -neurodynia -neurorthopteran -nidation -nieceship -nitrobacteria -nitrosification -nogheaded -nonassertive -noneuphonious -nonextant -nonincrease -nonintermittent -nonmetallic -nonprehensile -nonremunerative -nonsocial -nonvesting -noontime -noreaster -nounal -nub -nucleoplasm -nullisome -numero -numerous -oblongatal -observe -obtusilingual -obvert -occipitoatlantal -oceanside -ochlophobist -odontiasis -opalescence -opticon -oraculousness -orarium -organically -orthopedically -ostosis -overadvance -overbuilt -overdiscouragement -overdoer -overhardy -overjocular -overmagnify -overofficered -overpotent -overprizer -overrunner -overshrink -oversimply -oversplash -ovology -oxskin -oxychloride -oxygenant -ozokerite -pactional -palaeoanthropography -palaeographical -palaeopsychology -palliasse -palpebral -pandaric -pantelegraph -papicolist -papulate -parakinetic -parasitism -parochialic -parochialize -passionlike -patch -paucidentate -pawnbrokeress -pecite -pecky -pedipulation -pellitory -perfilograph -periblast -perigemmal -periost -periplus -perishable -periwig -permansive -persistingly -persymmetrical -phantom -phasmatrope -philocaly -philogyny -philosophister -philotherianism -phorology -phototrophic -phrator -phratral -phthisipneumony -physogastry -phytologic -phytoptid -pianograph -picqueter -piculet -pigeoner -pimaric -pinesap -pist -planometer -platano -playful -plea -pleuropneumonic -plowwoman -plump -pluviographical -pneumocele -podophthalmate -polyad -polythalamian -poppyhead -portamento -portmanteau -portraitlike -possible -potassamide -powderer -praepubis -preanesthetic -prebarbaric -predealer -predomination -prefactory -preirrigational -prelector -presbytership -presecure -preservable -prespecialist -preventionism -prewound -princely -priorship -proannexationist -proanthropos -probeable -probouleutic -profitless -proplasma -prosectorial -protecting -protochemistry -protosulphate -pseudoataxia -psilology -psychoneurotic -pterygial -publicist -purgation -purplishness -putatively -pyracene -pyrenomycete -pyromancy -pyrophone -quadroon -quailhead -qualifier -quaternal -rabblelike -rambunctious -rapidness -ratably -rationalism -razor -reannoy -recultivation -regulable -reimplant -reimposition -reimprison -reinjure -reinspiration -reintroduce -remantle -reprehensibility -reptant -require -resteal -restful -returnability -revisableness -rewash -rewhirl -reyield -rhizotomy -rhodamine -rigwiddie -rimester -ripper -rippet -rockish -rockwards -rollicky -roosters -rooted -rosal -rozum -saccharated -sagamore -sagy -salesmanship -salivous -sallet -salta -saprostomous -satiation -sauropsid -sawarra -sawback -scabish -scabrate -scampavia -scientificophilosophical -scirrosity -scoliometer -scolopendrelloid -secantly -seignioral -semibull -semic -seminarianism -semiped -semiprivate -semispherical -semispontaneous -seneschal -septendecimal -serotherapist -servation -sesquisulphuret -severish -sextipartite -sextubercular -shipyard -shuckpen -siderosis -silex -sillyhow -silverbelly -silverbelly -simulacrum -sisham -sixte -skeiner -skiapod -slopped -slubby -smalts -sockmaker -solute -somethingness -somnify -southwester -spathilla -spectrochemical -sphagnology -spinales -spiriting -spirling -spirochetemia -spreadboard -spurflower -squawdom -squeezing -staircase -staker -stamphead -statolith -stekan -stellulate -stinker -stomodaea -streamingly -strikingness -strouthocamelian -stuprum -subacutely -subboreal -subcontractor -subendorsement -subprofitable -subserviate -subsneer -subungual -sucuruju -sugan -sulphocarbolate -summerwood -superficialist -superinference -superregenerative -supplicate -suspendible -synchronizer -syntectic -tachyglossate -tailless -taintment -takingly -taletelling -tarpon -tasteful -taxeater -taxy -teache -teachless -teg -tegmen -teletyper -temperable -ten -tenent -teskere -testes -thallogen -thapsia -thewness -thickety -thiobacteria -thorniness -throwing -thyroprivic -tinnitus -tocalote -tolerationist -tonalamatl -torvous -totality -tottering -toug -tracheopathia -tragedical -translucent -trifoveolate -trilaurin -trophoplasmatic -trunkless -turbanless -turnpiker -twangle -twitterboned -ultraornate -umbilication -unabatingly -unabjured -unadequateness -unaffectedness -unarriving -unassorted -unattacked -unbenumbed -unboasted -unburning -uncensorious -uncongested -uncontemnedly -uncontemporary -uncrook -uncrystallizability -uncurb -uncustomariness -underbillow -undercanopy -underestimation -underhanging -underpetticoated -underpropped -undersole -understocking -underworld -undevout -undisappointing -undistinctive -unfiscal -unfluted -unfreckled -ungentilize -unglobe -unhelped -unhomogeneously -unifoliate -uninflammable -uninterrogated -unisonal -unkindled -unlikeableness -unlisty -unlocked -unmoving -unmultipliable -unnestled -unnoticed -unobservable -unobviated -unoffensively -unofficerlike -unpoetic -unpractically -unquestionableness -unrehearsed -unrevised -unrhetorical -unsadden -unsaluting -unscriptural -unseeking -unshowed -unsolicitous -unsprouted -unsubjective -unsubsidized -unsymbolic -untenant -unterrified -untranquil -untraversed -untrusty -untying -unwillful -unwinding -upspring -uptwist -urachovesical -uropygial -vagabondism -varicoid -varletess -vasal -ventrocaudal -verisimilitude -vermigerous -vibrometer -viminal -virus -vocationalism -voguey -vulnerability -waggle -wamblingly -warmus -waxer -waying -wedgeable -wellmaker -whomever -wigged -witchlike -wokas -woodrowel -woodsman -woolding -xanthelasmic -xiphosternum -yachtman -yachtsmanlike -yelp -zoophytal \ No newline at end of file -- cgit v1.2.2