3 namespace MediaWiki\Tests\Maintenance
;
10 use PHPUnit_Framework_ExpectationFailedException
;
14 * Mock for the input/output of FetchText
16 * FetchText internally tries to access stdin and stdout. We mock those aspects
19 class SemiMockedFetchText
extends FetchText
{
22 * @var string|null Text to pass as stdin
24 private $mockStdinText = null;
27 * @var bool Whether or not a text for stdin has been provided
29 private $mockSetUp = false;
32 * @var array Invocation counters for the mocked aspects
34 private $mockInvocations = [ 'getStdin' => 0 ];
37 * Data for the fake stdin
39 * @param string $stdin The string to be used instead of stdin
41 function mockStdin( $stdin ) {
42 $this->mockStdinText
= $stdin;
43 $this->mockSetUp
= true;
47 * Gets invocation counters for mocked methods.
49 * @return array An array, whose keys are function names. The corresponding values
50 * denote the number of times the function has been invoked.
52 function mockGetInvocations() {
53 return $this->mockInvocations
;
56 // -----------------------------------------------------------------
57 // Mocked functions from FetchText follow.
59 function getStdin( $len = null ) {
60 $this->mockInvocations
['getStdin']++
;
61 if ( $len !== null ) {
62 throw new PHPUnit_Framework_ExpectationFailedException(
63 "Tried to get stdin with non null parameter" );
66 if ( !$this->mockSetUp
) {
67 throw new PHPUnit_Framework_ExpectationFailedException(
68 "Tried to get stdin before setting up rerouting" );
71 return fopen( 'data://text/plain,' . $this->mockStdinText
, 'r' );
76 * TestCase for FetchText
82 class FetchTextTest
extends MediaWikiTestCase
{
84 // We add 5 Revisions for this test. Their corresponding text id's
85 // are stored in the following 5 variables.
86 protected static $textId1;
87 protected static $textId2;
88 protected static $textId3;
89 protected static $textId4;
90 protected static $textId5;
93 * @var Exception|null As the current MediaWikiTestCase::run is not
94 * robust enough to recover from thrown exceptions directly, we cannot
95 * throw frow within addDBData, although it would be appropriate. Hence,
96 * we catch the exception and store it until we are in setUp and may
97 * finally rethrow the exception without crashing the test suite.
99 protected static $exceptionFromAddDBDataOnce;
102 * @var FetchText The (mocked) FetchText that is to test
107 * Adds a revision to a page, while returning the resuting text's id
109 * @param WikiPage $page The page to add the revision to
110 * @param string $text The revisions text
111 * @param string $summary The revisions summare
113 * @throws MWException
115 private function addRevision( $page, $text, $summary ) {
116 $status = $page->doEditContent(
117 ContentHandler
::makeContent( $text, $page->getTitle() ),
121 if ( $status->isGood() ) {
122 $value = $status->getValue();
123 $revision = $value['revision'];
124 $id = $revision->getTextId();
131 throw new MWException( "Could not determine text id" );
134 function addDBDataOnce() {
135 $wikitextNamespace = $this->getDefaultWikitextNS();
138 $title = Title
::newFromText( 'FetchTextTestPage1', $wikitextNamespace );
139 $page = WikiPage
::factory( $title );
140 self
::$textId1 = $this->addRevision(
142 "FetchTextTestPage1Text1",
143 "FetchTextTestPage1Summary1"
146 $title = Title
::newFromText( 'FetchTextTestPage2', $wikitextNamespace );
147 $page = WikiPage
::factory( $title );
148 self
::$textId2 = $this->addRevision(
150 "FetchTextTestPage2Text1",
151 "FetchTextTestPage2Summary1"
153 self
::$textId3 = $this->addRevision(
155 "FetchTextTestPage2Text2",
156 "FetchTextTestPage2Summary2"
158 self
::$textId4 = $this->addRevision(
160 "FetchTextTestPage2Text3",
161 "FetchTextTestPage2Summary3"
163 self
::$textId5 = $this->addRevision(
165 "FetchTextTestPage2Text4 some additional Text ",
166 "FetchTextTestPage2Summary4 extra "
168 } catch ( Exception
$e ) {
169 // We'd love to pass $e directly. However, ... see
170 // documentation of exceptionFromAddDBDataOnce
171 self
::$exceptionFromAddDBDataOnce = $e;
175 protected function setUp() {
178 // Check if any Exception is stored for rethrowing from addDBData
179 if ( self
::$exceptionFromAddDBDataOnce !== null ) {
180 throw self
::$exceptionFromAddDBDataOnce;
183 $this->fetchText
= new SemiMockedFetchText();
187 * Helper to relate FetchText's input and output
188 * @param string $input
189 * @param string $expectedOutput
191 private function assertFilter( $input, $expectedOutput ) {
192 $this->fetchText
->mockStdin( $input );
193 $this->fetchText
->execute();
194 $invocations = $this->fetchText
->mockGetInvocations();
195 $this->assertEquals( 1, $invocations['getStdin'],
196 "getStdin invocation counter" );
197 $this->expectOutputString( $expectedOutput );
200 // Instead of the following functions, a data provider would be great.
201 // However, as data providers are evaluated /before/ addDBData, a data
202 // provider would not know the required ids.
204 function testExistingSimple() {
205 $this->assertFilter( self
::$textId2,
206 self
::$textId2 . "\n23\nFetchTextTestPage2Text1" );
209 function testExistingSimpleWithNewline() {
210 $this->assertFilter( self
::$textId2 . "\n",
211 self
::$textId2 . "\n23\nFetchTextTestPage2Text1" );
214 function testExistingSeveral() {
223 self
::$textId1 . "\n23\nFetchTextTestPage1Text1",
224 self
::$textId5 . "\n44\nFetchTextTestPage2Text4 "
225 . "some additional Text",
226 self
::$textId3 . "\n23\nFetchTextTestPage2Text2",
227 self
::$textId3 . "\n23\nFetchTextTestPage2Text2"
231 function testEmpty() {
232 $this->assertFilter( "", null );
235 function testNonExisting() {
236 $this->assertFilter( self
::$textId5 +
10, ( self
::$textId5 +
10 ) . "\n-1\n" );
239 function testNegativeInteger() {
240 $this->assertFilter( "-42", "-42\n-1\n" );
243 function testFloatingPointNumberExisting() {
244 // float -> int -> revision
245 $this->assertFilter( self
::$textId3 +
0.14159,
246 self
::$textId3 . "\n23\nFetchTextTestPage2Text2" );
249 function testFloatingPointNumberNonExisting() {
250 $this->assertFilter( self
::$textId5 +
3.14159,
251 ( self
::$textId5 +
3 ) . "\n-1\n" );
254 function testCharacters() {
255 $this->assertFilter( "abc", "0\n-1\n" );
259 $this->assertFilter( "ab\n" . self
::$textId4 . ".5cd\n\nefg\n" . self
::$textId2
260 . "\n" . self
::$textId3,
263 self
::$textId4 . "\n23\nFetchTextTestPage2Text3",
266 self
::$textId2 . "\n23\nFetchTextTestPage2Text1",
267 self
::$textId3 . "\n23\nFetchTextTestPage2Text2"