_plg = $this->make('e107plugin'); } catch (Exception $e) { $this->assertTrue(false, "Couldn't load e107plugin object"); }*/ } private function makePluginReport($pluginDir) { $debug = $this->_debugPlugin; $debug_text = "\n\n---- Log \n\n"; $log = e107::getPlugin()->getLog(); foreach($log as $line) { $debug_text .= " - ".$line."\n"; } $debug_text .= "----------------------------------------\n\n"; $debug_text .= "---- Pref: plug_installed (version)\n\n"; $pref = e107::getConfig('core',true,true)->get('plug_installed'); $installedPref = isset($pref[$pluginDir]) ? $pref[$pluginDir] : false; $debug_text .= print_r($installedPref,true); $debug_text .= "\n\n---- Plugin Prefs: \n\n"; $pluginPref = e107::pref($pluginDir); $debug_text .= print_r($pluginPref,true); $debug_text .= "\n---- Plugin Table: ".$pluginDir."\n\n"; $pluginTable = e107::getDb()->retrieve('plugin','*', "plugin_path='".$pluginDir."' LIMIT 1", true); $debug_text .= print_r($pluginTable,true); $debug_text .= "\n---- Menu Table: ".$pluginDir."\n\n"; $menuTable = e107::getDb()->retrieve('menus','*', "menu_location = 0 AND menu_path='".$pluginDir."/' LIMIT 10", true); $debug_text .= print_r($menuTable, true); $debug_text .= "\n---- Site Links Table: ".$pluginDir."\n\n"; $linksTable = e107::getDb()->retrieve('links','*', "link_owner='".$pluginDir."' ", true); $debug_text .= print_r($linksTable, true); $files_in_plugin_directory = @scandir(e_PLUGIN.$pluginDir) ?: []; $corePref = e107::getConfig('core',true,true)->getPref(); $debug_text .= "\n---- Addons\n\n"; $debug_text .= "-------------------------------------------------------------------\n"; $debug_text .= "Addon file In Core pref e_xxxx_list \n"; $debug_text .= "-------------------------------------------------------------------\n"; $addonPref = array(); $plugin_addon_names = $this->pluginFileListToPluginAddonNames($files_in_plugin_directory); foreach($plugin_addon_names as $plugin_addon_name) { $key = $plugin_addon_name."_list"; $addon_pref_is_present = !empty($corePref[$key][$pluginDir]); $debug_addon_pref_is_present = ($addon_pref_is_present) ? 'YES' : 'NO'; if($key === 'e_admin_events_list') { $debug_addon_pref_is_present = "DEPRECATED by Admin-UI events"; } if($key === 'e_help_list') { $debug_addon_pref_is_present = "DEPRECATED by Admin-UI renderHelp()"; } else { $addonPref[$plugin_addon_name] = $addon_pref_is_present; } $debug_text .= str_pad("$plugin_addon_name.php",20) ."\t\t$debug_addon_pref_is_present\n"; } $debug_text .= "-------------------------------------------------------------------\n"; if(!empty($debug) && $pluginDir === $debug) { codecept_debug($debug_text); echo $debug_text; } return array( 'log' => $log, 'installedPref' => $installedPref, 'pluginPref' => $pluginPref, 'pluginTable' => $pluginTable, 'menuTable' => $menuTable, 'linksTable' => $linksTable, 'addonPref' => $addonPref ); } /** * @see https://github.com/e107inc/e107/issues/3547 */ public function testBlank() { // $this->_debugPlugin = '_blank'; $result = $this->pluginInstall('_blank'); // print_r($result); // $this->pluginUninstall('_blank'); } public function testSortOrderPeriodUnderscore() { $expected = ['banner.php', 'banner_menu.php']; $input = ['banner_menu.php', 'banner.php']; sort($input); $this->assertEquals($expected, $input); } /** * @runInSeparateProcess * @return void */ public function testPluginScripts() { $core = e107::getPlug()->getCorePluginList(); $exclude = [ 'forum/forum_post.php', 'forum/forum_viewtopic.php', 'forum/index.php', 'online/online_menu.php', 'pm/pm.php', 'poll/admin_config.php', 'rss_menu/rss.php', 'tagcloud/tagcloud_menu.php', 'tinymce4/wysiwyg.php', ]; $focus = []; $errors = []; foreach($core as $plug) { $path = realpath(e107::getFolder('plugins') . $plug); if($path === false) { fwrite(STDOUT, "Plugin directory not found: {$plug}\n"); continue; } $file[$plug] = scandir($path); unset($file[$plug][0], $file[$plug][1]); sort($file[$plug]); if(!empty($focus) && !isset($focus[$plug])) { unset($file[$plug]); continue; } e107::plugLan($plug, 'global'); e107::getConfig()->setPref('plug_installed/' . $plug, 1); } foreach($file as $plug => $files) { $pluginFiles = []; foreach($files as $f) { $filePath = realpath(e107::getFolder('plugins') . $plug) . DIRECTORY_SEPARATOR . $f; if(!empty($focus) && $f !== $focus[$plug]) { continue; } if(is_dir($filePath) || strpos($f, '_sql.php') !== false || strpos($f, '.php') === false || in_array($plug . '/' . $f, $exclude)) { continue; } if(!file_exists($filePath)) { fwrite(STDOUT, "File not found: {$plug}/{$f}\n"); continue; } $pluginFiles[$plug . '/' . $f] = $filePath; } if(empty($pluginFiles)) { fwrite(STDOUT, "No testable files found for plugin: {$plug}\n"); continue; } fwrite(STDOUT, "Testing plugin: {$plug}\n"); foreach($pluginFiles as $relativePath => $_) { fwrite(STDOUT, " - $relativePath\n"); } // Build the command $requireStatements = ''; $firstFilePath = reset($pluginFiles); $e107Root = realpath(dirname($firstFilePath) . '/../../'); $class2Path = $e107Root . '/class2.php'; if($class2Path === false || !file_exists($class2Path)) { fwrite(STDOUT, "Error: Could not locate class2.php at $class2Path\n"); $errors[] = "Error: Could not locate class2.php for plugin {$plug}"; continue; } $lanAdminPath = $e107Root . '/e107_languages/English/admin/lan_admin.php'; if(!file_exists($lanAdminPath)) { fwrite(STDOUT, "Error: Could not locate lan_admin.php at $lanAdminPath\n"); $errors[] = "Error: Could not locate lan_admin.php for plugin {$plug}"; continue; } $requireStatements .= "error_reporting(E_ALL); ini_set('display_errors', 1); "; $requireStatements .= "require_once ('" . addslashes($class2Path) . "'); "; $requireStatements .= "e107::includeLan( '" . addslashes($lanAdminPath) . "'); "; $requireStatements .= "e107::plugLan('" . addslashes($plug) . "', 'global'); "; $requireStatements .= "e107::getConfig()->setPref('plug_installed/" . addslashes($plug) . "', 1); "; foreach($pluginFiles as $relativePath => $filePath) { $requireStatements .= "echo 'START: " . addslashes($relativePath) . "\\n'; "; $requireStatements .= "require_once '" . addslashes($filePath) . "'; "; $requireStatements .= "echo 'END: " . addslashes($relativePath) . "\\n'; "; } $runCommand = sprintf('php -r %s 1>NUL 2>&1', escapeshellarg($requireStatements)); // fwrite(STDOUT, "Debug run command:\n$runCommand\n\n\n\n"); // Execute and capture errors exec($runCommand, $runOutput, $runExitCode); if($runExitCode !== 0 || !empty($runOutput)) { $output = implode("\n", $runOutput); if(!empty($output)) { if(preg_match('/(Parse error|Fatal error|Warning|Notice):.*in\s+([^\s]+)\s+on\s+line\s+(\d+)/i', $output, $match)) { $errorMessage = $match[0]; $errorFile = $match[2]; $relativePath = array_search($errorFile, $pluginFiles) ?: $plug . '/unknown'; $error = "Error in {$relativePath}: $errorMessage"; fwrite(STDOUT, "$error\n"); $errors[] = $error; } else { $firstLine = strtok($output, "\n"); $error = "Error in {$plug}: $firstLine"; fwrite(STDOUT, "$error\n"); $errors[] = $error; } } else { // Sequentially check files to find the error $lastGoodFile = null; foreach($pluginFiles as $relativePath => $filePath) { $testCommand = sprintf('php -r %s 1>NUL 2>&1', escapeshellarg( "error_reporting(E_ALL); ini_set('display_errors', 1); " . "require_once('" . addslashes($class2Path) . "'); " . "e107::includeLan('" . addslashes($lanAdminPath) . "'); " . "e107::plugLan('" . addslashes($plug) . "', 'global'); " . "e107::getConfig()->setPref('plug_installed/" . addslashes($plug) . "', 1); " . "e107::includeLan('" . addslashes($filePath) . "');" )); exec($testCommand, $testOutput, $testExitCode); if($testExitCode !== 0) { $errorOutput = !empty($testOutput) ? implode("\n", $testOutput) : "Syntax error detected (exit code $testExitCode)"; if(preg_match('/(Parse error|Fatal error|Warning|Notice):.*in\s+([^\s]+)\s+on\s+line\s+(\d+)/i', $errorOutput, $match)) { $errorMessage = $match[0]; } else { $errorMessage = $errorOutput; } $error = "Error in {$relativePath}: $errorMessage"; fwrite(STDOUT, "$error\n"); $errors[] = $error; break; } $lastGoodFile = $relativePath; } if(empty($errors) && $lastGoodFile) { $error = "Error after {$lastGoodFile}: Syntax error detected (exit code $runExitCode)"; fwrite(STDOUT, "$error\n"); $errors[] = $error; } } } } if(!empty($errors)) { self::fail("Errors found in plugin scripts:\n" . implode("\n", $errors)); } } /** * @see https://github.com/e107inc/e107/issues/3547 */ public function testBanner() { $this->pluginInstall('banner'); // App needs e_parse_shortcode to be reloaded because another test // could have initialized e_parse_shortcode already before the // "banner" plugin was installed. e107::getScParser()->__construct(); $tp = e107::getParser(); $result = $tp->parseTemplate("{BANNER=e107promo}", true); $this->assertStringContainsString('parseTemplate("{BANNER=e107promo}", false, e107::getScBatch('banner', true)); $this->assertStringContainsString('parseTemplate("{BANNER=e107promo}", false); $this->assertEquals("", $result); $this->pluginUninstall('banner'); $result = $tp->parseTemplate("{BANNER=e107promo}", true); // The expected value below was the actual observed output when the assertion was written: $this->assertEquals(' ', $result, "Banner shortcode is not returning an empty value, despite banner being uninstalled"); } public function testChatbox_Menu() { $this->pluginInstall('chatbox_menu'); $this->pluginUninstall('chatbox_menu'); } public function testDownload() { $this->pluginInstall('download'); $this->pluginUninstall('download'); } public function testFaqs() { $this->pluginInstall('faqs'); $this->pluginUninstall('faqs'); } public function testFeaturebox() { $this->pluginInstall('featurebox'); $this->pluginUninstall('featurebox'); } public function testForum() { $this->pluginInstall('forum'); $this->pluginUninstall('forum'); } public function testGallery() { $this->pluginInstall('gallery'); $this->pluginUninstall('gallery'); } public function testGsitemap() { $this->pluginInstall('gsitemap'); $this->pluginUninstall('gsitemap'); } public function testImport() { $this->pluginInstall('import'); $this->pluginUninstall('import'); } public function testLinkwords() { $this->pluginInstall('linkwords'); $pref1 = e107::pref('linkwords', 'lw_custom_class'); $this->assertNotEmpty($pref1); $pref2 = e107::pref('linkwords', 'lw_context_visibility'); $this->assertNotEmpty($pref2['SUMMARY']); $this->pluginUninstall('linkwords'); $pref2 = e107::pref('linkwords', 'lw_context_visibility'); $this->assertEmpty($pref2); } public function testPm() { $this->pluginInstall('pm'); $this->pluginUninstall('pm'); } public function testPoll() { $this->pluginInstall('poll'); $this->pluginUninstall('poll'); } public function testRss_menu() { $this->pluginInstall('rss_menu'); $installed = e107::isInstalled('rss_menu'); self::assertTrue($installed); $this->pluginUninstall('rss_menu'); $installed = e107::isInstalled('rss_menu'); self::assertFalse($installed); } public function testSocial() { $this->pluginUninstall('social'); $this->pluginInstall('social'); } public function testTagcloud() { $this->pluginInstall('tagcloud'); $this->pluginUninstall('tagcloud'); } public function testRefreshExtendedFields() { $this->pluginInstall('_blank'); $count = e107::getDb()->count('user_extended_struct', '(*)', "user_extended_struct_name LIKE 'plugin__blank_custom%'"); $this::assertEquals(1, $count, 'Field was not installed'); $this->pluginRefresh('_blank'); $count = e107::getDb()->count('user_extended_struct', '(*)', "user_extended_struct_name LIKE 'plugin__blank_custom%'"); $this::assertEquals(1, $count, 'Field was duplicated or is missing'); e107::getDb()->delete('user_extended_struct', "user_extended_struct_name LIKE 'plugin__blank_custom%'"); $count = e107::getDb()->count('user_extended_struct', '(*)', "user_extended_struct_name LIKE 'plugin__blank_custom%'"); $this::assertEquals(0, $count, 'Field was not deleted'); $this->pluginRefresh('_blank'); $count = e107::getDb()->count('user_extended_struct', '(*)', "user_extended_struct_name LIKE 'plugin__blank_custom%'"); $this::assertEquals(1, $count, 'Field was not re-installed'); $this->pluginUninstall('_blank'); } /* public function testThirdParty() { $coreList = e107::getPlug()->getCorePluginList(); $all = scandir(e_PLUGIN); unset($all[0], $all[1]); $diff = array_diff($all, $coreList); foreach($diff as $plug) { if(!is_dir(e_PLUGIN.$plug) || !is_dir(e_PLUGIN.$plug.'/tests')) { continue; } $tests = scandir(e_PLUGIN.$plug.'/tests'); unset($tests[0], $tests[1]); foreach($tests as $t) { require_once(e_PLUGIN.$plug.'/tests/'.$t); $Codecept = new \Codeception\Codecept(array( 'steps' => true, 'verbosity' => 1, // some other options (see Codeception docs/sources) )); // var_export($Codecept); $Codecept->run('unit'); // require_once '/path/to/codeception/autoload.php'; } } //array_intersect( }*/ public function testplugInstalledStatus() { $sql = e107::getDb(); $plg = e107::getPlug()->clearCache(); $plg->load('tagcloud'); // check it's NOT installed. $status = $plg->isInstalled(); $dbStatus = (bool) $sql->retrieve('plugin', "plugin_installflag", "plugin_path='tagcloud'"); $this->assertEquals($status,$dbStatus,"e_plugin:isInstalled() doesn't match plugin_installflag in db table."); $this->assertFalse($status, "Status for tagcloud being installed should be false"); e107::getPlugin()->install('tagcloud'); // check it's installed. $status = (int) $plg->isInstalled(); $actual = (bool) $status; $dbStatus = (int) $sql->retrieve('plugin', "plugin_installflag", "plugin_path='tagcloud'"); $this->assertEquals($status,$dbStatus,"e_plugin:isInstalled() = ".$status." but plugin_installflag = ".$dbStatus." after install."); $this->assertTrue($actual, "Status for tagcloud being installed should be true after being installed."); e107::getPlugin()->uninstall('tagcloud'); // check it's NOT installed. $status = (int) $plg->isInstalled(); $actual = (bool) $status; $dbStatus = (int) $sql->retrieve('plugin', "plugin_installflag", "plugin_path='tagcloud'"); $this->assertEquals($status,$dbStatus,"e_plugin:isInstalled() = ".$status." but plugin_installflag = ".$dbStatus." after uninstall."); $this->assertFalse($actual, "Status for tagcloud being installed should be false after being uninstalled."); } /** * @runInSeparateProcess * @return void */ public function testPluginAddons() { $plg = e107::getPlug()->clearCache(); $plg->buildAddonPrefLists(); $errors = array( 1 => 'PHP tag Syntax issue', 2 => "File Missing", ); foreach($plg->getCorePluginList() as $folder) { $plg->load($folder); $errMsg = ''; $addons = $plg->getAddons(); foreach(explode(',', $addons) as $this_addon) { if(empty($this_addon)) { continue; } $result = $plg->getAddonErrors($this_addon); if(is_numeric($result) && $result != 0) { $errMsg = " (".$errors[$result].")"; } elseif(isset($result['msg'])) { $errMsg = " (".$result['msg'].")"; } $this->assertEmpty($result, $folder." > ".$this_addon." returned error #".$result.$errMsg); } } } public function testRemotePlugin() { require_once(e_HANDLER."e_marketplace.php"); try { $mock_adapter = $this->make('e_marketplace_adapter_wsdl', [ 'getRemoteFile' => function($remote_url, $local_file, $type='temp') { file_put_contents(e_TEMP.$local_file, self::samplePluginContents()); return true; } ]); $mp = $this->make('e_marketplace', [ 'adapter' => $mock_adapter ]); } catch (Exception $e) { $this->fail("Couldn't load e_marketplace object"); } $mp->__construct(); $id = 912; // No-follow plugin on e107.org $this->assertFalse(is_dir(e_PLUGIN."nofollow"), "Plugin nofollow exists before download"); $mp->download($id, '', 'plugin'); $this->assertTrue(is_dir(e_PLUGIN."nofollow"), "Plugin nofollow is missing after download"); $this->pluginInstall('nofollow'); $opts = array( 'delete_tables' => 1, 'delete_files' => 1 ); $this->pluginUninstall('nofollow',$opts); $status = is_dir(e_PLUGIN."nofollow"); $this->assertFalse($status,"nofollow plugin still exists, despite opt to have it removed during uninstall."); } private function pluginRefresh($pluginDir) { $return_text = e107::getPlugin()->refresh($pluginDir); } private function pluginInstall($pluginDir) { e107::setRegistry('core/form/related'); // reset. e107::getPlugin()->uninstall($pluginDir); $return_text = e107::getPlugin()->install($pluginDir); $this->assertNotEquals("Plugin is already installed.", $return_text); $install = $this->makePluginReport($pluginDir); //todo additional checks foreach($install['addonPref'] as $key=>$val) { $this->assertTrue($val, $key." list pref is missing for ".$pluginDir); } return $install; } private function pluginUninstall($pluginDir, $opts=array()) { if(empty($opts)) { $opts = array( 'delete_tables' => 1, 'delete_files' => 0 ); } e107::getPlugin()->uninstall($pluginDir, $opts); $uninstall = $this->makePluginReport($pluginDir); //todo additional checks $this->assertEmpty($uninstall['linksTable'], $pluginDir." link still exists in the links table"); foreach($uninstall['addonPref'] as $key=>$val) { $message = $key." list pref still contains '".$pluginDir."' after uninstall of ".$pluginDir.". "; $message .= print_r($uninstall,true); $this->assertEmpty($val, $message); } return $uninstall; } /** * @param $plugin_file_list * @return array */ private function pluginFileListToPluginAddonNames($plugin_file_list) { $plugin_addon_names = array_map(function ($addon_path) { return basename($addon_path, '.php'); }, $plugin_file_list); $class_name_that_has_plugin_addons_array = 'e107plugin'; try { $reflectionClass = new ReflectionClass($class_name_that_has_plugin_addons_array); } catch(ReflectionException $e) { $this->fail("Could not instantiate $class_name_that_has_plugin_addons_array to get \$plugin_addons"); } $reflectionProperty = $reflectionClass->getProperty('plugin_addons'); $reflectionProperty->setAccessible(true); $valid_plugin_addon_names = $reflectionProperty->getValue(new $class_name_that_has_plugin_addons_array()); $plugin_addon_names = array_filter($plugin_addon_names, function ($plugin_addon_name) use ($valid_plugin_addon_names) { return in_array($plugin_addon_name, $valid_plugin_addon_names); }); return $plugin_addon_names; } private static function samplePluginContents() { return base64_decode(<<