From 8b2d4c362c77530b5c7d487ccce0e9caac7b92f2 Mon Sep 17 00:00:00 2001 From: Ruslan Kabalin Date: Fri, 11 Oct 2024 22:17:32 +0100 Subject: [PATCH 1/2] Adjust filter plugin requirements to comply with 4.5. Per https://moodledev.io/docs/4.5/devupdate#filter-plugins Fixes #321 --- .../Requirements/AbstractRequirements.php | 12 ++++ .../Requirements/FilterRequirements.php | 21 +++++-- .../Requirements/FilterRequirementsTest.php | 62 ++++++++++++++++--- 3 files changed, 82 insertions(+), 13 deletions(-) diff --git a/src/PluginValidate/Requirements/AbstractRequirements.php b/src/PluginValidate/Requirements/AbstractRequirements.php index 9978dd2f..32c1e154 100644 --- a/src/PluginValidate/Requirements/AbstractRequirements.php +++ b/src/PluginValidate/Requirements/AbstractRequirements.php @@ -64,6 +64,18 @@ protected function behatTagsFactory(array $tags): array return $fileTokens; } + /** + * Helper method to check file existence. + * + * @param string $file + * + * @return bool + */ + protected function fileExists(string $file): bool + { + return file_exists($this->plugin->directory . '/' . $file); + } + /** * An array of required files, paths are relative to the plugin directory. * diff --git a/src/PluginValidate/Requirements/FilterRequirements.php b/src/PluginValidate/Requirements/FilterRequirements.php index cbed68db..c6c2ed90 100644 --- a/src/PluginValidate/Requirements/FilterRequirements.php +++ b/src/PluginValidate/Requirements/FilterRequirements.php @@ -21,15 +21,28 @@ class FilterRequirements extends GenericRequirements { public function getRequiredFiles(): array { - return array_merge(parent::getRequiredFiles(), [ - 'filter.php', - ]); + $files = []; + if ($this->moodleVersion >= 405) { + $files[] = 'classes/text_filter.php'; + } else { + // This must exist in 4.5 if plugin supports older version, but we don't identify support range to validate it. + $files[] = 'filter.php'; + } + + return array_merge(parent::getRequiredFiles(), $files); } public function getRequiredClasses(): array { + if ($this->moodleVersion <= 404 && !$this->fileExists('classes/text_filter.php')) { + // Plugin does not support 4.5, check class presence in filter.php + return [ + FileTokens::create('filter.php')->mustHave('filter_' . $this->plugin->name), + ]; + } + return [ - FileTokens::create('filter.php')->mustHave('filter_' . $this->plugin->name), + FileTokens::create('classes/text_filter.php')->mustHave("filter_{$this->plugin->name}\\text_filter"), ]; } diff --git a/tests/PluginValidate/Requirements/FilterRequirementsTest.php b/tests/PluginValidate/Requirements/FilterRequirementsTest.php index de2b2d4d..7f44ed44 100644 --- a/tests/PluginValidate/Requirements/FilterRequirementsTest.php +++ b/tests/PluginValidate/Requirements/FilterRequirementsTest.php @@ -23,14 +23,21 @@ class FilterRequirementsTest extends \PHPUnit\Framework\TestCase */ private $requirements; + /** + * @var FilterRequirements + */ + private $requirements404; + protected function setUp(): void { - $this->requirements = new FilterRequirements(new Plugin('filter_activitynames', 'filter', 'activitynames', ''), 29); + $this->requirements404 = new FilterRequirements(new Plugin('filter_activitynames', 'filter', 'activitynames', ''), 404); + $this->requirements = new FilterRequirements(new Plugin('filter_activitynames', 'filter', 'activitynames', ''), 405); } protected function tearDown(): void { - $this->requirements = null; + $this->requirements404 = null; + $this->requirements = null; } public function testResolveRequirements() @@ -39,29 +46,66 @@ public function testResolveRequirements() $this->assertInstanceOf( 'MoodlePluginCI\PluginValidate\Requirements\FilterRequirements', - $resolver->resolveRequirements(new Plugin('', 'filter', '', ''), 29) + $resolver->resolveRequirements(new Plugin('', 'filter', '', ''), 404) ); } + public function testGetRequiredFiles404() + { + $files = $this->requirements404->getRequiredFiles(); + + $this->assertTrue(in_array('filter.php', $files, true)); + $this->assertFalse(in_array('classes/text_filter.php', $files, true)); + foreach ($files as $file) { + $this->assertIsString($file); + } + } + public function testGetRequiredFiles() { $files = $this->requirements->getRequiredFiles(); - $this->assertNotEmpty($files); - $this->assertTrue(in_array('filter.php', $files, true)); + $this->assertFalse(in_array('filter.php', $files, true)); + $this->assertTrue(in_array('classes/text_filter.php', $files, true)); foreach ($files as $file) { $this->assertIsString($file); } } + public function testGetRequiredClasses404() + { + $requirements = $this->getMockBuilder('MoodlePluginCI\PluginValidate\Requirements\FilterRequirements') + ->setConstructorArgs([new Plugin('filter_activitynames', 'filter', 'activitynames', ''), 404]) + ->onlyMethods(['fileExists']) + ->getMock(); + // On first call fileExists return false, on second call return true. + $requirements->method('fileExists') + ->with($this->identicalTo('classes/text_filter.php')) + ->willReturn(false, true); + + // If classes/text_filter.php does not exist, expect class presence in filter.php. + $classes = $requirements->getRequiredClasses(); + $this->assertCount(1, $classes); + $class = reset($classes); + $this->assertInstanceOf('MoodlePluginCI\PluginValidate\Finder\FileTokens', $class); + $this->assertSame('filter.php', $class->file); + + // If classes/text_filter.php exists, expect class presence in it (4.5 plugin backward compatibility). + $classes = $requirements->getRequiredClasses(); + $this->assertCount(1, $classes); + $class = reset($classes); + $this->assertInstanceOf('MoodlePluginCI\PluginValidate\Finder\FileTokens', $class); + $this->assertSame('classes/text_filter.php', $class->file); + } + public function testGetRequiredClasses() { $classes = $this->requirements->getRequiredClasses(); - $this->assertNotEmpty($classes); - foreach ($classes as $class) { - $this->assertInstanceOf('MoodlePluginCI\PluginValidate\Finder\FileTokens', $class); - } + $this->assertCount(1, $classes); + $class = reset($classes); + $this->assertInstanceOf('MoodlePluginCI\PluginValidate\Finder\FileTokens', $class); + $this->assertSame('classes/text_filter.php', $class->file); } public function testGetRequiredStrings() From 2b36993a5017c3050cfaab2406ca45e2b6b7df9d Mon Sep 17 00:00:00 2001 From: Ruslan Kabalin Date: Thu, 10 Oct 2024 12:44:03 +0100 Subject: [PATCH 2/2] Implement getRequiredFunctionCalls check and use it in filter plugin validation. This address scenario where file is supposed to contain certain function call, such as `class_alias` in Filter plugin type backward compatibility support per https://moodledev.io/docs/4.5/devupdate#filter-plugins The patch makes possible for deleveloper to specify: * getRequiredFunctionCalls to make sure file contains function call as name suggests. * FileTokens::notFoundHint to give some context for requirement to improve developer experience. This works with FileTokens in any other validation methods. --- .php-cs-fixer.cache | 1 + .phpunit.result.cache | 1 + docs/CHANGELOG.md | 6 +++ src/Parser/StatementFilter.php | 22 ++++++++++ src/PluginValidate/Finder/FileTokens.php | 31 +++++++++++++ .../Finder/FunctionCallFinder.php | 37 ++++++++++++++++ src/PluginValidate/PluginValidate.php | 5 +++ .../Requirements/AbstractRequirements.php | 7 +++ .../Requirements/FilterRequirements.php | 11 +++++ .../Requirements/GenericRequirements.php | 5 +++ tests/Fixture/moodle-local_ci/lib.php | 5 +++ .../Finder/FunctionCallFinderTest.php | 44 +++++++++++++++++++ .../Requirements/FilterRequirementsTest.php | 33 ++++++++++++++ 13 files changed, 208 insertions(+) create mode 100644 .php-cs-fixer.cache create mode 100644 .phpunit.result.cache create mode 100644 src/PluginValidate/Finder/FunctionCallFinder.php create mode 100644 tests/PluginValidate/Finder/FunctionCallFinderTest.php diff --git a/.php-cs-fixer.cache b/.php-cs-fixer.cache new file mode 100644 index 00000000..2a9572bb --- /dev/null +++ b/.php-cs-fixer.cache @@ -0,0 +1 @@ +{"php":"8.2.20","version":"3.59.3:v3.59.3#30ba9ecc2b0e5205e578fe29973c15653d9bfd29","indent":" ","lineEnding":"\n","rules":{"align_multiline_comment":true,"array_syntax":{"syntax":"short"},"backtick_to_shell_exec":true,"binary_operator_spaces":{"operators":{"=":"align","=>":"align"}},"blank_line_before_statement":{"statements":["return"]},"braces_position":{"allow_single_line_anonymous_functions":true,"allow_single_line_empty_anonymous_classes":true},"class_attributes_separation":{"elements":{"method":"one"}},"class_definition":{"single_line":true},"class_reference_name_casing":true,"clean_namespace":true,"concat_space":{"spacing":"one"},"declare_parentheses":true,"echo_tag_syntax":true,"empty_loop_body":{"style":"braces"},"empty_loop_condition":true,"fully_qualified_strict_types":true,"function_declaration":true,"general_phpdoc_tag_rename":{"replacements":{"inheritDocs":"inheritDoc"}},"global_namespace_import":{"import_classes":false,"import_constants":false,"import_functions":false},"include":true,"increment_style":true,"integer_literal_case":true,"lambda_not_used_import":true,"linebreak_after_opening_tag":true,"magic_constant_casing":true,"magic_method_casing":true,"method_argument_space":{"on_multiline":"ignore"},"native_function_casing":true,"native_type_declaration_casing":true,"no_alias_language_construct_call":true,"no_alternative_syntax":true,"no_binary_string":true,"no_blank_lines_after_phpdoc":true,"no_empty_comment":true,"no_empty_phpdoc":true,"no_empty_statement":true,"no_extra_blank_lines":{"tokens":["break","continue","extra","return","throw","use","parenthesis_brace_block","square_brace_block","curly_brace_block"]},"no_leading_namespace_whitespace":true,"no_mixed_echo_print":true,"no_multiline_whitespace_around_double_arrow":true,"no_null_property_initialization":true,"no_short_bool_cast":true,"no_singleline_whitespace_before_semicolons":true,"no_spaces_around_offset":true,"no_trailing_comma_in_singleline":true,"no_unneeded_braces":{"namespaces":true},"no_unneeded_control_parentheses":{"statements":["break","clone","continue","echo_print","others","return","switch_case","yield","yield_from"]},"no_unneeded_import_alias":true,"no_unset_cast":true,"no_unused_imports":true,"no_useless_concat_operator":true,"no_useless_nullsafe_operator":true,"no_whitespace_before_comma_in_array":true,"normalize_index_brace":true,"nullable_type_declaration":true,"nullable_type_declaration_for_default_null_value":true,"object_operator_without_whitespace":true,"operator_linebreak":{"only_booleans":true},"ordered_imports":true,"ordered_types":{"null_adjustment":"always_last","sort_algorithm":"none"},"php_unit_fqcn_annotation":true,"php_unit_method_casing":true,"phpdoc_align":true,"phpdoc_annotation_without_dot":true,"phpdoc_indent":true,"phpdoc_inline_tag_normalizer":true,"phpdoc_no_access":true,"phpdoc_no_alias_tag":true,"phpdoc_no_package":true,"phpdoc_no_useless_inheritdoc":true,"phpdoc_order":true,"phpdoc_return_self_reference":true,"phpdoc_scalar":true,"phpdoc_separation":{"groups":[["Annotation","NamedArgumentConstructor","Target"],["author","copyright","license"],["category","package","subpackage"],["property","property-read","property-write"],["deprecated","link","see","since"]]},"phpdoc_single_line_var_spacing":true,"phpdoc_summary":true,"phpdoc_tag_type":{"tags":{"inheritDoc":"inline"}},"phpdoc_to_comment":true,"phpdoc_trim":true,"phpdoc_trim_consecutive_blank_line_separation":true,"phpdoc_types":true,"phpdoc_types_order":{"null_adjustment":"always_last","sort_algorithm":"none"},"phpdoc_var_without_name":true,"semicolon_after_instruction":true,"simple_to_complex_string_variable":true,"single_class_element_per_statement":true,"single_import_per_statement":true,"single_line_comment_spacing":true,"single_line_comment_style":{"comment_types":["hash"]},"single_line_throw":true,"single_quote":true,"single_space_around_construct":true,"space_after_semicolon":{"remove_in_empty_for_expressions":true},"standardize_increment":true,"standardize_not_equals":true,"statement_indentation":{"stick_comment_to_next_continuous_control_statement":true},"switch_continue_to_break":true,"trailing_comma_in_multiline":true,"trim_array_spaces":true,"type_declaration_spaces":true,"types_spaces":true,"unary_operator_spaces":true,"whitespace_after_comma_in_array":true,"array_indentation":true,"cast_spaces":true,"blank_line_after_opening_tag":true,"blank_line_between_import_groups":true,"blank_lines_before_namespace":true,"compact_nullable_type_declaration":true,"declare_equal_normalize":true,"lowercase_cast":true,"lowercase_static_reference":true,"new_with_parentheses":true,"no_blank_lines_after_class_opening":true,"no_leading_import_slash":true,"no_whitespace_in_blank_line":true,"ordered_class_elements":{"order":["use_trait"]},"return_type_declaration":true,"short_scalar_cast":true,"single_trait_insert_per_statement":true,"ternary_operator_spaces":true,"visibility_required":true,"blank_line_after_namespace":true,"constant_case":true,"control_structure_braces":true,"control_structure_continuation_position":true,"elseif":true,"indentation_type":true,"line_ending":true,"lowercase_keywords":true,"no_break_comment":true,"no_closing_tag":true,"no_multiple_statements_per_line":true,"no_space_around_double_colon":true,"no_spaces_after_function_name":true,"no_trailing_whitespace":true,"no_trailing_whitespace_in_comment":true,"single_blank_line_at_eof":true,"single_line_after_imports":true,"spaces_inside_parentheses":true,"switch_case_semicolon_to_colon":true,"switch_case_space":true,"encoding":true,"full_opening_tag":true,"array_push":true,"combine_nested_dirname":true,"dir_constant":true,"ereg_to_preg":true,"error_suppression":true,"fopen_flag_order":true,"fopen_flags":{"b_mode":false},"function_to_constant":true,"get_class_to_class_keyword":true,"implode_call":true,"is_null":true,"logical_operators":true,"long_to_shorthand_operator":true,"modernize_types_casting":true,"no_alias_functions":true,"no_homoglyph_names":true,"no_php4_constructor":true,"no_unneeded_final_method":true,"no_useless_sprintf":true,"non_printable_character":true,"ordered_traits":true,"php_unit_construct":true,"php_unit_mock_short_will_return":true,"php_unit_set_up_tear_down_visibility":true,"php_unit_test_annotation":true,"psr_autoloading":true,"self_accessor":true,"set_type_to_cast":true,"string_length_to_empty":true,"string_line_ending":true,"ternary_to_elvis_operator":true,"pow_to_exponentiation":true,"no_trailing_whitespace_in_string":true,"no_unreachable_default_argument_value":true,"combine_consecutive_unsets":true,"combine_consecutive_issets":true,"general_phpdoc_annotation_remove":{"annotations":["expectedException","expectedExceptionMessage","expectedExceptionMessageRegExp"]},"heredoc_to_nowdoc":true,"no_useless_else":true,"no_useless_return":true,"php_unit_strict":true,"phpdoc_add_missing_param_annotation":true,"strict_comparison":true,"strict_param":true,"compact_nullable_typehint":true},"hashes":{"bin\/validate-version":"9e7f56ea9df7a5e0fea9e919e735de23","bin\/moodle-plugin-ci":"c53f14adffa4228484f36d4342dc0f25","src\/Command\/BehatCommand.php":"eb2ead466d31bd11593758f16b506408","src\/Command\/SavePointsCommand.php":"bd24543b89b4a482327920fa8289c187","src\/Command\/ExecuteTrait.php":"51cf9c9fce9046d0f4489b7f4357a1c8","src\/Command\/MustacheCommand.php":"104dad5f19fb42b5f7d230a88df61f74","src\/Command\/AbstractMoodleCommand.php":"54cd940972aaf038a74c1c78b14cb36d","src\/Command\/AddPluginCommand.php":"e17f51a86ea9d829de46ed3df9a4774e","src\/Command\/MessDetectorCommand.php":"f0d5242d387c5c849a6fac29ac6ab201","src\/Command\/SelfUpdateCommand.php":"57f649f3a72639edccef8bff2be3aacc","src\/Command\/PHPDocCommand.php":"dd232a9b6b96ff67e74eb7a27f507c0c","src\/Command\/PHPLintCommand.php":"e4de88ab3b2d18de9b514359db01cf25","src\/Command\/ValidateCommand.php":"4b9cb6ff86238f22a1d1973e6230a532","src\/Command\/CodeCheckerCommand.php":"d75242d5ea91c451693494cc1854156a","src\/Command\/ParallelCommand.php":"f7067386e9ab0c4c84ce5038a88583c3","src\/Command\/CopyPasteDetectorCommand.php":"3fd08493e571d6bc839a4d39e756b595","src\/Command\/CoverallsUploadCommand.php":"831f7b7efd0eeb1904640451d0e49e73","src\/Command\/AbstractPluginCommand.php":"2f14f652a8bde5df8b31da8d04f51e7a","src\/Command\/CodeFixerCommand.php":"a866211a5233ee69a1103bff31ea7917","src\/Command\/MoodleOptionTrait.php":"65360706a32ba87cb3984cf13d875627","src\/Command\/InstallCommand.php":"148a39f7e1a2014334f070cd68ccb79d","src\/Command\/GruntCommand.php":"7e0fa5d5ed2d26f114b7efe69edbd00c","src\/Command\/AddConfigCommand.php":"13875e522642cfcfddc37a408fd84adb","src\/Command\/PHPUnitCommand.php":"e98213c9fefbe48c4045ebd0367df8a7","src\/Bridge\/MessDetectorRenderer.php":"9bf2c3b072f4a82583cf14e7de1af0a9","src\/Bridge\/Vendors.php":"ee988f039bc4e340fb569511d0fa10ee","src\/Bridge\/MoodlePlugin.php":"7fe84e546ff1ba653764b035b2798be0","src\/Bridge\/MoodlePluginCollection.php":"38d91c73d4cbe306606f2ef0c3dab77d","src\/Bridge\/MoodleConfig.php":"70d43155633b325fe30f10e966866ac5","src\/Bridge\/Moodle.php":"7226aaa358e1f471f83e15093d13b062","src\/Validate.php":"20fab527ac2d155a45dbd6c7d0fa0840","src\/Model\/GruntTaskModel.php":"e30732cf1b5e95c150a2cd592666e372","src\/Installer\/AbstractInstaller.php":"c72ee8e22bcebf018676bc1579bf06d4","src\/Installer\/TestSuiteInstaller.php":"4ccbb6e5302618323e56bb07c0871258","src\/Installer\/MoodleAppInstaller.php":"6a25d31a0a8bbce89ca73b1d67cb2e7a","src\/Installer\/Install.php":"cdc927d1186ea565b8caa43f3a6f0d6d","src\/Installer\/InstallerFactory.php":"cb6165d55b739596451df0e4e27cd87d","src\/Installer\/VendorInstaller.php":"1d78baee04dd4aa1ae84860346a95421","src\/Installer\/InstallerCollection.php":"ccb4baed4c5d16159344443a2dcdce77","src\/Installer\/Database\/PostgresDatabase.php":"c466e0e568115dcd4f86f1037938ecce","src\/Installer\/Database\/MySQLDatabase.php":"eb375f2facad6630469f9490f5a95830","src\/Installer\/Database\/MariaDBDatabase.php":"60537cd88c7f9d5877816bf31c835e04","src\/Installer\/Database\/DatabaseResolver.php":"ca50e581886f40b01d7cae6c36165733","src\/Installer\/Database\/AbstractDatabase.php":"0c24f605453004fdcc67060f0d73db37","src\/Installer\/InstallOutput.php":"ba0b2bdae039fd920a73456458c712f1","src\/Installer\/MoodleInstaller.php":"ce89eac14f025161d707d74f67d70a57","src\/Installer\/EnvDumper.php":"637b031b1c9cb7c70ace666dd613c3cf","src\/Installer\/ConfigDumper.php":"1c93bd8119ecadf0844848b0620e823d","src\/Installer\/PluginInstaller.php":"05bbeb6059ab38b35d42e69a5d18c6a2","src\/PluginValidate\/Requirements\/RepositoryRequirements.php":"819673dd500e0295eebcb9a92ae36e63","src\/PluginValidate\/Requirements\/ThemeRequirements.php":"43262e8d4ea587da75443f5ab0058965","src\/PluginValidate\/Requirements\/AbstractRequirements.php":"16361f455cca8453cc460e8edd13ece3","src\/PluginValidate\/Requirements\/ModuleRequirements.php":"ee27e8358e44b673852f6f18bcc4a5cd","src\/PluginValidate\/Requirements\/GenericRequirements.php":"06f365d731804e99edae3e6e1a258fe8","src\/PluginValidate\/Requirements\/DataformatRequirements.php":"27173dfc355463a2e0183852fea03f05","src\/PluginValidate\/Requirements\/RequirementsResolver.php":"8f746f5d92375a280c1bbb21b7a59ad5","src\/PluginValidate\/Requirements\/FormatRequirements.php":"c5f6f3fce3f5c05492b88580088d81f5","src\/PluginValidate\/Requirements\/AuthRequirements.php":"9af546f3c1b648fc98f332846bd37928","src\/PluginValidate\/Requirements\/BlockRequirements.php":"cfec8163ee0d98d23ad87384bb849dac","src\/PluginValidate\/Requirements\/QuestionRequirements.php":"0e8dfe3ede3b1c04768f48d42c4448d0","src\/PluginValidate\/Requirements\/FilterRequirements.php":"6e0338a0f746a880006742fe56880274","src\/PluginValidate\/Finder\/ClassFinder.php":"d4aa3051df4a564124d8a7f5e3ed35c6","src\/PluginValidate\/Finder\/BehatTagFinder.php":"4d530fb2966db146be835f1ae126a68f","src\/PluginValidate\/Finder\/AbstractParserFinder.php":"97b49065ce8ec9b9238a7609da2917bd","src\/PluginValidate\/Finder\/CapabilityFinder.php":"325e5d859b3e5b816636c87113e1f33b","src\/PluginValidate\/Finder\/FunctionFinder.php":"44096751480c3e8bf78dc2692a230ecf","src\/PluginValidate\/Finder\/FunctionCallFinder.php":"21f3ac114e030775be0c8d433b01f008","src\/PluginValidate\/Finder\/TablePrefixFinder.php":"67a5aebd2cc66f6a7f919512695a7ac9","src\/PluginValidate\/Finder\/Token.php":"8a3948831b41e8434664b7f18713a9bc","src\/PluginValidate\/Finder\/FileTokens.php":"e16363539617419c0ca55e9989fc49a1","src\/PluginValidate\/Finder\/LangFinder.php":"93503f82ea89eee41b6f38fa85b81579","src\/PluginValidate\/Finder\/TableFinder.php":"ac3fe083f82f224eb10ada5451064086","src\/PluginValidate\/Finder\/FinderInterface.php":"d2dcb1a8cfd39767d3a67ae33f5e0c77","src\/PluginValidate\/PluginValidate.php":"5594cb003542b155d3576854e7cbe742","src\/PluginValidate\/Plugin.php":"8a424f043a8acde96b11006a6a437f75","src\/StandardResolver.php":"cbcb9ea625e8561ce4ae1c9251968bbf","src\/Parser\/CodeParser.php":"ba2bbc0cd768cf9fd9578a137d234eab","src\/Parser\/StatementFilter.php":"2c24df8087a4bb911db68277c4f604ff","src\/Process\/MoodleProcess.php":"b21d609e14ccc30ad0d5e24f873ab408","src\/Process\/MoodlePhpException.php":"6a3d71f771ae4d75d857ec3636b66a3f","src\/Process\/Execute.php":"9854b3c654ea12a3ca1361c6a963460d","src\/Process\/MoodleDebugException.php":"654ffa1edc6cc39312965e313ac48455","tests\/Command\/AddPluginCommandTest.php":"db7f0f90384dc73e2188039a571d90a8","tests\/Command\/ValidateCommandTest.php":"8fc95388b8111b98bfcc3efcb8598c7b","tests\/Command\/CodeCheckerCommandTest.php":"8a99f81bcd0b3298eba01c372410fb1d","tests\/Command\/SelfUpdateCommandTest.php":"58dd80fdb31f7295e1d198c5cc254878","tests\/Command\/MessDetectorCommandTest.php":"bfdfe2c126897cc7aa4ee5a1c0704e92","tests\/Command\/CoverallsUploadCommandTest.php":"3e2b5d32189a7645fbf9402d8421fa31","tests\/Command\/ParallelCommandTest.php":"b990eaab840f672c0b43dc882aebe989","tests\/Command\/PHPDocCommandTest.php":"b76650de60e28c00ea19b950d4e58dec","tests\/Command\/CopyPasteDetectorCommandTest.php":"ff352ee76bc3fe8812bc82b5ba8b9c28","tests\/Command\/AddConfigCommandTest.php":"aa12b6c54c1c45a8b1684673d09db36b","tests\/Command\/CodeFixerCommandTest.php":"79119c6b21ca6b2c9d51fb8e86966e99","tests\/Command\/SavePointsCommandTest.php":"edf0a7909fd3e0ed80c169021f06c82f","tests\/Command\/InstallCommandTest.php":"e5e4622a0a858dcb9a8bf412cd9afdc7","tests\/Command\/PHPUnitCommandTest.php":"8aad1c3bf20e1fab9d9b61c4cd9a7d63","tests\/Command\/GruntCommandTest.php":"209456542bcd044b08d224baf4f3726a","tests\/Command\/BehatCommandTest.php":"4406852d1eef556e4d998eab0a3447aa","tests\/Command\/MustacheCommandTest.php":"da798f843a2621191957d8df8572b2e0","tests\/Command\/PHPLintCommandTest.php":"2b0e0c943339b870c04959213dccc3b1","tests\/ValidateTest.php":"fd96b127a3c547d8f50d60b8546581a7","tests\/Fake\/Bridge\/DummyMoodlePlugin.php":"5ab1727ee6a319fee46616395f08574f","tests\/Fake\/Bridge\/DummyMoodle311.php":"5c0bc07a5320349873641e302c68459b","tests\/Fake\/Bridge\/DummyMoodle.php":"8f4af8a33f1ac8434355b63df521c98f","tests\/Fake\/Bridge\/DummyMoodlePHPDoc.php":"61e87a473bd048323dece3f02ab0d82e","tests\/Fake\/Installer\/DummyInstaller.php":"b2d540b2484801b4cb60c896c7ae720f","tests\/Fake\/Installer\/DummyInstall.php":"b7b94082e75e520518d9f208a3fa4424","tests\/Fake\/Process\/DummyExecute.php":"6de928547add817ba7916c62bf7c9fcf","tests\/Bridge\/MoodlePluginCollectionTest.php":"4c9d0137585ef56af4d9021cd1b790f0","tests\/Bridge\/MoodlePluginTest.php":"27887e3faff0a72524126b01bb347a4a","tests\/Bridge\/MoodleConfigTest.php":"0f92b5349c99bb05f77bfeee3e7c8e84","tests\/Bridge\/MoodleTest.php":"a84904052c26e1ea27815443469f0b9e","tests\/Installer\/TestSuiteInstallerTest.php":"5efb0e1c0ec004300d9a1f200d90c204","tests\/Installer\/InstallerCollectionTest.php":"011d7404c7ba8551cfa7c77e3f4940d0","tests\/Installer\/EnvDumperTest.php":"803c370842b1df12762b55f18d25d98b","tests\/Installer\/InstallTest.php":"b4e534d6bf9c42fab95d84f3aad48f98","tests\/Installer\/VendorInstallerTest.php":"a8b9b9cad179e9c5df4220c96d7c5f18","tests\/Installer\/MoodleInstallerTest.php":"576b869019989fe7fb1b4a5c682245c8","tests\/Installer\/MoodleAppInstallerTest.php":"910ab92a2107bf357e2aa1e2d028de48","tests\/Installer\/Database\/DatabaseResolverTest.php":"1f94af1d06e6f861e0d47d695af15ec7","tests\/Installer\/Database\/MySQLDatabaseTest.php":"b3c5babeeb48be25682126425d63e5b1","tests\/Installer\/Database\/MariaDBDatabaseTest.php":"cd5bfa117342b459ff3c65a07ae8c39c","tests\/Installer\/Database\/PostgresDatabaseTest.php":"4da7a2bf8a8eda648c33ad1e65303d8b","tests\/Installer\/PluginInstallerTest.php":"e897820c13c83c1ebd42035ddf2ea23a","tests\/Installer\/InstallOutputTest.php":"367bcb2ff4ce3bb447d86e861e80b5f1","tests\/FilesystemTestCase.php":"04bb780882ba5e8e1acfb983202b6369","tests\/StandardResolverTest.php":"d4daf7ed247e2537f54a1b1782b8d965","tests\/FileUpdatesTest.php":"47acaf90267f511c8a9eb30cfd0a2b6e","tests\/MoodleTestCase.php":"73d15919152e606fb20c07e8216fdcf0","tests\/PluginValidate\/Requirements\/FilterRequirementsTest.php":"ef6a80f97c2cb613e9ce4f03b9575f9b","tests\/PluginValidate\/Requirements\/QuestionRequirementsTest.php":"df8fb37995075f8d13c1efd853e54135","tests\/PluginValidate\/Requirements\/ThemeRequirementsTest.php":"1fea81cdc40722fd3bbeb3af6d12c73b","tests\/PluginValidate\/Requirements\/BlockRequirementsTest.php":"42439265af6adcb0be81b92651bf51bb","tests\/PluginValidate\/Requirements\/RepositoryRequirementsTest.php":"a89e4cb7862c57dd15458dc9f5276f96","tests\/PluginValidate\/Requirements\/RequirementsResolverTest.php":"60a709261b8294aa67a4a19615adb020","tests\/PluginValidate\/Requirements\/ModuleRequirementsTest.php":"d804a1ba8bf9b88ab9992b0070f96624","tests\/PluginValidate\/Requirements\/DataformatRequirementsTest.php":"708010078eeb6cbbeb3abd777eb94b36","tests\/PluginValidate\/Requirements\/FormatRequirementsTest.php":"c2511ff5e7a086dd33321f7daf68dc6a","tests\/PluginValidate\/Requirements\/AuthRequirementsTest.php":"f51d5a696e27f8059ed68eeffb0326f2","tests\/PluginValidate\/PluginValidateTest.php":"c62ab99d27bb14a7fe4feccf2ee39686","tests\/PluginValidate\/Finder\/CapabilityFinderTest.php":"4ddabfc8cf1e53696e5521b3d47519cd","tests\/PluginValidate\/Finder\/ClassFinderTest.php":"f52cdab5884c494f5743c26e5ba0d5cb","tests\/PluginValidate\/Finder\/TablePrefixFinderTest.php":"64a452603b0d97114448f931462963c0","tests\/PluginValidate\/Finder\/FunctionFinderTest.php":"fe6e2f6d100df0cbf8da0959aac9c003","tests\/PluginValidate\/Finder\/FunctionCallFinderTest.php":"3a7f7b2b79cc9cb14a97a46f03c0e29b","tests\/Process\/MoodleProcessTest.php":"11fdc1e14e91ebe508df8723fbb0bcb4","tests\/Process\/ExecuteTest.php":"e7d3ad02773b3f1ab7227f040107dbb8"}} \ No newline at end of file diff --git a/.phpunit.result.cache b/.phpunit.result.cache new file mode 100644 index 00000000..0fc1996c --- /dev/null +++ b/.phpunit.result.cache @@ -0,0 +1 @@ +{"version":1,"defects":{"MoodlePluginCI\\Tests\\Bridge\\MoodlePluginTest::testHasPhpUnitConfig":3,"MoodlePluginCI\\Tests\\Command\\PHPUnitCommandTest::testExecute":3,"MoodlePluginCI\\Tests\\Command\\PHPUnitCommandTest::testExecuteWithTestSuite":3,"MoodlePluginCI\\Tests\\Command\\PHPUnitCommandTest::testExecuteWithFilter":3,"MoodlePluginCI\\Tests\\Installer\\TestSuiteInstallerTest::testBehatProcesses":3,"MoodlePluginCI\\Tests\\Installer\\TestSuiteInstallerTest::testUnitTestProcesses":3,"MoodlePluginCI\\Tests\\PluginValidate\\FilterRequirementsTest::testGetRequiredFunctionCalls404":3},"times":{"MoodlePluginCI\\Tests\\Bridge\\MoodleConfigTest::testCreateContents":0.003,"MoodlePluginCI\\Tests\\Bridge\\MoodleConfigTest::testConfigureChromeBrowserCapabilities":0,"MoodlePluginCI\\Tests\\Bridge\\MoodleConfigTest::testConfigureFirefoxBrowserCapabilities":0,"MoodlePluginCI\\Tests\\Bridge\\MoodleConfigTest::testInjectLineIntoConfig":0,"MoodlePluginCI\\Tests\\Bridge\\MoodleConfigTest::testInjectLineIntoConfigMissingPlaceholder":0,"MoodlePluginCI\\Tests\\Bridge\\MoodleConfigTest::testRead":0,"MoodlePluginCI\\Tests\\Bridge\\MoodleConfigTest::testReadFileNotFound":0.002,"MoodlePluginCI\\Tests\\Bridge\\MoodleConfigTest::testReadFail":0.001,"MoodlePluginCI\\Tests\\Bridge\\MoodleConfigTest::testDump":0.001,"MoodlePluginCI\\Tests\\Bridge\\MoodlePluginCollectionTest::testSortByDependencies":0.001,"MoodlePluginCI\\Tests\\Bridge\\MoodlePluginCollectionTest::testSortByDependenciesCircularError":0,"MoodlePluginCI\\Tests\\Bridge\\MoodlePluginCollectionTest::testSortByDependenciesWithSubplugins":0,"MoodlePluginCI\\Tests\\Bridge\\MoodlePluginTest::testGetComponent":0.028,"MoodlePluginCI\\Tests\\Bridge\\MoodlePluginTest::testGetDependencies":0.006,"MoodlePluginCI\\Tests\\Bridge\\MoodlePluginTest::testGetSubpluginTypes":0.005,"MoodlePluginCI\\Tests\\Bridge\\MoodlePluginTest::testHasUnitTests":0.007,"MoodlePluginCI\\Tests\\Bridge\\MoodlePluginTest::testHasPhpUnitConfig":0.006,"MoodlePluginCI\\Tests\\Bridge\\MoodlePluginTest::testNoUnitTests":0.006,"MoodlePluginCI\\Tests\\Bridge\\MoodlePluginTest::testHasBehatFeatures":0.006,"MoodlePluginCI\\Tests\\Bridge\\MoodlePluginTest::testNoBehatFeatures":0.006,"MoodlePluginCI\\Tests\\Bridge\\MoodlePluginTest::testGetThirdPartyLibraryPaths":0.005,"MoodlePluginCI\\Tests\\Bridge\\MoodlePluginTest::testGetThirdPartyLibraryPathsError":0.005,"MoodlePluginCI\\Tests\\Bridge\\MoodlePluginTest::testGetIgnores":0.01,"MoodlePluginCI\\Tests\\Bridge\\MoodlePluginTest::testGetFiles":0.007,"MoodlePluginCI\\Tests\\Bridge\\MoodlePluginTest::testGetRelativeFiles":0.007,"MoodlePluginCI\\Tests\\Bridge\\MoodleTest::testGetBranch":0.002,"MoodlePluginCI\\Tests\\Command\\AddConfigCommandTest::testExecute":0.05,"MoodlePluginCI\\Tests\\Command\\AddConfigCommandTest::testExecuteSyntaxError":0.038,"MoodlePluginCI\\Tests\\Command\\AddPluginCommandTest::testExecute":0.012,"MoodlePluginCI\\Tests\\Command\\AddPluginCommandTest::testExecuteWithClone":0.001,"MoodlePluginCI\\Tests\\Command\\AddPluginCommandTest::testExecuteWithCloneAndBranch":0.001,"MoodlePluginCI\\Tests\\Command\\AddPluginCommandTest::testExecuteBothProjectAndClone":0.001,"MoodlePluginCI\\Tests\\Command\\AddPluginCommandTest::testExecuteMissingProjectAndClone":0,"MoodlePluginCI\\Tests\\Command\\BehatCommandTest::testExecute":0.009,"MoodlePluginCI\\Tests\\Command\\BehatCommandTest::testExecuteWithTags":0.008,"MoodlePluginCI\\Tests\\Command\\BehatCommandTest::testExecuteWithSeleniumImageOption":5.022,"MoodlePluginCI\\Tests\\Command\\BehatCommandTest::testExecuteWithSeleniumImageEnv":5.04,"MoodlePluginCI\\Tests\\Command\\BehatCommandTest::testExecuteWithChromeProfile":5.033,"MoodlePluginCI\\Tests\\Command\\BehatCommandTest::testExecuteWithFirefoxProfile":5.031,"MoodlePluginCI\\Tests\\Command\\BehatCommandTest::testExecuteWithLegacyFirefoxProfile":5.033,"MoodlePluginCI\\Tests\\Command\\BehatCommandTest::testExecuteWithName":0.018,"MoodlePluginCI\\Tests\\Command\\BehatCommandTest::testExecuteNoFeatures":0.009,"MoodlePluginCI\\Tests\\Command\\BehatCommandTest::testExecuteNoPlugin":0.006,"MoodlePluginCI\\Tests\\Command\\BehatCommandTest::testExecuteNoMoodle":0.005,"MoodlePluginCI\\Tests\\Command\\CodeCheckerCommandTest::testExecute":0.265,"MoodlePluginCI\\Tests\\Command\\CodeCheckerCommandTest::testExecuteFail":0.261,"MoodlePluginCI\\Tests\\Command\\CodeCheckerCommandTest::testExecuteWithWarningsAndThreshold":1.246,"MoodlePluginCI\\Tests\\Command\\CodeCheckerCommandTest::testExecuteWithTestVersion":1.704,"MoodlePluginCI\\Tests\\Command\\CodeCheckerCommandTest::testExecuteWithExclusions":0.474,"MoodlePluginCI\\Tests\\Command\\CodeCheckerCommandTest::testExecuteWithTodoCommentRegex":0.501,"MoodlePluginCI\\Tests\\Command\\CodeCheckerCommandTest::testExecuteWithLicenseRegex":0.722,"MoodlePluginCI\\Tests\\Command\\CodeCheckerCommandTest::testExecuteNoFiles":0.006,"MoodlePluginCI\\Tests\\Command\\CodeCheckerCommandTest::testExecuteNoPlugin":0.006,"MoodlePluginCI\\Tests\\Command\\CodeCheckerCommandTest::testCommandFailedSomethingIsWrong with data set \"default\"":0.043,"MoodlePluginCI\\Tests\\Command\\CodeCheckerCommandTest::testCommandFailedSomethingIsWrong with data set \"zero\"":0.049,"MoodlePluginCI\\Tests\\Command\\CodeCheckerCommandTest::testCommandFailedSomethingIsWrong with data set \"positive\"":0.052,"MoodlePluginCI\\Tests\\Command\\CodeFixerCommandTest::testExecute":0.249,"MoodlePluginCI\\Tests\\Command\\CodeFixerCommandTest::testExecuteNoFiles":0.015,"MoodlePluginCI\\Tests\\Command\\CopyPasteDetectorCommandTest::testExecute":0.004,"MoodlePluginCI\\Tests\\Command\\CopyPasteDetectorCommandTest::testExecuteNoFiles":0,"MoodlePluginCI\\Tests\\Command\\CopyPasteDetectorCommandTest::testExecuteNoPlugin":0,"MoodlePluginCI\\Tests\\Command\\CoverallsUploadCommandTest::testExecute":0.007,"MoodlePluginCI\\Tests\\Command\\CoverallsUploadCommandTest::testExecuteNoPlugin":0.005,"MoodlePluginCI\\Tests\\Command\\CoverallsUploadCommandTest::testExecuteNoCoverageFile":0.006,"MoodlePluginCI\\Tests\\Command\\GruntCommandTest::testExecute":0.02,"MoodlePluginCI\\Tests\\Command\\GruntCommandTest::testToGruntTaskWithAMD":0.005,"MoodlePluginCI\\Tests\\Command\\GruntCommandTest::testToGruntTaskWithYUI":0.005,"MoodlePluginCI\\Tests\\Command\\GruntCommandTest::testToGruntTaskWithLegacyYUI":0.006,"MoodlePluginCI\\Tests\\Command\\GruntCommandTest::testToGruntTaskWithGherkin":0.006,"MoodlePluginCI\\Tests\\Command\\GruntCommandTest::testToGruntTaskWithStyles":0.009,"MoodlePluginCI\\Tests\\Command\\GruntCommandTest::testToGruntTaskDefaultTask":0.005,"MoodlePluginCI\\Tests\\Command\\GruntCommandTest::testValidatePluginFiles":0.05,"MoodlePluginCI\\Tests\\Command\\InstallCommandTest::testExecute":0.009,"MoodlePluginCI\\Tests\\Command\\InstallCommandTest::testCsvToArray with data set #0":0.005,"MoodlePluginCI\\Tests\\Command\\InstallCommandTest::testCsvToArray with data set #1":0.005,"MoodlePluginCI\\Tests\\Command\\InstallCommandTest::testCsvToArray with data set #2":0.005,"MoodlePluginCI\\Tests\\Command\\InstallCommandTest::testInitializePluginConfigDumper":0.006,"MoodlePluginCI\\Tests\\Command\\MessDetectorCommandTest::testExecute":0.081,"MoodlePluginCI\\Tests\\Command\\MessDetectorCommandTest::testExecuteNoFiles":0,"MoodlePluginCI\\Tests\\Command\\MessDetectorCommandTest::testExecuteNoPlugin":0,"MoodlePluginCI\\Tests\\Command\\MustacheCommandTest::testExecute":0.035,"MoodlePluginCI\\Tests\\Command\\MustacheCommandTest::testExecuteNoPlugin":0.005,"MoodlePluginCI\\Tests\\Command\\MustacheCommandTest::testExecuteNoMoodle":0.005,"MoodlePluginCI\\Tests\\Command\\PHPDocCommandTest::testExecute":0.015,"MoodlePluginCI\\Tests\\Command\\PHPDocCommandTest::testExecuteFail":0.016,"MoodlePluginCI\\Tests\\Command\\PHPDocCommandTest::testExecuteWithWarningsAndThreshold":0.053,"MoodlePluginCI\\Tests\\Command\\PHPDocCommandTest::testExecuteNoFiles":0.013,"MoodlePluginCI\\Tests\\Command\\PHPDocCommandTest::testExecuteNoPlugin":0.013,"MoodlePluginCI\\Tests\\Command\\PHPLintCommandTest::testExecute":0.076,"MoodlePluginCI\\Tests\\Command\\PHPLintCommandTest::testExecuteNoFiles":0,"MoodlePluginCI\\Tests\\Command\\PHPLintCommandTest::testExecuteNoPlugin":0.001,"MoodlePluginCI\\Tests\\Command\\PHPUnitCommandTest::testExecute":0.009,"MoodlePluginCI\\Tests\\Command\\PHPUnitCommandTest::testExecuteWithCustomPHPUnitXMLFile":0.009,"MoodlePluginCI\\Tests\\Command\\PHPUnitCommandTest::testExecuteWithGeneratedPHPUnitXMLFile":0.007,"MoodlePluginCI\\Tests\\Command\\PHPUnitCommandTest::testExecuteWithTestSuite":0.009,"MoodlePluginCI\\Tests\\Command\\PHPUnitCommandTest::testExecuteWithFilter":0.008,"MoodlePluginCI\\Tests\\Command\\PHPUnitCommandTest::testExecuteNoTests":0.009,"MoodlePluginCI\\Tests\\Command\\PHPUnitCommandTest::testExecuteNoPlugin":0.005,"MoodlePluginCI\\Tests\\Command\\PHPUnitCommandTest::testExecuteNoMoodle":0.006,"MoodlePluginCI\\Tests\\Command\\ParallelCommandTest::testExecute":0.035,"MoodlePluginCI\\Tests\\Command\\ParallelCommandTest::testExecuteFailedProcess":0.031,"MoodlePluginCI\\Tests\\Command\\ParallelCommandTest::testInitializeProcesses":0.001,"MoodlePluginCI\\Tests\\Command\\SavePointsCommandTest::testExecute":0.034,"MoodlePluginCI\\Tests\\Command\\SavePointsCommandTest::testExecuteNoUpgradeFile":0.005,"MoodlePluginCI\\Tests\\Command\\SavePointsCommandTest::testExecuteFail":0.033,"Command\\SelfUpdateCommandTest::testGetBackupPathNotExists":0,"Command\\SelfUpdateCommandTest::testGetBackupPathExists":0,"MoodlePluginCI\\Tests\\Command\\ValidateCommandTest::testExecute":0.012,"MoodlePluginCI\\Tests\\FileUpdatesTest::testLocalCIPackageJSON":0,"MoodlePluginCI\\Tests\\Installer\\Database\\DatabaseResolverTest::testType":0,"MoodlePluginCI\\Tests\\Installer\\Database\\DatabaseResolverTest::testTypeError":0,"MoodlePluginCI\\Tests\\Installer\\Database\\DatabaseResolverTest::testOptions":0,"MoodlePluginCI\\Tests\\Installer\\Database\\MariaDBDatabaseTest::testGetCreateDatabaseCommand":0,"MoodlePluginCI\\Tests\\Installer\\Database\\MySQLDatabaseTest::testGetCreateDatabaseCommand":0,"MoodlePluginCI\\Tests\\Installer\\Database\\PostgresDatabaseTest::testGetCreateDatabaseCommand":0,"MoodlePluginCI\\Tests\\Installer\\EnvDumperTest::testDump":0,"MoodlePluginCI\\Tests\\Installer\\EnvDumperTest::testNoDump":0,"MoodlePluginCI\\Tests\\Installer\\InstallOutputTest::testProgressBar":0,"MoodlePluginCI\\Tests\\Installer\\InstallOutputTest::testLogInfo":0,"MoodlePluginCI\\Tests\\Installer\\InstallOutputTest::testQuietLogInfo":0,"MoodlePluginCI\\Tests\\Installer\\InstallOutputTest::testLogDebug":0,"MoodlePluginCI\\Tests\\Installer\\InstallOutputTest::testQuietLogDebug":0,"MoodlePluginCI\\Tests\\Installer\\InstallTest::testRunInstallation":0,"MoodlePluginCI\\Tests\\Installer\\InstallerCollectionTest::testAll":0,"MoodlePluginCI\\Tests\\Installer\\InstallerCollectionTest::testMergeEnv":0,"MoodlePluginCI\\Tests\\Installer\\InstallerCollectionTest::testTotalSteps":0,"MoodlePluginCI\\Tests\\Installer\\MoodleAppInstallerTest::testInstall":0.007,"MoodlePluginCI\\Tests\\Installer\\MoodleInstallerTest::testInstall":0.006,"MoodlePluginCI\\Tests\\Installer\\PluginInstallerTest::testInstall":0.02,"MoodlePluginCI\\Tests\\Installer\\PluginInstallerTest::testInstallPluginIntoMoodle":0.008,"MoodlePluginCI\\Tests\\Installer\\PluginInstallerTest::testInstallPluginIntoMoodleAlreadyExists":0.001,"MoodlePluginCI\\Tests\\Installer\\PluginInstallerTest::testCreateIgnoreFile":0.001,"MoodlePluginCI\\Tests\\Installer\\PluginInstallerTest::testScanForPlugins":0.005,"MoodlePluginCI\\Tests\\Installer\\TestSuiteInstallerTest::testInstall":0.01,"MoodlePluginCI\\Tests\\Installer\\TestSuiteInstallerTest::testBehatProcesses":0.007,"MoodlePluginCI\\Tests\\Installer\\TestSuiteInstallerTest::testUnitTestProcesses":0.007,"MoodlePluginCI\\Tests\\Installer\\TestSuiteInstallerTest::testPHPUnitXMLFile":0.011,"MoodlePluginCI\\Tests\\Installer\\TestSuiteInstallerTest::testPHPUnitXMLFile311":0.01,"MoodlePluginCI\\Tests\\Installer\\VendorInstallerTest::testInstall":0.006,"MoodlePluginCI\\Tests\\Installer\\VendorInstallerTest::testInstallNodeNoNvmrc":0.006,"MoodlePluginCI\\Tests\\Installer\\VendorInstallerTest::testInstallNodeUserVersion":0.005,"MoodlePluginCI\\Tests\\Installer\\VendorInstallerTest::testInstallNodePluginDependencies":0.007,"MoodlePluginCI\\Tests\\Installer\\VendorInstallerTest::testSkipNodePluginDependencies":0.006,"MoodlePluginCI\\Tests\\PluginValidate\\Finder\\CapabilityFinderTest::testFindTokens":0.001,"MoodlePluginCI\\Tests\\PluginValidate\\Finder\\ClassFinderTest::testFindTokens":0.002,"MoodlePluginCI\\Tests\\PluginValidate\\Finder\\ClassFinderTest::testFindTokensNameSpaceClass":0.001,"MoodlePluginCI\\Tests\\PluginValidate\\Finder\\FunctionCallFinderTest::testFindTokens":0.001,"MoodlePluginCI\\Tests\\PluginValidate\\Finder\\FunctionCallFinderTest::testFindTokensNotFound":0.001,"MoodlePluginCI\\Tests\\PluginValidate\\Finder\\FunctionFinderTest::testFindTokens":0.001,"MoodlePluginCI\\Tests\\PluginValidate\\Finder\\TablePrefixFinderTest::testFindTokens":0,"MoodlePluginCI\\Tests\\PluginValidate\\Finder\\TablePrefixFinderTest::testFindTokensFail":0,"MoodlePluginCI\\Tests\\PluginValidate\\PluginValidateTest::testVerifyRequirements":0.002,"MoodlePluginCI\\Tests\\PluginValidate\\PluginValidateTest::testVerifyRequirementsFail":0.005,"MoodlePluginCI\\Tests\\PluginValidate\\AuthRequirementsTest::testResolveRequirements":0,"MoodlePluginCI\\Tests\\PluginValidate\\AuthRequirementsTest::testGetRequiredFiles":0,"MoodlePluginCI\\Tests\\PluginValidate\\AuthRequirementsTest::testGetRequiredClasses":0,"MoodlePluginCI\\Tests\\PluginValidate\\BlockRequirementsTest::testResolveRequirements":0,"MoodlePluginCI\\Tests\\PluginValidate\\BlockRequirementsTest::testGetRequiredFiles":0,"MoodlePluginCI\\Tests\\PluginValidate\\BlockRequirementsTest::testGetRequiredFunctions":0,"MoodlePluginCI\\Tests\\PluginValidate\\BlockRequirementsTest::testGetRequiredClasses":0,"MoodlePluginCI\\Tests\\PluginValidate\\BlockRequirementsTest::testGetRequiredStrings":0,"MoodlePluginCI\\Tests\\PluginValidate\\BlockRequirementsTest::testGetRequiredCapabilities":0,"MoodlePluginCI\\Tests\\PluginValidate\\DataformatRequirementsTest::testResolveRequirements":0,"MoodlePluginCI\\Tests\\PluginValidate\\DataformatRequirementsTest::testGetRequiredStrings":0,"MoodlePluginCI\\Tests\\PluginValidate\\FilterRequirementsTest::testResolveRequirements":0,"MoodlePluginCI\\Tests\\PluginValidate\\FilterRequirementsTest::testGetRequiredFiles404":0,"MoodlePluginCI\\Tests\\PluginValidate\\FilterRequirementsTest::testGetRequiredFiles":0,"MoodlePluginCI\\Tests\\PluginValidate\\FilterRequirementsTest::testGetRequiredClasses404":0.005,"MoodlePluginCI\\Tests\\PluginValidate\\FilterRequirementsTest::testGetRequiredClasses":0,"MoodlePluginCI\\Tests\\PluginValidate\\FilterRequirementsTest::testGetRequiredStrings":0,"MoodlePluginCI\\Tests\\PluginValidate\\FormatRequirementsTest::testResolveRequirements":0,"MoodlePluginCI\\Tests\\PluginValidate\\FormatRequirementsTest::testGetRequiredFiles":0,"MoodlePluginCI\\Tests\\PluginValidate\\FormatRequirementsTest::testGetRequiredClasses":0,"MoodlePluginCI\\Tests\\PluginValidate\\ModuleRequirementsTest::testResolveRequirements":0,"MoodlePluginCI\\Tests\\PluginValidate\\ModuleRequirementsTest::testGetRequiredFiles":0,"MoodlePluginCI\\Tests\\PluginValidate\\ModuleRequirementsTest::testGetRequiredFunctions":0,"MoodlePluginCI\\Tests\\PluginValidate\\ModuleRequirementsTest::testGetRequiredStrings":0,"MoodlePluginCI\\Tests\\PluginValidate\\ModuleRequirementsTest::testGetRequiredCapabilities":0,"MoodlePluginCI\\Tests\\PluginValidate\\QuestionRequirementsTest::testResolveRequirements":0,"MoodlePluginCI\\Tests\\PluginValidate\\QuestionRequirementsTest::testGetRequiredPrefixes":0,"MoodlePluginCI\\Tests\\PluginValidate\\RepositoryRequirementsTest::testResolveRequirements":0,"MoodlePluginCI\\Tests\\PluginValidate\\RepositoryRequirementsTest::testGetRequiredFiles":0,"MoodlePluginCI\\Tests\\PluginValidate\\RepositoryRequirementsTest::testGetRequiredClasses":0,"MoodlePluginCI\\Tests\\PluginValidate\\RepositoryRequirementsTest::testGetRequiredStrings":0,"MoodlePluginCI\\Tests\\PluginValidate\\RepositoryRequirementsTest::testGetRequiredCapabilities":0,"MoodlePluginCI\\Tests\\PluginValidate\\RequirementsResolverTest::testResolveRequirements":0,"MoodlePluginCI\\Tests\\PluginValidate\\ThemeRequirementsTest::testResolveRequirements":0,"MoodlePluginCI\\Tests\\PluginValidate\\ThemeRequirementsTest::testGetRequiredFiles":0,"MoodlePluginCI\\Tests\\Process\\ExecuteTest::testSetNodeEnv":0.01,"MoodlePluginCI\\Tests\\Process\\ExecuteTest::testRun":0.004,"MoodlePluginCI\\Tests\\Process\\ExecuteTest::testMustRun":0.003,"MoodlePluginCI\\Tests\\Process\\ExecuteTest::testRunAllVerbose":0.009,"MoodlePluginCI\\Tests\\Process\\ExecuteTest::testMustRunAll":0.027,"MoodlePluginCI\\Tests\\Process\\ExecuteTest::testMustRunAllFail":0.03,"MoodlePluginCI\\Tests\\Process\\ExecuteTest::testPassThrough":0.028,"MoodlePluginCI\\Tests\\Process\\MoodleProcessTest::testDetectDebuggingMessages":0.049,"MoodlePluginCI\\Tests\\Process\\MoodleProcessTest::testHasPhpErrorMessages":0.051,"MoodlePluginCI\\Tests\\Process\\MoodleProcessTest::testIsSuccessful":0.051,"MoodlePluginCI\\Tests\\Process\\MoodleProcessTest::testMustRun":0.026,"MoodlePluginCI\\Tests\\Process\\MoodleProcessTest::testMustRunError":0.024,"MoodlePluginCI\\Tests\\Process\\MoodleProcessTest::testCheckOutputForProblemsNotStarted":0,"MoodlePluginCI\\Tests\\Process\\MoodleProcessTest::testCheckOutputForProblemsOutputDisabled":0.027,"MoodlePluginCI\\Tests\\Process\\MoodleProcessTest::testCheckOutputForProblemsPhpError":0.024,"MoodlePluginCI\\Tests\\Process\\MoodleProcessTest::testCheckOutputForProblemsDebuggingMessage":0.026,"MoodlePluginCI\\Tests\\StandardResolverTest::testHasStandard":0,"MoodlePluginCI\\Tests\\StandardResolverTest::testResolve":0,"MoodlePluginCI\\Tests\\StandardResolverTest::testResolveUnknown":0,"MoodlePluginCI\\Tests\\StandardResolverTest::testResolveNotFound":0,"MoodlePluginCI\\Tests\\ValidateTest::testDirectory":0,"MoodlePluginCI\\Tests\\ValidateTest::testDirectoryRealPathFail":0,"MoodlePluginCI\\Tests\\ValidateTest::testDirectoryIsFile":0,"MoodlePluginCI\\Tests\\ValidateTest::testFilePath":0,"MoodlePluginCI\\Tests\\ValidateTest::testFilePathRealPathFail":0,"MoodlePluginCI\\Tests\\ValidateTest::testFilePathIsDirectory":0,"MoodlePluginCI\\Tests\\ValidateTest::testGitBranch with data set #0":0,"MoodlePluginCI\\Tests\\ValidateTest::testGitBranch with data set #1":0,"MoodlePluginCI\\Tests\\ValidateTest::testGitBranch with data set #2":0,"MoodlePluginCI\\Tests\\ValidateTest::testGitBranch with data set #3":0,"MoodlePluginCI\\Tests\\ValidateTest::testGitBranch with data set #4":0,"MoodlePluginCI\\Tests\\ValidateTest::testGitBranchInvalid with data set #0":0,"MoodlePluginCI\\Tests\\ValidateTest::testGitBranchInvalid with data set #1":0,"MoodlePluginCI\\Tests\\ValidateTest::testUrl with data set #0":0,"MoodlePluginCI\\Tests\\ValidateTest::testUrl with data set #1":0,"MoodlePluginCI\\Tests\\ValidateTest::testInvalidUrl with data set #0":0,"MoodlePluginCI\\Tests\\ValidateTest::testInvalidUrl with data set #1":0,"MoodlePluginCI\\Tests\\ValidateTest::testInvalidUrl with data set #2":0,"MoodlePluginCI\\Tests\\PluginValidate\\FilterRequirementsTest::testGetRequiredFunctionCalls":0,"MoodlePluginCI\\Tests\\PluginValidate\\FilterRequirementsTest::testGetRequiredFunctionCalls404":0}} \ No newline at end of file diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md index 0e65f7df..f67c5f75 100644 --- a/docs/CHANGELOG.md +++ b/docs/CHANGELOG.md @@ -9,8 +9,14 @@ This project adheres to [Semantic Versioning](https://semver.org/). The format of this change log follows the advice given at [Keep a CHANGELOG](https://keepachangelog.com). ## [Unreleased] +### Added +- Improvments to plugin validation implementation: + `getRequiredFunctionCalls` in plugin type specific `Requirements` class can be used to validate that file contains function call. + `FileTokens::notFoundHint` can be used to give some context for validation error to improve DX. + ### Fixed - Fixed stylelinting error in non-theme plugins containing scss. +- Updated filter plugin validation requirements to comply with Moodle 4.5 ### Removed - Stylelint less component task (`grunt stylelint:less`) has been deprecated in diff --git a/src/Parser/StatementFilter.php b/src/Parser/StatementFilter.php index 04649de6..4d5bb4e2 100644 --- a/src/Parser/StatementFilter.php +++ b/src/Parser/StatementFilter.php @@ -14,9 +14,11 @@ use PhpParser\Node\Expr\Array_; use PhpParser\Node\Expr\Assign; +use PhpParser\Node\Expr\FuncCall; use PhpParser\Node\Expr\PropertyFetch; use PhpParser\Node\Expr\Variable; use PhpParser\Node\Identifier; +use PhpParser\Node\Name; use PhpParser\Node\Scalar\String_; use PhpParser\Node\Stmt; use PhpParser\Node\Stmt\Class_; @@ -116,6 +118,26 @@ public function filterAssignments(array $statements): array return $assigns; } + /** + * Extract all the function call expressions from the statements. + * + * @param Stmt[] $statements + * + * @return FuncCall[] + */ + public function filterFunctionCalls(array $statements): array + { + $calls = []; + foreach ($statements as $statement) { + // Only expressions that are function calls. + if ($statement instanceof Expression && $statement->expr instanceof FuncCall) { + $calls[] = $statement->expr; + } + } + + return $calls; + } + /** * Find first variable assignment with a given name. * diff --git a/src/PluginValidate/Finder/FileTokens.php b/src/PluginValidate/Finder/FileTokens.php index ca0014e3..4f5b08f4 100644 --- a/src/PluginValidate/Finder/FileTokens.php +++ b/src/PluginValidate/Finder/FileTokens.php @@ -29,6 +29,13 @@ class FileTokens */ public string $file; + /** + * Not found error hint. + * + * @var string + */ + public string $hint = ''; + /** * @param string $file */ @@ -59,6 +66,16 @@ public function hasTokens(): bool return !empty($this->tokens); } + /** + * Do we have any hint? + * + * @return bool + */ + public function hasHint(): bool + { + return !empty($this->hint); + } + /** * @param Token $token * @@ -166,4 +183,18 @@ public function resetTokens(): void $token->reset(); } } + + /** + * Not found error additional information guiding user how to fix it (optional). + * + * @param string $hint + * + * @return self + */ + public function notFoundHint(string $hint): self + { + $this->hint = $hint; + + return $this; + } } diff --git a/src/PluginValidate/Finder/FunctionCallFinder.php b/src/PluginValidate/Finder/FunctionCallFinder.php new file mode 100644 index 00000000..609ec1e3 --- /dev/null +++ b/src/PluginValidate/Finder/FunctionCallFinder.php @@ -0,0 +1,37 @@ + + * License http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +namespace MoodlePluginCI\PluginValidate\Finder; + +use PhpParser\Node\Name; + +/** + * Finds function call. + */ +class FunctionCallFinder extends AbstractParserFinder +{ + public function getType(): string + { + return 'function call'; + } + + public function findTokens($file, FileTokens $fileTokens): void + { + $statements = $this->parser->parseFile($file); + + foreach ($this->filter->filterFunctionCalls($statements) as $funccall) { + if ($funccall->name instanceof Name) { + $fileTokens->compare((string) $funccall->name); + } + } + } +} diff --git a/src/PluginValidate/PluginValidate.php b/src/PluginValidate/PluginValidate.php index 93c03b82..be9c5ee9 100644 --- a/src/PluginValidate/PluginValidate.php +++ b/src/PluginValidate/PluginValidate.php @@ -17,6 +17,7 @@ use MoodlePluginCI\PluginValidate\Finder\ClassFinder; use MoodlePluginCI\PluginValidate\Finder\FileTokens; use MoodlePluginCI\PluginValidate\Finder\FinderInterface; +use MoodlePluginCI\PluginValidate\Finder\FunctionCallFinder; use MoodlePluginCI\PluginValidate\Finder\FunctionFinder; use MoodlePluginCI\PluginValidate\Finder\LangFinder; use MoodlePluginCI\PluginValidate\Finder\TableFinder; @@ -98,6 +99,9 @@ public function addMessagesFromTokens(string $type, FileTokens $fileTokens): voi $this->addSuccess(sprintf('In %s, found %s %s', $fileTokens->file, $type, implode(' OR ', $token->tokens))); } else { $this->addError(sprintf('In %s, failed to find %s %s', $fileTokens->file, $type, implode(' OR ', $token->tokens))); + if ($fileTokens->hasHint()) { + $this->addError(sprintf('Hint: %s', $fileTokens->hint)); + } } } } @@ -115,6 +119,7 @@ public function verifyRequirements(): void $this->findRequiredTokens(new TableFinder(), [$this->requirements->getRequiredTables()]); $this->findRequiredTokens(new TablePrefixFinder(), [$this->requirements->getRequiredTablePrefix()]); $this->findRequiredTokens(new BehatTagFinder(), $this->requirements->getRequiredBehatTags()); + $this->findRequiredTokens(new FunctionCallFinder(), $this->requirements->getRequiredFunctionCalls()); } /** diff --git a/src/PluginValidate/Requirements/AbstractRequirements.php b/src/PluginValidate/Requirements/AbstractRequirements.php index 32c1e154..5ae9d70e 100644 --- a/src/PluginValidate/Requirements/AbstractRequirements.php +++ b/src/PluginValidate/Requirements/AbstractRequirements.php @@ -76,6 +76,13 @@ protected function fileExists(string $file): bool return file_exists($this->plugin->directory . '/' . $file); } + /** + * Required function calls. + * + * @return FileTokens[] + */ + abstract public function getRequiredFunctionCalls(): array; + /** * An array of required files, paths are relative to the plugin directory. * diff --git a/src/PluginValidate/Requirements/FilterRequirements.php b/src/PluginValidate/Requirements/FilterRequirements.php index c6c2ed90..caa8fd94 100644 --- a/src/PluginValidate/Requirements/FilterRequirements.php +++ b/src/PluginValidate/Requirements/FilterRequirements.php @@ -50,4 +50,15 @@ public function getRequiredStrings(): FileTokens { return FileTokens::create($this->getLangFile())->mustHave('filtername'); } + + public function getRequiredFunctionCalls(): array + { + if ($this->moodleVersion <= 404 && !$this->fileExists('classes/text_filter.php')) { + return []; + } + + return [ + FileTokens::create('filter.php')->mustHave('class_alias')->notFoundHint('https://moodledev.io/docs/4.5/devupdate#filter-plugins'), + ]; + } } diff --git a/src/PluginValidate/Requirements/GenericRequirements.php b/src/PluginValidate/Requirements/GenericRequirements.php index d1dccf4f..d6cd412b 100644 --- a/src/PluginValidate/Requirements/GenericRequirements.php +++ b/src/PluginValidate/Requirements/GenericRequirements.php @@ -46,6 +46,11 @@ public function getRequiredClasses(): array return []; } + public function getRequiredFunctionCalls(): array + { + return []; + } + public function getRequiredStrings(): FileTokens { return FileTokens::create($this->getLangFile())->mustHave('pluginname'); diff --git a/tests/Fixture/moodle-local_ci/lib.php b/tests/Fixture/moodle-local_ci/lib.php index 75451d04..2a000d7d 100644 --- a/tests/Fixture/moodle-local_ci/lib.php +++ b/tests/Fixture/moodle-local_ci/lib.php @@ -28,6 +28,8 @@ // set then can check for anything, like CUSTOM-123 or https://github.com // or whatever. +defined('MOODLE_INTERNAL') || die(); + /** * Add * @@ -72,3 +74,6 @@ public function add($a, $b) { return $a + $b; } } + +// Call function. +local_ci_subtract(1, 2); diff --git a/tests/PluginValidate/Finder/FunctionCallFinderTest.php b/tests/PluginValidate/Finder/FunctionCallFinderTest.php new file mode 100644 index 00000000..69f44e2b --- /dev/null +++ b/tests/PluginValidate/Finder/FunctionCallFinderTest.php @@ -0,0 +1,44 @@ + + * License http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +namespace MoodlePluginCI\Tests\PluginValidate\Finder; + +use MoodlePluginCI\PluginValidate\Finder\FileTokens; +use MoodlePluginCI\PluginValidate\Finder\FunctionCallFinder; + +class FunctionCallFinderTest extends \PHPUnit\Framework\TestCase +{ + public function testFindTokens() + { + $file = __DIR__ . '/../../Fixture/moodle-local_ci/lib.php'; + $fileTokens = FileTokens::create('lib.php')->mustHave('local_ci_subtract'); + + $finder = new FunctionCallFinder(); + $finder->findTokens($file, $fileTokens); + + $this->assertTrue($fileTokens->hasFoundAllTokens()); + $this->assertFalse($fileTokens->hasHint()); + } + + public function testFindTokensNotFound() + { + $file = __DIR__ . '/../../Fixture/moodle-local_ci/lib.php'; + $fileTokens = FileTokens::create('lib.php')->mustHave('exit')->notFoundHint('Exit not found'); + + $finder = new FunctionCallFinder(); + $finder->findTokens($file, $fileTokens); + + $this->assertFalse($fileTokens->hasFoundAllTokens()); + $this->assertTrue($fileTokens->hasHint()); + $this->assertSame('Exit not found', $fileTokens->hint); + } +} diff --git a/tests/PluginValidate/Requirements/FilterRequirementsTest.php b/tests/PluginValidate/Requirements/FilterRequirementsTest.php index 7f44ed44..13dd7765 100644 --- a/tests/PluginValidate/Requirements/FilterRequirementsTest.php +++ b/tests/PluginValidate/Requirements/FilterRequirementsTest.php @@ -114,4 +114,37 @@ public function testGetRequiredStrings() $this->assertInstanceOf('MoodlePluginCI\PluginValidate\Finder\FileTokens', $fileToken); $this->assertSame('lang/en/filter_activitynames.php', $fileToken->file); } + + public function testGetRequiredFunctionCalls404() + { + $requirements = $this->getMockBuilder('MoodlePluginCI\PluginValidate\Requirements\FilterRequirements') + ->setConstructorArgs([new Plugin('filter_activitynames', 'filter', 'activitynames', ''), 404]) + ->onlyMethods(['fileExists']) + ->getMock(); + // On first call fileExists return false, on second call return true. + $requirements->method('fileExists') + ->with($this->identicalTo('classes/text_filter.php')) + ->willReturn(false, true); + + // If classes/text_filter.php does not exist, expect class alias is not needed in filter.php. + $calls = $requirements->getRequiredFunctionCalls(); + $this->assertCount(0, $calls); + + // If classes/text_filter.php exists, expect class alias in filter.php (4.5 plugin backward compatibility). + $calls = $requirements->getRequiredFunctionCalls(); + $this->assertCount(1, $calls); + $call = reset($calls); + $this->assertInstanceOf('MoodlePluginCI\PluginValidate\Finder\FileTokens', $call); + $this->assertSame('filter.php', $call->file); + } + + public function testGetRequiredFunctionCalls() + { + $calls = $this->requirements->getRequiredFunctionCalls(); + + $this->assertCount(1, $calls); + $call = reset($calls); + $this->assertInstanceOf('MoodlePluginCI\PluginValidate\Finder\FileTokens', $call); + $this->assertSame('filter.php', $call->file); + } }