Skip to content

Commit 4ef55d5

Browse files
committed
Allow all components to be loaded from file instead of calculated
A new phpcs config setting: moodleComponentsListPath Enables the possibility of loading the list of components from a file, instead of calculating them from codebase. This is interesting when the complete codebase is not available, like happens for example with CiBoT, that only keeps the files modified in place, to reduce the stuff being checked. The format of the components file is the same than the moodle-local_ci/list_valid_components one: [plugin|subsystem],component_name,component_full_path Covered with unit tests, will be used in the remote branch checker (a.k.a. CiBoT). If the setting is not configured then the standard load of components from codebase (core_component execution) is performed.
1 parent dce0fbe commit 4ef55d5

File tree

2 files changed

+88
-0
lines changed

2 files changed

+88
-0
lines changed

moodle/Util/MoodleUtil.php

+41
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,47 @@ protected static function calculateAllComponents(string $moodleRoot): ?array {
9393

9494
// We haven't the components yet, let's calculate all them.
9595

96+
// First, try to get it from configuration/runtime option.
97+
// This accepts the full path to a file like the one generated
98+
// by moodle-local_ci/list_valid_components, which format is:
99+
// [plugin|subsystem],component_name,component_full_path.
100+
// Useful to load them when not all the code base is available
101+
// like it happens with CiBoT runs, for example.
102+
if ($componentsFile = Config::getConfigData('moodleComponentsListPath')) {
103+
if (!is_readable($componentsFile)) {
104+
throw new DeepExitException(
105+
"ERROR: Incorrect 'moodleComponentsListPath' config/runtime option. File not found: '$componentsFile'", 3);
106+
}
107+
// Go processing the file.
108+
$handle = fopen($componentsFile, "r");
109+
if ($handle) {
110+
while (($line = fgets($handle)) !== false) {
111+
$aline = explode(',', trim($line));
112+
// Exclude any line not starting by plugin|sybsystem.
113+
if ($aline[0] !== 'plugin' && $aline[0] !== 'subsystem') {
114+
continue;
115+
}
116+
// Exclude any component not being valid one.
117+
if (!preg_match('/^[a-z][a-z0-9]*(_[a-z][a-z0-9_]*)?[a-z0-9]+$/', $aline[1])) {
118+
continue;
119+
}
120+
// Exclude any path not being under Mooddle dirroot.
121+
if (strpos($aline[2], $moodleRoot) !== 0) {
122+
continue;
123+
}
124+
// Arrived here, it's a valid line, annotate the component.
125+
self::$moodleComponents[$aline[1]] = $aline[2];
126+
}
127+
fclose($handle);
128+
}
129+
// Let's sort the array in ascending order, so more specific matches first.
130+
arsort(self::$moodleComponents);
131+
132+
return self::$moodleComponents;
133+
}
134+
135+
// Let's try to get the components from core.
136+
96137
// Verify that core_component class is already available.
97138
// Make an exception for PHPUnit runs, to be able to test everything
98139
// because within tests it's always available and never invoked.

moodle/tests/moodleutil_test.php

+47
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
namespace local_codechecker;
1818

1919
use MoodleCodeSniffer\moodle\Util\MoodleUtil;
20+
use org\bovigo\vfs\vfsStream;
2021
use PHP_CodeSniffer\Config;
2122
use PHP_CodeSniffer\Exceptions\DeepExitException;
2223
use PHP_CodeSniffer\Files\File;
@@ -41,6 +42,52 @@
4142
*/
4243
class moodleutil_test extends local_codechecker_testcase {
4344

45+
/**
46+
* Unit test for calculateAllComponents.
47+
*
48+
* Not 100% orthodox because {@see calculateAllComponents()} is protected,
49+
* and it's already indirectly tested by {@see test_getMoodleComponent()}
50+
* but it has some feature that we need to test individually here.
51+
*/
52+
public function test_calculateAllComponents() {
53+
// Let's calculate moodleRoot.
54+
$moodleRoot = MoodleUtil::getMoodleRoot();
55+
56+
// Let's prepare a components file, with some correct and incorrect entries.
57+
$components =
58+
"nonono,mod_forum,{$moodleRoot}/mod_forum\n" . // Wrong type.
59+
"plugin,mod__nono,{$moodleRoot}/mod_forum\n" . // Wrong component.
60+
"plugin,mod_forum,/no/no/no/no//mod_forum\n" . // Wrong path.
61+
"plugin,local_codechecker,{$moodleRoot}/local/codechecker\n" .// All ok.
62+
"plugin,mod_forum,{$moodleRoot}/mod/forum\n"; // All ok.
63+
64+
// Let's use virtual filesystem instead of real one.
65+
$vfs = vfsStream::setup('root', null, ['components.txt' => $components]);
66+
67+
// Set codechecker config to point to it.
68+
Config::setConfigData('moodleComponentsListPath', $vfs->url() . '/components.txt', true);
69+
70+
// Let's run calculateAllComponents() and evaluate results.
71+
$method = new \ReflectionMethod(MoodleUtil::class, 'calculateAllComponents');
72+
$method->setAccessible(true);
73+
$method->invokeArgs(null, [$moodleRoot]);
74+
75+
// Let's inspect which components have been loaded.
76+
$property = new \ReflectionProperty(MoodleUtil::class, 'moodleComponents');
77+
$property->setAccessible(true);
78+
$loadedComponents = $property->getValue();
79+
80+
$this->assertCount(2, $loadedComponents);
81+
$this->assertSame(['mod_forum', 'local_codechecker'],
82+
array_keys($loadedComponents)); // Verify they are ordered in ascending order.
83+
$this->assertSame(["{$moodleRoot}/mod/forum", "{$moodleRoot}/local/codechecker"],
84+
array_values($loadedComponents)); // Verify component paths are also the expected ones.
85+
86+
// Ensure cached information doesn't affect other tests.
87+
$this->cleanMoodleUtilCaches();
88+
Config::setConfigData('moodleComponentsListPath', null, true);
89+
}
90+
4491
/**
4592
* Provider for test_getMoodleComponent.
4693
*/

0 commit comments

Comments
 (0)