From f8029e070d7c2528dfd6d29f37a2886d4dfbe894 Mon Sep 17 00:00:00 2001 From: Stephen Gallagher Date: Tue, 11 Feb 2020 12:59:14 -0500 Subject: [PATCH] Add ModuleStreamV2.search_profiles() Signed-off-by: Stephen Gallagher --- .../modulemd-2.0/modulemd-module-stream-v2.h | 17 ++++ modulemd/modulemd-module-stream-v2.c | 46 ++++++++++ modulemd/tests/ModulemdTests/modulestream.py | 88 +++++++++++++++++++ 3 files changed, 151 insertions(+) diff --git a/modulemd/include/modulemd-2.0/modulemd-module-stream-v2.h b/modulemd/include/modulemd-2.0/modulemd-module-stream-v2.h index 6fc6976fb..047e80573 100644 --- a/modulemd/include/modulemd-2.0/modulemd-module-stream-v2.h +++ b/modulemd/include/modulemd-2.0/modulemd-module-stream-v2.h @@ -543,6 +543,23 @@ modulemd_module_stream_v2_get_profile (ModulemdModuleStreamV2 *self, const gchar *profile_name); +/** + * modulemd_module_stream_v2_search_profiles: + * @self: This #ModulemdModuleStreamV2 object. + * @profile_pattern: (nullable): A globbing pattern to locate one or more + * profiles in this #ModulemdModuleStreamV2 object. The names will be compared + * using [fnmatch(3)](https://www.mankier.com/3/fnmatch). + * + * Returns: (transfer container) (element-type ModulemdProfile): The list of + * #ModulemdProfile objects whose name matched @profile_pattern. This function + * cannot fail, but it may return a zero-length list if no matches were found. + * The returned profiles will be sorted alphabetically by profile name. + */ +GPtrArray * +modulemd_module_stream_v2_search_profiles (ModulemdModuleStreamV2 *self, + const gchar *profile_pattern); + + /** * modulemd_module_stream_v2_add_rpm_api: * @self: (in): This #ModulemdModuleStreamV2 object. diff --git a/modulemd/modulemd-module-stream-v2.c b/modulemd/modulemd-module-stream-v2.c index a06e0b3e2..ca2c19cad 100644 --- a/modulemd/modulemd-module-stream-v2.c +++ b/modulemd/modulemd-module-stream-v2.c @@ -734,6 +734,52 @@ modulemd_module_stream_v2_get_profile (ModulemdModuleStreamV2 *self, } +struct profile_match_ctx +{ + GHashTable *profiles; + GPtrArray *found; + const gchar *pattern; +}; + +static void +profile_match (gpointer data, gpointer user_data) +{ + struct profile_match_ctx *match_ctx = (struct profile_match_ctx *)user_data; + + /* Add it to the found list if it matches the pattern */ + if (modulemd_fnmatch (match_ctx->pattern, (const gchar *)data)) + { + g_ptr_array_add (match_ctx->found, + g_object_ref (g_hash_table_lookup ( + match_ctx->profiles, (const gchar *)data))); + } +} + +GPtrArray * +modulemd_module_stream_v2_search_profiles (ModulemdModuleStreamV2 *self, + const gchar *profile_pattern) +{ + /* The list of profiles will probably never be large, so we'll optimize for + * the worst-case and preallocate the array to the number of profiles. + */ + GPtrArray *found = + g_ptr_array_new_full (g_hash_table_size (self->profiles), g_object_unref); + + g_return_val_if_fail (MODULEMD_IS_MODULE_STREAM_V2 (self), found); + + g_autoptr (GPtrArray) profile_names = + modulemd_ordered_str_keys (self->profiles, modulemd_strcmp_sort); + + struct profile_match_ctx match_ctx = { .profiles = self->profiles, + .found = found, + .pattern = profile_pattern }; + + g_ptr_array_foreach (profile_names, profile_match, &match_ctx); + + return found; +} + + void modulemd_module_stream_v2_add_rpm_api (ModulemdModuleStreamV2 *self, const gchar *rpm) diff --git a/modulemd/tests/ModulemdTests/modulestream.py b/modulemd/tests/ModulemdTests/modulestream.py index 56da437ff..c10b468e1 100644 --- a/modulemd/tests/ModulemdTests/modulestream.py +++ b/modulemd/tests/ModulemdTests/modulestream.py @@ -1311,6 +1311,94 @@ def test_xmd_issue_290(self): self.assertIsNotNone(output_yaml) pass + def test_search_profiles(self): + stream = Modulemd.ModuleStreamV2.new("themodule", "thestream") + + # First with no profiles added. Make sure we get back a zero-length + # array + + profiles = stream.search_profiles(None) + self.assertIsNotNone(profiles) + self.assertEqual(len(profiles), 0) + + profiles = stream.search_profiles("*") + self.assertIsNotNone(profiles) + self.assertEqual(len(profiles), 0) + + profiles = stream.search_profiles("thefirstprofile") + self.assertIsNotNone(profiles) + self.assertEqual(len(profiles), 0) + + # Now add three profiles, and confirm that searches are + # returned in alphabetical order + stream.add_profile(Modulemd.Profile.new("thesecondprofile")) + stream.add_profile(Modulemd.Profile.new("thefirstprofile")) + stream.add_profile(Modulemd.Profile.new("thethirdprofile")) + + profiles = stream.search_profiles(None) + self.assertIsNotNone(profiles) + self.assertEqual(len(profiles), 3) + self.assertIsInstance(profiles[0], Modulemd.Profile) + self.assertEqual(profiles[0].get_name(), "thefirstprofile") + self.assertIsInstance(profiles[1], Modulemd.Profile) + self.assertEqual(profiles[1].get_name(), "thesecondprofile") + self.assertIsInstance(profiles[2], Modulemd.Profile) + self.assertEqual(profiles[2].get_name(), "thethirdprofile") + + profiles = stream.search_profiles("*") + self.assertIsNotNone(profiles) + self.assertEqual(len(profiles), 3) + self.assertIsInstance(profiles[0], Modulemd.Profile) + self.assertEqual(profiles[0].get_name(), "thefirstprofile") + self.assertIsInstance(profiles[1], Modulemd.Profile) + self.assertEqual(profiles[1].get_name(), "thesecondprofile") + self.assertIsInstance(profiles[2], Modulemd.Profile) + self.assertEqual(profiles[2].get_name(), "thethirdprofile") + + profiles = stream.search_profiles("thefirstprofile") + self.assertIsNotNone(profiles) + self.assertEqual(len(profiles), 1) + self.assertIsInstance(profiles[0], Modulemd.Profile) + self.assertEqual(profiles[0].get_name(), "thefirstprofile") + + profiles = stream.search_profiles("*profile") + self.assertIsNotNone(profiles) + self.assertEqual(len(profiles), 3) + self.assertIsInstance(profiles[0], Modulemd.Profile) + self.assertEqual(profiles[0].get_name(), "thefirstprofile") + self.assertIsInstance(profiles[1], Modulemd.Profile) + self.assertEqual(profiles[1].get_name(), "thesecondprofile") + self.assertIsInstance(profiles[2], Modulemd.Profile) + self.assertEqual(profiles[2].get_name(), "thethirdprofile") + + profiles = stream.search_profiles("*dprofile*") + self.assertIsNotNone(profiles) + self.assertEqual(len(profiles), 2) + self.assertIsInstance(profiles[0], Modulemd.Profile) + self.assertEqual(profiles[0].get_name(), "thesecondprofile") + self.assertIsInstance(profiles[1], Modulemd.Profile) + self.assertEqual(profiles[1].get_name(), "thethirdprofile") + + profiles = stream.search_profiles("the*profile") + self.assertIsNotNone(profiles) + self.assertEqual(len(profiles), 3) + self.assertIsInstance(profiles[0], Modulemd.Profile) + self.assertEqual(profiles[0].get_name(), "thefirstprofile") + self.assertIsInstance(profiles[1], Modulemd.Profile) + self.assertEqual(profiles[1].get_name(), "thesecondprofile") + self.assertIsInstance(profiles[2], Modulemd.Profile) + self.assertEqual(profiles[2].get_name(), "thethirdprofile") + + profiles = stream.search_profiles("the*") + self.assertIsNotNone(profiles) + self.assertEqual(len(profiles), 3) + self.assertIsInstance(profiles[0], Modulemd.Profile) + self.assertEqual(profiles[0].get_name(), "thefirstprofile") + self.assertIsInstance(profiles[1], Modulemd.Profile) + self.assertEqual(profiles[1].get_name(), "thesecondprofile") + self.assertIsInstance(profiles[2], Modulemd.Profile) + self.assertEqual(profiles[2].get_name(), "thethirdprofile") + if __name__ == "__main__": unittest.main()