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 $subSet = [$position => [
79 'required' => $required,
84 $this->assertArraySubset($subSet, $assertionArray, false, "expected subset: " . PHP_EOL . print_r($subSet, true) . PHP_EOL . "current subset: " . print_r($assertionArray, true));
88 * Replaces function_exists results with given mocks
90 * @param array $functions a list from function names and their result
92 private function setFunctions($functions)
95 $phpMock['function_exists'] = function($function) use ($functions) {
96 foreach ($functions as $name => $value) {
97 if ($function == $name) {
101 return '__phpunit_continue__';
106 * Replaces class_exist results with given mocks
108 * @param array $classes a list from class names and their results
110 private function setClasses($classes)
113 $phpMock['class_exists'] = function($class) use ($classes) {
114 foreach ($classes as $name => $value) {
115 if ($class == $name) {
119 return '__phpunit_continue__';
126 public function testCheckKeys()
128 $this->l10nMock->shouldReceive('t')->andReturnUsing(function ($args) { return $args; });
130 $this->setFunctions(['openssl_pkey_new' => false]);
131 $install = new Installer();
132 $this->assertFalse($install->checkKeys());
134 $this->setFunctions(['openssl_pkey_new' => true]);
135 $install = new Installer();
136 $this->assertTrue($install->checkKeys());
142 public function testCheckFunctions()
144 $this->mockFunctionL10TCalls();
145 $this->setFunctions(['curl_init' => false, 'imagecreatefromjpeg' => true]);
146 $install = new Installer();
147 $this->assertFalse($install->checkFunctions());
148 $this->assertCheckExist(3,
149 'libCurl PHP module',
150 'Error: libCURL PHP module required but not installed.',
153 $install->getChecks());
155 $this->mockFunctionL10TCalls();
156 $this->setFunctions(['imagecreatefromjpeg' => false]);
157 $install = new Installer();
158 $this->assertFalse($install->checkFunctions());
159 $this->assertCheckExist(4,
160 'GD graphics PHP module',
161 'Error: GD graphics PHP module with JPEG support required but not installed.',
164 $install->getChecks());
166 $this->mockFunctionL10TCalls();
167 $this->setFunctions(['openssl_public_encrypt' => false]);
168 $install = new Installer();
169 $this->assertFalse($install->checkFunctions());
170 $this->assertCheckExist(5,
171 'OpenSSL PHP module',
172 'Error: openssl PHP module required but not installed.',
175 $install->getChecks());
177 $this->mockFunctionL10TCalls();
178 $this->setFunctions(['mb_strlen' => false]);
179 $install = new Installer();
180 $this->assertFalse($install->checkFunctions());
181 $this->assertCheckExist(6,
182 'mb_string PHP module',
183 'Error: mb_string PHP module required but not installed.',
186 $install->getChecks());
188 $this->mockFunctionL10TCalls();
189 $this->setFunctions(['iconv_strlen' => false]);
190 $install = new Installer();
191 $this->assertFalse($install->checkFunctions());
192 $this->assertCheckExist(7,
194 'Error: iconv PHP module required but not installed.',
197 $install->getChecks());
199 $this->mockFunctionL10TCalls();
200 $this->setFunctions(['posix_kill' => false]);
201 $install = new Installer();
202 $this->assertFalse($install->checkFunctions());
203 $this->assertCheckExist(8,
205 'Error: POSIX PHP module required but not installed.',
208 $install->getChecks());
210 $this->mockFunctionL10TCalls();
211 $this->setFunctions(['json_encode' => false]);
212 $install = new Installer();
213 $this->assertFalse($install->checkFunctions());
214 $this->assertCheckExist(9,
216 'Error: JSON PHP module required but not installed.',
219 $install->getChecks());
221 $this->mockFunctionL10TCalls();
222 $this->setFunctions(['finfo_open' => false]);
223 $install = new Installer();
224 $this->assertFalse($install->checkFunctions());
225 $this->assertCheckExist(10,
226 'File Information PHP module',
227 'Error: File Information PHP module required but not installed.',
230 $install->getChecks());
232 $this->mockFunctionL10TCalls();
233 $this->setFunctions([
235 'imagecreatefromjpeg' => true,
236 'openssl_public_encrypt' => true,
238 'iconv_strlen' => true,
239 'posix_kill' => true,
240 'json_encode' => true,
241 'finfo_open' => true,
243 $install = new Installer();
244 $this->assertTrue($install->checkFunctions());
250 public function testCheckLocalIni()
252 $this->l10nMock->shouldReceive('t')->andReturnUsing(function ($args) { return $args; });
254 $this->assertTrue($this->root->hasChild('config/local.config.php'));
256 $install = new Installer();
257 $this->assertTrue($install->checkLocalIni());
259 $this->delConfigFile('local.config.php');
261 $this->assertFalse($this->root->hasChild('config/local.config.php'));
263 $install = new Installer();
264 $this->assertTrue($install->checkLocalIni());
269 * @runInSeparateProcess
270 * @preserveGlobalState disabled
272 public function testCheckHtAccessFail()
274 $this->l10nMock->shouldReceive('t')->andReturnUsing(function ($args) { return $args; });
276 // Mocking the CURL Response
277 $curlResult = \Mockery::mock(CurlResult::class);
279 ->shouldReceive('getReturnCode')
282 ->shouldReceive('getRedirectUrl')
285 ->shouldReceive('getError')
286 ->andReturn('test Error');
288 // Mocking the CURL Request
289 $networkMock = \Mockery::mock('alias:' . Network::class);
291 ->shouldReceive('fetchUrlFull')
292 ->with('https://test/install/testrewrite')
293 ->andReturn($curlResult);
295 ->shouldReceive('fetchUrlFull')
296 ->with('http://test/install/testrewrite')
297 ->andReturn($curlResult);
299 // Mocking that we can use CURL
300 $this->setFunctions(['curl_init' => true]);
302 $install = new Installer();
304 $this->assertFalse($install->checkHtAccess('https://test'));
305 $this->assertSame('test Error', $install->getChecks()[0]['error_msg']['msg']);
310 * @runInSeparateProcess
311 * @preserveGlobalState disabled
313 public function testCheckHtAccessWork()
315 $this->l10nMock->shouldReceive('t')->andReturnUsing(function ($args) { return $args; });
317 // Mocking the failed CURL Response
318 $curlResultF = \Mockery::mock(CurlResult::class);
320 ->shouldReceive('getReturnCode')
323 // Mocking the working CURL Response
324 $curlResultW = \Mockery::mock(CurlResult::class);
326 ->shouldReceive('getReturnCode')
329 // Mocking the CURL Request
330 $networkMock = \Mockery::mock('alias:' . Network::class);
332 ->shouldReceive('fetchUrlFull')
333 ->with('https://test/install/testrewrite')
334 ->andReturn($curlResultF);
336 ->shouldReceive('fetchUrlFull')
337 ->with('http://test/install/testrewrite')
338 ->andReturn($curlResultW);
340 // Mocking that we can use CURL
341 $this->setFunctions(['curl_init' => true]);
343 $install = new Installer();
345 $this->assertTrue($install->checkHtAccess('https://test'));
350 * @runInSeparateProcess
351 * @preserveGlobalState disabled
353 public function testImagick()
355 $this->markTestIncomplete('needs adapted class_exists() mock');
357 $this->l10nMock->shouldReceive('t')->andReturnUsing(function ($args) { return $args; });
359 $this->setClasses(['Imagick' => true]);
361 $install = new Installer();
363 // even there is no supported type, Imagick should return true (because it is not required)
364 $this->assertTrue($install->checkImagick());
366 $this->assertCheckExist(1,
367 $this->l10nMock->t('ImageMagick supports GIF'),
371 $install->getChecks());
376 * @runInSeparateProcess
377 * @preserveGlobalState disabled
379 public function testImagickNotFound()
381 $this->l10nMock->shouldReceive('t')->andReturnUsing(function ($args) { return $args; });
383 $this->setClasses(['Imagick' => true]);
385 $install = new Installer();
387 // even there is no supported type, Imagick should return true (because it is not required)
388 $this->assertTrue($install->checkImagick());
389 $this->assertCheckExist(1,
390 $this->l10nMock->t('ImageMagick supports GIF'),
394 $install->getChecks());
397 public function testImagickNotInstalled()
399 $this->setClasses(['Imagick' => false]);
400 $this->mockL10nT('ImageMagick PHP extension is not installed');
402 $install = new Installer();
404 // even there is no supported type, Imagick should return true (because it is not required)
405 $this->assertTrue($install->checkImagick());
406 $this->assertCheckExist(0,
407 'ImageMagick PHP extension is not installed',
411 $install->getChecks());
415 * Test the setup of the config cache for installation
417 public function testSetUpCache()
419 $this->l10nMock->shouldReceive('t')->andReturnUsing(function ($args) { return $args; });
421 $install = new Installer();
422 $configCache = \Mockery::mock(ConfigCache::class);
423 $configCache->shouldReceive('set')->with('config', 'php_path', \Mockery::any())->once();
424 $configCache->shouldReceive('set')->with('system', 'basepath', '/test/')->once();
426 $install->setUpCache($configCache, '/test/');
431 * A workaround to replace the PHP native function_exists with a mocked function
433 * @param string $function_name the Name of the function
435 * @return bool true or false
437 function function_exists($function_name)
440 if (isset($phpMock['function_exists'])) {
441 $result = call_user_func_array($phpMock['function_exists'], func_get_args());
442 if ($result !== '__phpunit_continue__') {
446 return call_user_func_array('\function_exists', func_get_args());
449 function class_exists($class_name)
452 if (isset($phpMock['class_exists'])) {
453 $result = call_user_func_array($phpMock['class_exists'], func_get_args());
454 if ($result !== '__phpunit_continue__') {
458 return call_user_func_array('\class_exists', func_get_args());