diff --git a/doc/flatpak-builder.xml b/doc/flatpak-builder.xml index 846de835..a5cad8c4 100644 --- a/doc/flatpak-builder.xml +++ b/doc/flatpak-builder.xml @@ -203,6 +203,14 @@ + + + + + Don't run any of the tests. + + + diff --git a/doc/flatpak-manifest.xml b/doc/flatpak-manifest.xml index 00d29f1f..f93c4e9c 100644 --- a/doc/flatpak-manifest.xml +++ b/doc/flatpak-manifest.xml @@ -289,6 +289,10 @@ (array of strings) This is an array containing extra options to pass to flatpak build. + + (array of strings) + Similar to build-args but affects the tests, not the normal build. + (array of strings) This is an array containing extra options to pass to configure. @@ -470,6 +474,18 @@ (array of strings) Extra files to clean up in the platform. + + (boolean) + If true this will run the tests after installing. + + + (string) + The target to build when running the tests. Defaults to "check" for make and "test" for ninja. Set to empty to disable. + + + (arrya of string) + Array of commands to run during the tests. + (array of objects or strings) An array of objects specifying nested modules to be built before this one. diff --git a/src/builder-context.c b/src/builder-context.c index 20de950a..b5383f33 100644 --- a/src/builder-context.c +++ b/src/builder-context.c @@ -75,6 +75,7 @@ struct BuilderContext gboolean rebuild_on_sdk_change; gboolean use_rofiles; gboolean have_rofiles; + gboolean run_tests; }; typedef struct @@ -826,6 +827,19 @@ builder_context_set_use_rofiles (BuilderContext *self, self->use_rofiles = use_rofiles; } +gboolean +builder_context_get_run_tests (BuilderContext *self) +{ + return self->run_tests; +} + +void +builder_context_set_run_tests (BuilderContext *self, + gboolean run_tests) +{ + self->run_tests = run_tests; +} + gboolean builder_context_get_rebuild_on_sdk_change (BuilderContext *self) { diff --git a/src/builder-context.h b/src/builder-context.h index 52a910e8..2df9ed39 100644 --- a/src/builder-context.h +++ b/src/builder-context.h @@ -125,6 +125,9 @@ gboolean builder_context_get_rofiles_active (BuilderContext *self); gboolean builder_context_get_use_rofiles (BuilderContext *self); void builder_context_set_use_rofiles (BuilderContext *self, gboolean use_rofiles); +gboolean builder_context_get_run_tests (BuilderContext *self); +void builder_context_set_run_tests (BuilderContext *self, + gboolean run_tests); char ** builder_context_extend_env (BuilderContext *self, char **envp); diff --git a/src/builder-main.c b/src/builder-main.c index 3a45f149..7278562f 100644 --- a/src/builder-main.c +++ b/src/builder-main.c @@ -38,6 +38,7 @@ static gboolean opt_verbose; static gboolean opt_version; static gboolean opt_run; static gboolean opt_disable_cache; +static gboolean opt_disable_tests; static gboolean opt_disable_rofiles; static gboolean opt_download_only; static gboolean opt_bundle_sources; @@ -87,6 +88,7 @@ static GOptionEntry entries[] = { { "run", 0, 0, G_OPTION_ARG_NONE, &opt_run, "Run a command in the build directory (see --run --help)", NULL }, { "ccache", 0, 0, G_OPTION_ARG_NONE, &opt_ccache, "Use ccache", NULL }, { "disable-cache", 0, 0, G_OPTION_ARG_NONE, &opt_disable_cache, "Disable cache lookups", NULL }, + { "disable-tests", 0, 0, G_OPTION_ARG_NONE, &opt_disable_tests, "Don't run tests", NULL }, { "disable-rofiles-fuse", 0, 0, G_OPTION_ARG_NONE, &opt_disable_rofiles, "Disable rofiles-fuse use", NULL }, { "disable-download", 0, 0, G_OPTION_ARG_NONE, &opt_disable_download, "Don't download any new sources", NULL }, { "disable-updates", 0, 0, G_OPTION_ARG_NONE, &opt_disable_updates, "Only download missing sources, never update to latest vcs version", NULL }, @@ -377,6 +379,7 @@ main (int argc, build_context = builder_context_new (cwd_dir, app_dir); builder_context_set_use_rofiles (build_context, !opt_disable_rofiles); + builder_context_set_run_tests (build_context, !opt_disable_tests); builder_context_set_keep_build_dirs (build_context, opt_keep_build_dirs); builder_context_set_delete_build_dirs (build_context, opt_delete_build_dirs); builder_context_set_sandboxed (build_context, opt_sandboxed); diff --git a/src/builder-module.c b/src/builder-module.c index 7c441502..1eb69b34 100644 --- a/src/builder-module.c +++ b/src/builder-module.c @@ -49,6 +49,7 @@ struct BuilderModule char **make_args; char **make_install_args; char *install_rule; + char *test_rule; char *buildsystem; char **ensure_writable; char **only_arches; @@ -61,6 +62,7 @@ struct BuilderModule gboolean no_python_timestamp_fix; gboolean cmake; gboolean builddir; + gboolean run_tests; BuilderOptions *build_options; GPtrArray *changes; char **cleanup; @@ -68,6 +70,7 @@ struct BuilderModule GList *sources; GList *modules; char **build_commands; + char **test_commands; }; typedef struct @@ -92,6 +95,7 @@ enum { PROP_NO_PYTHON_TIMESTAMP_FIX, PROP_CMAKE, PROP_INSTALL_RULE, + PROP_TEST_RULE, PROP_BUILDSYSTEM, PROP_BUILDDIR, PROP_CONFIG_OPTS, @@ -99,6 +103,7 @@ enum { PROP_MAKE_INSTALL_ARGS, PROP_ENSURE_WRITABLE, PROP_ONLY_ARCHES, + PROP_RUN_TESTS, PROP_SKIP_ARCHES, PROP_SOURCES, PROP_BUILD_OPTIONS, @@ -107,6 +112,7 @@ enum { PROP_POST_INSTALL, PROP_MODULES, PROP_BUILD_COMMANDS, + PROP_TEST_COMMANDS, LAST_PROP }; @@ -134,6 +140,7 @@ builder_module_finalize (GObject *object) g_free (self->name); g_free (self->subdir); g_free (self->install_rule); + g_free (self->test_rule); g_free (self->buildsystem); g_strfreev (self->post_install); g_strfreev (self->config_opts); @@ -148,6 +155,7 @@ builder_module_finalize (GObject *object) g_strfreev (self->cleanup_platform); g_list_free_full (self->modules, g_object_unref); g_strfreev (self->build_commands); + g_strfreev (self->test_commands); if (self->changes) g_ptr_array_unref (self->changes); @@ -209,6 +217,10 @@ builder_module_get_property (GObject *object, g_value_set_string (value, self->install_rule); break; + case PROP_TEST_RULE: + g_value_set_string (value, self->test_rule); + break; + case PROP_BUILDDIR: g_value_set_boolean (value, self->builddir); break; @@ -265,6 +277,14 @@ builder_module_get_property (GObject *object, g_value_set_boxed (value, self->build_commands); break; + case PROP_TEST_COMMANDS: + g_value_set_boxed (value, self->test_commands); + break; + + case PROP_RUN_TESTS: + g_value_set_boolean (value, self->run_tests); + break; + default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); } @@ -333,6 +353,11 @@ builder_module_set_property (GObject *object, self->install_rule = g_value_dup_string (value); break; + case PROP_TEST_RULE: + g_free (self->test_rule); + self->test_rule = g_value_dup_string (value); + break; + case PROP_BUILDDIR: self->builddir = g_value_get_boolean (value); break; @@ -413,6 +438,16 @@ builder_module_set_property (GObject *object, g_strfreev (tmp); break; + case PROP_TEST_COMMANDS: + tmp = self->test_commands; + self->test_commands = g_strdupv (g_value_get_boxed (value)); + g_strfreev (tmp); + break; + + case PROP_RUN_TESTS: + self->run_tests = g_value_get_boolean (value); + break; + default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); } @@ -504,6 +539,13 @@ builder_module_class_init (BuilderModuleClass *klass) "", NULL, G_PARAM_READWRITE)); + g_object_class_install_property (object_class, + PROP_TEST_RULE, + g_param_spec_string ("test-rule", + "", + "", + NULL, + G_PARAM_READWRITE)); g_object_class_install_property (object_class, PROP_BUILDDIR, g_param_spec_boolean ("builddir", @@ -600,6 +642,20 @@ builder_module_class_init (BuilderModuleClass *klass) "", G_TYPE_STRV, G_PARAM_READWRITE)); + g_object_class_install_property (object_class, + PROP_TEST_COMMANDS, + g_param_spec_boxed ("test-commands", + "", + "", + G_TYPE_STRV, + G_PARAM_READWRITE)); + g_object_class_install_property (object_class, + PROP_RUN_TESTS, + g_param_spec_boolean ("run-tests", + "", + "", + FALSE, + G_PARAM_READWRITE)); } static void @@ -1256,6 +1312,7 @@ builder_module_build_helper (BuilderModule *self, g_autofree char *make_j = NULL; g_autofree char *make_l = NULL; const char *make_cmd = NULL; + const char *test_arg = NULL; gboolean autotools = FALSE, cmake = FALSE, cmake_ninja = FALSE, meson = FALSE, simple = FALSE, qmake = FALSE; g_autoptr(GFile) configure_file = NULL; @@ -1583,11 +1640,20 @@ builder_module_build_helper (BuilderModule *self, builder_set_term_title (_("Installing %s"), self->name); if (meson || cmake_ninja) - make_cmd = "ninja"; + { + make_cmd = "ninja"; + test_arg = "test"; + } else if (simple) make_cmd = NULL; else - make_cmd = "make"; + { + make_cmd = "make"; + test_arg = "check"; + } + + if (self->test_rule) + test_arg = self->test_rule; if (make_cmd) { @@ -1645,6 +1711,41 @@ builder_module_build_helper (BuilderModule *self, } } + /* Run unit tests */ + + if (self->run_tests && builder_context_get_run_tests (context)) + { + g_auto(GStrv) test_args = NULL; + + builder_set_term_title (_("Testing %s"), self->name); + g_print ("Running tests\n"); + + test_args = builder_options_get_test_args (self->build_options, context, error); + if (test_args == NULL) + return FALSE; + + if (make_cmd && test_arg && *test_arg != 0) + { + if (!build (app_dir, self->name, context, source_dir, build_dir_relative, test_args, env, error, + make_cmd, test_arg, NULL)) + { + g_prefix_error (error, "Running %s %s failed: ", make_cmd, test_arg); + return FALSE; + } + } + + for (i = 0; self->test_commands != NULL && self->test_commands[i] != NULL; i++) + { + g_print ("Running: %s\n", self->test_commands[i]); + if (!build (app_dir, self->name, context, source_dir, build_dir_relative, test_args, env, error, + "/bin/sh", "-c", self->test_commands[i], NULL)) + { + g_prefix_error (error, "Running test command '%s' failed: ", self->test_commands[i]); + return FALSE; + } + } + } + if (!self->no_python_timestamp_fix) post_process_flags |= BUILDER_POST_PROCESS_FLAGS_PYTHON_TIMESTAMPS; @@ -1793,6 +1894,7 @@ builder_module_checksum (BuilderModule *self, builder_cache_checksum_strv (cache, self->build_commands); builder_cache_checksum_str (cache, self->buildsystem); builder_cache_checksum_str (cache, self->install_rule); + builder_cache_checksum_compat_boolean (cache, self->run_tests); if (self->build_options) builder_options_checksum (self->build_options, cache, context); diff --git a/src/builder-options.c b/src/builder-options.c index fd4119f7..7563b441 100644 --- a/src/builder-options.c +++ b/src/builder-options.c @@ -49,6 +49,7 @@ struct BuilderOptions char *prefix; char **env; char **build_args; + char **test_args; char **config_opts; char **make_args; char **make_install_args; @@ -78,6 +79,7 @@ enum { PROP_NO_DEBUGINFO_COMPRESSION, PROP_ARCH, PROP_BUILD_ARGS, + PROP_TEST_ARGS, PROP_CONFIG_OPTS, PROP_MAKE_ARGS, PROP_MAKE_INSTALL_ARGS, @@ -103,6 +105,7 @@ builder_options_finalize (GObject *object) g_free (self->prefix); g_strfreev (self->env); g_strfreev (self->build_args); + g_strfreev (self->test_args); g_strfreev (self->config_opts); g_strfreev (self->make_args); g_strfreev (self->make_install_args); @@ -165,6 +168,10 @@ builder_options_get_property (GObject *object, g_value_set_boxed (value, self->build_args); break; + case PROP_TEST_ARGS: + g_value_set_boxed (value, self->test_args); + break; + case PROP_CONFIG_OPTS: g_value_set_boxed (value, self->config_opts); break; @@ -263,6 +270,12 @@ builder_options_set_property (GObject *object, g_strfreev (tmp); break; + case PROP_TEST_ARGS: + tmp = self->test_args; + self->test_args = g_strdupv (g_value_get_boxed (value)); + g_strfreev (tmp); + break; + case PROP_CONFIG_OPTS: tmp = self->config_opts; self->config_opts = g_strdupv (g_value_get_boxed (value)); @@ -384,6 +397,13 @@ builder_options_class_init (BuilderOptionsClass *klass) "", G_TYPE_STRV, G_PARAM_READWRITE)); + g_object_class_install_property (object_class, + PROP_TEST_ARGS, + g_param_spec_boxed ("test-args", + "", + "", + G_TYPE_STRV, + G_PARAM_READWRITE)); g_object_class_install_property (object_class, PROP_CONFIG_OPTS, g_param_spec_boxed ("config-opts", @@ -942,6 +962,44 @@ builder_options_get_build_args (BuilderOptions *self, return (char **) g_ptr_array_free (g_steal_pointer (&array), FALSE); } +char ** +builder_options_get_test_args (BuilderOptions *self, + BuilderContext *context, + GError **error) +{ + g_autoptr(GList) options = get_all_options (self, context); + GList *l; + int i; + g_autoptr(GPtrArray) array = g_ptr_array_new_with_free_func (g_free); + + /* Last argument wins, so reverse the list for per-module to win */ + options = g_list_reverse (options); + + /* Always run tests readonly */ + g_ptr_array_add (array, g_strdup ("--readonly")); + + for (l = options; l != NULL; l = l->next) + { + BuilderOptions *o = l->data; + + if (o->test_args) + { + for (i = 0; o->test_args[i] != NULL; i++) + g_ptr_array_add (array, g_strdup (o->test_args[i])); + } + } + + if (array->len > 0 && builder_context_get_sandboxed (context)) + { + g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "Can't specify test-args in sandboxed build"); + return NULL; + } + + g_ptr_array_add (array, NULL); + + return (char **) g_ptr_array_free (g_steal_pointer (&array), FALSE); +} + static char ** builder_options_get_strv (BuilderOptions *self, BuilderContext *context, @@ -1019,6 +1077,7 @@ builder_options_checksum (BuilderOptions *self, builder_cache_checksum_str (cache, self->prefix); builder_cache_checksum_strv (cache, self->env); builder_cache_checksum_strv (cache, self->build_args); + builder_cache_checksum_compat_strv (cache, self->test_args); builder_cache_checksum_strv (cache, self->config_opts); builder_cache_checksum_strv (cache, self->make_args); builder_cache_checksum_strv (cache, self->make_install_args); diff --git a/src/builder-options.h b/src/builder-options.h index bc9b90bb..e210e2b4 100644 --- a/src/builder-options.h +++ b/src/builder-options.h @@ -52,6 +52,9 @@ char ** builder_options_get_env (BuilderOptions *self, char ** builder_options_get_build_args (BuilderOptions *self, BuilderContext *context, GError **error); +char ** builder_options_get_test_args (BuilderOptions *self, + BuilderContext *context, + GError **error); char ** builder_options_get_config_opts (BuilderOptions *self, BuilderContext *context, char **base_opts);