Split some Language methods to LanguageNameUtils
[lhc/web/wiklou.git] / tests / phpunit / MediaWikiUnitTestCase.php
1 <?php
2 /**
3 * This program is free software; you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License as published by
5 * the Free Software Foundation; either version 2 of the License, or
6 * (at your option) any later version.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License along
14 * with this program; if not, write to the Free Software Foundation, Inc.,
15 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
16 * http://www.gnu.org/copyleft/gpl.html
17 *
18 * @file
19 * @ingroup Testing
20 */
21
22 use PHPUnit\Framework\TestCase;
23 use PHPUnit\Framework\Exception;
24
25 /**
26 * Base class for unit tests.
27 *
28 * Extend this class if you are testing classes which use dependency injection and do not access
29 * global functions, variables, services or a storage backend.
30 *
31 * @since 1.34
32 */
33 abstract class MediaWikiUnitTestCase extends TestCase {
34 use PHPUnit4And6Compat;
35 use MediaWikiCoversValidator;
36 use MediaWikiTestCaseTrait;
37
38 private static $originalGlobals;
39 private static $unitGlobals;
40
41 /**
42 * Whitelist of globals to allow in MediaWikiUnitTestCase.
43 *
44 * Please, keep this list to the bare minimum.
45 *
46 * @return string[]
47 */
48 private static function getGlobalsWhitelist() {
49 return [
50 // The autoloader may change between bootstrap and the first test,
51 // so (lazily) capture these here instead.
52 'wgAutoloadClasses',
53 'wgAutoloadLocalClasses',
54 // Need for LoggerFactory. Default is NullSpi.
55 'wgMWLoggerDefaultSpi',
56 'wgAutoloadAttemptLowercase',
57 'wgLegalTitleChars'
58 ];
59 }
60
61 public static function setUpBeforeClass() {
62 parent::setUpBeforeClass();
63
64 $reflection = new ReflectionClass( static::class );
65 $dirSeparator = DIRECTORY_SEPARATOR;
66 if ( stripos( $reflection->getFilename(), "${dirSeparator}unit${dirSeparator}" ) === false ) {
67 self::fail( 'This unit test needs to be in "tests/phpunit/unit"!' );
68 }
69
70 if ( defined( 'HHVM_VERSION' ) ) {
71 // There are a number of issues we encountered in trying to make this
72 // work on HHVM. Specifically, once an MediaWikiIntegrationTestCase executes
73 // before us, the original globals go missing. This might have to do with
74 // one of the non-unit tests passing GLOBALS somewhere and causing HHVM
75 // to get confused somehow.
76 return;
77 }
78
79 self::$unitGlobals =& TestSetup::$bootstrapGlobals;
80
81 foreach ( self::getGlobalsWhitelist() as $global ) {
82 self::$unitGlobals[ $global ] =& $GLOBALS[ $global ];
83 }
84
85 // Would be nice if we coud simply replace $GLOBALS as a whole,
86 // but unsetting or re-assigning that breaks the reference of this magic
87 // variable. Thus we have to modify it in place.
88 self::$originalGlobals = [];
89 foreach ( $GLOBALS as $key => $_ ) {
90 // Stash current values
91 self::$originalGlobals[$key] =& $GLOBALS[$key];
92
93 // Remove globals not part of the snapshot (see bootstrap.php, phpunit.php).
94 // Support: HHVM (avoid self-ref)
95 if ( $key !== 'GLOBALS' && !array_key_exists( $key, self::$unitGlobals ) ) {
96 unset( $GLOBALS[$key] );
97 }
98 }
99 // Restore values from the early snapshot
100 // Not by ref because tests must not be able to modify the snapshot.
101 foreach ( self::$unitGlobals as $key => $value ) {
102 $GLOBALS[ $key ] = $value;
103 }
104 }
105
106 /**
107 * @inheritDoc
108 */
109 protected function runTest() {
110 try {
111 return parent::runTest();
112 } catch ( ConfigException $exception ) {
113 throw new Exception(
114 'Config variables must be mocked, they cannot be accessed directly in tests which extend '
115 . self::class,
116 $exception->getCode(),
117 $exception
118 );
119 }
120 }
121
122 protected function tearDown() {
123 if ( !defined( 'HHVM_VERSION' ) ) {
124 // Quick reset between tests
125 foreach ( $GLOBALS as $key => $_ ) {
126 if ( $key !== 'GLOBALS' && !array_key_exists( $key, self::$unitGlobals ) ) {
127 unset( $GLOBALS[$key] );
128 }
129 }
130 foreach ( self::$unitGlobals as $key => $value ) {
131 $GLOBALS[ $key ] = $value;
132 }
133 }
134
135 parent::tearDown();
136 }
137
138 public static function tearDownAfterClass() {
139 if ( !defined( 'HHVM_VERSION' ) ) {
140 // Remove globals created by the test
141 foreach ( $GLOBALS as $key => $_ ) {
142 if ( $key !== 'GLOBALS' && !array_key_exists( $key, self::$originalGlobals ) ) {
143 unset( $GLOBALS[$key] );
144 }
145 }
146 // Restore values (including reference!)
147 foreach ( self::$originalGlobals as $key => &$value ) {
148 $GLOBALS[ $key ] =& $value;
149 }
150 }
151
152 parent::tearDownAfterClass();
153 }
154
155 /**
156 * Create a temporary hook handler which will be reset by tearDown.
157 * This replaces other handlers for the same hook.
158 * @param string $hookName Hook name
159 * @param mixed $handler Value suitable for a hook handler
160 * @since 1.34
161 */
162 protected function setTemporaryHook( $hookName, $handler ) {
163 // This will be reset by tearDown() when it restores globals. We don't want to use
164 // Hooks::register()/clear() because they won't replace other handlers for the same hook,
165 // which doesn't match behavior of MediaWikiIntegrationTestCase.
166 global $wgHooks;
167 $wgHooks[$hookName] = [ $handler ];
168 }
169
170 protected function getMockMessage( $text, ...$params ) {
171 if ( isset( $params[0] ) && is_array( $params[0] ) ) {
172 $params = $params[0];
173 }
174
175 $msg = $this->getMockBuilder( Message::class )
176 ->disableOriginalConstructor()
177 ->setMethods( [] )
178 ->getMock();
179
180 $msg->method( 'toString' )->willReturn( $text );
181 $msg->method( '__toString' )->willReturn( $text );
182 $msg->method( 'text' )->willReturn( $text );
183 $msg->method( 'parse' )->willReturn( $text );
184 $msg->method( 'plain' )->willReturn( $text );
185 $msg->method( 'parseAsBlock' )->willReturn( $text );
186 $msg->method( 'escaped' )->willReturn( $text );
187
188 $msg->method( 'title' )->willReturn( $msg );
189 $msg->method( 'inLanguage' )->willReturn( $msg );
190 $msg->method( 'inContentLanguage' )->willReturn( $msg );
191 $msg->method( 'useDatabase' )->willReturn( $msg );
192 $msg->method( 'setContext' )->willReturn( $msg );
193
194 $msg->method( 'exists' )->willReturn( true );
195 $msg->method( 'content' )->willReturn( new MessageContent( $msg ) );
196
197 return $msg;
198 }
199 }