1<?php
2
3namespace MediaWiki\Tests\Unit\Revision;
4
5use Content;
6use InvalidArgumentException;
7use MediaWiki\Revision\MutableRevisionSlots;
8use MediaWiki\Revision\RevisionAccessException;
9use MediaWiki\Revision\RevisionSlots;
10use MediaWiki\Revision\SlotRecord;
11use WikitextContent;
12
13/**
14 * @covers \MediaWiki\Revision\MutableRevisionSlots
15 */
16class MutableRevisionSlotsTest extends RevisionSlotsTest {
17
18	/**
19	 * @param SlotRecord[] $slots
20	 * @return RevisionSlots
21	 */
22	protected function newRevisionSlots( $slots = [] ) {
23		return new MutableRevisionSlots( $slots );
24	}
25
26	public function provideConstructorFailue() {
27		yield 'array or the wrong thing' => [
28			[ 1, 2, 3 ]
29		];
30	}
31
32	/**
33	 * @dataProvider provideConstructorFailue
34	 * @param array $slots
35	 *
36	 * @covers \MediaWiki\Revision\RevisionSlots::__construct
37	 * @covers \MediaWiki\Revision\RevisionSlots::setSlotsInternal
38	 */
39	public function testConstructorFailue( $slots ) {
40		$this->expectException( InvalidArgumentException::class );
41
42		new MutableRevisionSlots( $slots );
43	}
44
45	public function testSetMultipleSlots() {
46		$slots = new MutableRevisionSlots();
47
48		$this->assertSame( [], $slots->getSlots() );
49
50		$slotA = SlotRecord::newUnsaved( 'some', new WikitextContent( 'A' ) );
51		$slots->setSlot( $slotA );
52		$this->assertTrue( $slots->hasSlot( 'some' ) );
53		$this->assertSame( $slotA, $slots->getSlot( 'some' ) );
54		$this->assertSame( [ 'some' => $slotA ], $slots->getSlots() );
55
56		$slotB = SlotRecord::newUnsaved( 'other', new WikitextContent( 'B' ) );
57		$slots->setSlot( $slotB );
58		$this->assertTrue( $slots->hasSlot( 'other' ) );
59		$this->assertSame( $slotB, $slots->getSlot( 'other' ) );
60		$this->assertSame( [ 'some' => $slotA, 'other' => $slotB ], $slots->getSlots() );
61	}
62
63	public function testSetExistingSlotOverwritesSlot() {
64		$slots = new MutableRevisionSlots();
65
66		$this->assertSame( [], $slots->getSlots() );
67
68		$slotA = SlotRecord::newUnsaved( SlotRecord::MAIN, new WikitextContent( 'A' ) );
69		$slots->setSlot( $slotA );
70		$this->assertSame( $slotA, $slots->getSlot( SlotRecord::MAIN ) );
71		$this->assertSame( [ 'main' => $slotA ], $slots->getSlots() );
72
73		$slotB = SlotRecord::newUnsaved( SlotRecord::MAIN, new WikitextContent( 'B' ) );
74		$slots->setSlot( $slotB );
75		$this->assertSame( $slotB, $slots->getSlot( SlotRecord::MAIN ) );
76		$this->assertSame( [ 'main' => $slotB ], $slots->getSlots() );
77	}
78
79	/**
80	 * @param string $role
81	 * @param Content $content
82	 * @return SlotRecord
83	 */
84	private function newSavedSlot( $role, Content $content ) {
85		return SlotRecord::newSaved( 7, 7, 'xyz', SlotRecord::newUnsaved( $role, $content ) );
86	}
87
88	public function testInheritSlotOverwritesSlot() {
89		$slots = new MutableRevisionSlots();
90		$slotA = SlotRecord::newUnsaved( SlotRecord::MAIN, new WikitextContent( 'A' ) );
91		$slots->setSlot( $slotA );
92		$slotB = $this->newSavedSlot( SlotRecord::MAIN, new WikitextContent( 'B' ) );
93		$slotC = $this->newSavedSlot( 'foo', new WikitextContent( 'C' ) );
94		$slots->inheritSlot( $slotB );
95		$slots->inheritSlot( $slotC );
96		$this->assertSame( [ 'main', 'foo' ], $slots->getSlotRoles() );
97		$this->assertNotSame( $slotB, $slots->getSlot( SlotRecord::MAIN ) );
98		$this->assertNotSame( $slotC, $slots->getSlot( 'foo' ) );
99		$this->assertTrue( $slots->getSlot( SlotRecord::MAIN )->isInherited() );
100		$this->assertTrue( $slots->getSlot( 'foo' )->isInherited() );
101		$this->assertSame( $slotB->getContent(), $slots->getSlot( SlotRecord::MAIN )->getContent() );
102		$this->assertSame( $slotC->getContent(), $slots->getSlot( 'foo' )->getContent() );
103	}
104
105	public function testSetContentOfExistingSlotOverwritesContent() {
106		$slots = new MutableRevisionSlots();
107
108		$this->assertSame( [], $slots->getSlots() );
109
110		$slotA = SlotRecord::newUnsaved( SlotRecord::MAIN, new WikitextContent( 'A' ) );
111		$slots->setSlot( $slotA );
112		$this->assertSame( $slotA, $slots->getSlot( SlotRecord::MAIN ) );
113		$this->assertSame( [ 'main' => $slotA ], $slots->getSlots() );
114
115		$newContent = new WikitextContent( 'B' );
116		$slots->setContent( SlotRecord::MAIN, $newContent );
117		$this->assertSame( $newContent, $slots->getContent( SlotRecord::MAIN ) );
118	}
119
120	public function testRemoveExistingSlot() {
121		$slotA = SlotRecord::newUnsaved( SlotRecord::MAIN, new WikitextContent( 'A' ) );
122		$slots = new MutableRevisionSlots( [ $slotA ] );
123
124		$this->assertSame( [ 'main' => $slotA ], $slots->getSlots() );
125
126		$slots->removeSlot( SlotRecord::MAIN );
127		$this->assertSame( [], $slots->getSlots() );
128		$this->expectException( RevisionAccessException::class );
129		$slots->getSlot( SlotRecord::MAIN );
130	}
131
132	public function testNewFromParentRevisionSlots() {
133		/** @var SlotRecord[] $parentSlots */
134		$parentSlots = [
135			'some' => $this->newSavedSlot( 'some', new WikitextContent( 'X' ) ),
136			'other' => $this->newSavedSlot( 'other', new WikitextContent( 'Y' ) ),
137		];
138		$slots = MutableRevisionSlots::newFromParentRevisionSlots( $parentSlots );
139		$this->assertSame( [ 'some', 'other' ], $slots->getSlotRoles() );
140		$this->assertNotSame( $parentSlots['some'], $slots->getSlot( 'some' ) );
141		$this->assertNotSame( $parentSlots['other'], $slots->getSlot( 'other' ) );
142		$this->assertTrue( $slots->getSlot( 'some' )->isInherited() );
143		$this->assertTrue( $slots->getSlot( 'other' )->isInherited() );
144		$this->assertSame( $parentSlots['some']->getContent(), $slots->getContent( 'some' ) );
145		$this->assertSame( $parentSlots['other']->getContent(), $slots->getContent( 'other' ) );
146	}
147
148	public function testResetCallback() {
149		$counter = 0;
150		$callback = static function () use ( &$counter ) {
151			$counter++;
152		};
153
154		$slots = new MutableRevisionSlots( [], $callback );
155
156		$slot = SlotRecord::newUnsaved( SlotRecord::MAIN, new WikitextContent( 'A' ) );
157		$slots->setSlot( $slot );
158		$this->assertSame( 1, $counter, 'setSlot triggers callback' );
159
160		$slots->setContent( 'other', new WikitextContent( 'Y' ) );
161		$this->assertSame( 2, $counter, 'setContent triggers callback' );
162
163		$slot = $this->newSavedSlot( 'some', new WikitextContent( 'X' ) );
164		$slots->inheritSlot( $slot );
165		$this->assertSame( 3, $counter, 'inheritSlot triggers callback' );
166
167		$slots->removeSlot( SlotRecord::MAIN );
168		$this->assertSame( 4, $counter, 'inheritSlot triggers callback' );
169	}
170
171}
172