fix: Deduplicate code by using DependencyAnalyzer in the AppManager

Was a bit more complicated than expected because of a dependency loop,
the L10N factory uses the app manager, thus the AppManager cannot depend
on I10N directly or indirectly in its constructor.

Signed-off-by: Côme Chilliet <come.chilliet@nextcloud.com>
pull/53895/head
Côme Chilliet 2025-07-31 17:44:44 +07:00
parent 7978b6153d
commit 12edd2b23a
No known key found for this signature in database
GPG Key ID: A3E2F658B28C760A
7 changed files with 351 additions and 406 deletions

@ -14,7 +14,6 @@ use OC\App\AppStore\Fetcher\AppFetcher;
use OC\App\AppStore\Fetcher\CategoryFetcher;
use OC\App\AppStore\Version\VersionParser;
use OC\App\DependencyAnalyzer;
use OC\App\Platform;
use OC\Installer;
use OCA\AppAPI\Service\ExAppsPageService;
use OCP\App\AppPathNotFoundException;
@ -361,7 +360,7 @@ class AppSettingsController extends Controller {
$this->fetchApps();
$apps = $this->getAllApps();
$dependencyAnalyzer = new DependencyAnalyzer(new Platform($this->config), $this->l10n);
$dependencyAnalyzer = Server::get(DependencyAnalyzer::class);
$ignoreMaxApps = $this->config->getSystemValue('app_install_overwrite', []);
if (!is_array($ignoreMaxApps)) {

@ -89,6 +89,7 @@ class AppManager implements IAppManager {
private LoggerInterface $logger,
private ServerVersion $serverVersion,
private ConfigManager $configManager,
private DependencyAnalyzer $dependencyAnalyzer,
) {
}
@ -1108,61 +1109,6 @@ class AppManager implements IAppManager {
}
public function isAppCompatible(string $serverVersion, array $appInfo, bool $ignoreMax = false): bool {
$requireMin = '';
$requireMax = '';
if (isset($appInfo['dependencies']['nextcloud']['@attributes']['min-version'])) {
$requireMin = $appInfo['dependencies']['nextcloud']['@attributes']['min-version'];
} elseif (isset($appInfo['dependencies']['owncloud']['@attributes']['min-version'])) {
$requireMin = $appInfo['dependencies']['owncloud']['@attributes']['min-version'];
} elseif (isset($appInfo['requiremin'])) {
$requireMin = $appInfo['requiremin'];
} elseif (isset($appInfo['require'])) {
$requireMin = $appInfo['require'];
}
if (isset($appInfo['dependencies']['nextcloud']['@attributes']['max-version'])) {
$requireMax = $appInfo['dependencies']['nextcloud']['@attributes']['max-version'];
} elseif (isset($appInfo['dependencies']['owncloud']['@attributes']['max-version'])) {
$requireMax = $appInfo['dependencies']['owncloud']['@attributes']['max-version'];
} elseif (isset($appInfo['requiremax'])) {
$requireMax = $appInfo['requiremax'];
}
if (!empty($requireMin)
&& version_compare($this->adjustVersionParts($serverVersion, $requireMin), $requireMin, '<')
) {
return false;
}
if (!$ignoreMax && !empty($requireMax)
&& version_compare($this->adjustVersionParts($serverVersion, $requireMax), $requireMax, '>')
) {
return false;
}
return true;
}
/**
* Adjust the number of version parts of $version1 to match
* the number of version parts of $version2.
*
* @param string $version1 version to adjust
* @param string $version2 version to take the number of parts from
* @return string shortened $version1
*/
private function adjustVersionParts(string $version1, string $version2): string {
//FIXME Most likely this function is not needed and version_compare directly will work. Should be tested.
$version1 = explode('.', $version1);
$version2 = explode('.', $version2);
// reduce $version1 to match the number of parts in $version2
while (count($version1) > count($version2)) {
array_pop($version1);
}
// if $version1 does not have enough parts, add some
while (count($version1) < count($version2)) {
$version1[] = '0';
}
return implode('.', $version1);
return count($this->dependencyAnalyzer->analyzeServerVersion($serverVersion, $appInfo, $ignoreMax)) === 0;
}
}

@ -11,20 +11,29 @@ declare(strict_types=1);
namespace OC\App;
use OCP\IL10N;
use OCP\L10N\IFactory;
use OCP\Server;
class DependencyAnalyzer {
// Cannot be injected because when this class is built IAppManager is not available yet
private ?IL10N $l = null;
public function __construct(
private Platform $platform,
private IL10N $l,
) {
}
private function getL(): IL10N {
$this->l ??= Server::get(IFactory::class)->get('lib');
return $this->l;
}
/**
* @return array of missing dependencies
*/
public function analyze(array $app, bool $ignoreMax = false): array {
if (isset($app['dependencies'])) {
$dependencies = $app['dependencies'];
public function analyze(array $appInfo, bool $ignoreMax = false): array {
if (isset($appInfo['dependencies'])) {
$dependencies = $appInfo['dependencies'];
} else {
$dependencies = [];
}
@ -36,18 +45,12 @@ class DependencyAnalyzer {
$this->analyzeCommands($dependencies),
$this->analyzeLibraries($dependencies),
$this->analyzeOS($dependencies),
$this->analyzeOC($dependencies, $app, $ignoreMax)
$this->analyzeServer($appInfo, $ignoreMax),
);
}
public function isMarkedCompatible(array $app): bool {
if (isset($app['dependencies'])) {
$dependencies = $app['dependencies'];
} else {
$dependencies = [];
}
$maxVersion = $this->getMaxVersion($dependencies, $app);
public function isMarkedCompatible(array $appInfo): bool {
$maxVersion = $this->getMaxVersion($appInfo);
if ($maxVersion === null) {
return true;
}
@ -76,6 +79,7 @@ class DependencyAnalyzer {
/**
* Parameters will be normalized and then passed into version_compare
* in the same order they are specified in the method header
* @param '<'|'lt'|'<='|'le'|'>'|'gt'|'>='|'ge'|'=='|'='|'eq'|'!='|'<>'|'ne' $operator
* @return bool result similar to version_compare
*/
private function compare(string $first, string $second, string $operator): bool {
@ -105,19 +109,19 @@ class DependencyAnalyzer {
if (isset($dependencies['php']['@attributes']['min-version'])) {
$minVersion = $dependencies['php']['@attributes']['min-version'];
if ($this->compareSmaller($this->platform->getPhpVersion(), $minVersion)) {
$missing[] = $this->l->t('PHP %s or higher is required.', [$minVersion]);
$missing[] = $this->getL()->t('PHP %s or higher is required.', [$minVersion]);
}
}
if (isset($dependencies['php']['@attributes']['max-version'])) {
$maxVersion = $dependencies['php']['@attributes']['max-version'];
if ($this->compareBigger($this->platform->getPhpVersion(), $maxVersion)) {
$missing[] = $this->l->t('PHP with a version lower than %s is required.', [$maxVersion]);
$missing[] = $this->getL()->t('PHP with a version lower than %s is required.', [$maxVersion]);
}
}
if (isset($dependencies['php']['@attributes']['min-int-size'])) {
$intSize = $dependencies['php']['@attributes']['min-int-size'];
if ($intSize > $this->platform->getIntSize() * 8) {
$missing[] = $this->l->t('%sbit or higher PHP required.', [$intSize]);
$missing[] = $this->getL()->t('%sbit or higher PHP required.', [$intSize]);
}
}
return $missing;
@ -141,7 +145,7 @@ class DependencyAnalyzer {
}, $supportedArchitectures);
$currentArchitecture = $this->platform->getArchitecture();
if (!in_array($currentArchitecture, $supportedArchitectures, true)) {
$missing[] = $this->l->t('The following architectures are supported: %s', [implode(', ', $supportedArchitectures)]);
$missing[] = $this->getL()->t('The following architectures are supported: %s', [implode(', ', $supportedArchitectures)]);
}
return $missing;
}
@ -167,7 +171,7 @@ class DependencyAnalyzer {
}, $supportedDatabases);
$currentDatabase = $this->platform->getDatabase();
if (!in_array($currentDatabase, $supportedDatabases)) {
$missing[] = $this->l->t('The following databases are supported: %s', [implode(', ', $supportedDatabases)]);
$missing[] = $this->getL()->t('The following databases are supported: %s', [implode(', ', $supportedDatabases)]);
}
return $missing;
}
@ -192,7 +196,7 @@ class DependencyAnalyzer {
}
$commandName = $this->getValue($command);
if (!$this->platform->isCommandKnown($commandName)) {
$missing[] = $this->l->t('The command line tool %s could not be found', [$commandName]);
$missing[] = $this->getL()->t('The command line tool %s could not be found', [$commandName]);
}
}
return $missing;
@ -215,7 +219,7 @@ class DependencyAnalyzer {
$libName = $this->getValue($lib);
$libVersion = $this->platform->getLibraryVersion($libName);
if (is_null($libVersion)) {
$missing[] = $this->l->t('The library %s is not available.', [$libName]);
$missing[] = $this->getL()->t('The library %s is not available.', [$libName]);
continue;
}
@ -223,14 +227,14 @@ class DependencyAnalyzer {
if (isset($lib['@attributes']['min-version'])) {
$minVersion = $lib['@attributes']['min-version'];
if ($this->compareSmaller($libVersion, $minVersion)) {
$missing[] = $this->l->t('Library %1$s with a version higher than %2$s is required - available version %3$s.',
$missing[] = $this->getL()->t('Library %1$s with a version higher than %2$s is required - available version %3$s.',
[$libName, $minVersion, $libVersion]);
}
}
if (isset($lib['@attributes']['max-version'])) {
$maxVersion = $lib['@attributes']['max-version'];
if ($this->compareBigger($libVersion, $maxVersion)) {
$missing[] = $this->l->t('Library %1$s with a version lower than %2$s is required - available version %3$s.',
$missing[] = $this->getL()->t('Library %1$s with a version lower than %2$s is required - available version %3$s.',
[$libName, $maxVersion, $libVersion]);
}
}
@ -258,44 +262,48 @@ class DependencyAnalyzer {
}
$currentOS = $this->platform->getOS();
if (!in_array($currentOS, $oss)) {
$missing[] = $this->l->t('The following platforms are supported: %s', [implode(', ', $oss)]);
$missing[] = $this->getL()->t('The following platforms are supported: %s', [implode(', ', $oss)]);
}
return $missing;
}
private function analyzeOC(array $dependencies, array $appInfo, bool $ignoreMax): array {
private function analyzeServer(array $appInfo, bool $ignoreMax): array {
return $this->analyzeServerVersion($this->platform->getOcVersion(), $appInfo, $ignoreMax);
}
public function analyzeServerVersion(string $serverVersion, array $appInfo, bool $ignoreMax): array {
$missing = [];
$minVersion = null;
if (isset($dependencies['nextcloud']['@attributes']['min-version'])) {
$minVersion = $dependencies['nextcloud']['@attributes']['min-version'];
} elseif (isset($dependencies['owncloud']['@attributes']['min-version'])) {
$minVersion = $dependencies['owncloud']['@attributes']['min-version'];
if (isset($appInfo['dependencies']['nextcloud']['@attributes']['min-version'])) {
$minVersion = $appInfo['dependencies']['nextcloud']['@attributes']['min-version'];
} elseif (isset($appInfo['dependencies']['owncloud']['@attributes']['min-version'])) {
$minVersion = $appInfo['dependencies']['owncloud']['@attributes']['min-version'];
} elseif (isset($appInfo['requiremin'])) {
$minVersion = $appInfo['requiremin'];
} elseif (isset($appInfo['require'])) {
$minVersion = $appInfo['require'];
}
$maxVersion = $this->getMaxVersion($dependencies, $appInfo);
$maxVersion = $this->getMaxVersion($appInfo);
if (!is_null($minVersion)) {
if ($this->compareSmaller($this->platform->getOcVersion(), $minVersion)) {
$missing[] = $this->l->t('Server version %s or higher is required.', [$minVersion]);
if ($this->compareSmaller($serverVersion, $minVersion)) {
$missing[] = $this->getL()->t('Server version %s or higher is required.', [$minVersion]);
}
}
if (!$ignoreMax && !is_null($maxVersion)) {
if ($this->compareBigger($this->platform->getOcVersion(), $maxVersion)) {
$missing[] = $this->l->t('Server version %s or lower is required.', [$maxVersion]);
if ($this->compareBigger($serverVersion, $maxVersion)) {
$missing[] = $this->getL()->t('Server version %s or lower is required.', [$maxVersion]);
}
}
return $missing;
}
private function getMaxVersion(array $dependencies, array $appInfo): ?string {
if (isset($dependencies['nextcloud']['@attributes']['max-version'])) {
return $dependencies['nextcloud']['@attributes']['max-version'];
private function getMaxVersion(array $appInfo): ?string {
if (isset($appInfo['dependencies']['nextcloud']['@attributes']['max-version'])) {
return $appInfo['dependencies']['nextcloud']['@attributes']['max-version'];
}
if (isset($dependencies['owncloud']['@attributes']['max-version'])) {
return $dependencies['owncloud']['@attributes']['max-version'];
if (isset($appInfo['dependencies']['owncloud']['@attributes']['max-version'])) {
return $appInfo['dependencies']['owncloud']['@attributes']['max-version'];
}
if (isset($appInfo['requiremax'])) {
return $appInfo['requiremax'];
@ -304,13 +312,9 @@ class DependencyAnalyzer {
return null;
}
/**
* @param mixed $element
* @return mixed
*/
private function getValue($element) {
private function getValue(mixed $element): string {
if (isset($element['@value'])) {
return $element['@value'];
return (string)$element['@value'];
}
return (string)$element;
}

@ -8,7 +8,6 @@ declare(strict_types=1);
*/
use OC\App\AppManager;
use OC\App\DependencyAnalyzer;
use OC\App\Platform;
use OC\AppFramework\Bootstrap\Coordinator;
use OC\Installer;
use OC\Repair;
@ -592,7 +591,7 @@ class OC_App {
* @throws \Exception
*/
public static function checkAppDependencies(\OCP\IConfig $config, \OCP\IL10N $l, array $info, bool $ignoreMax) {
$dependencyAnalyzer = new DependencyAnalyzer(new Platform($config), $l);
$dependencyAnalyzer = Server::get(DependencyAnalyzer::class);
$missing = $dependencyAnalyzer->analyze($info, $ignoreMax);
if (!empty($missing)) {
$missingMsg = implode(PHP_EOL, $missing);

@ -11,12 +11,12 @@ declare(strict_types=1);
namespace Test\App;
use OC\App\AppManager;
use OC\App\DependencyAnalyzer;
use OC\AppConfig;
use OC\Config\ConfigManager;
use OCP\App\AppPathNotFoundException;
use OCP\App\Events\AppDisableEvent;
use OCP\App\Events\AppEnableEvent;
use OCP\App\IAppManager;
use OCP\EventDispatcher\IEventDispatcher;
use OCP\ICache;
use OCP\ICacheFactory;
@ -95,9 +95,9 @@ class AppManagerTest extends TestCase {
protected IURLGenerator&MockObject $urlGenerator;
protected ServerVersion&MockObject $serverVersion;
protected ConfigManager&MockObject $configManager;
protected DependencyAnalyzer&MockObject $dependencyAnalyzer;
/** @var IAppManager */
protected $manager;
protected AppManager $manager;
protected function setUp(): void {
parent::setUp();
@ -113,6 +113,7 @@ class AppManagerTest extends TestCase {
$this->urlGenerator = $this->createMock(IURLGenerator::class);
$this->serverVersion = $this->createMock(ServerVersion::class);
$this->configManager = $this->createMock(ConfigManager::class);
$this->dependencyAnalyzer = $this->createMock(DependencyAnalyzer::class);
$this->overwriteService(AppConfig::class, $this->appConfig);
$this->overwriteService(IURLGenerator::class, $this->urlGenerator);
@ -136,6 +137,7 @@ class AppManagerTest extends TestCase {
$this->logger,
$this->serverVersion,
$this->configManager,
$this->dependencyAnalyzer,
);
}
@ -275,6 +277,7 @@ class AppManagerTest extends TestCase {
$this->logger,
$this->serverVersion,
$this->configManager,
$this->dependencyAnalyzer,
])
->onlyMethods([
'getAppPath',
@ -331,6 +334,7 @@ class AppManagerTest extends TestCase {
$this->logger,
$this->serverVersion,
$this->configManager,
$this->dependencyAnalyzer,
])
->onlyMethods([
'getAppPath',
@ -394,6 +398,7 @@ class AppManagerTest extends TestCase {
$this->logger,
$this->serverVersion,
$this->configManager,
$this->dependencyAnalyzer,
])
->onlyMethods([
'getAppPath',
@ -600,6 +605,7 @@ class AppManagerTest extends TestCase {
$this->logger,
$this->serverVersion,
$this->configManager,
$this->dependencyAnalyzer,
])
->onlyMethods(['getAppInfo'])
->getMock();
@ -661,6 +667,7 @@ class AppManagerTest extends TestCase {
$this->logger,
$this->serverVersion,
$this->configManager,
$this->dependencyAnalyzer,
])
->onlyMethods(['getAppInfo'])
->getMock();
@ -801,6 +808,7 @@ class AppManagerTest extends TestCase {
$this->logger,
$this->serverVersion,
$this->configManager,
$this->dependencyAnalyzer,
])
->onlyMethods([
'getAppInfo',
@ -833,6 +841,7 @@ class AppManagerTest extends TestCase {
$this->logger,
$this->serverVersion,
$this->configManager,
$this->dependencyAnalyzer,
])
->onlyMethods([
'getAppInfo',
@ -864,6 +873,7 @@ class AppManagerTest extends TestCase {
$this->logger,
$this->serverVersion,
$this->configManager,
$this->dependencyAnalyzer,
])
->onlyMethods([
'getAppInfo',
@ -884,5 +894,4 @@ class AppManagerTest extends TestCase {
$manager->getAppVersion('unknown'),
);
}
}

@ -9,18 +9,13 @@ namespace Test\App;
use OC\App\DependencyAnalyzer;
use OC\App\Platform;
use OCP\IL10N;
use Test\TestCase;
class DependencyAnalyzerTest extends TestCase {
/** @var Platform|\PHPUnit\Framework\MockObject\MockObject */
private $platformMock;
/** @var IL10N */
private $l10nMock;
/** @var DependencyAnalyzer */
private $analyser;
private DependencyAnalyzer $analyser;
protected function setUp(): void {
$this->platformMock = $this->getMockBuilder(Platform::class)
@ -55,16 +50,7 @@ class DependencyAnalyzerTest extends TestCase {
->method('getOcVersion')
->willReturn('8.0.2');
$this->l10nMock = $this->getMockBuilder(IL10N::class)
->disableOriginalConstructor()
->getMock();
$this->l10nMock->expects($this->any())
->method('t')
->willReturnCallback(function ($text, $parameters = []) {
return vsprintf($text, $parameters);
});
$this->analyser = new DependencyAnalyzer($this->platformMock, $this->l10nMock);
$this->analyser = new DependencyAnalyzer($this->platformMock);
}
/**
@ -485,4 +471,286 @@ class DependencyAnalyzerTest extends TestCase {
[[], '5.4', '5.4', null],
];
}
public static function appVersionsProvider(): array {
return [
// exact match
[
'6.0.0.0',
[
'requiremin' => '6.0',
'requiremax' => '6.0',
],
true
],
// in-between match
[
'6.0.0.0',
[
'requiremin' => '5.0',
'requiremax' => '7.0',
],
true
],
// app too old
[
'6.0.0.0',
[
'requiremin' => '5.0',
'requiremax' => '5.0',
],
false
],
// app too new
[
'5.0.0.0',
[
'requiremin' => '6.0',
'requiremax' => '6.0',
],
false
],
// only min specified
[
'6.0.0.0',
[
'requiremin' => '6.0',
],
true
],
// only min specified fail
[
'5.0.0.0',
[
'requiremin' => '6.0',
],
false
],
// only min specified legacy
[
'6.0.0.0',
[
'require' => '6.0',
],
true
],
// only min specified legacy fail
[
'4.0.0.0',
[
'require' => '6.0',
],
false
],
// only max specified
[
'5.0.0.0',
[
'requiremax' => '6.0',
],
true
],
// only max specified fail
[
'7.0.0.0',
[
'requiremax' => '6.0',
],
false
],
// variations of versions
// single OC number
[
'4',
[
'require' => '4.0',
],
true
],
// multiple OC number
[
'4.3.1',
[
'require' => '4.3',
],
true
],
// single app number
[
'4',
[
'require' => '4',
],
true
],
// single app number fail
[
'4.3',
[
'require' => '5',
],
false
],
// complex
[
'5.0.0',
[
'require' => '4.5.1',
],
true
],
// complex fail
[
'4.3.1',
[
'require' => '4.3.2',
],
false
],
// two numbers
[
'4.3.1',
[
'require' => '4.4',
],
false
],
// one number fail
[
'4.3.1',
[
'require' => '5',
],
false
],
// pre-alpha app
[
'5.0.3',
[
'require' => '4.93',
],
true
],
// pre-alpha OC
[
'6.90.0.2',
[
'require' => '6.90',
],
true
],
// pre-alpha OC max
[
'6.90.0.2',
[
'requiremax' => '7',
],
true
],
// expect same major number match
[
'5.0.3',
[
'require' => '5',
],
true
],
// expect same major number match
[
'5.0.3',
[
'requiremax' => '5',
],
true
],
// dependencies versions before require*
[
'6.0.0.0',
[
'requiremin' => '5.0',
'requiremax' => '7.0',
'dependencies' => [
'owncloud' => [
'@attributes' => [
'min-version' => '7.0',
'max-version' => '7.0',
],
],
],
],
false
],
[
'6.0.0.0',
[
'requiremin' => '5.0',
'requiremax' => '7.0',
'dependencies' => [
'owncloud' => [
'@attributes' => [
'min-version' => '5.0',
'max-version' => '5.0',
],
],
],
],
false
],
[
'6.0.0.0',
[
'requiremin' => '5.0',
'requiremax' => '5.0',
'dependencies' => [
'owncloud' => [
'@attributes' => [
'min-version' => '5.0',
'max-version' => '7.0',
],
],
],
],
true
],
[
'9.2.0.0',
[
'dependencies' => [
'owncloud' => [
'@attributes' => [
'min-version' => '9.0',
'max-version' => '9.1',
],
],
'nextcloud' => [
'@attributes' => [
'min-version' => '9.1',
'max-version' => '9.2',
],
],
],
],
true
],
[
'9.2.0.0',
[
'dependencies' => [
'nextcloud' => [
'@attributes' => [
'min-version' => '9.1',
'max-version' => '9.2',
],
],
],
],
true
],
];
}
#[\PHPUnit\Framework\Attributes\DataProvider('appVersionsProvider')]
public function testServerVersion($ncVersion, $appInfo, $expectedResult): void {
$this->assertEquals($expectedResult, count($this->analyser->analyzeServerVersion($ncVersion, $appInfo, false)) === 0);
}
}

@ -9,6 +9,7 @@
namespace Test;
use OC\App\AppManager;
use OC\App\DependencyAnalyzer;
use OC\AppConfig;
use OC\Config\ConfigManager;
use OCP\EventDispatcher\IEventDispatcher;
@ -36,288 +37,6 @@ class AppTest extends \Test\TestCase {
public const TEST_GROUP1 = 'group1';
public const TEST_GROUP2 = 'group2';
public static function appVersionsProvider(): array {
return [
// exact match
[
'6.0.0.0',
[
'requiremin' => '6.0',
'requiremax' => '6.0',
],
true
],
// in-between match
[
'6.0.0.0',
[
'requiremin' => '5.0',
'requiremax' => '7.0',
],
true
],
// app too old
[
'6.0.0.0',
[
'requiremin' => '5.0',
'requiremax' => '5.0',
],
false
],
// app too new
[
'5.0.0.0',
[
'requiremin' => '6.0',
'requiremax' => '6.0',
],
false
],
// only min specified
[
'6.0.0.0',
[
'requiremin' => '6.0',
],
true
],
// only min specified fail
[
'5.0.0.0',
[
'requiremin' => '6.0',
],
false
],
// only min specified legacy
[
'6.0.0.0',
[
'require' => '6.0',
],
true
],
// only min specified legacy fail
[
'4.0.0.0',
[
'require' => '6.0',
],
false
],
// only max specified
[
'5.0.0.0',
[
'requiremax' => '6.0',
],
true
],
// only max specified fail
[
'7.0.0.0',
[
'requiremax' => '6.0',
],
false
],
// variations of versions
// single OC number
[
'4',
[
'require' => '4.0',
],
true
],
// multiple OC number
[
'4.3.1',
[
'require' => '4.3',
],
true
],
// single app number
[
'4',
[
'require' => '4',
],
true
],
// single app number fail
[
'4.3',
[
'require' => '5',
],
false
],
// complex
[
'5.0.0',
[
'require' => '4.5.1',
],
true
],
// complex fail
[
'4.3.1',
[
'require' => '4.3.2',
],
false
],
// two numbers
[
'4.3.1',
[
'require' => '4.4',
],
false
],
// one number fail
[
'4.3.1',
[
'require' => '5',
],
false
],
// pre-alpha app
[
'5.0.3',
[
'require' => '4.93',
],
true
],
// pre-alpha OC
[
'6.90.0.2',
[
'require' => '6.90',
],
true
],
// pre-alpha OC max
[
'6.90.0.2',
[
'requiremax' => '7',
],
true
],
// expect same major number match
[
'5.0.3',
[
'require' => '5',
],
true
],
// expect same major number match
[
'5.0.3',
[
'requiremax' => '5',
],
true
],
// dependencies versions before require*
[
'6.0.0.0',
[
'requiremin' => '5.0',
'requiremax' => '7.0',
'dependencies' => [
'owncloud' => [
'@attributes' => [
'min-version' => '7.0',
'max-version' => '7.0',
],
],
],
],
false
],
[
'6.0.0.0',
[
'requiremin' => '5.0',
'requiremax' => '7.0',
'dependencies' => [
'owncloud' => [
'@attributes' => [
'min-version' => '5.0',
'max-version' => '5.0',
],
],
],
],
false
],
[
'6.0.0.0',
[
'requiremin' => '5.0',
'requiremax' => '5.0',
'dependencies' => [
'owncloud' => [
'@attributes' => [
'min-version' => '5.0',
'max-version' => '7.0',
],
],
],
],
true
],
[
'9.2.0.0',
[
'dependencies' => [
'owncloud' => [
'@attributes' => [
'min-version' => '9.0',
'max-version' => '9.1',
],
],
'nextcloud' => [
'@attributes' => [
'min-version' => '9.1',
'max-version' => '9.2',
],
],
],
],
true
],
[
'9.2.0.0',
[
'dependencies' => [
'nextcloud' => [
'@attributes' => [
'min-version' => '9.1',
'max-version' => '9.2',
],
],
],
],
true
],
];
}
#[\PHPUnit\Framework\Attributes\DataProvider('appVersionsProvider')]
public function testIsAppCompatible($ocVersion, $appInfo, $expectedResult): void {
$this->assertEquals($expectedResult, \OC_App::isAppCompatible($ocVersion, $appInfo));
}
/**
* Tests that the app order is correct
*/
@ -572,6 +291,7 @@ class AppTest extends \Test\TestCase {
Server::get(LoggerInterface::class),
Server::get(ServerVersion::class),
Server::get(ConfigManager::class),
Server::get(DependencyAnalyzer::class),
));
}