Merge "Revision: test and fix __construct exceptions"
[lhc/web/wiklou.git] / tests / phpunit / includes / RevisionTest.php
1 <?php
2
3 /**
4 * @group ContentHandler
5 */
6 class RevisionTest extends MediaWikiTestCase {
7
8 protected function setUp() {
9 global $wgContLang;
10
11 parent::setUp();
12
13 $this->setMwGlobals( [
14 'wgContLang' => Language::factory( 'en' ),
15 'wgLanguageCode' => 'en',
16 'wgLegacyEncoding' => false,
17 'wgCompressRevisions' => false,
18
19 'wgContentHandlerTextFallback' => 'ignore',
20 ] );
21
22 $this->mergeMwGlobalArrayValue(
23 'wgExtraNamespaces',
24 [
25 12312 => 'Dummy',
26 12313 => 'Dummy_talk',
27 ]
28 );
29
30 $this->mergeMwGlobalArrayValue(
31 'wgNamespaceContentModels',
32 [
33 12312 => 'testing',
34 ]
35 );
36
37 $this->mergeMwGlobalArrayValue(
38 'wgContentHandlers',
39 [
40 'testing' => 'DummyContentHandlerForTesting',
41 'RevisionTestModifyableContent' => 'RevisionTestModifyableContentHandler',
42 ]
43 );
44
45 MWNamespace::clearCaches();
46 // Reset namespace cache
47 $wgContLang->resetNamespaces();
48 }
49
50 protected function tearDown() {
51 global $wgContLang;
52
53 MWNamespace::clearCaches();
54 // Reset namespace cache
55 $wgContLang->resetNamespaces();
56
57 parent::tearDown();
58 }
59
60 public function provideConstruct() {
61 yield 'with text' => [
62 [
63 'text' => 'hello world.',
64 'content_model' => CONTENT_MODEL_JAVASCRIPT
65 ],
66 ];
67 yield 'with content' => [
68 [
69 'content' => new JavaScriptContent( 'hellow world.' )
70 ],
71 ];
72 }
73
74 /**
75 * @dataProvider provideConstruct
76 */
77 public function testConstruct( $rowArray ) {
78 $rev = new Revision( $rowArray );
79 $this->assertNotNull( $rev->getContent(), 'no content object available' );
80 $this->assertEquals( CONTENT_MODEL_JAVASCRIPT, $rev->getContent()->getModel() );
81 $this->assertEquals( CONTENT_MODEL_JAVASCRIPT, $rev->getContentModel() );
82 }
83
84 public function provideConstructThrowsExceptions() {
85 yield 'content and text_id both not empty' => [
86 [
87 'content' => new WikitextContent( 'GOAT' ),
88 'text_id' => 'someid',
89 ],
90 new MWException( "Text already stored in external store (id someid), " .
91 "can't serialize content object" )
92 ];
93 yield 'with bad content object (class)' => [
94 [ 'content' => new stdClass() ],
95 new MWException( '`content` field must contain a Content object.' )
96 ];
97 yield 'with bad content object (string)' => [
98 [ 'content' => 'ImAGoat' ],
99 new MWException( '`content` field must contain a Content object.' )
100 ];
101 yield 'bad row format' => [
102 'imastring, not a row',
103 new MWException( 'Revision constructor passed invalid row format.' )
104 ];
105 }
106
107 /**
108 * @dataProvider provideConstructThrowsExceptions
109 */
110 public function testConstructThrowsExceptions( $rowArray, Exception $expectedException ) {
111 $this->setExpectedException(
112 get_class( $expectedException ),
113 $expectedException->getMessage(),
114 $expectedException->getCode()
115 );
116 new Revision( $rowArray );
117 }
118
119 public function provideGetRevisionText() {
120 yield 'Generic test' => [
121 'This is a goat of revision text.',
122 [
123 'old_flags' => '',
124 'old_text' => 'This is a goat of revision text.',
125 ],
126 ];
127 }
128
129 /**
130 * @covers Revision::getRevisionText
131 * @dataProvider provideGetRevisionText
132 */
133 public function testGetRevisionText( $expected, $rowData, $prefix = 'old_', $wiki = false ) {
134 $this->assertEquals(
135 $expected,
136 Revision::getRevisionText( (object)$rowData, $prefix, $wiki ) );
137 }
138
139 public function provideGetRevisionTextWithZlibExtension() {
140 yield 'Generic gzip test' => [
141 'This is a small goat of revision text.',
142 [
143 'old_flags' => 'gzip',
144 'old_text' => gzdeflate( 'This is a small goat of revision text.' ),
145 ],
146 ];
147 }
148
149 /**
150 * @covers Revision::getRevisionText
151 * @dataProvider provideGetRevisionTextWithZlibExtension
152 */
153 public function testGetRevisionWithZlibExtension( $expected, $rowData ) {
154 $this->checkPHPExtension( 'zlib' );
155 $this->testGetRevisionText( $expected, $rowData );
156 }
157
158 public function provideGetRevisionTextWithLegacyEncoding() {
159 yield 'Utf8Native' => [
160 "Wiki est l'\xc3\xa9cole superieur !",
161 'iso-8859-1',
162 [
163 'old_flags' => 'utf-8',
164 'old_text' => "Wiki est l'\xc3\xa9cole superieur !",
165 ]
166 ];
167 yield 'Utf8Legacy' => [
168 "Wiki est l'\xc3\xa9cole superieur !",
169 'iso-8859-1',
170 [
171 'old_flags' => '',
172 'old_text' => "Wiki est l'\xe9cole superieur !",
173 ]
174 ];
175 }
176
177 /**
178 * @covers Revision::getRevisionText
179 * @dataProvider provideGetRevisionTextWithLegacyEncoding
180 */
181 public function testGetRevisionWithLegacyEncoding( $expected, $encoding, $rowData ) {
182 $GLOBALS['wgLegacyEncoding'] = $encoding;
183 $this->testGetRevisionText( $expected, $rowData );
184 }
185
186 public function provideGetRevisionTextWithGzipAndLegacyEncoding() {
187 yield 'Utf8NativeGzip' => [
188 "Wiki est l'\xc3\xa9cole superieur !",
189 'iso-8859-1',
190 [
191 'old_flags' => 'gzip,utf-8',
192 'old_text' => gzdeflate( "Wiki est l'\xc3\xa9cole superieur !" ),
193 ]
194 ];
195 yield 'Utf8LegacyGzip' => [
196 "Wiki est l'\xc3\xa9cole superieur !",
197 'iso-8859-1',
198 [
199 'old_flags' => 'gzip',
200 'old_text' => gzdeflate( "Wiki est l'\xe9cole superieur !" ),
201 ]
202 ];
203 }
204
205 /**
206 * @covers Revision::getRevisionText
207 * @dataProvider provideGetRevisionTextWithGzipAndLegacyEncoding
208 */
209 public function testGetRevisionWithGzipAndLegacyEncoding( $expected, $encoding, $rowData ) {
210 $this->checkPHPExtension( 'zlib' );
211 $GLOBALS['wgLegacyEncoding'] = $encoding;
212 $this->testGetRevisionText( $expected, $rowData );
213 }
214
215 /**
216 * @covers Revision::compressRevisionText
217 */
218 public function testCompressRevisionTextUtf8() {
219 $row = new stdClass;
220 $row->old_text = "Wiki est l'\xc3\xa9cole superieur !";
221 $row->old_flags = Revision::compressRevisionText( $row->old_text );
222 $this->assertTrue( false !== strpos( $row->old_flags, 'utf-8' ),
223 "Flags should contain 'utf-8'" );
224 $this->assertFalse( false !== strpos( $row->old_flags, 'gzip' ),
225 "Flags should not contain 'gzip'" );
226 $this->assertEquals( "Wiki est l'\xc3\xa9cole superieur !",
227 $row->old_text, "Direct check" );
228 $this->assertEquals( "Wiki est l'\xc3\xa9cole superieur !",
229 Revision::getRevisionText( $row ), "getRevisionText" );
230 }
231
232 /**
233 * @covers Revision::compressRevisionText
234 */
235 public function testCompressRevisionTextUtf8Gzip() {
236 $this->checkPHPExtension( 'zlib' );
237 $this->setMwGlobals( 'wgCompressRevisions', true );
238
239 $row = new stdClass;
240 $row->old_text = "Wiki est l'\xc3\xa9cole superieur !";
241 $row->old_flags = Revision::compressRevisionText( $row->old_text );
242 $this->assertTrue( false !== strpos( $row->old_flags, 'utf-8' ),
243 "Flags should contain 'utf-8'" );
244 $this->assertTrue( false !== strpos( $row->old_flags, 'gzip' ),
245 "Flags should contain 'gzip'" );
246 $this->assertEquals( "Wiki est l'\xc3\xa9cole superieur !",
247 gzinflate( $row->old_text ), "Direct check" );
248 $this->assertEquals( "Wiki est l'\xc3\xa9cole superieur !",
249 Revision::getRevisionText( $row ), "getRevisionText" );
250 }
251
252 /**
253 * @param string $text
254 * @param string $title
255 * @param string $model
256 * @param string $format
257 *
258 * @return Revision
259 */
260 private function newTestRevision( $text, $title = "Test",
261 $model = CONTENT_MODEL_WIKITEXT, $format = null
262 ) {
263 if ( is_string( $title ) ) {
264 $title = Title::newFromText( $title );
265 }
266
267 $content = ContentHandler::makeContent( $text, $title, $model, $format );
268
269 $rev = new Revision(
270 [
271 'id' => 42,
272 'page' => 23,
273 'title' => $title,
274
275 'content' => $content,
276 'length' => $content->getSize(),
277 'comment' => "testing",
278 'minor_edit' => false,
279
280 'content_format' => $format,
281 ]
282 );
283
284 return $rev;
285 }
286
287 public function provideGetContentModel() {
288 // NOTE: we expect the help namespace to always contain wikitext
289 return [
290 [ 'hello world', 'Help:Hello', null, null, CONTENT_MODEL_WIKITEXT ],
291 [ 'hello world', 'User:hello/there.css', null, null, CONTENT_MODEL_CSS ],
292 [ serialize( 'hello world' ), 'Dummy:Hello', null, null, "testing" ],
293 ];
294 }
295
296 /**
297 * @group Database
298 * @dataProvider provideGetContentModel
299 * @covers Revision::getContentModel
300 */
301 public function testGetContentModel( $text, $title, $model, $format, $expectedModel ) {
302 $rev = $this->newTestRevision( $text, $title, $model, $format );
303
304 $this->assertEquals( $expectedModel, $rev->getContentModel() );
305 }
306
307 public function provideGetContentFormat() {
308 // NOTE: we expect the help namespace to always contain wikitext
309 return [
310 [ 'hello world', 'Help:Hello', null, null, CONTENT_FORMAT_WIKITEXT ],
311 [ 'hello world', 'Help:Hello', CONTENT_MODEL_CSS, null, CONTENT_FORMAT_CSS ],
312 [ 'hello world', 'User:hello/there.css', null, null, CONTENT_FORMAT_CSS ],
313 [ serialize( 'hello world' ), 'Dummy:Hello', null, null, "testing" ],
314 ];
315 }
316
317 /**
318 * @group Database
319 * @dataProvider provideGetContentFormat
320 * @covers Revision::getContentFormat
321 */
322 public function testGetContentFormat( $text, $title, $model, $format, $expectedFormat ) {
323 $rev = $this->newTestRevision( $text, $title, $model, $format );
324
325 $this->assertEquals( $expectedFormat, $rev->getContentFormat() );
326 }
327
328 public function provideGetContentHandler() {
329 // NOTE: we expect the help namespace to always contain wikitext
330 return [
331 [ 'hello world', 'Help:Hello', null, null, 'WikitextContentHandler' ],
332 [ 'hello world', 'User:hello/there.css', null, null, 'CssContentHandler' ],
333 [ serialize( 'hello world' ), 'Dummy:Hello', null, null, 'DummyContentHandlerForTesting' ],
334 ];
335 }
336
337 /**
338 * @group Database
339 * @dataProvider provideGetContentHandler
340 * @covers Revision::getContentHandler
341 */
342 public function testGetContentHandler( $text, $title, $model, $format, $expectedClass ) {
343 $rev = $this->newTestRevision( $text, $title, $model, $format );
344
345 $this->assertEquals( $expectedClass, get_class( $rev->getContentHandler() ) );
346 }
347
348 public function provideGetContent() {
349 // NOTE: we expect the help namespace to always contain wikitext
350 return [
351 [ 'hello world', 'Help:Hello', null, null, Revision::FOR_PUBLIC, 'hello world' ],
352 [
353 serialize( 'hello world' ),
354 'Hello',
355 "testing",
356 null,
357 Revision::FOR_PUBLIC,
358 serialize( 'hello world' )
359 ],
360 [
361 serialize( 'hello world' ),
362 'Dummy:Hello',
363 null,
364 null,
365 Revision::FOR_PUBLIC,
366 serialize( 'hello world' )
367 ],
368 ];
369 }
370
371 /**
372 * @group Database
373 * @dataProvider provideGetContent
374 * @covers Revision::getContent
375 */
376 public function testGetContent( $text, $title, $model, $format,
377 $audience, $expectedSerialization
378 ) {
379 $rev = $this->newTestRevision( $text, $title, $model, $format );
380 $content = $rev->getContent( $audience );
381
382 $this->assertEquals(
383 $expectedSerialization,
384 is_null( $content ) ? null : $content->serialize( $format )
385 );
386 }
387
388 public function provideGetSize() {
389 return [
390 [ "hello world.", CONTENT_MODEL_WIKITEXT, 12 ],
391 [ serialize( "hello world." ), "testing", 12 ],
392 ];
393 }
394
395 /**
396 * @covers Revision::getSize
397 * @group Database
398 * @dataProvider provideGetSize
399 */
400 public function testGetSize( $text, $model, $expected_size ) {
401 $rev = $this->newTestRevision( $text, 'RevisionTest_testGetSize', $model );
402 $this->assertEquals( $expected_size, $rev->getSize() );
403 }
404
405 public function provideGetSha1() {
406 return [
407 [ "hello world.", CONTENT_MODEL_WIKITEXT, Revision::base36Sha1( "hello world." ) ],
408 [
409 serialize( "hello world." ),
410 "testing",
411 Revision::base36Sha1( serialize( "hello world." ) )
412 ],
413 ];
414 }
415
416 /**
417 * @covers Revision::getSha1
418 * @group Database
419 * @dataProvider provideGetSha1
420 */
421 public function testGetSha1( $text, $model, $expected_hash ) {
422 $rev = $this->newTestRevision( $text, 'RevisionTest_testGetSha1', $model );
423 $this->assertEquals( $expected_hash, $rev->getSha1() );
424 }
425
426 /**
427 * Tests whether $rev->getContent() returns a clone when needed.
428 *
429 * @group Database
430 * @covers Revision::getContent
431 */
432 public function testGetContentClone() {
433 $content = new RevisionTestModifyableContent( "foo" );
434
435 $rev = new Revision(
436 [
437 'id' => 42,
438 'page' => 23,
439 'title' => Title::newFromText( "testGetContentClone_dummy" ),
440
441 'content' => $content,
442 'length' => $content->getSize(),
443 'comment' => "testing",
444 'minor_edit' => false,
445 ]
446 );
447
448 /** @var RevisionTestModifyableContent $content */
449 $content = $rev->getContent( Revision::RAW );
450 $content->setText( "bar" );
451
452 /** @var RevisionTestModifyableContent $content2 */
453 $content2 = $rev->getContent( Revision::RAW );
454 // content is mutable, expect clone
455 $this->assertNotSame( $content, $content2, "expected a clone" );
456 // clone should contain the original text
457 $this->assertEquals( "foo", $content2->getText() );
458
459 $content2->setText( "bla bla" );
460 // clones should be independent
461 $this->assertEquals( "bar", $content->getText() );
462 }
463
464 /**
465 * Tests whether $rev->getContent() returns the same object repeatedly if appropriate.
466 *
467 * @group Database
468 * @covers Revision::getContent
469 */
470 public function testGetContentUncloned() {
471 $rev = $this->newTestRevision( "hello", "testGetContentUncloned_dummy", CONTENT_MODEL_WIKITEXT );
472 $content = $rev->getContent( Revision::RAW );
473 $content2 = $rev->getContent( Revision::RAW );
474
475 // for immutable content like wikitext, this should be the same object
476 $this->assertSame( $content, $content2 );
477 }
478 }