3 namespace MediaWiki\Tests\Revision
;
5 use InvalidArgumentException
;
7 use MediaWiki\Revision\IncompleteRevisionException
;
8 use MediaWiki\Revision\SlotRecord
;
9 use MediaWiki\Revision\SuppressedDataException
;
10 use MediaWikiTestCase
;
14 * @covers \MediaWiki\Revision\SlotRecord
16 class SlotRecordTest
extends MediaWikiTestCase
{
18 private function makeRow( $data = [] ) {
21 'slot_content_id' => 33,
22 'content_size' => '5',
23 'content_sha1' => 'someHash',
24 'content_address' => 'tt:456',
25 'model_name' => CONTENT_MODEL_WIKITEXT
,
26 'format_name' => CONTENT_FORMAT_WIKITEXT
,
27 'slot_revision_id' => '2',
29 'role_name' => 'myRole',
34 public function testCompleteConstruction() {
35 $row = $this->makeRow();
36 $record = new SlotRecord( $row, new WikitextContent( 'A' ) );
38 $this->assertTrue( $record->hasAddress() );
39 $this->assertTrue( $record->hasContentId() );
40 $this->assertTrue( $record->hasRevision() );
41 $this->assertTrue( $record->isInherited() );
42 $this->assertSame( 'A', $record->getContent()->getText() );
43 $this->assertSame( 5, $record->getSize() );
44 $this->assertSame( 'someHash', $record->getSha1() );
45 $this->assertSame( CONTENT_MODEL_WIKITEXT
, $record->getModel() );
46 $this->assertSame( 2, $record->getRevision() );
47 $this->assertSame( 1, $record->getOrigin() );
48 $this->assertSame( 'tt:456', $record->getAddress() );
49 $this->assertSame( 33, $record->getContentId() );
50 $this->assertSame( CONTENT_FORMAT_WIKITEXT
, $record->getFormat() );
51 $this->assertSame( 'myRole', $record->getRole() );
54 public function testConstructionDeferred() {
55 $row = $this->makeRow( [
56 'content_size' => null, // to be computed
57 'content_sha1' => null, // to be computed
58 'format_name' => function () {
59 return CONTENT_FORMAT_WIKITEXT
;
61 'slot_revision_id' => '2',
63 'slot_content_id' => function () {
68 $content = function () {
69 return new WikitextContent( 'A' );
72 $record = new SlotRecord( $row, $content );
74 $this->assertTrue( $record->hasAddress() );
75 $this->assertTrue( $record->hasRevision() );
76 $this->assertFalse( $record->hasContentId() );
77 $this->assertFalse( $record->isInherited() );
78 $this->assertSame( 'A', $record->getContent()->getText() );
79 $this->assertSame( 1, $record->getSize() );
80 $this->assertNotNull( $record->getSha1() );
81 $this->assertSame( CONTENT_MODEL_WIKITEXT
, $record->getModel() );
82 $this->assertSame( 2, $record->getRevision() );
83 $this->assertSame( 2, $record->getRevision() );
84 $this->assertSame( 'tt:456', $record->getAddress() );
85 $this->assertSame( CONTENT_FORMAT_WIKITEXT
, $record->getFormat() );
86 $this->assertSame( 'myRole', $record->getRole() );
89 public function testNewUnsaved() {
90 $record = SlotRecord
::newUnsaved( 'myRole', new WikitextContent( 'A' ) );
92 $this->assertFalse( $record->hasAddress() );
93 $this->assertFalse( $record->hasContentId() );
94 $this->assertFalse( $record->hasRevision() );
95 $this->assertFalse( $record->isInherited() );
96 $this->assertFalse( $record->hasOrigin() );
97 $this->assertSame( 'A', $record->getContent()->getText() );
98 $this->assertSame( 1, $record->getSize() );
99 $this->assertNotNull( $record->getSha1() );
100 $this->assertSame( CONTENT_MODEL_WIKITEXT
, $record->getModel() );
101 $this->assertSame( 'myRole', $record->getRole() );
104 public function provideInvalidConstruction() {
105 yield
'both null' => [ null, null ];
106 yield
'null row' => [ null, new WikitextContent( 'A' ) ];
107 yield
'array row' => [ [], new WikitextContent( 'A' ) ];
108 yield
'empty row' => [ (object)[], new WikitextContent( 'A' ) ];
109 yield
'null content' => [ (object)[], null ];
113 * @dataProvider provideInvalidConstruction
115 public function testInvalidConstruction( $row, $content ) {
116 $this->setExpectedException( InvalidArgumentException
::class );
117 new SlotRecord( $row, $content );
120 public function testGetContentId_fails() {
121 $record = SlotRecord
::newUnsaved( SlotRecord
::MAIN
, new WikitextContent( 'A' ) );
122 $this->setExpectedException( IncompleteRevisionException
::class );
124 $record->getContentId();
127 public function testGetAddress_fails() {
128 $record = SlotRecord
::newUnsaved( SlotRecord
::MAIN
, new WikitextContent( 'A' ) );
129 $this->setExpectedException( IncompleteRevisionException
::class );
131 $record->getAddress();
134 public function provideIncomplete() {
135 $unsaved = SlotRecord
::newUnsaved( SlotRecord
::MAIN
, new WikitextContent( 'A' ) );
136 yield
'unsaved' => [ $unsaved ];
138 $parent = new SlotRecord( $this->makeRow(), new WikitextContent( 'A' ) );
139 $inherited = SlotRecord
::newInherited( $parent );
140 yield
'inherited' => [ $inherited ];
144 * @dataProvider provideIncomplete
146 public function testGetRevision_fails( SlotRecord
$record ) {
147 $record = SlotRecord
::newUnsaved( SlotRecord
::MAIN
, new WikitextContent( 'A' ) );
148 $this->setExpectedException( IncompleteRevisionException
::class );
150 $record->getRevision();
154 * @dataProvider provideIncomplete
156 public function testGetOrigin_fails( SlotRecord
$record ) {
157 $record = SlotRecord
::newUnsaved( SlotRecord
::MAIN
, new WikitextContent( 'A' ) );
158 $this->setExpectedException( IncompleteRevisionException
::class );
160 $record->getOrigin();
163 public function provideHashStability() {
164 yield
[ '', 'phoiac9h4m842xq45sp7s6u21eteeq1' ];
165 yield
[ 'Lorem ipsum', 'hcr5u40uxr81d3nx89nvwzclfz6r9c5' ];
169 * @dataProvider provideHashStability
171 public function testHashStability( $text, $hash ) {
172 // Changing the output of the hash function will break things horribly!
174 $this->assertSame( $hash, SlotRecord
::base36Sha1( $text ) );
176 $record = SlotRecord
::newUnsaved( SlotRecord
::MAIN
, new WikitextContent( $text ) );
177 $this->assertSame( $hash, $record->getSha1() );
180 public function testNewWithSuppressedContent() {
181 $input = new SlotRecord( $this->makeRow(), new WikitextContent( 'A' ) );
182 $output = SlotRecord
::newWithSuppressedContent( $input );
184 $this->setExpectedException( SuppressedDataException
::class );
185 $output->getContent();
188 public function testNewInherited() {
189 $row = $this->makeRow( [ 'slot_revision_id' => 7, 'slot_origin' => 7 ] );
190 $parent = new SlotRecord( $row, new WikitextContent( 'A' ) );
192 // This would happen while doing an edit, before saving revision meta-data.
193 $inherited = SlotRecord
::newInherited( $parent );
195 $this->assertSame( $parent->getContentId(), $inherited->getContentId() );
196 $this->assertSame( $parent->getAddress(), $inherited->getAddress() );
197 $this->assertSame( $parent->getContent(), $inherited->getContent() );
198 $this->assertTrue( $inherited->isInherited() );
199 $this->assertTrue( $inherited->hasOrigin() );
200 $this->assertFalse( $inherited->hasRevision() );
202 // make sure we didn't mess with the internal state of $parent
203 $this->assertFalse( $parent->isInherited() );
204 $this->assertSame( 7, $parent->getRevision() );
206 // This would happen while doing an edit, after saving the revision meta-data
207 // and content meta-data.
208 $saved = SlotRecord
::newSaved(
210 $inherited->getContentId(),
211 $inherited->getAddress(),
214 $this->assertSame( $parent->getContentId(), $saved->getContentId() );
215 $this->assertSame( $parent->getAddress(), $saved->getAddress() );
216 $this->assertSame( $parent->getContent(), $saved->getContent() );
217 $this->assertTrue( $saved->isInherited() );
218 $this->assertTrue( $saved->hasRevision() );
219 $this->assertSame( 10, $saved->getRevision() );
221 // make sure we didn't mess with the internal state of $parent or $inherited
222 $this->assertSame( 7, $parent->getRevision() );
223 $this->assertFalse( $inherited->hasRevision() );
226 public function testNewSaved() {
227 // This would happen while doing an edit, before saving revision meta-data.
228 $unsaved = SlotRecord
::newUnsaved( SlotRecord
::MAIN
, new WikitextContent( 'A' ) );
230 // This would happen while doing an edit, after saving the revision meta-data
231 // and content meta-data.
232 $saved = SlotRecord
::newSaved( 10, 20, 'theNewAddress', $unsaved );
233 $this->assertFalse( $saved->isInherited() );
234 $this->assertTrue( $saved->hasOrigin() );
235 $this->assertTrue( $saved->hasRevision() );
236 $this->assertTrue( $saved->hasAddress() );
237 $this->assertTrue( $saved->hasContentId() );
238 $this->assertSame( 'theNewAddress', $saved->getAddress() );
239 $this->assertSame( 20, $saved->getContentId() );
240 $this->assertSame( 'A', $saved->getContent()->getText() );
241 $this->assertSame( 10, $saved->getRevision() );
242 $this->assertSame( 10, $saved->getOrigin() );
244 // make sure we didn't mess with the internal state of $unsaved
245 $this->assertFalse( $unsaved->hasAddress() );
246 $this->assertFalse( $unsaved->hasContentId() );
247 $this->assertFalse( $unsaved->hasRevision() );
250 public function provideNewSaved_LogicException() {
251 $freshRow = $this->makeRow( [
253 'content_address' => 'address:1',
255 'slot_revision_id' => 1,
258 $freshSlot = new SlotRecord( $freshRow, new WikitextContent( 'A' ) );
259 yield
'mismatching address' => [ 1, 10, 'address:BAD', $freshSlot ];
260 yield
'mismatching revision' => [ 5, 10, 'address:1', $freshSlot ];
261 yield
'mismatching content ID' => [ 1, 17, 'address:1', $freshSlot ];
263 $inheritedRow = $this->makeRow( [
264 'content_id' => null,
265 'content_address' => null,
267 'slot_revision_id' => 1,
270 $inheritedSlot = new SlotRecord( $inheritedRow, new WikitextContent( 'A' ) );
271 yield
'inherited, but no address' => [ 1, 10, 'address:2', $inheritedSlot ];
275 * @dataProvider provideNewSaved_LogicException
277 public function testNewSaved_LogicException(
281 SlotRecord
$protoSlot
283 $this->setExpectedException( LogicException
::class );
284 SlotRecord
::newSaved( $revisionId, $contentId, $contentAddress, $protoSlot );
287 public function provideNewSaved_InvalidArgumentException() {
288 $unsaved = SlotRecord
::newUnsaved( SlotRecord
::MAIN
, new WikitextContent( 'A' ) );
290 yield
'bad revision id' => [ 'xyzzy', 5, 'address', $unsaved ];
291 yield
'bad content id' => [ 7, 'xyzzy', 'address', $unsaved ];
292 yield
'bad content address' => [ 7, 5, 77, $unsaved ];
296 * @dataProvider provideNewSaved_InvalidArgumentException
298 public function testNewSaved_InvalidArgumentException(
302 SlotRecord
$protoSlot
304 $this->setExpectedException( InvalidArgumentException
::class );
305 SlotRecord
::newSaved( $revisionId, $contentId, $contentAddress, $protoSlot );
308 public function provideHasSameContent() {
309 $fail = function () {
310 self
::fail( 'There should be no need to actually load the content.' );
313 $a100a1 = new SlotRecord(
317 'content_size' => 100,
318 'content_sha1' => 'hash-a',
319 'content_address' => 'xxx:a1',
324 $a100a1b = new SlotRecord(
328 'content_size' => 100,
329 'content_sha1' => 'hash-a',
330 'content_address' => 'xxx:a1',
335 $a100null = new SlotRecord(
339 'content_size' => 100,
340 'content_sha1' => 'hash-a',
341 'content_address' => null,
346 $a100a2 = new SlotRecord(
350 'content_size' => 100,
351 'content_sha1' => 'hash-a',
352 'content_address' => 'xxx:a2',
357 $b100a1 = new SlotRecord(
361 'content_size' => 100,
362 'content_sha1' => 'hash-a',
363 'content_address' => 'xxx:a1',
368 $a200a1 = new SlotRecord(
372 'content_size' => 200,
373 'content_sha1' => 'hash-a',
374 'content_address' => 'xxx:a2',
379 $a100x1 = new SlotRecord(
383 'content_size' => 100,
384 'content_sha1' => 'hash-x',
385 'content_address' => 'xxx:x1',
391 yield
'same instance' => [ $a100a1, $a100a1, true ];
392 yield
'no address' => [ $a100a1, $a100null, true ];
393 yield
'same address' => [ $a100a1, $a100a1b, true ];
394 yield
'different address' => [ $a100a1, $a100a2, true ];
395 yield
'different model' => [ $a100a1, $b100a1, false ];
396 yield
'different size' => [ $a100a1, $a200a1, false ];
397 yield
'different hash' => [ $a100a1, $a100x1, false ];
401 * @dataProvider provideHasSameContent
403 public function testHasSameContent( SlotRecord
$a, SlotRecord
$b, $sameContent ) {
404 $this->assertSame( $sameContent, $a->hasSameContent( $b ) );
405 $this->assertSame( $sameContent, $b->hasSameContent( $a ) );