Localisation updates from https://translatewiki.net.
[mediawiki.git] / tests / phpunit / maintenance / FetchTextTest.php
blob3c7844f7891c9435c801baaf3aa3f9c9c8ae71c2
1 <?php
3 namespace MediaWiki\Tests\Maintenance;
5 use Exception;
6 use FetchText;
7 use MediaWiki\Content\ContentHandler;
8 use MediaWiki\Revision\SlotRecord;
9 use MediaWiki\Title\Title;
10 use MediaWikiIntegrationTestCase;
11 use PHPUnit\Framework\ExpectationFailedException;
12 use RuntimeException;
13 use WikiPage;
15 /**
16 * Mock for the input/output of FetchText
18 * FetchText internally tries to access stdin and stdout. We mock those aspects
19 * for testing.
21 class SemiMockedFetchText extends FetchText {
23 /**
24 * @var string|null Text to pass as stdin
26 private $mockStdinText = null;
28 /**
29 * @var bool Whether or not a text for stdin has been provided
31 private $mockSetUp = false;
33 /**
34 * @var array Invocation counters for the mocked aspects
36 private $mockInvocations = [ 'getStdin' => 0 ];
38 /**
39 * Data for the fake stdin
41 * @param string $stdin The string to be used instead of stdin
43 public function mockStdin( $stdin ) {
44 $this->mockStdinText = $stdin;
45 $this->mockSetUp = true;
48 /**
49 * Gets invocation counters for mocked methods.
51 * @return array An array, whose keys are function names. The corresponding values
52 * denote the number of times the function has been invoked.
54 public function mockGetInvocations() {
55 return $this->mockInvocations;
58 // -----------------------------------------------------------------
59 // Mocked functions from FetchText follow.
61 public function getStdin( $len = null ) {
62 $this->mockInvocations['getStdin']++;
63 if ( $len !== null ) {
64 throw new ExpectationFailedException(
65 "Tried to get stdin with non null parameter" );
68 if ( !$this->mockSetUp ) {
69 throw new ExpectationFailedException(
70 "Tried to get stdin before setting up rerouting" );
73 return fopen( 'data://text/plain,' . $this->mockStdinText, 'r' );
77 /**
78 * TestCase for FetchText
80 * @group Database
81 * @group Dump
82 * @covers \FetchText
84 class FetchTextTest extends MediaWikiIntegrationTestCase {
86 // We add 5 Revisions for this test. Their corresponding text id's
87 // are stored in the following 5 variables.
88 /** @var int */
89 protected static $textId1;
90 /** @var int */
91 protected static $textId2;
92 /** @var int */
93 protected static $textId3;
94 /** @var int */
95 protected static $textId4;
96 /** @var int */
97 protected static $textId5;
99 /**
100 * @var Exception|null As the current MediaWikiIntegrationTestCase::run is not
101 * robust enough to recover from thrown exceptions directly, we cannot
102 * throw frow within addDBData, although it would be appropriate. Hence,
103 * we catch the exception and store it until we are in setUp and may
104 * finally rethrow the exception without crashing the test suite.
106 protected static $exceptionFromAddDBDataOnce;
109 * @var FetchText The (mocked) FetchText that is to test
111 private $fetchText;
114 * Adds a revision to a page and returns the main slot's blob address
116 * @param WikiPage $page The page to add the revision to
117 * @param string $text The revisions text
118 * @param string $summary The revisions summare
119 * @return string
121 private function addRevision( $page, $text, $summary ) {
122 $status = $page->doUserEditContent(
123 ContentHandler::makeContent( $text, $page->getTitle() ),
124 $this->getTestSysop()->getUser(),
125 $summary
128 if ( $status->isGood() ) {
129 $revision = $status->getNewRevision();
130 $address = $revision->getSlot( SlotRecord::MAIN )->getAddress();
131 return $address;
134 throw new RuntimeException( "Could not create revision" );
137 public function addDBDataOnce() {
138 $wikitextNamespace = $this->getDefaultWikitextNS();
139 $wikiPageFactory = $this->getServiceContainer()->getWikiPageFactory();
141 try {
142 $title = Title::makeTitle( $wikitextNamespace, 'FetchTextTestPage1' );
143 $page = $wikiPageFactory->newFromTitle( $title );
144 self::$textId1 = $this->addRevision(
145 $page,
146 "FetchTextTestPage1Text1",
147 "FetchTextTestPage1Summary1"
150 $title = Title::makeTitle( $wikitextNamespace, 'FetchTextTestPage2' );
151 $page = $wikiPageFactory->newFromTitle( $title );
152 self::$textId2 = $this->addRevision(
153 $page,
154 "FetchTextTestPage2Text1",
155 "FetchTextTestPage2Summary1"
157 self::$textId3 = $this->addRevision(
158 $page,
159 "FetchTextTestPage2Text2",
160 "FetchTextTestPage2Summary2"
162 self::$textId4 = $this->addRevision(
163 $page,
164 "FetchTextTestPage2Text3",
165 "FetchTextTestPage2Summary3"
167 self::$textId5 = $this->addRevision(
168 $page,
169 "FetchTextTestPage2Text4 some additional Text ",
170 "FetchTextTestPage2Summary4 extra "
172 } catch ( Exception $e ) {
173 // We'd love to pass $e directly. However, ... see
174 // documentation of exceptionFromAddDBDataOnce
175 self::$exceptionFromAddDBDataOnce = $e;
179 protected function setUp(): void {
180 parent::setUp();
182 // Check if any Exception is stored for rethrowing from addDBData
183 if ( self::$exceptionFromAddDBDataOnce !== null ) {
184 throw self::$exceptionFromAddDBDataOnce;
187 $this->fetchText = new SemiMockedFetchText();
191 * Helper to relate FetchText's input and output
192 * @param string $input
193 * @param string $expectedOutput
195 private function assertFilter( $input, $expectedOutput ) {
196 $this->fetchText->mockStdin( $input );
197 $this->fetchText->execute();
198 $invocations = $this->fetchText->mockGetInvocations();
199 $this->assertSame( 1, $invocations['getStdin'],
200 "getStdin invocation counter" );
201 $this->expectOutputString( $expectedOutput );
204 // Instead of the following functions, a data provider would be great.
205 // However, as data providers are evaluated /before/ addDBData, a data
206 // provider would not know the required ids.
208 public function testExistingSimple() {
209 $this->assertFilter( self::$textId2,
210 self::$textId2 . "\n23\nFetchTextTestPage2Text1" );
213 public function testExistingSimpleWithNewline() {
214 $this->assertFilter( self::$textId2 . "\n",
215 self::$textId2 . "\n23\nFetchTextTestPage2Text1" );
218 public function testExistingInteger() {
219 $this->assertFilter( (int)preg_replace( '/^tt:/', '', self::$textId2 ),
220 self::$textId2 . "\n23\nFetchTextTestPage2Text1" );
223 public function testExistingSeveral() {
224 $this->assertFilter(
225 implode( "\n", [
226 self::$textId1,
227 self::$textId5,
228 self::$textId3,
229 self::$textId3,
230 ] ),
231 implode( '', [
232 self::$textId1 . "\n23\nFetchTextTestPage1Text1",
233 self::$textId5 . "\n44\nFetchTextTestPage2Text4 "
234 . "some additional Text",
235 self::$textId3 . "\n23\nFetchTextTestPage2Text2",
236 self::$textId3 . "\n23\nFetchTextTestPage2Text2"
237 ] ) );
240 public function testEmpty() {
241 $this->assertFilter( "", "" );
244 public function testNonExisting() {
245 @$this->assertFilter( 'tt:77889911', 'tt:77889911' . "\n-1\n" );
248 public function testNonExistingInteger() {
249 @$this->assertFilter( '77889911', 'tt:77889911' . "\n-1\n" );
252 public function testBadBlobAddressWithColon() {
253 $this->assertFilter( 'foo:bar', 'foo:bar' . "\n-1\n" );
256 public function testNegativeInteger() {
257 $this->assertFilter( "-42", "tt:-42\n-1\n" );
260 public function testFloatingPointNumberExisting() {
261 // float -> int -> address -> revision
262 $id = intval( preg_replace( '/^tt:/', '', self::$textId3 ) ) + 0.14159;
263 $this->assertFilter( 'tt:' . intval( $id ),
264 self::$textId3 . "\n23\nFetchTextTestPage2Text2" );
267 public function testFloatingPointNumberNonExisting() {
268 $id = intval( preg_replace( '/^tt:/', '', self::$textId5 ) ) + 3.14159;
269 @$this->assertFilter( $id, 'tt:' . intval( $id ) . "\n-1\n" );
272 public function testCharacters() {
273 $this->assertFilter( "abc", "abc\n-1\n" );
276 public function testMix() {
277 $this->assertFilter( "ab\n" . self::$textId4 . ".5cd\n\nefg\nfoo:bar\n" . self::$textId2
278 . "\n" . self::$textId3,
279 implode( "", [
280 "ab\n-1\n",
281 self::$textId4 . ".5cd\n-1\n",
282 "\n-1\n",
283 "efg\n-1\n",
284 "foo:bar\n-1\n",
285 self::$textId2 . "\n23\nFetchTextTestPage2Text1",
286 self::$textId3 . "\n23\nFetchTextTestPage2Text2"
287 ] ) );