3 // this is in the same namespace as Install for mocking 'function_exists'
4 namespace Friendica\Core;
7 use Friendica\Core\Config\Cache\ConfigCache;
9 use Friendica\Network\CurlResult;
10 use Friendica\Test\MockedTest;
11 use Friendica\Test\Util\VFSTrait;
12 use Friendica\Util\Network;
13 use Mockery\MockInterface;
15 class InstallerTest extends MockedTest
20 * @var \Friendica\Core\L10n\L10n|MockInterface
24 public function setUp()
30 $this->l10nMock = \Mockery::mock(\Friendica\Core\L10n\L10n::class);
32 /** @var Dice|MockInterface $dice */
33 $dice = \Mockery::mock(Dice::class)->makePartial();
34 $dice = $dice->addRules(include __DIR__ . '/../../../static/dependencies.config.php');
36 $dice->shouldReceive('create')
37 ->with(\Friendica\Core\L10n\L10n::class)
38 ->andReturn($this->l10nMock);
43 private function mockL10nT(string $text, $times = null)
45 $this->l10nMock->shouldReceive('t')->with($text)->andReturn($text)->times($times);
49 * Mocking the L10n::t() calls for the function checks
51 private function mockFunctionL10TCalls()
53 $this->mockL10nT('Apache mod_rewrite module', 1);
54 $this->mockL10nT('PDO or MySQLi PHP module', 1);
55 $this->mockL10nT('libCurl PHP module', 1);
56 $this->mockL10nT('Error: libCURL PHP module required but not installed.', 1);
57 $this->mockL10nT('XML PHP module', 1);
58 $this->mockL10nT('GD graphics PHP module', 1);
59 $this->mockL10nT('Error: GD graphics PHP module with JPEG support required but not installed.', 1);
60 $this->mockL10nT('OpenSSL PHP module', 1);
61 $this->mockL10nT('Error: openssl PHP module required but not installed.', 1);
62 $this->mockL10nT('mb_string PHP module', 1);
63 $this->mockL10nT('Error: mb_string PHP module required but not installed.', 1);
64 $this->mockL10nT('iconv PHP module', 1);
65 $this->mockL10nT('Error: iconv PHP module required but not installed.', 1);
66 $this->mockL10nT('POSIX PHP module', 1);
67 $this->mockL10nT('Error: POSIX PHP module required but not installed.', 1);
68 $this->mockL10nT('JSON PHP module', 1);
69 $this->mockL10nT('Error: JSON PHP module required but not installed.', 1);
70 $this->mockL10nT('File Information PHP module', 1);
71 $this->mockL10nT('Error: File Information PHP module required but not installed.', 1);
74 private function assertCheckExist($position, $title, $help, $status, $required, $assertionArray)
76 $this->assertArraySubset([$position => [
79 'required' => $required,
86 * Replaces function_exists results with given mocks
88 * @param array $functions a list from function names and their result
90 private function setFunctions($functions)
93 $phpMock['function_exists'] = function($function) use ($functions) {
94 foreach ($functions as $name => $value) {
95 if ($function == $name) {
99 return '__phpunit_continue__';
104 * Replaces class_exist results with given mocks
106 * @param array $classes a list from class names and their results
108 private function setClasses($classes)
111 $phpMock['class_exists'] = function($class) use ($classes) {
112 foreach ($classes as $name => $value) {
113 if ($class == $name) {
117 return '__phpunit_continue__';
124 public function testCheckKeys()
126 $this->l10nMock->shouldReceive('t')->andReturnUsing(function ($args) { return $args; });
128 $this->setFunctions(['openssl_pkey_new' => false]);
129 $install = new Installer();
130 $this->assertFalse($install->checkKeys());
132 $this->setFunctions(['openssl_pkey_new' => true]);
133 $install = new Installer();
134 $this->assertTrue($install->checkKeys());
140 public function testCheckFunctions()
142 $this->mockFunctionL10TCalls();
143 $this->setFunctions(['curl_init' => false, 'imagecreatefromjpeg' => true]);
144 $install = new Installer();
145 $this->assertFalse($install->checkFunctions());
146 $this->assertCheckExist(3,
147 'libCurl PHP module',
148 'Error: libCURL PHP module required but not installed.',
151 $install->getChecks());
153 $this->mockFunctionL10TCalls();
154 $this->setFunctions(['imagecreatefromjpeg' => false]);
155 $install = new Installer();
156 $this->assertFalse($install->checkFunctions());
157 $this->assertCheckExist(4,
158 'GD graphics PHP module',
159 'Error: GD graphics PHP module with JPEG support required but not installed.',
162 $install->getChecks());
164 $this->mockFunctionL10TCalls();
165 $this->setFunctions(['openssl_public_encrypt' => false]);
166 $install = new Installer();
167 $this->assertFalse($install->checkFunctions());
168 $this->assertCheckExist(5,
169 'OpenSSL PHP module',
170 'Error: openssl PHP module required but not installed.',
173 $install->getChecks());
175 $this->mockFunctionL10TCalls();
176 $this->setFunctions(['mb_strlen' => false]);
177 $install = new Installer();
178 $this->assertFalse($install->checkFunctions());
179 $this->assertCheckExist(6,
180 'mb_string PHP module',
181 'Error: mb_string PHP module required but not installed.',
184 $install->getChecks());
186 $this->mockFunctionL10TCalls();
187 $this->setFunctions(['iconv_strlen' => false]);
188 $install = new Installer();
189 $this->assertFalse($install->checkFunctions());
190 $this->assertCheckExist(7,
192 'Error: iconv PHP module required but not installed.',
195 $install->getChecks());
197 $this->mockFunctionL10TCalls();
198 $this->setFunctions(['posix_kill' => false]);
199 $install = new Installer();
200 $this->assertFalse($install->checkFunctions());
201 $this->assertCheckExist(8,
203 'Error: POSIX PHP module required but not installed.',
206 $install->getChecks());
208 $this->mockFunctionL10TCalls();
209 $this->setFunctions(['json_encode' => false]);
210 $install = new Installer();
211 $this->assertFalse($install->checkFunctions());
212 $this->assertCheckExist(9,
214 'Error: JSON PHP module required but not installed.',
217 $install->getChecks());
219 $this->mockFunctionL10TCalls();
220 $this->setFunctions(['finfo_open' => false]);
221 $install = new Installer();
222 $this->assertFalse($install->checkFunctions());
223 $this->assertCheckExist(10,
224 'File Information PHP module',
225 'Error: File Information PHP module required but not installed.',
228 $install->getChecks());
230 $this->mockFunctionL10TCalls();
231 $this->setFunctions([
233 'imagecreatefromjpeg' => true,
234 'openssl_public_encrypt' => true,
236 'iconv_strlen' => true,
237 'posix_kill' => true,
238 'json_encode' => true,
239 'finfo_open' => true,
241 $install = new Installer();
242 $this->assertTrue($install->checkFunctions());
248 public function testCheckLocalIni()
250 $this->l10nMock->shouldReceive('t')->andReturnUsing(function ($args) { return $args; });
252 $this->assertTrue($this->root->hasChild('config/local.config.php'));
254 $install = new Installer();
255 $this->assertTrue($install->checkLocalIni());
257 $this->delConfigFile('local.config.php');
259 $this->assertFalse($this->root->hasChild('config/local.config.php'));
261 $install = new Installer();
262 $this->assertTrue($install->checkLocalIni());
267 * @runInSeparateProcess
268 * @preserveGlobalState disabled
270 public function testCheckHtAccessFail()
272 $this->l10nMock->shouldReceive('t')->andReturnUsing(function ($args) { return $args; });
274 // Mocking the CURL Response
275 $curlResult = \Mockery::mock(CurlResult::class);
277 ->shouldReceive('getReturnCode')
280 ->shouldReceive('getRedirectUrl')
283 ->shouldReceive('getError')
284 ->andReturn('test Error');
286 // Mocking the CURL Request
287 $networkMock = \Mockery::mock('alias:' . Network::class);
289 ->shouldReceive('fetchUrlFull')
290 ->with('https://test/install/testrewrite')
291 ->andReturn($curlResult);
293 ->shouldReceive('fetchUrlFull')
294 ->with('http://test/install/testrewrite')
295 ->andReturn($curlResult);
297 // Mocking that we can use CURL
298 $this->setFunctions(['curl_init' => true]);
300 $install = new Installer();
302 $this->assertFalse($install->checkHtAccess('https://test'));
303 $this->assertSame('test Error', $install->getChecks()[0]['error_msg']['msg']);
308 * @runInSeparateProcess
309 * @preserveGlobalState disabled
311 public function testCheckHtAccessWork()
313 $this->l10nMock->shouldReceive('t')->andReturnUsing(function ($args) { return $args; });
315 // Mocking the failed CURL Response
316 $curlResultF = \Mockery::mock(CurlResult::class);
318 ->shouldReceive('getReturnCode')
321 // Mocking the working CURL Response
322 $curlResultW = \Mockery::mock(CurlResult::class);
324 ->shouldReceive('getReturnCode')
327 // Mocking the CURL Request
328 $networkMock = \Mockery::mock('alias:' . Network::class);
330 ->shouldReceive('fetchUrlFull')
331 ->with('https://test/install/testrewrite')
332 ->andReturn($curlResultF);
334 ->shouldReceive('fetchUrlFull')
335 ->with('http://test/install/testrewrite')
336 ->andReturn($curlResultW);
338 // Mocking that we can use CURL
339 $this->setFunctions(['curl_init' => true]);
341 $install = new Installer();
343 $this->assertTrue($install->checkHtAccess('https://test'));
348 * @runInSeparateProcess
349 * @preserveGlobalState disabled
351 public function testImagick()
353 $this->markTestIncomplete('needs adapted class_exists() mock');
355 $this->l10nMock->shouldReceive('t')->andReturnUsing(function ($args) { return $args; });
357 $this->setClasses(['Imagick' => true]);
359 $install = new Installer();
361 // even there is no supported type, Imagick should return true (because it is not required)
362 $this->assertTrue($install->checkImagick());
364 $this->assertCheckExist(1,
365 L10n::t('ImageMagick supports GIF'),
369 $install->getChecks());
374 * @runInSeparateProcess
375 * @preserveGlobalState disabled
377 public function testImagickNotFound()
379 $this->l10nMock->shouldReceive('t')->andReturnUsing(function ($args) { return $args; });
381 $this->setClasses(['Imagick' => true]);
383 $install = new Installer();
385 // even there is no supported type, Imagick should return true (because it is not required)
386 $this->assertTrue($install->checkImagick());
387 $this->assertCheckExist(1,
388 L10n::t('ImageMagick supports GIF'),
392 $install->getChecks());
395 public function testImagickNotInstalled()
397 $this->setClasses(['Imagick' => false]);
398 $this->mockL10nT('ImageMagick PHP extension is not installed');
400 $install = new Installer();
402 // even there is no supported type, Imagick should return true (because it is not required)
403 $this->assertTrue($install->checkImagick());
404 $this->assertCheckExist(0,
405 'ImageMagick PHP extension is not installed',
409 $install->getChecks());
413 * Test the setup of the config cache for installation
415 public function testSetUpCache()
417 $this->l10nMock->shouldReceive('t')->andReturnUsing(function ($args) { return $args; });
419 $install = new Installer();
420 $configCache = \Mockery::mock(ConfigCache::class);
421 $configCache->shouldReceive('set')->with('config', 'php_path', \Mockery::any())->once();
422 $configCache->shouldReceive('set')->with('system', 'basepath', '/test/')->once();
424 $install->setUpCache($configCache, '/test/');
429 * A workaround to replace the PHP native function_exists with a mocked function
431 * @param string $function_name the Name of the function
433 * @return bool true or false
435 function function_exists($function_name)
438 if (isset($phpMock['function_exists'])) {
439 $result = call_user_func_array($phpMock['function_exists'], func_get_args());
440 if ($result !== '__phpunit_continue__') {
444 return call_user_func_array('\function_exists', func_get_args());
447 function class_exists($class_name)
450 if (isset($phpMock['class_exists'])) {
451 $result = call_user_func_array($phpMock['class_exists'], func_get_args());
452 if ($result !== '__phpunit_continue__') {
456 return call_user_func_array('\class_exists', func_get_args());