Add parameter to API modules to apply change tags to log entries
[lhc/web/wiklou.git] / tests / phpunit / maintenance / fetchTextTest.php
1 <?php
2
3 require_once __DIR__ . "/../../../maintenance/fetchText.php";
4
5 /**
6 * Mock for the input/output of FetchText
7 *
8 * FetchText internally tries to access stdin and stdout. We mock those aspects
9 * for testing.
10 */
11 class SemiMockedFetchText extends FetchText {
12
13 /**
14 * @var string|null Text to pass as stdin
15 */
16 private $mockStdinText = null;
17
18 /**
19 * @var bool Whether or not a text for stdin has been provided
20 */
21 private $mockSetUp = false;
22
23 /**
24 * @var array Invocation counters for the mocked aspects
25 */
26 private $mockInvocations = [ 'getStdin' => 0 ];
27
28 /**
29 * Data for the fake stdin
30 *
31 * @param string $stdin The string to be used instead of stdin
32 */
33 function mockStdin( $stdin ) {
34 $this->mockStdinText = $stdin;
35 $this->mockSetUp = true;
36 }
37
38 /**
39 * Gets invocation counters for mocked methods.
40 *
41 * @return array An array, whose keys are function names. The corresponding values
42 * denote the number of times the function has been invoked.
43 */
44 function mockGetInvocations() {
45 return $this->mockInvocations;
46 }
47
48 // -----------------------------------------------------------------
49 // Mocked functions from FetchText follow.
50
51 function getStdin( $len = null ) {
52 $this->mockInvocations['getStdin']++;
53 if ( $len !== null ) {
54 throw new PHPUnit_Framework_ExpectationFailedException(
55 "Tried to get stdin with non null parameter" );
56 }
57
58 if ( !$this->mockSetUp ) {
59 throw new PHPUnit_Framework_ExpectationFailedException(
60 "Tried to get stdin before setting up rerouting" );
61 }
62
63 return fopen( 'data://text/plain,' . $this->mockStdinText, 'r' );
64 }
65 }
66
67 /**
68 * TestCase for FetchText
69 *
70 * @group Database
71 * @group Dump
72 * @covers FetchText
73 */
74 class FetchTextTest extends MediaWikiTestCase {
75
76 // We add 5 Revisions for this test. Their corresponding text id's
77 // are stored in the following 5 variables.
78 private $textId1;
79 private $textId2;
80 private $textId3;
81 private $textId4;
82 private $textId5;
83
84 /**
85 * @var Exception|null As the current MediaWikiTestCase::run is not
86 * robust enough to recover from thrown exceptions directly, we cannot
87 * throw frow within addDBData, although it would be appropriate. Hence,
88 * we catch the exception and store it until we are in setUp and may
89 * finally rethrow the exception without crashing the test suite.
90 */
91 private $exceptionFromAddDBData;
92
93 /**
94 * @var FetchText The (mocked) FetchText that is to test
95 */
96 private $fetchText;
97
98 /**
99 * Adds a revision to a page, while returning the resuting text's id
100 *
101 * @param WikiPage $page The page to add the revision to
102 * @param string $text The revisions text
103 * @param string $summary The revisions summare
104 * @return int
105 * @throws MWException
106 */
107 private function addRevision( $page, $text, $summary ) {
108 $status = $page->doEditContent(
109 ContentHandler::makeContent( $text, $page->getTitle() ),
110 $summary
111 );
112
113 if ( $status->isGood() ) {
114 $value = $status->getValue();
115 $revision = $value['revision'];
116 $id = $revision->getTextId();
117
118 if ( $id > 0 ) {
119 return $id;
120 }
121 }
122
123 throw new MWException( "Could not determine text id" );
124 }
125
126 function addDBData() {
127 $this->tablesUsed[] = 'page';
128 $this->tablesUsed[] = 'revision';
129 $this->tablesUsed[] = 'text';
130
131 $wikitextNamespace = $this->getDefaultWikitextNS();
132
133 try {
134 $title = Title::newFromText( 'FetchTextTestPage1', $wikitextNamespace );
135 $page = WikiPage::factory( $title );
136 $this->textId1 = $this->addRevision(
137 $page,
138 "FetchTextTestPage1Text1",
139 "FetchTextTestPage1Summary1"
140 );
141
142 $title = Title::newFromText( 'FetchTextTestPage2', $wikitextNamespace );
143 $page = WikiPage::factory( $title );
144 $this->textId2 = $this->addRevision(
145 $page,
146 "FetchTextTestPage2Text1",
147 "FetchTextTestPage2Summary1"
148 );
149 $this->textId3 = $this->addRevision(
150 $page,
151 "FetchTextTestPage2Text2",
152 "FetchTextTestPage2Summary2"
153 );
154 $this->textId4 = $this->addRevision(
155 $page,
156 "FetchTextTestPage2Text3",
157 "FetchTextTestPage2Summary3"
158 );
159 $this->textId5 = $this->addRevision(
160 $page,
161 "FetchTextTestPage2Text4 some additional Text ",
162 "FetchTextTestPage2Summary4 extra "
163 );
164 } catch ( Exception $e ) {
165 // We'd love to pass $e directly. However, ... see
166 // documentation of exceptionFromAddDBData
167 $this->exceptionFromAddDBData = $e;
168 }
169 }
170
171 protected function setUp() {
172 parent::setUp();
173
174 // Check if any Exception is stored for rethrowing from addDBData
175 if ( $this->exceptionFromAddDBData !== null ) {
176 throw $this->exceptionFromAddDBData;
177 }
178
179 $this->fetchText = new SemiMockedFetchText();
180 }
181
182 /**
183 * Helper to relate FetchText's input and output
184 * @param string $input
185 * @param string $expectedOutput
186 */
187 private function assertFilter( $input, $expectedOutput ) {
188 $this->fetchText->mockStdin( $input );
189 $this->fetchText->execute();
190 $invocations = $this->fetchText->mockGetInvocations();
191 $this->assertEquals( 1, $invocations['getStdin'],
192 "getStdin invocation counter" );
193 $this->expectOutputString( $expectedOutput );
194 }
195
196 // Instead of the following functions, a data provider would be great.
197 // However, as data providers are evaluated /before/ addDBData, a data
198 // provider would not know the required ids.
199
200 function testExistingSimple() {
201 $this->assertFilter( $this->textId2,
202 $this->textId2 . "\n23\nFetchTextTestPage2Text1" );
203 }
204
205 function testExistingSimpleWithNewline() {
206 $this->assertFilter( $this->textId2 . "\n",
207 $this->textId2 . "\n23\nFetchTextTestPage2Text1" );
208 }
209
210 function testExistingSeveral() {
211 $this->assertFilter( "$this->textId1\n$this->textId5\n"
212 . "$this->textId3\n$this->textId3",
213 implode( "", [
214 $this->textId1 . "\n23\nFetchTextTestPage1Text1",
215 $this->textId5 . "\n44\nFetchTextTestPage2Text4 "
216 . "some additional Text",
217 $this->textId3 . "\n23\nFetchTextTestPage2Text2",
218 $this->textId3 . "\n23\nFetchTextTestPage2Text2"
219 ] ) );
220 }
221
222 function testEmpty() {
223 $this->assertFilter( "", null );
224 }
225
226 function testNonExisting() {
227 $this->assertFilter( $this->textId5 + 10, ( $this->textId5 + 10 ) . "\n-1\n" );
228 }
229
230 function testNegativeInteger() {
231 $this->assertFilter( "-42", "-42\n-1\n" );
232 }
233
234 function testFloatingPointNumberExisting() {
235 // float -> int -> revision
236 $this->assertFilter( $this->textId3 + 0.14159,
237 $this->textId3 . "\n23\nFetchTextTestPage2Text2" );
238 }
239
240 function testFloatingPointNumberNonExisting() {
241 $this->assertFilter( $this->textId5 + 3.14159,
242 ( $this->textId5 + 3 ) . "\n-1\n" );
243 }
244
245 function testCharacters() {
246 $this->assertFilter( "abc", "0\n-1\n" );
247 }
248
249 function testMix() {
250 $this->assertFilter( "ab\n" . $this->textId4 . ".5cd\n\nefg\n" . $this->textId2
251 . "\n" . $this->textId3,
252 implode( "", [
253 "0\n-1\n",
254 $this->textId4 . "\n23\nFetchTextTestPage2Text3",
255 "0\n-1\n",
256 "0\n-1\n",
257 $this->textId2 . "\n23\nFetchTextTestPage2Text1",
258 $this->textId3 . "\n23\nFetchTextTestPage2Text2"
259 ] ) );
260 }
261 }