19declare(strict_types=1);
32use PHPUnit\Framework\TestCase;
34class ImplementationOfAgentFinderTest
extends TestCase
36 private string $plugin_directory;
38 protected function setUp(): void
40 $this->plugin_directory = sys_get_temp_dir() .
"/ilias_agent_finder_" . bin2hex(random_bytes(4))
41 .
"/Customizing/global/plugins";
46 $root = dirname($this->plugin_directory, 3);
50 $files = new \RecursiveIteratorIterator(
51 new \RecursiveDirectoryIterator($root, \FilesystemIterator::SKIP_DOTS),
52 \RecursiveIteratorIterator::CHILD_FIRST
54 foreach (
$files as $file) {
55 $file->isDir() ? rmdir($file->getPathname()) : unlink($file->getPathname());
60 private function finder(
61 array $component_agents,
62 ?ImplementationOfInterfaceFinder $interface_finder =
null
63 ): ImplementationOfAgentFinder {
64 $refinery = new Refinery(new DataFactory(), $this->createStub(Language::class));
66 return new ImplementationOfAgentFinder(
69 $this->createStub(Language::class),
70 $interface_finder ?? $this->createStub(ImplementationOfInterfaceFinder::class),
72 $this->plugin_directory
76 private function createPlugin(
string $path,
string ...$agent_classes): void
78 mkdir($this->plugin_directory .
"/" . $path .
"/classes", 0777,
true);
79 foreach ($agent_classes as $class) {
80 $file = $this->plugin_directory .
"/" . $path .
"/classes/class.$class.php";
81 file_put_contents($file, sprintf(<<<
'PHP'
87 \
ILIAS\Data\Factory $data_factory,
103 private function ensureTestObjectiveClass(): void
105 if (class_exists(\TestPluginObjective::class,
false)) {
108 $file = sys_get_temp_dir() .
"/ilias_agent_finder_test_objective.php";
111 "<?php\n\nclass TestPluginObjective extends \\ILIAS\\Setup\\Objective\\NullObjective\n{\n}\n"
121 private function writePluginAgent(
string $plugin,
string $template): string
123 $class =
"il" .
$plugin .
"Agent" . bin2hex(random_bytes(4));
124 $dir = $this->plugin_directory .
"/Services/Repository/RepositoryObject/" .
$plugin .
"/classes";
126 mkdir($dir, 0777,
true);
128 $file = $dir .
"/class.$class.php";
129 file_put_contents($file,
"<?php\n\n" . sprintf($template, $class));
135 private function defaultAgentTemplate(): string
138 class %1$s extends \ilPluginDefaultAgent
142 \
ILIAS\Data\Factory $data_factory,
152 private function implementingAgentTemplate(): string
155 class %1$s implements \ILIAS\Setup\Agent
159 \
ILIAS\Data\Factory $data_factory,
164 public function hasConfig(): bool
169 public function getArrayToConfigTransformation(): \
ILIAS\Refinery\Transformation
171 throw new \LogicException(
'not needed');
174 public function getInstallObjective(?\
ILIAS\Setup\Config $config =
null): \
ILIAS\Setup\Objective
176 return new \TestPluginObjective();
179 public function getUpdateObjective(?\
ILIAS\Setup\Config $config =
null): \
ILIAS\Setup\Objective
181 return new \TestPluginObjective();
184 public function getBuildObjective(): \
ILIAS\Setup\Objective
186 return new \ILIAS\Setup\Objective\NullObjective();
189 public function getStatusObjective(\
ILIAS\Setup\Metrics\Storage $storage): \
ILIAS\Setup\Objective
191 return new \ILIAS\Setup\Objective\NullObjective();
194 public function getMigrations(): array
196 return [
'test_migration' =>
new class () implements \
ILIAS\Setup\Migration {
199 return 'test migration';
202 public function getDefaultAmountOfStepsPerRun():
int
207 public function getPreconditions(\
ILIAS\Setup\Environment $environment): array
212 public function prepare(\
ILIAS\Setup\Environment $environment): void
216 public function step(\
ILIAS\Setup\Environment $environment): void
220 public function getRemainingAmountOfSteps():
int
227 public function getNamedObjectives(?\
ILIAS\Setup\Config $config =
null): array
230 'test_objective' => new \ILIAS\Setup\ObjectiveConstructor(
232 static fn(): \
ILIAS\Setup\Objective =>
new \
ILIAS\Setup\Objective\NullObjective()
240 private function pluginFinder(array $agent_classes): ImplementationOfAgentFinder
242 $interface_finder = $this->createStub(ImplementationOfInterfaceFinder::class);
244 ->method(
'getMatchingClassNames')
245 ->willReturn(
new \ArrayIterator($agent_classes));
247 return $this->finder([], $interface_finder);
253 protected function flatten(Setup\Objective $objective): array
255 if (!$objective instanceof Setup\ObjectiveCollection) {
256 return [get_class($objective)];
259 return array_merge(...array_map(
260 fn(Setup\Objective $o): array => $this->flatten($o),
261 $objective->getObjectives()
265 public function testNamedAgentIsKeyedByItsDeclaredName(): void
267 $named = $this->createStub(NamedAgent::class);
268 $named->method(
'getAgentName')->willReturn(
'content_isolation');
270 $collection = $this->finder([$named])->getComponentAgents();
272 $this->assertSame($named, $collection->getAgent(
'content_isolation'));
275 public function testPlainAgentFallsBackToClassNameKeying(): void
277 $plain = $this->createStub(Agent::class);
279 $collection = $this->finder([$plain])->getComponentAgents();
282 $this->assertNull($collection->getAgent(
'content_isolation'));
283 $this->assertSame($plain, $collection->getAgent($plain::class));
286 public function testPluginsAreFoundFourLevelsBelowThePluginDirectory(): void
288 $this->createPlugin(
"Services/Repository/RepositoryObject/Alpha");
289 $this->createPlugin(
"Modules/Course/CourseHook/Beta");
290 mkdir($this->plugin_directory .
"/Services/Repository/RepositoryObject/.hidden", 0777,
true);
291 touch($this->plugin_directory .
"/Services/Repository/RepositoryObject/README.md");
293 $agents = $this->finder([])->getAgents();
295 $this->assertEqualsCanonicalizing([
'Alpha',
'Beta'], array_keys($agents->getAgents()));
296 $this->assertInstanceOf(\ilPluginDefaultAgent::class, $agents->getAgent(
'Alpha'));
299 public function testAgentsOfAllPluginsAreSearchedInOnePass(): void
301 $suffix = bin2hex(random_bytes(4));
302 $gamma =
"ilGammaSetupAgent$suffix";
303 $delta_1 =
"ilDeltaOneSetupAgent$suffix";
304 $delta_2 =
"ilDeltaTwoSetupAgent$suffix";
305 $this->createPlugin(
"Services/Repository/RepositoryObject/Gamma", $gamma);
306 $this->createPlugin(
"Services/Repository/RepositoryObject/Delta", $delta_1, $delta_2);
307 $this->createPlugin(
"Services/Repository/RepositoryObject/Epsilon");
309 $interface_finder = $this->createMock(ImplementationOfInterfaceFinder::class);
311 ->expects($this->once())
312 ->method(
'getMatchingClassNames')
313 ->willReturn(
new \ArrayIterator([$gamma, $delta_1, $delta_2]));
315 $agents = $this->finder([], $interface_finder)->getAgents();
317 $this->assertInstanceOf($gamma, $agents->getAgent(
'Gamma'));
318 $this->assertInstanceOf(AgentCollection::class, $agents->getAgent(
'Delta'));
319 $this->assertCount(2, $agents->getAgent(
'Delta')->getAgents());
320 $this->assertInstanceOf(\ilPluginDefaultAgent::class, $agents->getAgent(
'Epsilon'));
323 public function testPluginAgentIgnoresClassesOfOtherPlugins(): void
325 $suffix = bin2hex(random_bytes(4));
326 $zeta =
"ilZetaSetupAgent$suffix";
328 $this->createPlugin(
"Services/Repository/RepositoryObject/Eta/lib/Zeta", $zeta);
330 $interface_finder = $this->createStub(ImplementationOfInterfaceFinder::class);
331 $interface_finder->method(
'getMatchingClassNames')->willReturn(
new \ArrayIterator([$zeta]));
333 $agent = $this->finder([], $interface_finder)->getPluginAgent(
'Zeta');
335 $this->assertNotInstanceOf($zeta, $agent);
336 $this->assertInstanceOf(\ilPluginDefaultAgent::class, $agent);
339 public function testPluginWithoutAgentGetsTheDefaultAgent(): void
341 $agent = $this->pluginFinder([])->getPluginAgent(
'TestPlugin');
343 $this->assertInstanceOf(\ilPluginDefaultAgent::class, $agent);
344 $this->assertNotInstanceOf(\ilPluginAgentWithDefaultObjectives::class, $agent);
347 public function testAgentExtendingTheDefaultAgentIsUsedDirectly(): void
349 $class = $this->writePluginAgent(
'TestPlugin', $this->defaultAgentTemplate());
351 $agent = $this->pluginFinder([$class])->getPluginAgent(
'TestPlugin');
353 $this->assertInstanceOf($class, $agent);
356 public function testAgentImplementingAgentGetsTheDefaultObjectives(): void
358 $this->ensureTestObjectiveClass();
359 $class = $this->writePluginAgent(
'TestPlugin', $this->implementingAgentTemplate());
361 $agent = $this->pluginFinder([$class])->getPluginAgent(
'TestPlugin');
363 $this->assertInstanceOf(\ilPluginAgentWithDefaultObjectives::class, $agent);
365 $install_objectives = $this->flatten($agent->getInstallObjective());
366 $this->assertContains(\ilComponentInstallPluginObjective::class, $install_objectives);
367 $this->assertContains(\ilComponentActivatePluginsObjective::class, $install_objectives);
368 $this->assertContains(\TestPluginObjective::class, $install_objectives);
370 $update_objectives = $this->flatten($agent->getUpdateObjective());
371 $this->assertContains(\ilComponentUpdatePluginObjective::class, $update_objectives);
372 $this->assertNotContains(\ilComponentActivatePluginsObjective::class, $update_objectives);
373 $this->assertContains(\TestPluginObjective::class, $update_objectives);
376 public function testAgentImplementingAgentKeepsItsPaths(): void
378 $this->ensureTestObjectiveClass();
379 $class = $this->writePluginAgent(
'TestPlugin', $this->implementingAgentTemplate());
381 $agent = $this->pluginFinder([$class])->getPluginAgent(
'TestPlugin');
383 $this->assertSame([
'test_objective'], array_keys($agent->getNamedObjectives()));
384 $this->assertSame([
'test_migration'], array_keys($agent->getMigrations()));
385 $this->assertTrue($agent->hasConfig());
388 public function testSeveralAgentsAreCollectedUnderTheirShortClassNames(): void
390 $default_agent = $this->writePluginAgent(
'TestPlugin', $this->defaultAgentTemplate());
391 $implementing_agent = $this->writePluginAgent(
'TestPlugin', $this->implementingAgentTemplate());
393 $agent = $this->pluginFinder([$default_agent, $implementing_agent])->getPluginAgent(
'TestPlugin');
395 $this->assertInstanceOf(AgentCollection::class, $agent);
396 $this->assertInstanceOf($default_agent, $agent->getAgent($default_agent));
397 $this->assertInstanceOf($implementing_agent, $agent->getAgent($implementing_agent));
An agent that is just a collection of some other agents.
__construct()
Constructor setup ILIAS global object @access public.
An agent that declares its own name in the agent collection.
__construct(Container $dic, ilPlugin $plugin)
@inheritDoc
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
Interface Observer \BackgroundTasks Contains several chained tasks and infos about them.