diff --git a/administrator/components/com_installer/src/Model/DiscoverModel.php b/administrator/components/com_installer/src/Model/DiscoverModel.php
index e33a3937e95aa..349592d64b4a0 100644
--- a/administrator/components/com_installer/src/Model/DiscoverModel.php
+++ b/administrator/components/com_installer/src/Model/DiscoverModel.php
@@ -87,7 +87,7 @@ protected function populateState($ordering = 'name', $direction = 'asc')
/**
* Method to get the database query.
*
- * @return DatabaseQuery the database query
+ * @return DatabaseQuery The database query
*
* @since 3.1
*/
@@ -147,7 +147,7 @@ protected function getListQuery()
*
* Finds uninstalled extensions
*
- * @return void
+ * @return int The count of discovered extensions
*
* @since 1.6
*/
@@ -173,6 +173,8 @@ public function discover()
$extensions[$key] = $install;
}
+ $count = 0;
+
foreach ($results as $result)
{
// Check if we have a match on the element
@@ -183,8 +185,11 @@ public function discover()
// Put it into the table
$result->check();
$result->store();
+ $count++;
}
}
+
+ return $count;
}
/**
diff --git a/libraries/src/Console/ExtensionDiscoverCommand.php b/libraries/src/Console/ExtensionDiscoverCommand.php
new file mode 100644
index 0000000000000..e6c5955bcfdfa
--- /dev/null
+++ b/libraries/src/Console/ExtensionDiscoverCommand.php
@@ -0,0 +1,166 @@
+
+ * @license GNU General Public License version 2 or later; see LICENSE.txt
+ */
+
+namespace Joomla\CMS\Console;
+
+\defined('JPATH_PLATFORM') or die;
+
+use Joomla\CMS\Installer\Installer;
+use Joomla\Component\Installer\Administrator\Model\DiscoverModel;
+use Joomla\Console\Command\AbstractCommand;
+use Joomla\Database\DatabaseInterface;
+use Symfony\Component\Console\Command\Command;
+use Symfony\Component\Console\Input\InputInterface;
+use Symfony\Component\Console\Input\InputOption;
+use Symfony\Component\Console\Output\OutputInterface;
+use Symfony\Component\Console\Style\SymfonyStyle;
+
+/**
+ * Console command for discovering extensions
+ *
+ * @since __DEPLOY_VERSION__
+ */
+class ExtensionDiscoverCommand extends AbstractCommand
+{
+ /**
+ * The default command name
+ *
+ * @var string
+ *
+ * @since __DEPLOY_VERSION__
+ */
+ protected static $defaultName = 'extension:discover';
+
+ /**
+ * Stores the Input Object
+ *
+ * @var InputInterface
+ *
+ * @since __DEPLOY_VERSION__
+ */
+ private $cliInput;
+
+ /**
+ * SymfonyStyle Object
+ *
+ * @var SymfonyStyle
+ *
+ * @since __DEPLOY_VERSION__
+ */
+ private $ioStyle;
+
+ /**
+ * Instantiate the command.
+ *
+ * @since __DEPLOY_VERSION__
+ */
+ public function __construct()
+ {
+ parent::__construct();
+ }
+
+ /**
+ * Configures the IO
+ *
+ * @param InputInterface $input Console Input
+ * @param OutputInterface $output Console Output
+ *
+ * @return void
+ *
+ * @since __DEPLOY_VERSION__
+ *
+ */
+ private function configureIO(InputInterface $input, OutputInterface $output): void
+ {
+ $this->cliInput = $input;
+ $this->ioStyle = new SymfonyStyle($input, $output);
+ }
+
+ /**
+ * Initialise the command.
+ *
+ * @return void
+ *
+ * @since __DEPLOY_VERSION__
+ */
+ protected function configure(): void
+ {
+ $help = "%command.name% is used to discover extensions
+ \nUsage:
+ \n php %command.full_name%";
+
+ $this->setDescription('Discover extensions');
+ $this->setHelp($help);
+ }
+
+ /**
+ * Used for discovering extensions
+ *
+ * @return integer The count of discovered extensions
+ *
+ * @throws \Exception
+ *
+ * @since __DEPLOY_VERSION__
+ */
+ public function processDiscover(): int
+ {
+ $app = $this->getApplication();
+
+ $mvcFactory = $app->bootComponent('com_installer')->getMVCFactory();
+
+ $model = $mvcFactory->createModel('Discover', 'Administrator');
+
+ return $model->discover();
+ }
+
+ /**
+ * Used for finding the text for the note
+ *
+ * @param int $count The count of installed Extensions
+ *
+ * @return string The text for the note
+ *
+ * @since __DEPLOY_VERSION__
+ */
+ public function getNote(int $count): string
+ {
+ if ($count < 1)
+ {
+ return 'No extensions were discovered.';
+ }
+ elseif ($count === 1)
+ {
+ return $count . ' extension has been discovered.';
+ }
+ else
+ {
+ return $count . ' extensions have been discovered.';
+ }
+ }
+
+ /**
+ * Internal function to execute the command.
+ *
+ * @param InputInterface $input The input to inject into the command.
+ * @param OutputInterface $output The output to inject into the command.
+ *
+ * @return integer The command exit code
+ *
+ * @since __DEPLOY_VERSION__
+ */
+ protected function doExecute(InputInterface $input, OutputInterface $output): int
+ {
+ $this->configureIO($input, $output);
+
+ $count = $this->processDiscover();
+
+ $this->ioStyle->note($this->getNote($count));
+
+ return Command::SUCCESS;
+ }
+}
diff --git a/libraries/src/Console/ExtensionDiscoverInstallCommand.php b/libraries/src/Console/ExtensionDiscoverInstallCommand.php
index 85bd804ae3cdb..dcfdb6d795b76 100644
--- a/libraries/src/Console/ExtensionDiscoverInstallCommand.php
+++ b/libraries/src/Console/ExtensionDiscoverInstallCommand.php
@@ -13,6 +13,7 @@
use Joomla\CMS\Installer\Installer;
use Joomla\Console\Command\AbstractCommand;
use Joomla\Database\DatabaseInterface;
+use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
@@ -31,7 +32,7 @@ class ExtensionDiscoverInstallCommand extends AbstractCommand
* @var string
* @since __DEPLOY_VERSION__
*/
- protected static $defaultName = 'extension:discoverinstall';
+ protected static $defaultName = 'extension:discover:install';
/**
* Stores the Input Object
@@ -53,23 +54,10 @@ class ExtensionDiscoverInstallCommand extends AbstractCommand
* Database connector
*
* @var DatabaseInterface
- * @since 4.0.0
- */
- private $db;
-
- /**
- * Exit Code For Discover Failure
- *
- * @since __DEPLOY_VERSION__
- */
- public const DISCOVER_FAILED = 1;
-
- /**
- * Exit Code For Discover Success
*
* @since __DEPLOY_VERSION__
*/
- public const DISCOVER_SUCCESSFUL = 0;
+ private $db;
/**
* Instantiate the command.
@@ -115,10 +103,11 @@ protected function configure(): void
$help = "%command.name% is used to discover extensions
\nYou can provide the following option to the command:
\n --eid: The ID of the extension
+ \n If you do not provide a ID all discovered extensions are installed.
\nUsage:
\n php %command.full_name% --eid=";
- $this->setDescription('Discover and install all extensions or a specified extension');
+ $this->setDescription('Install discovered extensions');
$this->setHelp($help);
}
@@ -127,19 +116,19 @@ protected function configure(): void
*
* @param string $eid Id of the extension
*
- * @return boolean
+ * @return integer The count of installed extensions
*
* @throws \Exception
* @since __DEPLOY_VERSION__
*/
- public function processDiscover($eid): bool
+ public function processDiscover($eid): int
{
$jInstaller = new Installer;
- $result = true;
+ $count = 0;
if ($eid === -1)
{
- $db = $this->db;
+ $db = $this->db;
$query = $db->getQuery(true)
->select($db->quoteName(['extension_id']))
->from($db->quoteName('#__extensions'))
@@ -151,21 +140,71 @@ public function processDiscover($eid): bool
{
if (!$jInstaller->discover_install($eidToDiscover->extension_id))
{
- $this->ioStyle->warning('There was a problem installing the extension with ID ' . $eidToDiscover->extension_id . '.');
- $result = false;
+ return -1;
}
+
+ $count++;
}
if (empty($eidsToDiscover))
{
- $this->ioStyle->warning('There is no extension to discover and install.');
+ return 0;
+ }
+ }
+ else
+ {
+ if ($jInstaller->discover_install($eid))
+ {
+ return 1;
+ }
+ else
+ {
+ return -1;
}
+ }
+
+ return $count;
+ }
- return $result;
+ /**
+ * Used for finding the text for the note
+ *
+ * @param int $count Number of extensions to install
+ * @param int $eid ID of the extension or -1 if no special
+ *
+ * @return string The text for the note
+ *
+ * @since __DEPLOY_VERSION__
+ */
+ public function getNote(int $count, int $eid): string
+ {
+ if ($count < 0 && $eid >= 0)
+ {
+ return 'Unable to install the extension with ID ' . $eid;
+ }
+ elseif ($count < 0 && $eid < 0)
+ {
+ return 'Unable to install discovered extensions.';
+ }
+ elseif ($count === 0)
+ {
+ return 'There are no pending discovered extensions for install. Perhaps you need to run extension:discover first?';
+ }
+ elseif ($count === 1 && $eid > 0)
+ {
+ return 'Extension with ID ' . $eid . ' installed successfully.';
+ }
+ elseif ($count === 1 && $eid < 0)
+ {
+ return $count . ' discovered extension has been installed.';
+ }
+ elseif ($count > 1 && $eid < 0)
+ {
+ return $count . ' discovered extensions have been installed.';
}
else
{
- return $jInstaller->discover_install($eid);
+ return 'The return value is not possible and has to be checked.';
}
}
@@ -177,7 +216,6 @@ public function processDiscover($eid): bool
*
* @return integer The command exit code
*
- * @throws \Exception
* @since __DEPLOY_VERSION__
*/
protected function doExecute(InputInterface $input, OutputInterface $output): int
@@ -188,31 +226,42 @@ protected function doExecute(InputInterface $input, OutputInterface $output): in
{
$result = $this->processDiscover($eid);
- if (!$result)
+ if ($result === -1)
{
- $this->ioStyle->error('Unable to discover and install the extension with ID ' . $eid);
+ $this->ioStyle->error($this->getNote($result, $eid));
- return self::DISCOVER_FAILED;
+ return Command::FAILURE;
}
+ else
+ {
+ $this->ioStyle->success($this->getNote($result, $eid));
- $this->ioStyle->success('Extension with ID ' . $eid . ' discovered and installed successfully.');
-
- return self::DISCOVER_SUCCESSFUL;
+ return Command::SUCCESS;
+ }
}
else
{
$result = $this->processDiscover(-1);
- if (!$result)
+ if ($result < 0)
+ {
+ $this->ioStyle->error($this->getNote($result, -1));
+
+ return Command::FAILURE;
+ }
+ elseif ($result === 0)
{
- $this->ioStyle->error('Unable to discover and install all extensions');
+ $this->ioStyle->note($this->getNote($result, -1));
- return self::DISCOVER_FAILED;
+ return Command::SUCCESS;
}
- $this->ioStyle->success('All extensions discovered and installed successfully.');
+ else
+ {
+ $this->ioStyle->note($this->getNote($result, -1));
- return self::DISCOVER_SUCCESSFUL;
+ return Command::SUCCESS;
+ }
}
}
}
diff --git a/libraries/src/Console/ExtensionDiscoverListCommand.php b/libraries/src/Console/ExtensionDiscoverListCommand.php
new file mode 100644
index 0000000000000..2ccea39f6c47c
--- /dev/null
+++ b/libraries/src/Console/ExtensionDiscoverListCommand.php
@@ -0,0 +1,109 @@
+
+ * @license GNU General Public License version 2 or later; see LICENSE.txt
+ */
+
+namespace Joomla\CMS\Console;
+
+\defined('JPATH_PLATFORM') or die;
+
+use Joomla\Console\Command\AbstractCommand;
+use Symfony\Component\Console\Command\Command;
+use Symfony\Component\Console\Input\InputInterface;
+use Symfony\Component\Console\Output\OutputInterface;
+
+/**
+ * Console command for list discovered extensions
+ *
+ * @since __DEPLOY_VERSION__
+ */
+class ExtensionDiscoverListCommand extends ExtensionsListCommand
+{
+ /**
+ * The default command name
+ *
+ * @var string
+ *
+ * @since __DEPLOY_VERSION__
+ */
+ protected static $defaultName = 'extension:discover:list';
+
+ /**
+ * Initialise the command.
+ *
+ * @return void
+ *
+ * @since __DEPLOY_VERSION__
+ */
+ protected function configure(): void
+ {
+ $help = "%command.name% is used to list all extensions that could be installed via discoverinstall
+ \nUsage:
+ \n php %command.full_name%";
+
+ $this->setDescription('List discovered extensions');
+ $this->setHelp($help);
+ }
+
+ /**
+ * Filters the extension state
+ *
+ * @param array $extensions The Extensions
+ * @param string $state The Extension state
+ *
+ * @return array
+ *
+ * @since __DEPLOY_VERSION__
+ */
+ public function filterExtensionsBasedOnState($extensions, $state): array
+ {
+ $filteredExtensions = [];
+
+ foreach ($extensions as $key => $extension)
+ {
+ if ($extension['state'] === $state)
+ {
+ $filteredExtensions[] = $extension;
+ }
+ }
+
+ return $filteredExtensions;
+ }
+
+ /**
+ * Internal function to execute the command.
+ *
+ * @param InputInterface $input The input to inject into the command.
+ * @param OutputInterface $output The output to inject into the command.
+ *
+ * @return integer The command exit code
+ *
+ * @since __DEPLOY_VERSION__
+ */
+ protected function doExecute(InputInterface $input, OutputInterface $output): int
+ {
+ $this->configureIO($input, $output);
+
+ $extensions = $this->getExtensions();
+ $state = -1;
+
+ $discovered_extensions = $this->filterExtensionsBasedOnState($extensions, $state);
+
+ if (empty($discovered_extensions))
+ {
+ $this->ioStyle->note("There are no pending discovered extensions to install. Perhaps you need to run extension:discover first?");
+
+ return Command::SUCCESS;
+ }
+
+ $discovered_extensions = $this->getExtensionsNameAndId($discovered_extensions);
+
+ $this->ioStyle->title('Discovered extensions.');
+ $this->ioStyle->table(['Name', 'Extension ID', 'Version', 'Type', 'Active'], $discovered_extensions);
+
+ return Command::SUCCESS;
+ }
+}
diff --git a/libraries/src/Console/ExtensionsListCommand.php b/libraries/src/Console/ExtensionsListCommand.php
index bd1ddd7af3502..2142facdfc2ca 100644
--- a/libraries/src/Console/ExtensionsListCommand.php
+++ b/libraries/src/Console/ExtensionsListCommand.php
@@ -38,21 +38,21 @@ class ExtensionsListCommand extends AbstractCommand
* @var array
* @since 4.0
*/
- private $extensions;
+ protected $extensions;
/**
* Stores the Input Object
* @var InputInterface
* @since 4.0
*/
- private $cliInput;
+ protected $cliInput;
/**
* SymfonyStyle Object
* @var SymfonyStyle
* @since 4.0
*/
- private $ioStyle;
+ protected $ioStyle;
/**
* Database connector
@@ -86,7 +86,7 @@ public function __construct(DatabaseInterface $db)
* @since 4.0
*
*/
- private function configureIO(InputInterface $input, OutputInterface $output): void
+ protected function configureIO(InputInterface $input, OutputInterface $output): void
{
$this->cliInput = $input;
$this->ioStyle = new SymfonyStyle($input, $output);
@@ -179,7 +179,7 @@ private function getAllExtensionsFromDB(): array
*
* @since 4.0
*/
- private function getExtensionsNameAndId($extensions): array
+ protected function getExtensionsNameAndId($extensions): array
{
$extInfo = [];
diff --git a/libraries/src/Service/Provider/Application.php b/libraries/src/Service/Provider/Application.php
index 66514d6d2b59f..2fa5c893185bc 100644
--- a/libraries/src/Service/Provider/Application.php
+++ b/libraries/src/Service/Provider/Application.php
@@ -17,7 +17,9 @@
use Joomla\CMS\Application\SiteApplication;
use Joomla\CMS\Console\CheckJoomlaUpdatesCommand;
use Joomla\CMS\Console\ExtensionInstallCommand;
+use Joomla\CMS\Console\ExtensionDiscoverCommand;
use Joomla\CMS\Console\ExtensionDiscoverInstallCommand;
+use Joomla\CMS\Console\ExtensionDiscoverListCommand;
use Joomla\CMS\Console\ExtensionRemoveCommand;
use Joomla\CMS\Console\ExtensionsListCommand;
use Joomla\CMS\Console\FinderIndexCommand;
@@ -158,7 +160,9 @@ function (Container $container)
CheckJoomlaUpdatesCommand::getDefaultName() => CheckJoomlaUpdatesCommand::class,
ExtensionRemoveCommand::getDefaultName() => ExtensionRemoveCommand::class,
ExtensionInstallCommand::getDefaultName() => ExtensionInstallCommand::class,
+ ExtensionDiscoverCommand::getDefaultName() => ExtensionDiscoverCommand::class,
ExtensionDiscoverInstallCommand::getDefaultName() => ExtensionDiscoverInstallCommand::class,
+ ExtensionDiscoverListCommand::getDefaultName() => ExtensionDiscoverListCommand::class,
UpdateCoreCommand::getDefaultName() => UpdateCoreCommand::class,
FinderIndexCommand::getDefaultName() => FinderIndexCommand::class,
];
diff --git a/libraries/src/Service/Provider/Console.php b/libraries/src/Service/Provider/Console.php
index 9aee01df2ea9f..acf6a6aea6cc3 100644
--- a/libraries/src/Service/Provider/Console.php
+++ b/libraries/src/Service/Provider/Console.php
@@ -13,7 +13,9 @@
use Joomla\CMS\Console\CheckJoomlaUpdatesCommand;
use Joomla\CMS\Console\ExtensionInstallCommand;
+use Joomla\CMS\Console\ExtensionDiscoverCommand;
use Joomla\CMS\Console\ExtensionDiscoverInstallCommand;
+use Joomla\CMS\Console\ExtensionDiscoverListCommand;
use Joomla\CMS\Console\ExtensionRemoveCommand;
use Joomla\CMS\Console\ExtensionsListCommand;
use Joomla\CMS\Console\FinderIndexCommand;
@@ -163,6 +165,15 @@ function (Container $container)
true
);
+ $container->share(
+ ExtensionDiscoverCommand::class,
+ function (Container $container)
+ {
+ return new ExtensionDiscoverCommand;
+ },
+ true
+ );
+
$container->share(
ExtensionDiscoverInstallCommand::class,
function (Container $container)
@@ -172,6 +183,15 @@ function (Container $container)
true
);
+ $container->share(
+ ExtensionDiscoverListCommand::class,
+ function (Container $container)
+ {
+ return new ExtensionDiscoverListCommand($container->get('db'));
+ },
+ true
+ );
+
$container->share(
UpdateCoreCommand::class,
function (Container $container)
diff --git a/tests/Unit/Libraries/Cms/Console/ExtensionDiscoverCommandTest.php b/tests/Unit/Libraries/Cms/Console/ExtensionDiscoverCommandTest.php
new file mode 100644
index 0000000000000..59bacd5753b95
--- /dev/null
+++ b/tests/Unit/Libraries/Cms/Console/ExtensionDiscoverCommandTest.php
@@ -0,0 +1,83 @@
+
+ * @license GNU General Public License version 2 or later; see LICENSE.txt
+ */
+namespace Joomla\Tests\Unit\Libraries\Cms\Console;
+
+use Joomla\CMS\Console\ExtensionDiscoverCommand;
+
+/**
+ * Test class for Joomla\CMS\Console\ExtensionDiscoverCommand.
+ *
+ * @since __DEPLOY_VERSION__
+ */
+class ExtensionDiscoverCommandTest extends \PHPUnit\Framework\TestCase
+{
+ /**
+ * Tests the constructor
+ *
+ * @return void
+ *
+ * @since __DEPLOY_VERSION__
+ */
+ public function testIsConstructable()
+ {
+ $this->assertInstanceOf(ExtensionDiscoverCommand::class, $this->createExtensionDiscoverCommand());
+ }
+
+ /**
+ * Tests the processDiscover method
+ * Ensure that the return value is an integer.
+ *
+ * @return void
+ *
+ * @since __DEPLOY_VERSION__
+ */
+ public function testProcessDiscoverReturnIsInt()
+ {
+ $command = $this->createMock(ExtensionDiscoverCommand::class);
+
+ $countOfDiscoveredExtensions = $command->processDiscover();
+
+ $this->assertIsInt($countOfDiscoveredExtensions);
+ }
+
+ /**
+ * Tests the getNote method
+ * Ensure that the note is correct.
+ *
+ * @param int $count Number of extensions to discover
+ *
+ * @return void
+ *
+ * @since __DEPLOY_VERSION__
+ */
+ public function testGetNote()
+ {
+ $command = $this->createExtensionDiscoverCommand();
+
+ $note0 = $command->getNote(0);
+ $note1 = $command->getNote(1);
+ $note2 = $command->getNote(2);
+
+ $this->assertSame($note0, 'No extensions were discovered.');
+ $this->assertSame($note1, '1 extension has been discovered.');
+ $this->assertSame($note2, '2 extensions have been discovered.');
+ }
+
+ /**
+ * Helper function to create a ExtensionDiscoverCommand
+ *
+ * @return ExtensionDiscoverCommand
+ *
+ * @since __DEPLOY_VERSION__
+ */
+ protected function createExtensionDiscoverCommand(): ExtensionDiscoverCommand
+ {
+ return new ExtensionDiscoverCommand;
+ }
+}
diff --git a/tests/Unit/Libraries/Cms/Console/ExtensionDiscoverInstallCommandTest.php b/tests/Unit/Libraries/Cms/Console/ExtensionDiscoverInstallCommandTest.php
new file mode 100644
index 0000000000000..eb5790c12f86e
--- /dev/null
+++ b/tests/Unit/Libraries/Cms/Console/ExtensionDiscoverInstallCommandTest.php
@@ -0,0 +1,96 @@
+
+ * @license GNU General Public License version 2 or later; see LICENSE.txt
+ */
+namespace Joomla\Tests\Unit\Libraries\Cms\Console;
+
+use Joomla\CMS\Console\ExtensionDiscoverInstallCommand;
+use Joomla\Database\DatabaseInterface;
+
+/**
+ * Test class for Joomla\CMS\Console\ExtensionDiscoverInstallCommand.
+ *
+ * @since __DEPLOY_VERSION__
+ */
+class ExtensionDiscoverInstallCommandTest extends \PHPUnit\Framework\TestCase
+{
+ /**
+ * Tests the constructor
+ *
+ * @return void
+ *
+ * @since __DEPLOY_VERSION__
+ */
+ public function testIsConstructable()
+ {
+ $this->assertInstanceOf(ExtensionDiscoverInstallCommand::class, $this->createExtensionDiscoverInstallCommand());
+ }
+
+ /**
+ * Tests the processDiscover method
+ * Ensure that the return value is an integer.
+ *
+ * @return void
+ *
+ * @since __DEPLOY_VERSION__
+ */
+ public function testProcessDiscoverReturnIsInt()
+ {
+ $command = $this->createMock(ExtensionDiscoverInstallCommand::class);
+
+ $countOfDiscoveredExtensions1 = $command->processDiscover(-1);
+ $countOfDiscoveredExtensions0 = $command->processDiscover(0);
+ $countOfDiscoveredExtensions245 = $command->processDiscover(245);
+
+ $this->assertIsInt($countOfDiscoveredExtensions1);
+ $this->assertIsInt($countOfDiscoveredExtensions0);
+ $this->assertIsInt($countOfDiscoveredExtensions245);
+ }
+
+ /**
+ * Tests the getNote method
+ * Ensure that the note is correct.
+ *
+ * @return void
+ *
+ * @since __DEPLOY_VERSION__
+ */
+ public function testGetNote()
+ {
+ $command = $this->createExtensionDiscoverInstallCommand();
+
+ $note0 = $command->getNote(-1, 1);
+ $note1 = $command->getNote(-1, -1);
+ $note2 = $command->getNote(0, 1);
+ $note3 = $command->getNote(1, 1);
+ $note4 = $command->getNote(1, -1);
+ $note5 = $command->getNote(2, -1);
+ $note6 = $command->getNote(2, 1);
+
+ $this->assertSame($note0, 'Unable to install the extension with ID 1');
+ $this->assertSame($note1, 'Unable to install discovered extensions.');
+ $this->assertSame($note2, 'There are no pending discovered extensions for install. Perhaps you need to run extension:discover first?');
+ $this->assertSame($note3, 'Extension with ID 1 installed successfully.');
+ $this->assertSame($note4, '1 discovered extension has been installed.');
+ $this->assertSame($note5, '2 discovered extensions have been installed.');
+ $this->assertSame($note6, 'The return value is not possible and has to be checked.');
+ }
+
+ /**
+ * Helper function to create a ExtensionDiscoverInstallCommand
+ *
+ * @return ExtensionDiscoverInstallCommand
+ *
+ * @since __DEPLOY_VERSION__
+ */
+ protected function createExtensionDiscoverInstallCommand(): ExtensionDiscoverInstallCommand
+ {
+ $db = $this->createMock(DatabaseInterface::class);
+
+ return new ExtensionDiscoverInstallCommand($db);
+ }
+}
diff --git a/tests/Unit/Libraries/Cms/Console/ExtensionDiscoverListCommandTest.php b/tests/Unit/Libraries/Cms/Console/ExtensionDiscoverListCommandTest.php
new file mode 100644
index 0000000000000..06433527b6fc7
--- /dev/null
+++ b/tests/Unit/Libraries/Cms/Console/ExtensionDiscoverListCommandTest.php
@@ -0,0 +1,88 @@
+
+ * @license GNU General Public License version 2 or later; see LICENSE.txt
+ */
+namespace Joomla\Tests\Unit\Libraries\Cms\Console;
+
+use Joomla\CMS\Console\ExtensionDiscoverListCommand;
+use Joomla\Database\DatabaseInterface;
+
+/**
+ * Test class for Joomla\CMS\Console\ExtensionDiscoverCommand.
+ *
+ * @since __DEPLOY_VERSION__
+ */
+class ExtensionDiscoverListCommandTest extends \PHPUnit\Framework\TestCase
+{
+ /**
+ * Tests the constructor
+ *
+ * @return void
+ *
+ * @since __DEPLOY_VERSION__
+ */
+ public function testIsConstructable()
+ {
+ $this->assertInstanceOf(ExtensionDiscoverListCommand::class, $this->createExtensionDiscoverListCommand());
+ }
+
+ /**
+ * Tests the filterExtensionsBasedOnState method
+ * Ensure that the return value is an array and the filter works correcly.
+ *
+ * @return void
+ *
+ * @since __DEPLOY_VERSION__
+ */
+ public function testFilterExtensions()
+ {
+ $command = $this->createExtensionDiscoverListCommand();
+
+ $state = -1;
+
+ $extensions0 = array();
+ $extensions0[0] = array('state' => 0);
+ $extensions0[1] = array('state' => 0);
+
+ $extensions1 = array();
+ $extensions1[0] = array('state' => 0);
+ $extensions1[1] = array('state' => -1);
+
+ $extensions2 = array();
+ $extensions2[0] = array('state' => -1);
+ $extensions2[1] = array('state' => -1);
+
+ $filteredextensionsArray0 = $command->filterExtensionsBasedOnState($extensions0, $state);
+ $filteredextensionsArray1 = $command->filterExtensionsBasedOnState($extensions1, $state);
+ $filteredextensionsArray2 = $command->filterExtensionsBasedOnState($extensions2, $state);
+
+ $size0 = sizeof($filteredextensionsArray0);
+ $size1 = sizeof($filteredextensionsArray1);
+ $size2 = sizeof($filteredextensionsArray2);
+
+ $this->assertSame($size0, 0);
+ $this->assertSame($size1, 1);
+ $this->assertSame($size2, 2);
+
+ $this->assertIsArray($filteredextensionsArray0);
+ $this->assertIsArray($filteredextensionsArray1);
+ $this->assertIsArray($filteredextensionsArray2);
+ }
+
+ /**
+ * Helper function to create a ExtensionDiscoverCommand
+ *
+ * @return ExtensionDiscoverCommand
+ *
+ * @since __DEPLOY_VERSION__
+ */
+ protected function createExtensionDiscoverListCommand(): ExtensionDiscoverListCommand
+ {
+ $db = $this->createMock(DatabaseInterface::class);
+ return new ExtensionDiscoverListCommand($db);
+ }
+}