fwupd/src/fu-self-test.c
Richard Hughes 0eeaad76ec Modernize and refactor the requirement checking code
Before we had FuInstallTask which represented the specific install
action (install foo on bar), FuEngineRequest which described why the
install task was created, and FuEngine. The latter being both reposible
for calling *into* FuInstallTask and also *from* FuInstallTask as well.

Depending on the code, we had XbNodes of component and release, *and*
FwupdReleases as releases and lots of duplicated code to marshall the
former into various forms of the latter.

Create a FuRelease wrapper around FwupdRelease to replace FuInstallTask
and to simplify the code. Make the FuRelease builder do the device
requirement checks, and then use the engine to do checks requiring
engine state and context.
2022-03-30 08:17:24 +01:00

3997 lines
141 KiB
C

/*
* Copyright (C) 2015 Richard Hughes <richard@hughsie.com>
*
* SPDX-License-Identifier: LGPL-2.1+
*/
#include "config.h"
#include <fwupdplugin.h>
#include <glib-object.h>
#include <glib/gstdio.h>
#include <libgcab.h>
#include <stdlib.h>
#include <string.h>
#include <xmlb.h>
#include "fwupd-security-attr-private.h"
#include "fu-config.h"
#include "fu-context-private.h"
#include "fu-device-list.h"
#include "fu-device-private.h"
#include "fu-engine.h"
#include "fu-hash.h"
#include "fu-history.h"
#include "fu-plugin-list.h"
#include "fu-plugin-private.h"
#include "fu-progressbar.h"
#include "fu-security-attr.h"
#include "fu-smbios-private.h"
typedef struct {
FuPlugin *plugin;
FuContext *ctx;
} FuTest;
static GMainLoop *_test_loop = NULL;
static guint _test_loop_timeout_id = 0;
static gboolean
fu_test_hang_check_cb(gpointer user_data)
{
g_main_loop_quit(_test_loop);
_test_loop_timeout_id = 0;
return G_SOURCE_REMOVE;
}
static void
fu_test_loop_run_with_timeout(guint timeout_ms)
{
g_assert_cmpint(_test_loop_timeout_id, ==, 0);
g_assert_null(_test_loop);
_test_loop = g_main_loop_new(NULL, FALSE);
_test_loop_timeout_id = g_timeout_add(timeout_ms, fu_test_hang_check_cb, NULL);
g_main_loop_run(_test_loop);
}
static void
fu_test_loop_quit(void)
{
if (_test_loop_timeout_id > 0) {
g_source_remove(_test_loop_timeout_id);
_test_loop_timeout_id = 0;
}
if (_test_loop != NULL) {
g_main_loop_quit(_test_loop);
g_main_loop_unref(_test_loop);
_test_loop = NULL;
}
}
static void
fu_self_test_mkroot(void)
{
if (g_file_test("/tmp/fwupd-self-test", G_FILE_TEST_EXISTS)) {
g_autoptr(GError) error = NULL;
if (!fu_common_rmtree("/tmp/fwupd-self-test", &error))
g_warning("failed to mkroot: %s", error->message);
}
g_assert_cmpint(g_mkdir_with_parents("/tmp/fwupd-self-test/var/lib/fwupd", 0755), ==, 0);
}
static gboolean
fu_test_compare_lines(const gchar *txt1, const gchar *txt2, GError **error)
{
g_autofree gchar *output = NULL;
if (g_strcmp0(txt1, txt2) == 0)
return TRUE;
if (fu_common_fnmatch(txt2, txt1))
return TRUE;
if (!g_file_set_contents("/tmp/a", txt1, -1, error))
return FALSE;
if (!g_file_set_contents("/tmp/b", txt2, -1, error))
return FALSE;
if (!g_spawn_command_line_sync("diff -urNp /tmp/b /tmp/a", &output, NULL, NULL, error))
return FALSE;
g_set_error_literal(error, 1, 0, output);
return FALSE;
}
static void
fu_test_free(FuTest *self)
{
if (self->ctx != NULL)
g_object_unref(self->ctx);
if (self->plugin != NULL)
g_object_unref(self->plugin);
g_free(self);
}
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wunused-function"
G_DEFINE_AUTOPTR_CLEANUP_FUNC(FuTest, fu_test_free)
#pragma clang diagnostic pop
static void
fu_engine_generate_md_func(gconstpointer user_data)
{
FuTest *self = (FuTest *)user_data;
const gchar *tmp;
gboolean ret;
g_autofree gchar *filename = NULL;
g_autoptr(FuDevice) device = fu_device_new_with_context(self->ctx);
g_autoptr(FuEngine) engine = fu_engine_new(FU_APP_FLAGS_NONE);
g_autoptr(GBytes) data = NULL;
g_autoptr(GError) error = NULL;
g_autoptr(XbNode) component = NULL;
/* put cab file somewhere we can parse it */
filename =
g_test_build_filename(G_TEST_DIST, "tests", "colorhug", "colorhug-als-3.0.2.cab", NULL);
data = fu_common_get_contents_bytes(filename, &error);
g_assert_no_error(error);
g_assert_nonnull(data);
ret = fu_common_set_contents_bytes("/tmp/fwupd-self-test/var/cache/fwupd/foo.cab",
data,
&error);
g_assert_no_error(error);
g_assert_true(ret);
/* load engine and check the device was found */
ret = fu_engine_load(engine,
FU_ENGINE_LOAD_FLAG_REMOTES | FU_ENGINE_LOAD_FLAG_NO_CACHE,
&error);
g_assert_no_error(error);
g_assert_true(ret);
fu_device_add_guid(device, "12345678-1234-1234-1234-123456789012");
fu_device_set_version_format(device, FWUPD_VERSION_FORMAT_TRIPLET);
fu_device_set_version(device, "1.2.3");
component = fu_engine_get_component_by_guids(engine, device);
g_assert_nonnull(component);
/* check remote ID set */
tmp = xb_node_query_text(component, "../custom/value[@key='fwupd::RemoteId']", NULL);
g_assert_cmpstr(tmp, ==, "directory");
/* verify checksums */
tmp = xb_node_query_text(component, "releases/release/checksum[@target='container']", NULL);
g_assert_cmpstr(tmp, ==, "3da49ddd961144a79336b3ac3b0e469cb2531d0e");
tmp = xb_node_query_text(component, "releases/release/checksum[@target='content']", NULL);
g_assert_cmpstr(tmp, ==, NULL);
}
static void
fu_plugin_hash_func(gconstpointer user_data)
{
GError *error = NULL;
g_autofree gchar *pluginfn = NULL;
g_autoptr(FuEngine) engine = fu_engine_new(FU_APP_FLAGS_NONE);
g_autoptr(FuPlugin) plugin = fu_plugin_new(NULL);
gboolean ret = FALSE;
ret = fu_engine_load(engine, FU_ENGINE_LOAD_FLAG_NO_CACHE, &error);
g_assert_no_error(error);
g_assert_true(ret);
/* make sure not tainted */
ret = fu_engine_get_tainted(engine);
g_assert_false(ret);
/* create a tainted plugin */
pluginfn = g_test_build_filename(G_TEST_BUILT,
"..",
"plugins",
"test",
"libfu_plugin_invalid." G_MODULE_SUFFIX,
NULL);
ret = fu_plugin_open(plugin, pluginfn, &error);
g_assert_true(ret);
g_assert_no_error(error);
/* make sure it tainted now */
g_test_expect_message("FuEngine", G_LOG_LEVEL_WARNING, "* has incorrect built version*");
fu_engine_add_plugin(engine, plugin);
ret = fu_engine_get_tainted(engine);
g_assert_true(ret);
}
static void
fu_engine_requirements_missing_func(gconstpointer user_data)
{
gboolean ret;
g_autoptr(XbNode) component = NULL;
g_autoptr(XbSilo) silo = NULL;
g_autoptr(FuEngine) engine = fu_engine_new(FU_APP_FLAGS_NONE);
g_autoptr(FuEngineRequest) request = fu_engine_request_new(FU_ENGINE_REQUEST_KIND_ACTIVE);
g_autoptr(FuRelease) release = fu_release_new();
g_autoptr(GError) error = NULL;
const gchar *xml = "<component>"
" <requires>"
" <id compare=\"ge\" version=\"1.2.3\">not.going.to.exist</id>"
" </requires>"
" <releases>"
" <release version=\"1.2.3\"/>"
" </releases>"
"</component>";
/* set up a dummy version */
fu_engine_add_runtime_version(engine, "org.test.dummy", "1.2.3");
/* make the component require one thing */
silo = xb_silo_new_from_xml(xml, &error);
g_assert_no_error(error);
g_assert_nonnull(silo);
component = xb_silo_query_first(silo, "component", &error);
g_assert_no_error(error);
g_assert_nonnull(component);
/* check this fails */
fu_release_set_request(release, request);
ret = fu_release_load(release, component, NULL, FWUPD_INSTALL_FLAG_NONE, &error);
g_assert_no_error(error);
g_assert_true(ret);
ret = fu_engine_check_requirements(engine, release, FWUPD_INSTALL_FLAG_NONE, &error);
g_assert_error(error, FWUPD_ERROR, FWUPD_ERROR_NOT_FOUND);
g_assert_false(ret);
}
static void
fu_engine_requirements_soft_func(gconstpointer user_data)
{
gboolean ret;
g_autoptr(XbNode) component = NULL;
g_autoptr(XbSilo) silo = NULL;
g_autoptr(FuEngine) engine = fu_engine_new(FU_APP_FLAGS_NONE);
g_autoptr(FuEngineRequest) request = fu_engine_request_new(FU_ENGINE_REQUEST_KIND_ACTIVE);
g_autoptr(FuRelease) release = fu_release_new();
g_autoptr(GError) error = NULL;
const gchar *xml = "<component>"
" <suggests>"
" <id compare=\"ge\" version=\"1.2.3\">not.going.to.exist</id>"
" </suggests>"
" <releases>"
" <release version=\"1.2.3\"/>"
" </releases>"
"</component>";
/* set up a dummy version */
fu_engine_add_runtime_version(engine, "org.test.dummy", "1.2.3");
/* make the component require one thing */
silo = xb_silo_new_from_xml(xml, &error);
g_assert_no_error(error);
g_assert_nonnull(silo);
component = xb_silo_query_first(silo, "component", &error);
g_assert_no_error(error);
g_assert_nonnull(component);
/* check this passes */
fu_release_set_request(release, request);
ret = fu_release_load(release, component, NULL, FWUPD_INSTALL_FLAG_NONE, &error);
g_assert_no_error(error);
g_assert_true(ret);
ret = fu_engine_check_requirements(engine, release, FWUPD_INSTALL_FLAG_FORCE, &error);
g_assert_no_error(error);
g_assert_true(ret);
}
static void
fu_engine_requirements_client_fail_func(gconstpointer user_data)
{
gboolean ret;
g_autoptr(XbNode) component = NULL;
g_autoptr(XbSilo) silo = NULL;
g_autoptr(FuEngine) engine = fu_engine_new(FU_APP_FLAGS_NONE);
g_autoptr(FuEngineRequest) request = fu_engine_request_new(FU_ENGINE_REQUEST_KIND_ACTIVE);
g_autoptr(FuRelease) release = fu_release_new();
g_autoptr(GError) error = NULL;
const gchar *xml = "<component>"
" <requires>"
" <client>detach-action</client>"
" </requires>"
" <releases>"
" <release version=\"1.2.3\"/>"
" </releases>"
"</component>";
/* make the component require one thing */
silo = xb_silo_new_from_xml(xml, &error);
g_assert_no_error(error);
g_assert_nonnull(silo);
component = xb_silo_query_first(silo, "component", &error);
g_assert_no_error(error);
g_assert_nonnull(component);
/* check this fails */
fu_release_set_request(release, request);
ret = fu_release_load(release, component, NULL, FWUPD_INSTALL_FLAG_NONE, &error);
g_assert_no_error(error);
g_assert_true(ret);
ret = fu_engine_check_requirements(engine, release, FWUPD_INSTALL_FLAG_NONE, &error);
g_assert_error(error, FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED);
g_assert_false(ret);
}
static void
fu_engine_requirements_client_invalid_func(gconstpointer user_data)
{
gboolean ret;
g_autoptr(XbNode) component = NULL;
g_autoptr(XbSilo) silo = NULL;
g_autoptr(FuEngine) engine = fu_engine_new(FU_APP_FLAGS_NONE);
g_autoptr(FuEngineRequest) request = fu_engine_request_new(FU_ENGINE_REQUEST_KIND_ACTIVE);
g_autoptr(FuRelease) release = fu_release_new();
g_autoptr(GError) error = NULL;
const gchar *xml = "<component>"
" <requires>"
" <client>hello-dave</client>"
" </requires>"
" <releases>"
" <release version=\"1.2.3\"/>"
" </releases>"
"</component>";
/* make the component require one thing */
silo = xb_silo_new_from_xml(xml, &error);
g_assert_no_error(error);
g_assert_nonnull(silo);
component = xb_silo_query_first(silo, "component", &error);
g_assert_no_error(error);
g_assert_nonnull(component);
/* check this fails */
fu_release_set_request(release, request);
ret = fu_release_load(release, component, NULL, FWUPD_INSTALL_FLAG_NONE, &error);
g_assert_no_error(error);
g_assert_true(ret);
ret = fu_engine_check_requirements(engine, release, FWUPD_INSTALL_FLAG_NONE, &error);
g_assert_error(error, FWUPD_ERROR, FWUPD_ERROR_NOT_FOUND);
g_assert_false(ret);
}
static void
fu_engine_requirements_client_pass_func(gconstpointer user_data)
{
gboolean ret;
g_autoptr(XbNode) component = NULL;
g_autoptr(XbSilo) silo = NULL;
g_autoptr(FuEngine) engine = fu_engine_new(FU_APP_FLAGS_NONE);
g_autoptr(FuEngineRequest) request = fu_engine_request_new(FU_ENGINE_REQUEST_KIND_ACTIVE);
g_autoptr(FuRelease) release = fu_release_new();
g_autoptr(GError) error = NULL;
const gchar *xml = "<component>"
" <requires>"
" <client>detach-action</client>"
" </requires>"
" <releases>"
" <release version=\"1.2.3\"/>"
" </releases>"
"</component>";
/* set up a dummy version */
fu_engine_request_set_feature_flags(request, FWUPD_FEATURE_FLAG_DETACH_ACTION);
/* make the component require one thing */
silo = xb_silo_new_from_xml(xml, &error);
g_assert_no_error(error);
g_assert_nonnull(silo);
component = xb_silo_query_first(silo, "component", &error);
g_assert_no_error(error);
g_assert_nonnull(component);
/* check this passes */
fu_release_set_request(release, request);
ret = fu_release_load(release, component, NULL, FWUPD_INSTALL_FLAG_NONE, &error);
g_assert_no_error(error);
g_assert_true(ret);
ret = fu_engine_check_requirements(engine, release, FWUPD_INSTALL_FLAG_NONE, &error);
g_assert_no_error(error);
g_assert_true(ret);
}
static void
fu_engine_requirements_version_require_func(gconstpointer user_data)
{
FuTest *self = (FuTest *)user_data;
gboolean ret;
g_autoptr(FuDevice) device = fu_device_new_with_context(self->ctx);
g_autoptr(FuEngineRequest) request = fu_engine_request_new(FU_ENGINE_REQUEST_KIND_ACTIVE);
g_autoptr(FuRelease) release = fu_release_new();
g_autoptr(GError) error = NULL;
g_autoptr(XbNode) component = NULL;
g_autoptr(XbSilo) silo = NULL;
const gchar *xml =
"<component>"
" <provides>"
" <firmware type=\"flashed\">12345678-1234-1234-1234-123456789012</firmware>"
" </provides>"
" <releases>"
" <release version=\"1.2.4\">"
" </release>"
" </releases>"
"</component>";
/* set up a dummy device */
fu_device_set_version_format(device, FWUPD_VERSION_FORMAT_TRIPLET);
fu_device_set_version(device, "1.2.3");
fu_device_set_version_bootloader(device, "4.5.6");
fu_device_add_vendor_id(device, "FFFF");
fu_device_add_flag(device, FWUPD_DEVICE_FLAG_UPDATABLE);
fu_device_add_flag(device, FWUPD_DEVICE_FLAG_UNSIGNED_PAYLOAD);
fu_device_add_flag(device, FWUPD_DEVICE_FLAG_VERSION_CHECK_REQUIRED);
fu_device_add_guid(device, "12345678-1234-1234-1234-123456789012");
/* make the component require one thing */
silo = xb_silo_new_from_xml(xml, &error);
g_assert_no_error(error);
g_assert_nonnull(silo);
component = xb_silo_query_first(silo, "component", &error);
g_assert_no_error(error);
g_assert_nonnull(component);
/* check this fails */
fu_release_set_device(release, device);
fu_release_set_request(release, request);
ret = fu_release_load(release, component, NULL, FWUPD_INSTALL_FLAG_NONE, &error);
g_assert_error(error, FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED);
g_assert_true(
g_str_has_prefix(error->message, "device requires firmware with a version check"));
g_assert_false(ret);
}
static void
fu_engine_requirements_version_lowest_func(gconstpointer user_data)
{
FuTest *self = (FuTest *)user_data;
gboolean ret;
g_autoptr(FuDevice) device = fu_device_new_with_context(self->ctx);
g_autoptr(FuEngineRequest) request = fu_engine_request_new(FU_ENGINE_REQUEST_KIND_ACTIVE);
g_autoptr(FuRelease) release = fu_release_new();
g_autoptr(GError) error = NULL;
g_autoptr(XbNode) component = NULL;
g_autoptr(XbSilo) silo = NULL;
const gchar *xml =
"<component>"
" <provides>"
" <firmware type=\"flashed\">12345678-1234-1234-1234-123456789012</firmware>"
" </provides>"
" <releases>"
" <release version=\"1.2.2\">"
" </release>"
" </releases>"
"</component>";
/* set up a dummy device */
fu_device_set_version_format(device, FWUPD_VERSION_FORMAT_TRIPLET);
fu_device_set_version(device, "1.2.3");
fu_device_set_version_lowest(device, "1.2.3");
fu_device_add_vendor_id(device, "FFFF");
fu_device_add_flag(device, FWUPD_DEVICE_FLAG_UPDATABLE);
fu_device_add_flag(device, FWUPD_DEVICE_FLAG_UNSIGNED_PAYLOAD);
fu_device_add_guid(device, "12345678-1234-1234-1234-123456789012");
/* make the component require one thing */
silo = xb_silo_new_from_xml(xml, &error);
g_assert_no_error(error);
g_assert_nonnull(silo);
component = xb_silo_query_first(silo, "component", &error);
g_assert_no_error(error);
g_assert_nonnull(component);
/* check this fails */
fu_release_set_device(release, device);
fu_release_set_request(release, request);
ret = fu_release_load(release, component, NULL, FWUPD_INSTALL_FLAG_NONE, &error);
g_assert_error(error, FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED);
g_assert_true(
g_str_has_prefix(error->message, "Specified firmware is older than the minimum"));
g_assert_false(ret);
}
static void
fu_engine_requirements_unsupported_func(gconstpointer user_data)
{
gboolean ret;
g_autoptr(XbNode) component = NULL;
g_autoptr(XbSilo) silo = NULL;
g_autoptr(FuEngine) engine = fu_engine_new(FU_APP_FLAGS_NONE);
g_autoptr(FuEngineRequest) request = fu_engine_request_new(FU_ENGINE_REQUEST_KIND_ACTIVE);
g_autoptr(FuRelease) release = fu_release_new();
g_autoptr(GError) error = NULL;
const gchar *xml = "<component>"
" <requires>"
" <UNKNOWN compare=\"ge\" version=\"2.6.0\"/>"
" </requires>"
" <releases>"
" <release version=\"1.2.3\"/>"
" </releases>"
"</component>";
/* set up a dummy version */
fu_engine_add_runtime_version(engine, "org.test.dummy", "1.2.3");
/* make the component require one thing that we don't support */
silo = xb_silo_new_from_xml(xml, &error);
g_assert_no_error(error);
g_assert_nonnull(silo);
component = xb_silo_query_first(silo, "component", &error);
g_assert_no_error(error);
g_assert_nonnull(component);
/* check this fails */
fu_release_set_request(release, request);
ret = fu_release_load(release, component, NULL, FWUPD_INSTALL_FLAG_NONE, &error);
g_assert_no_error(error);
g_assert_true(ret);
ret = fu_engine_check_requirements(engine, release, FWUPD_INSTALL_FLAG_NONE, &error);
g_assert_error(error, FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED);
g_assert_false(ret);
}
static void
fu_engine_requirements_child_func(gconstpointer user_data)
{
FuTest *self = (FuTest *)user_data;
gboolean ret;
g_autoptr(FuDevice) device = fu_device_new_with_context(self->ctx);
g_autoptr(FuDevice) child = fu_device_new();
g_autoptr(FuEngine) engine = fu_engine_new(FU_APP_FLAGS_NONE);
g_autoptr(FuEngineRequest) request = fu_engine_request_new(FU_ENGINE_REQUEST_KIND_ACTIVE);
g_autoptr(FuRelease) release = fu_release_new();
g_autoptr(GError) error = NULL;
g_autoptr(XbNode) component = NULL;
g_autoptr(XbSilo) silo = NULL;
const gchar *xml =
"<component>"
" <requires>"
" <firmware compare=\"eq\" version=\"0.0.1\">not-child</firmware>"
" </requires>"
" <provides>"
" <firmware type=\"flashed\">12345678-1234-1234-1234-123456789012</firmware>"
" </provides>"
" <releases>"
" <release version=\"1.2.4\">"
" <checksum type=\"sha1\" filename=\"bios.bin\" target=\"content\"/>"
" </release>"
" </releases>"
"</component>";
/* set up a dummy device */
fu_device_set_version_format(device, FWUPD_VERSION_FORMAT_TRIPLET);
fu_device_set_version(device, "1.2.3");
fu_device_set_version_bootloader(device, "4.5.6");
fu_device_add_vendor_id(device, "FFFF");
fu_device_add_flag(device, FWUPD_DEVICE_FLAG_UPDATABLE);
fu_device_add_flag(device, FWUPD_DEVICE_FLAG_UNSIGNED_PAYLOAD);
fu_device_add_guid(device, "12345678-1234-1234-1234-123456789012");
fu_device_set_version_format(child, FWUPD_VERSION_FORMAT_TRIPLET);
fu_device_set_version(child, "0.0.999");
fu_device_set_physical_id(child, "dummy");
fu_device_add_child(device, child);
/* make the component require three things */
silo = xb_silo_new_from_xml(xml, &error);
g_assert_no_error(error);
g_assert_nonnull(silo);
component = xb_silo_query_first(silo, "component", &error);
g_assert_no_error(error);
g_assert_nonnull(component);
/* check this passes */
fu_release_set_device(release, device);
fu_release_set_request(release, request);
ret = fu_release_load(release, component, NULL, FWUPD_INSTALL_FLAG_NONE, &error);
g_assert_no_error(error);
g_assert_true(ret);
ret = fu_engine_check_requirements(engine, release, FWUPD_INSTALL_FLAG_NONE, &error);
g_assert_no_error(error);
g_assert_true(ret);
}
static void
fu_engine_requirements_child_fail_func(gconstpointer user_data)
{
FuTest *self = (FuTest *)user_data;
gboolean ret;
g_autoptr(FuDevice) device = fu_device_new_with_context(self->ctx);
g_autoptr(FuDevice) child = fu_device_new();
g_autoptr(FuEngine) engine = fu_engine_new(FU_APP_FLAGS_NONE);
g_autoptr(FuEngineRequest) request = fu_engine_request_new(FU_ENGINE_REQUEST_KIND_ACTIVE);
g_autoptr(FuRelease) release = fu_release_new();
g_autoptr(GError) error = NULL;
g_autoptr(XbNode) component = NULL;
g_autoptr(XbSilo) silo = NULL;
const gchar *xml =
"<component>"
" <requires>"
" <firmware compare=\"glob\" version=\"0.0.*\">not-child</firmware>"
" </requires>"
" <provides>"
" <firmware type=\"flashed\">12345678-1234-1234-1234-123456789012</firmware>"
" </provides>"
" <releases>"
" <release version=\"1.2.4\">"
" <checksum type=\"sha1\" filename=\"bios.bin\" target=\"content\"/>"
" </release>"
" </releases>"
"</component>";
/* set up a dummy device */
fu_device_set_version_format(device, FWUPD_VERSION_FORMAT_TRIPLET);
fu_device_set_version(device, "1.2.3");
fu_device_set_version_bootloader(device, "4.5.6");
fu_device_add_vendor_id(device, "FFFF");
fu_device_add_flag(device, FWUPD_DEVICE_FLAG_UPDATABLE);
fu_device_add_flag(device, FWUPD_DEVICE_FLAG_UNSIGNED_PAYLOAD);
fu_device_add_guid(device, "12345678-1234-1234-1234-123456789012");
fu_device_set_version_format(child, FWUPD_VERSION_FORMAT_TRIPLET);
fu_device_set_version(child, "0.0.1");
fu_device_set_physical_id(child, "dummy");
fu_device_add_child(device, child);
/* make the component require three things */
silo = xb_silo_new_from_xml(xml, &error);
g_assert_no_error(error);
g_assert_nonnull(silo);
component = xb_silo_query_first(silo, "component", &error);
g_assert_no_error(error);
g_assert_nonnull(component);
/* check this passes */
fu_release_set_device(release, device);
fu_release_set_request(release, request);
ret = fu_release_load(release, component, NULL, FWUPD_INSTALL_FLAG_NONE, &error);
g_assert_no_error(error);
g_assert_true(ret);
ret = fu_engine_check_requirements(engine, release, FWUPD_INSTALL_FLAG_NONE, &error);
g_assert_error(error, FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED);
g_assert_nonnull(
g_strstr_len(error->message, -1, "Not compatible with child device version"));
g_assert_false(ret);
}
static void
fu_engine_requirements_func(gconstpointer user_data)
{
gboolean ret;
g_autoptr(FuEngine) engine = fu_engine_new(FU_APP_FLAGS_NONE);
g_autoptr(FuRelease) release = fu_release_new();
g_autoptr(FuEngineRequest) request = fu_engine_request_new(FU_ENGINE_REQUEST_KIND_ACTIVE);
g_autoptr(GError) error = NULL;
g_autoptr(XbNode) component = NULL;
g_autoptr(XbSilo) silo = NULL;
const gchar *xml = "<component>"
" <requires>"
" <id compare=\"ge\" version=\"1.2.3\">org.test.dummy</id>"
" </requires>"
" <releases>"
" <release version=\"1.2.3\"/>"
" </releases>"
"</component>";
/* set up some dummy versions */
fu_engine_add_runtime_version(engine, "org.test.dummy", "1.2.3");
fu_engine_add_runtime_version(engine, "com.hughski.colorhug", "7.8.9");
/* make the component require one thing */
silo = xb_silo_new_from_xml(xml, &error);
g_assert_no_error(error);
g_assert_nonnull(silo);
component = xb_silo_query_first(silo, "component", &error);
g_assert_no_error(error);
g_assert_nonnull(component);
/* check this passes */
fu_release_set_request(release, request);
ret = fu_release_load(release, component, NULL, FWUPD_INSTALL_FLAG_NONE, &error);
g_assert_no_error(error);
g_assert_true(ret);
ret = fu_engine_check_requirements(engine, release, FWUPD_INSTALL_FLAG_NONE, &error);
g_assert_no_error(error);
g_assert_true(ret);
}
static void
fu_engine_requirements_device_func(gconstpointer user_data)
{
FuTest *self = (FuTest *)user_data;
gboolean ret;
g_autoptr(FuDevice) device = fu_device_new_with_context(self->ctx);
g_autoptr(FuEngine) engine = fu_engine_new(FU_APP_FLAGS_NONE);
g_autoptr(FuEngineRequest) request = fu_engine_request_new(FU_ENGINE_REQUEST_KIND_ACTIVE);
g_autoptr(FuRelease) release = fu_release_new();
g_autoptr(GError) error = NULL;
g_autoptr(XbNode) component = NULL;
g_autoptr(XbSilo) silo = NULL;
const gchar *xml =
"<component>"
" <requires>"
" <firmware compare=\"ge\" version=\"1.2.3\"/>"
" <firmware compare=\"eq\" version=\"4.5.6\">bootloader</firmware>"
" <firmware compare=\"regex\" version=\"USB:0xFFFF|DMI:Lenovo\">vendor-id</firmware>"
#ifdef __linux__
" <id compare=\"ge\" version=\"4.0.0\">org.kernel</id>"
#endif
" </requires>"
" <provides>"
" <firmware type=\"flashed\">12345678-1234-1234-1234-123456789012</firmware>"
" </provides>"
" <releases>"
" <release version=\"1.2.4\">"
" <checksum type=\"sha1\" filename=\"bios.bin\" target=\"content\"/>"
" </release>"
" </releases>"
"</component>";
/* set up a dummy device */
fu_device_set_version_format(device, FWUPD_VERSION_FORMAT_TRIPLET);
fu_device_set_version(device, "1.2.3");
fu_device_set_version_bootloader(device, "4.5.6");
fu_device_add_vendor_id(device, "USB:0xFFFF");
fu_device_add_vendor_id(device, "PCI:0x0000");
fu_device_add_flag(device, FWUPD_DEVICE_FLAG_UPDATABLE);
fu_device_add_flag(device, FWUPD_DEVICE_FLAG_UNSIGNED_PAYLOAD);
fu_device_add_flag(device, FWUPD_DEVICE_FLAG_VERSION_CHECK_REQUIRED);
fu_device_add_guid(device, "12345678-1234-1234-1234-123456789012");
/* make the component require three things */
silo = xb_silo_new_from_xml(xml, &error);
g_assert_no_error(error);
g_assert_nonnull(silo);
component = xb_silo_query_first(silo, "component", &error);
g_assert_no_error(error);
g_assert_nonnull(component);
/* check this passes */
fu_release_set_device(release, device);
fu_release_set_request(release, request);
ret = fu_release_load(release, component, NULL, FWUPD_INSTALL_FLAG_NONE, &error);
g_assert_no_error(error);
g_assert_true(ret);
ret = fu_engine_check_requirements(engine, release, FWUPD_INSTALL_FLAG_NONE, &error);
g_assert_no_error(error);
g_assert_true(ret);
}
static void
fu_engine_requirements_device_plain_func(gconstpointer user_data)
{
FuTest *self = (FuTest *)user_data;
gboolean ret;
g_autoptr(FuDevice) device = fu_device_new_with_context(self->ctx);
g_autoptr(FuEngine) engine = fu_engine_new(FU_APP_FLAGS_NONE);
g_autoptr(FuEngineRequest) request = fu_engine_request_new(FU_ENGINE_REQUEST_KIND_ACTIVE);
g_autoptr(FuRelease) release = fu_release_new();
g_autoptr(GError) error = NULL;
g_autoptr(XbNode) component = NULL;
g_autoptr(XbSilo) silo = NULL;
const gchar *xml =
"<component>"
" <provides>"
" <firmware type=\"flashed\">12345678-1234-1234-1234-123456789012</firmware>"
" </provides>"
" <releases>"
" <release version=\"51H0AALB\">"
" <checksum type=\"sha1\" filename=\"bios.bin\" target=\"content\"/>"
" </release>"
" </releases>"
"</component>";
/* set up a dummy device */
fu_device_set_version_format(device, FWUPD_VERSION_FORMAT_PLAIN);
fu_device_set_version(device, "5101AALB");
fu_device_add_vendor_id(device, "FFFF");
fu_device_add_flag(device, FWUPD_DEVICE_FLAG_UPDATABLE);
fu_device_add_flag(device, FWUPD_DEVICE_FLAG_UNSIGNED_PAYLOAD);
fu_device_add_guid(device, "12345678-1234-1234-1234-123456789012");
/* make the component require three things */
silo = xb_silo_new_from_xml(xml, &error);
g_assert_no_error(error);
g_assert_nonnull(silo);
component = xb_silo_query_first(silo, "component", &error);
g_assert_no_error(error);
g_assert_nonnull(component);
/* check this passes */
fu_release_set_device(release, device);
fu_release_set_request(release, request);
ret = fu_release_load(release, component, NULL, FWUPD_INSTALL_FLAG_NONE, &error);
g_assert_no_error(error);
g_assert_true(ret);
ret = fu_engine_check_requirements(engine, release, FWUPD_INSTALL_FLAG_NONE, &error);
g_assert_no_error(error);
g_assert_true(ret);
}
static void
fu_engine_requirements_version_format_func(gconstpointer user_data)
{
FuTest *self = (FuTest *)user_data;
gboolean ret;
g_autoptr(FuDevice) device = fu_device_new_with_context(self->ctx);
g_autoptr(FuEngineRequest) request = fu_engine_request_new(FU_ENGINE_REQUEST_KIND_ACTIVE);
g_autoptr(FuRelease) release = fu_release_new();
g_autoptr(GError) error = NULL;
g_autoptr(XbNode) component = NULL;
g_autoptr(XbSilo) silo = NULL;
const gchar *xml =
"<component>"
" <provides>"
" <firmware type=\"flashed\">12345678-1234-1234-1234-123456789012</firmware>"
" </provides>"
" <releases>"
" <release version=\"1.2.4\">"
" <checksum type=\"sha1\" filename=\"bios.bin\" target=\"content\"/>"
" </release>"
" </releases>"
" <custom>"
" <value key=\"LVFS::VersionFormat\">triplet</value>"
" </custom>"
"</component>";
/* set up a dummy device */
fu_device_set_version_format(device, FWUPD_VERSION_FORMAT_QUAD);
fu_device_set_version(device, "1.2.3.4");
fu_device_add_flag(device, FWUPD_DEVICE_FLAG_UPDATABLE);
fu_device_add_flag(device, FWUPD_DEVICE_FLAG_UNSIGNED_PAYLOAD);
fu_device_add_guid(device, "12345678-1234-1234-1234-123456789012");
/* make the component require three things */
silo = xb_silo_new_from_xml(xml, &error);
g_assert_no_error(error);
g_assert_nonnull(silo);
component = xb_silo_query_first(silo, "component", &error);
g_assert_no_error(error);
g_assert_nonnull(component);
/* check this fails */
fu_release_set_device(release, device);
fu_release_set_request(release, request);
ret = fu_release_load(release, component, NULL, FWUPD_INSTALL_FLAG_NONE, &error);
g_assert_error(error, FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED);
g_assert_nonnull(
g_strstr_len(error->message, -1, "Firmware version formats were different"));
g_assert_false(ret);
}
static void
fu_engine_requirements_only_upgrade_func(gconstpointer user_data)
{
FuTest *self = (FuTest *)user_data;
gboolean ret;
g_autoptr(FuDevice) device = fu_device_new_with_context(self->ctx);
g_autoptr(FuEngineRequest) request = fu_engine_request_new(FU_ENGINE_REQUEST_KIND_ACTIVE);
g_autoptr(FuRelease) release = fu_release_new();
g_autoptr(GError) error = NULL;
g_autoptr(XbNode) component = NULL;
g_autoptr(XbSilo) silo = NULL;
const gchar *xml =
"<component>"
" <provides>"
" <firmware type=\"flashed\">12345678-1234-1234-1234-123456789012</firmware>"
" </provides>"
" <releases>"
" <release version=\"1.2.3\"/>"
" </releases>"
"</component>";
/* set up a dummy device */
fu_device_set_version(device, "1.2.4");
fu_device_add_flag(device, FWUPD_DEVICE_FLAG_UPDATABLE);
fu_device_add_flag(device, FWUPD_DEVICE_FLAG_UNSIGNED_PAYLOAD);
fu_device_add_flag(device, FWUPD_DEVICE_FLAG_ONLY_VERSION_UPGRADE);
fu_device_add_guid(device, "12345678-1234-1234-1234-123456789012");
/* make the component require three things */
silo = xb_silo_new_from_xml(xml, &error);
g_assert_no_error(error);
g_assert_nonnull(silo);
component = xb_silo_query_first(silo, "component", &error);
g_assert_no_error(error);
g_assert_nonnull(component);
/* check this fails */
fu_release_set_device(release, device);
fu_release_set_request(release, request);
ret = fu_release_load(release, component, NULL, FWUPD_INSTALL_FLAG_NONE, &error);
g_assert_error(error, FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED);
g_assert_nonnull(g_strstr_len(error->message, -1, "Device only supports version upgrades"));
g_assert_false(ret);
}
static void
fu_engine_requirements_sibling_device_func(gconstpointer user_data)
{
FuTest *self = (FuTest *)user_data;
gboolean ret;
g_autoptr(FuDevice) device1 = fu_device_new_with_context(self->ctx);
g_autoptr(FuDevice) device2 = fu_device_new_with_context(self->ctx);
g_autoptr(FuDevice) unrelated_device3 = fu_device_new_with_context(self->ctx);
g_autoptr(FuDevice) parent = fu_device_new_with_context(self->ctx);
g_autoptr(FuEngine) engine = fu_engine_new(FU_APP_FLAGS_NONE);
g_autoptr(FuEngineRequest) request = fu_engine_request_new(FU_ENGINE_REQUEST_KIND_ACTIVE);
g_autoptr(FuRelease) release1 = fu_release_new();
g_autoptr(FuRelease) release2 = fu_release_new();
g_autoptr(GError) error = NULL;
g_autoptr(XbNode) component = NULL;
g_autoptr(XbSilo) silo = NULL;
g_autoptr(XbSilo) silo_empty = xb_silo_new();
const gchar *xml =
"<component>"
" <requires>"
" <firmware depth=\"0\">1ff60ab2-3905-06a1-b476-0371f00c9e9b</firmware>"
" </requires>"
" <provides>"
" <firmware type=\"flashed\">12345678-1234-1234-1234-123456789012</firmware>"
" </provides>"
" <releases>"
" <release version=\"1.2.4\">"
" <checksum type=\"sha1\" filename=\"bios.bin\" target=\"content\"/>"
" </release>"
" </releases>"
"</component>";
/* no metadata in daemon */
fu_engine_set_silo(engine, silo_empty);
/* set up a dummy device */
fu_device_set_id(device1, "id1");
fu_device_set_version_format(device1, FWUPD_VERSION_FORMAT_TRIPLET);
fu_device_set_version(device1, "1.2.3");
fu_device_add_vendor_id(device1, "FFFF");
fu_device_add_flag(device1, FWUPD_DEVICE_FLAG_UPDATABLE);
fu_device_add_flag(device1, FWUPD_DEVICE_FLAG_UNSIGNED_PAYLOAD);
fu_device_add_guid(device1, "12345678-1234-1234-1234-123456789012");
fu_device_add_protocol(device1, "com.acme");
fu_engine_add_device(engine, device1);
/* setup the parent */
fu_device_set_id(parent, "parent");
fu_device_set_version_format(parent, FWUPD_VERSION_FORMAT_TRIPLET);
fu_device_set_version(parent, "1.0.0");
fu_device_add_flag(parent, FWUPD_DEVICE_FLAG_UPDATABLE);
fu_device_add_flag(parent, FWUPD_DEVICE_FLAG_UNSIGNED_PAYLOAD);
fu_device_add_guid(parent, "42f3d696-0b6f-4d69-908f-357f98ef115e");
fu_device_add_protocol(parent, "com.acme");
fu_device_add_child(parent, device1);
fu_engine_add_device(engine, parent);
/* set up a different device */
fu_device_set_id(unrelated_device3, "id3");
fu_device_add_vendor_id(unrelated_device3, "USB:FFFF");
fu_device_add_protocol(unrelated_device3, "com.acme");
fu_device_set_name(unrelated_device3, "Foo bar device");
fu_device_set_version_format(unrelated_device3, FWUPD_VERSION_FORMAT_TRIPLET);
fu_device_set_version(unrelated_device3, "1.5.3");
fu_device_add_vendor_id(unrelated_device3, "FFFF");
fu_device_add_flag(unrelated_device3, FWUPD_DEVICE_FLAG_UPDATABLE);
fu_device_add_flag(unrelated_device3, FWUPD_DEVICE_FLAG_UNSIGNED_PAYLOAD);
fu_device_add_guid(unrelated_device3, "3e455c08-352e-4a16-84d3-f04287289fa2");
fu_engine_add_device(engine, unrelated_device3);
/* import firmware metainfo */
silo = xb_silo_new_from_xml(xml, &error);
g_assert_no_error(error);
g_assert_nonnull(silo);
component = xb_silo_query_first(silo, "component", &error);
g_assert_no_error(error);
g_assert_nonnull(component);
/* check this fails */
fu_release_set_device(release1, device1);
fu_release_set_request(release1, request);
ret = fu_release_load(release1, component, NULL, FWUPD_INSTALL_FLAG_NONE, &error);
g_assert_no_error(error);
g_assert_true(ret);
ret = fu_engine_check_requirements(engine, release1, FWUPD_INSTALL_FLAG_NONE, &error);
g_assert_error(error, FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED);
g_assert_false(ret);
g_clear_error(&error);
/* set up a sibling device */
fu_device_set_id(device2, "id2");
fu_device_add_vendor_id(device2, "USB:FFFF");
fu_device_add_protocol(device2, "com.acme");
fu_device_set_name(device2, "Secondary firmware");
fu_device_set_version_format(device2, FWUPD_VERSION_FORMAT_TRIPLET);
fu_device_set_version(device2, "4.5.6");
fu_device_add_vendor_id(device2, "FFFF");
fu_device_add_flag(device2, FWUPD_DEVICE_FLAG_UPDATABLE);
fu_device_add_flag(device2, FWUPD_DEVICE_FLAG_UNSIGNED_PAYLOAD);
fu_device_add_guid(device2, "1ff60ab2-3905-06a1-b476-0371f00c9e9b");
fu_device_add_child(parent, device2);
fu_engine_add_device(engine, device2);
/* check this passes */
fu_release_set_device(release2, device1);
fu_release_set_request(release2, request);
ret = fu_release_load(release2, component, NULL, FWUPD_INSTALL_FLAG_NONE, &error);
g_assert_no_error(error);
g_assert_true(ret);
ret = fu_engine_check_requirements(engine, release2, FWUPD_INSTALL_FLAG_NONE, &error);
g_assert_no_error(error);
g_assert_true(ret);
}
static void
fu_engine_requirements_other_device_func(gconstpointer user_data)
{
FuTest *self = (FuTest *)user_data;
gboolean ret;
g_autoptr(FuDevice) device1 = fu_device_new_with_context(self->ctx);
g_autoptr(FuDevice) device2 = fu_device_new_with_context(self->ctx);
g_autoptr(FuEngine) engine = fu_engine_new(FU_APP_FLAGS_NONE);
g_autoptr(FuEngineRequest) request = fu_engine_request_new(FU_ENGINE_REQUEST_KIND_ACTIVE);
g_autoptr(FuRelease) release = fu_release_new();
g_autoptr(GError) error = NULL;
g_autoptr(XbNode) component = NULL;
g_autoptr(XbSilo) silo = NULL;
g_autoptr(XbSilo) silo_empty = xb_silo_new();
const gchar *xml =
"<component>"
" <requires>"
" <firmware compare=\"gt\" "
"version=\"4.0.0\">1ff60ab2-3905-06a1-b476-0371f00c9e9b</firmware>"
" </requires>"
" <provides>"
" <firmware type=\"flashed\">12345678-1234-1234-1234-123456789012</firmware>"
" </provides>"
" <releases>"
" <release version=\"1.2.4\">"
" <checksum type=\"sha1\" filename=\"bios.bin\" target=\"content\"/>"
" </release>"
" </releases>"
"</component>";
/* no metadata in daemon */
fu_engine_set_silo(engine, silo_empty);
/* set up a dummy device */
fu_device_set_version_format(device1, FWUPD_VERSION_FORMAT_TRIPLET);
fu_device_set_version(device1, "1.2.3");
fu_device_add_flag(device1, FWUPD_DEVICE_FLAG_UPDATABLE);
fu_device_add_flag(device1, FWUPD_DEVICE_FLAG_UNSIGNED_PAYLOAD);
fu_device_add_guid(device1, "12345678-1234-1234-1234-123456789012");
/* set up a different device */
fu_device_set_id(device2, "id2");
fu_device_add_vendor_id(device2, "USB:FFFF");
fu_device_add_protocol(device2, "com.acme");
fu_device_set_name(device2, "Secondary firmware");
fu_device_set_version_format(device2, FWUPD_VERSION_FORMAT_TRIPLET);
fu_device_set_version(device2, "4.5.6");
fu_device_add_vendor_id(device2, "FFFF");
fu_device_add_guid(device2, "1ff60ab2-3905-06a1-b476-0371f00c9e9b");
fu_engine_add_device(engine, device2);
/* import firmware metainfo */
silo = xb_silo_new_from_xml(xml, &error);
g_assert_no_error(error);
g_assert_nonnull(silo);
component = xb_silo_query_first(silo, "component", &error);
g_assert_no_error(error);
g_assert_nonnull(component);
/* check this passes */
fu_release_set_device(release, device1);
fu_release_set_request(release, request);
ret = fu_release_load(release, component, NULL, FWUPD_INSTALL_FLAG_NONE, &error);
g_assert_no_error(error);
g_assert_true(ret);
ret = fu_engine_check_requirements(engine, release, FWUPD_INSTALL_FLAG_NONE, &error);
g_assert_no_error(error);
g_assert_true(ret);
}
static void
fu_engine_requirements_protocol_check_func(gconstpointer user_data)
{
FuTest *self = (FuTest *)user_data;
g_autoptr(FuDevice) device1 = fu_device_new_with_context(self->ctx);
g_autoptr(FuDevice) device2 = fu_device_new_with_context(self->ctx);
g_autoptr(FuEngine) engine = fu_engine_new(FU_APP_FLAGS_NONE);
g_autoptr(FuEngineRequest) request = fu_engine_request_new(FU_ENGINE_REQUEST_KIND_ACTIVE);
g_autoptr(GPtrArray) devices = NULL;
g_autoptr(FuRelease) release1 = fu_release_new();
g_autoptr(FuRelease) release2 = fu_release_new();
g_autoptr(GError) error = NULL;
g_autoptr(XbNode) component = NULL;
g_autoptr(XbSilo) silo = NULL;
g_autoptr(XbSilo) silo_empty = xb_silo_new();
gboolean ret;
const gchar *xml =
"<component>"
" <provides>"
" <firmware type=\"flashed\">12345678-1234-1234-1234-123456789012</firmware>"
" </provides>"
" <releases>"
" <release version=\"4.5.7\">"
" <checksum type=\"sha1\" filename=\"bios.bin\" target=\"content\"/>"
" </release>"
" </releases>"
" <custom>"
" <value key=\"LVFS::UpdateProtocol\">org.bar</value>"
" </custom>"
"</component>";
/* no metadata in daemon */
fu_engine_set_silo(engine, silo_empty);
fu_device_set_id(device1, "NVME");
fu_device_add_protocol(device1, "com.acme");
fu_device_set_name(device1, "NVME device");
fu_device_add_vendor_id(device1, "ACME");
fu_device_set_version_format(device1, FWUPD_VERSION_FORMAT_TRIPLET);
fu_device_set_version(device1, "1.2.3");
fu_device_add_guid(device1, "12345678-1234-1234-1234-123456789012");
fu_device_add_flag(device1, FWUPD_DEVICE_FLAG_UPDATABLE);
fu_device_add_flag(device1, FWUPD_DEVICE_FLAG_UNSIGNED_PAYLOAD);
fu_engine_add_device(engine, device1);
fu_device_set_id(device2, "UEFI");
fu_device_add_protocol(device2, "org.bar");
fu_device_set_name(device2, "UEFI device");
fu_device_add_vendor_id(device2, "ACME");
fu_device_set_version_format(device2, FWUPD_VERSION_FORMAT_TRIPLET);
fu_device_set_version(device2, "1.2.3");
fu_device_add_guid(device2, "12345678-1234-1234-1234-123456789012");
fu_device_add_flag(device2, FWUPD_DEVICE_FLAG_UPDATABLE);
fu_device_add_flag(device2, FWUPD_DEVICE_FLAG_UNSIGNED_PAYLOAD);
fu_engine_add_device(engine, device2);
/* make sure both devices added */
devices = fu_engine_get_devices(engine, &error);
g_assert_no_error(error);
g_assert_nonnull(devices);
g_assert_cmpint(devices->len, ==, 2);
/* import firmware metainfo */
silo = xb_silo_new_from_xml(xml, &error);
g_assert_no_error(error);
g_assert_nonnull(silo);
component = xb_silo_query_first(silo, "component", &error);
g_assert_no_error(error);
g_assert_nonnull(component);
/* check this fails */
fu_release_set_device(release1, device1);
fu_release_set_request(release1, request);
ret = fu_release_load(release1, component, NULL, FWUPD_INSTALL_FLAG_NONE, &error);
g_assert_error(error, FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED);
g_assert_false(ret);
g_clear_error(&error);
/* check this passes */
fu_release_set_device(release2, device2);
fu_release_set_request(release2, request);
ret = fu_release_load(release2, component, NULL, FWUPD_INSTALL_FLAG_NONE, &error);
g_assert_no_error(error);
g_assert_true(ret);
}
static void
fu_engine_requirements_parent_device_func(gconstpointer user_data)
{
FuTest *self = (FuTest *)user_data;
gboolean ret;
g_autoptr(FuDevice) device1 = fu_device_new_with_context(self->ctx);
g_autoptr(FuDevice) device2 = fu_device_new_with_context(self->ctx);
g_autoptr(FuEngine) engine = fu_engine_new(FU_APP_FLAGS_NONE);
g_autoptr(FuEngineRequest) request = fu_engine_request_new(FU_ENGINE_REQUEST_KIND_ACTIVE);
g_autoptr(FuRelease) release = fu_release_new();
g_autoptr(GError) error = NULL;
g_autoptr(XbNode) component = NULL;
g_autoptr(XbSilo) silo = NULL;
g_autoptr(XbSilo) silo_empty = xb_silo_new();
const gchar *xml =
"<component>"
" <requires>"
" <firmware depth=\"1\" compare=\"eq\" version=\"1.2.3\"/>"
" <firmware depth=\"1\">12345678-1234-1234-1234-123456789012</firmware>"
" </requires>"
" <provides>"
" <firmware type=\"flashed\">1ff60ab2-3905-06a1-b476-0371f00c9e9b</firmware>"
" </provides>"
" <releases>"
" <release version=\"4.5.7\">"
" <checksum type=\"sha1\" filename=\"bios.bin\" target=\"content\"/>"
" </release>"
" </releases>"
"</component>";
/* no metadata in daemon */
fu_engine_set_silo(engine, silo_empty);
/* set up child device */
fu_device_set_id(device2, "child");
fu_device_set_name(device2, "child");
fu_device_set_version_format(device2, FWUPD_VERSION_FORMAT_TRIPLET);
fu_device_set_version(device2, "4.5.6");
fu_device_add_flag(device2, FWUPD_DEVICE_FLAG_UPDATABLE);
fu_device_add_flag(device2, FWUPD_DEVICE_FLAG_UNSIGNED_PAYLOAD);
fu_device_add_guid(device2, "1ff60ab2-3905-06a1-b476-0371f00c9e9b");
/* set up a parent device */
fu_device_set_id(device1, "parent");
fu_device_add_vendor_id(device1, "USB:FFFF");
fu_device_add_protocol(device1, "com.acme");
fu_device_set_name(device1, "parent");
fu_device_set_version_format(device1, FWUPD_VERSION_FORMAT_TRIPLET);
fu_device_set_version(device1, "1.2.3");
fu_device_add_guid(device1, "12345678-1234-1234-1234-123456789012");
fu_device_add_child(device1, device2);
fu_engine_add_device(engine, device1);
/* import firmware metainfo */
silo = xb_silo_new_from_xml(xml, &error);
g_assert_no_error(error);
g_assert_nonnull(silo);
component = xb_silo_query_first(silo, "component", &error);
g_assert_no_error(error);
g_assert_nonnull(component);
/* check this passes */
fu_release_set_device(release, device2);
fu_release_set_request(release, request);
ret = fu_release_load(release, component, NULL, FWUPD_INSTALL_FLAG_NONE, &error);
g_assert_no_error(error);
g_assert_true(ret);
ret = fu_engine_check_requirements(engine, release, FWUPD_INSTALL_FLAG_NONE, &error);
g_assert_no_error(error);
g_assert_true(ret);
}
static void
fu_engine_device_parent_guid_func(gconstpointer user_data)
{
FuTest *self = (FuTest *)user_data;
g_autoptr(FuDevice) device1 = fu_device_new_with_context(self->ctx);
g_autoptr(FuDevice) device2 = fu_device_new_with_context(self->ctx);
g_autoptr(FuDevice) device3 = fu_device_new_with_context(self->ctx);
g_autoptr(FuEngine) engine = fu_engine_new(FU_APP_FLAGS_NONE);
g_autoptr(XbSilo) silo_empty = xb_silo_new();
/* no metadata in daemon */
fu_engine_set_silo(engine, silo_empty);
/* add child */
fu_device_set_id(device1, "child");
fu_device_add_vendor_id(device2, "USB:FFFF");
fu_device_add_protocol(device2, "com.acme");
fu_device_add_instance_id(device1, "child-GUID-1");
fu_device_add_parent_guid(device1, "parent-GUID");
fu_device_convert_instance_ids(device1);
fu_engine_add_device(engine, device1);
/* parent */
fu_device_set_id(device2, "parent");
fu_device_add_vendor_id(device2, "USB:FFFF");
fu_device_add_protocol(device2, "com.acme");
fu_device_add_instance_id(device2, "parent-GUID");
fu_device_set_vendor(device2, "oem");
fu_device_convert_instance_ids(device2);
/* add another child */
fu_device_set_id(device3, "child2");
fu_device_add_instance_id(device3, "child-GUID-2");
fu_device_add_parent_guid(device3, "parent-GUID");
fu_device_convert_instance_ids(device3);
fu_device_add_child(device2, device3);
/* add two together */
fu_engine_add_device(engine, device2);
/* this is normally done by fu_plugin_device_add() */
fu_engine_add_device(engine, device3);
/* verify both children were adopted */
g_assert_true(fu_device_get_parent(device3) == device2);
g_assert_true(fu_device_get_parent(device1) == device2);
g_assert_cmpstr(fu_device_get_vendor(device3), ==, "oem");
/* verify order */
g_assert_cmpint(fu_device_get_order(device1), ==, -1);
g_assert_cmpint(fu_device_get_order(device2), ==, 0);
g_assert_cmpint(fu_device_get_order(device3), ==, -1);
}
static void
fu_engine_device_parent_id_func(gconstpointer user_data)
{
FuTest *self = (FuTest *)user_data;
g_autoptr(FuDevice) device1 = fu_device_new_with_context(self->ctx);
g_autoptr(FuDevice) device2 = fu_device_new_with_context(self->ctx);
g_autoptr(FuDevice) device3 = fu_device_new_with_context(self->ctx);
g_autoptr(FuDevice) device4 = fu_device_new_with_context(self->ctx);
g_autoptr(FuEngine) engine = fu_engine_new(FU_APP_FLAGS_NONE);
g_autoptr(XbSilo) silo_empty = xb_silo_new();
/* no metadata in daemon */
fu_engine_set_silo(engine, silo_empty);
/* add child */
fu_device_set_id(device1, "child1");
fu_device_set_name(device1, "Child1");
fu_device_set_physical_id(device1, "child-ID1");
fu_device_add_vendor_id(device1, "USB:FFFF");
fu_device_add_protocol(device1, "com.acme");
fu_device_add_instance_id(device1, "child-GUID-1");
fu_device_add_parent_physical_id(device1, "parent-ID-notfound");
fu_device_add_parent_physical_id(device1, "parent-ID");
fu_device_convert_instance_ids(device1);
fu_engine_add_device(engine, device1);
/* parent */
fu_device_set_id(device2, "parent");
fu_device_set_name(device2, "Parent");
fu_device_set_physical_id(device2, "parent-ID");
fu_device_add_vendor_id(device2, "USB:FFFF");
fu_device_add_protocol(device2, "com.acme");
fu_device_add_instance_id(device2, "parent-GUID");
fu_device_set_vendor(device2, "oem");
fu_device_add_internal_flag(device2, FU_DEVICE_INTERNAL_FLAG_AUTO_PARENT_CHILDREN);
fu_device_convert_instance_ids(device2);
/* add another child */
fu_device_set_id(device3, "child2");
fu_device_set_name(device3, "Child2");
fu_device_set_physical_id(device3, "child-ID2");
fu_device_add_instance_id(device3, "child-GUID-2");
fu_device_add_parent_physical_id(device3, "parent-ID");
fu_device_convert_instance_ids(device3);
fu_device_add_child(device2, device3);
/* add two together */
fu_engine_add_device(engine, device2);
/* add non-child */
fu_device_set_id(device4, "child4");
fu_device_set_name(device4, "Child4");
fu_device_set_physical_id(device4, "child-ID4");
fu_device_add_vendor_id(device4, "USB:FFFF");
fu_device_add_protocol(device4, "com.acme");
fu_device_add_instance_id(device4, "child-GUID-4");
fu_device_add_parent_physical_id(device4, "parent-ID");
fu_device_convert_instance_ids(device4);
fu_engine_add_device(engine, device4);
/* this is normally done by fu_plugin_device_add() */
fu_engine_add_device(engine, device4);
/* verify both children were adopted */
g_assert_true(fu_device_get_parent(device3) == device2);
g_assert_true(fu_device_get_parent(device4) == device2);
g_assert_true(fu_device_get_parent(device1) == device2);
g_assert_cmpstr(fu_device_get_vendor(device3), ==, "oem");
}
static void
fu_engine_partial_hash_func(gconstpointer user_data)
{
FuTest *self = (FuTest *)user_data;
gboolean ret;
g_autoptr(FuDevice) device1 = fu_device_new_with_context(self->ctx);
g_autoptr(FuDevice) device2 = fu_device_new_with_context(self->ctx);
g_autoptr(FuEngine) engine = fu_engine_new(FU_APP_FLAGS_NONE);
g_autoptr(FuPlugin) plugin = fu_plugin_new(NULL);
g_autoptr(GError) error = NULL;
g_autoptr(GError) error_none = NULL;
g_autoptr(GError) error_both = NULL;
g_autoptr(XbSilo) silo_empty = xb_silo_new();
/* no metadata in daemon */
fu_engine_set_silo(engine, silo_empty);
/* set up dummy plugin */
fu_plugin_set_name(plugin, "test");
fu_engine_add_plugin(engine, plugin);
/* add two dummy devices */
fu_device_set_id(device1, "device1");
fu_device_add_vendor_id(device1, "USB:FFFF");
fu_device_add_protocol(device1, "com.acme");
fu_device_set_plugin(device1, "test");
fu_device_add_guid(device1, "12345678-1234-1234-1234-123456789012");
fu_engine_add_device(engine, device1);
fu_device_set_id(device2, "device21");
fu_device_add_vendor_id(device2, "USB:FFFF");
fu_device_add_protocol(device2, "com.acme");
fu_device_set_plugin(device2, "test");
fu_device_set_equivalent_id(device2, "b92f5b7560b84ca005a79f5a15de3c003ce494cf");
fu_device_add_guid(device2, "87654321-1234-1234-1234-123456789012");
fu_engine_add_device(engine, device2);
/* match nothing */
ret = fu_engine_unlock(engine, "deadbeef", &error_none);
g_assert_error(error_none, FWUPD_ERROR, FWUPD_ERROR_NOT_FOUND);
g_assert_false(ret);
/* match both */
ret = fu_engine_unlock(engine, "9", &error_both);
g_assert_error(error_both, FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED);
g_assert_false(ret);
/* match one exactly */
fu_device_add_flag(device1, FWUPD_DEVICE_FLAG_LOCKED);
fu_device_add_flag(device2, FWUPD_DEVICE_FLAG_LOCKED);
ret = fu_engine_unlock(engine, "934b4162a6daa0b033d649c8d464529cec41d3de", &error);
g_assert_no_error(error);
g_assert_true(ret);
/* match one partially */
fu_device_add_flag(device1, FWUPD_DEVICE_FLAG_LOCKED);
fu_device_add_flag(device2, FWUPD_DEVICE_FLAG_LOCKED);
ret = fu_engine_unlock(engine, "934b", &error);
g_assert_no_error(error);
g_assert_true(ret);
/* match equivalent ID */
fu_device_add_flag(device1, FWUPD_DEVICE_FLAG_LOCKED);
fu_device_add_flag(device2, FWUPD_DEVICE_FLAG_LOCKED);
ret = fu_engine_unlock(engine, "b92f", &error);
g_assert_no_error(error);
g_assert_true(ret);
}
static void
fu_engine_device_unlock_func(gconstpointer user_data)
{
FuTest *self = (FuTest *)user_data;
gboolean ret;
g_autofree gchar *filename = NULL;
g_autoptr(FuDevice) device = fu_device_new_with_context(self->ctx);
g_autoptr(FuEngine) engine = fu_engine_new(FU_APP_FLAGS_NONE);
g_autoptr(GError) error = NULL;
g_autoptr(GFile) file = NULL;
g_autoptr(XbBuilder) builder = xb_builder_new();
g_autoptr(XbBuilderSource) source = xb_builder_source_new();
g_autoptr(XbSilo) silo = NULL;
/* load engine to get FuConfig set up */
ret = fu_engine_load(engine, FU_ENGINE_LOAD_FLAG_NO_CACHE, &error);
g_assert_no_error(error);
g_assert_true(ret);
/* add the hardcoded 'fwupd' metadata */
filename = g_test_build_filename(G_TEST_DIST, "tests", "metadata.xml", NULL);
file = g_file_new_for_path(filename);
ret = xb_builder_source_load_file(source, file, XB_BUILDER_SOURCE_FLAG_NONE, NULL, &error);
g_assert_no_error(error);
g_assert_true(ret);
xb_builder_import_source(builder, source);
silo = xb_builder_compile(builder, XB_BUILDER_COMPILE_FLAG_NONE, NULL, &error);
g_assert_no_error(error);
g_assert_nonnull(silo);
fu_engine_set_silo(engine, silo);
/* add a dummy device */
fu_device_set_id(device, "UEFI-dummy-dev0");
fu_device_add_vendor_id(device, "USB:FFFF");
fu_device_add_protocol(device, "com.acme");
fu_device_add_guid(device, "2d47f29b-83a2-4f31-a2e8-63474f4d4c2e");
fu_device_add_flag(device, FWUPD_DEVICE_FLAG_LOCKED);
fu_device_set_version_format(device, FWUPD_VERSION_FORMAT_PLAIN);
fu_engine_add_device(engine, device);
/* ensure the metainfo was matched */
g_assert_nonnull(fwupd_device_get_release_default(FWUPD_DEVICE(device)));
}
static void
fu_engine_require_hwid_func(gconstpointer user_data)
{
FuTest *self = (FuTest *)user_data;
gboolean ret;
g_autofree gchar *filename = NULL;
g_autoptr(FuDevice) device = fu_device_new_with_context(self->ctx);
g_autoptr(FuEngine) engine = fu_engine_new(FU_APP_FLAGS_NONE);
g_autoptr(FuEngineRequest) request = fu_engine_request_new(FU_ENGINE_REQUEST_KIND_ACTIVE);
g_autoptr(FuRelease) release = fu_release_new();
g_autoptr(GBytes) blob_cab = NULL;
g_autoptr(GError) error = NULL;
g_autoptr(XbNode) component = NULL;
g_autoptr(XbSilo) silo_empty = xb_silo_new();
g_autoptr(XbSilo) silo = NULL;
#if defined(__s390x__)
/* See https://github.com/fwupd/fwupd/issues/318 for more information */
g_test_skip("Skipping HWID test on s390x due to known problem with gcab");
return;
#endif
/* no metadata in daemon */
fu_engine_set_silo(engine, silo_empty);
/* load engine to get FuConfig set up */
ret = fu_engine_load(engine, FU_ENGINE_LOAD_FLAG_NO_CACHE, &error);
g_assert_no_error(error);
g_assert_true(ret);
/* get generated file as a blob */
filename =
g_test_build_filename(G_TEST_BUILT, "tests", "missing-hwid", "hwid-1.2.3.cab", NULL);
blob_cab = fu_common_get_contents_bytes(filename, &error);
g_assert_no_error(error);
g_assert_nonnull(blob_cab);
silo = fu_engine_get_silo_from_blob(engine, blob_cab, &error);
g_assert_no_error(error);
g_assert_nonnull(silo);
/* add a dummy device */
fu_device_set_id(device, "test_device");
fu_device_add_vendor_id(device, "USB:FFFF");
fu_device_add_protocol(device, "com.acme");
fu_device_set_version_format(device, FWUPD_VERSION_FORMAT_TRIPLET);
fu_device_set_version(device, "1.2.2");
fu_device_add_guid(device, "12345678-1234-1234-1234-123456789012");
fu_device_add_flag(device, FWUPD_DEVICE_FLAG_UPDATABLE);
fu_device_add_flag(device, FWUPD_DEVICE_FLAG_UNSIGNED_PAYLOAD);
fu_engine_add_device(engine, device);
/* get component */
component =
xb_silo_query_first(silo,
"components/component/id[text()='com.hughski.test.firmware']/..",
&error);
g_assert_no_error(error);
g_assert_nonnull(component);
/* check requirements */
fu_release_set_device(release, device);
fu_release_set_request(release, request);
ret = fu_release_load(release, component, NULL, FWUPD_INSTALL_FLAG_NONE, &error);
g_assert_no_error(error);
g_assert_true(ret);
ret = fu_engine_check_requirements(engine, release, FWUPD_INSTALL_FLAG_NONE, &error);
g_assert_error(error, FWUPD_ERROR, FWUPD_ERROR_INVALID_FILE);
g_assert_nonnull(error);
g_assert_cmpstr(error->message,
==,
"no HWIDs matched 9342d47a-1bab-5709-9869-c840b2eac501");
g_assert_false(ret);
}
static void
fu_engine_downgrade_func(gconstpointer user_data)
{
FuTest *self = (FuTest *)user_data;
FwupdRelease *rel;
gboolean ret;
g_autoptr(FuDevice) device = fu_device_new_with_context(self->ctx);
g_autoptr(FuEngine) engine = fu_engine_new(FU_APP_FLAGS_NONE);
g_autoptr(FuEngineRequest) request = fu_engine_request_new(FU_ENGINE_REQUEST_KIND_ACTIVE);
g_autoptr(GError) error = NULL;
g_autoptr(GPtrArray) devices = NULL;
g_autoptr(GPtrArray) devices_pre = NULL;
g_autoptr(GPtrArray) releases_dg = NULL;
g_autoptr(GPtrArray) releases = NULL;
g_autoptr(GPtrArray) releases_up = NULL;
g_autoptr(GPtrArray) remotes = NULL;
g_autoptr(XbSilo) silo_empty = xb_silo_new();
/* ensure empty tree */
fu_self_test_mkroot();
/* no metadata in daemon */
fu_engine_set_silo(engine, silo_empty);
/* write a broken file */
ret = g_file_set_contents("/tmp/fwupd-self-test/broken.xml.gz",
"this is not a valid",
-1,
&error);
g_assert_no_error(error);
g_assert_true(ret);
/* write the main file */
ret = g_file_set_contents(
"/tmp/fwupd-self-test/stable.xml",
"<components>"
" <component type=\"firmware\">"
" <id>test</id>"
" <name>Test Device</name>"
" <provides>"
" <firmware type=\"flashed\">aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee</firmware>"
" </provides>"
" <releases>"
" <release version=\"1.2.3\" date=\"2017-09-15\">"
" <size type=\"installed\">123</size>"
" <size type=\"download\">456</size>"
" <location>https://test.org/foo.cab</location>"
" <checksum filename=\"foo.cab\" target=\"container\" "
"type=\"md5\">deadbeefdeadbeefdeadbeefdeadbeef</checksum>"
" <checksum filename=\"firmware.bin\" target=\"content\" "
"type=\"md5\">deadbeefdeadbeefdeadbeefdeadbeef</checksum>"
" </release>"
" <release version=\"1.2.2\" date=\"2017-09-01\">"
" <size type=\"installed\">123</size>"
" <size type=\"download\">456</size>"
" <location>https://test.org/foo.cab</location>"
" <checksum filename=\"foo.cab\" target=\"container\" "
"type=\"md5\">deadbeefdeadbeefdeadbeefdeadbeef</checksum>"
" <checksum filename=\"firmware.bin\" target=\"content\" "
"type=\"md5\">deadbeefdeadbeefdeadbeefdeadbeef</checksum>"
" </release>"
" </releases>"
" </component>"
"</components>",
-1,
&error);
g_assert_no_error(error);
g_assert_true(ret);
/* write the extra file */
ret = g_file_set_contents(
"/tmp/fwupd-self-test/testing.xml",
"<components>"
" <component type=\"firmware\">"
" <id>test</id>"
" <name>Test Device</name>"
" <provides>"
" <firmware type=\"flashed\">aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee</firmware>"
" </provides>"
" <releases>"
" <release version=\"1.2.5\" date=\"2017-09-16\">"
" <size type=\"installed\">123</size>"
" <size type=\"download\">456</size>"
" <location>https://test.org/foo.cab</location>"
" <checksum filename=\"foo.cab\" target=\"container\" "
"type=\"md5\">deadbeefdeadbeefdeadbeefdeadbeef</checksum>"
" <checksum filename=\"firmware.bin\" target=\"content\" "
"type=\"md5\">deadbeefdeadbeefdeadbeefdeadbeef</checksum>"
" </release>"
" <release version=\"1.2.4\" date=\"2017-09-15\">"
" <size type=\"installed\">123</size>"
" <size type=\"download\">456</size>"
" <location>https://test.org/foo.cab</location>"
" <checksum filename=\"foo.cab\" target=\"container\" "
"type=\"md5\">deadbeefdeadbeefdeadbeefdeadbeef</checksum>"
" <checksum filename=\"firmware.bin\" target=\"content\" "
"type=\"md5\">deadbeefdeadbeefdeadbeefdeadbeef</checksum>"
" </release>"
" </releases>"
" </component>"
"</components>",
-1,
&error);
g_assert_no_error(error);
g_assert_true(ret);
ret = fu_engine_load(engine,
FU_ENGINE_LOAD_FLAG_REMOTES | FU_ENGINE_LOAD_FLAG_NO_CACHE,
&error);
g_assert_no_error(error);
g_assert_true(ret);
g_assert_cmpint(fu_engine_get_status(engine), ==, FWUPD_STATUS_IDLE);
g_test_assert_expected_messages();
/* return all the remotes, even the broken one */
remotes = fu_engine_get_remotes(engine, &error);
g_assert_no_error(error);
g_assert_nonnull(remotes);
g_assert_cmpint(remotes->len, ==, 4);
/* ensure there are no devices already */
devices_pre = fu_engine_get_devices(engine, &error);
g_assert_error(error, FWUPD_ERROR, FWUPD_ERROR_NOTHING_TO_DO);
g_assert_null(devices_pre);
g_clear_error(&error);
/* add a device so we can get upgrades and downgrades */
fu_device_set_version_format(device, FWUPD_VERSION_FORMAT_TRIPLET);
fu_device_set_version(device, "1.2.3");
fu_device_set_id(device, "test_device");
fu_device_add_vendor_id(device, "USB:FFFF");
fu_device_add_protocol(device, "com.acme");
fu_device_set_name(device, "Test Device");
fu_device_add_guid(device, "aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee");
fu_device_add_flag(device, FWUPD_DEVICE_FLAG_UPDATABLE);
fu_device_add_flag(device, FWUPD_DEVICE_FLAG_UNSIGNED_PAYLOAD);
#ifndef HAVE_POLKIT
g_test_expect_message("FuEngine",
G_LOG_LEVEL_WARNING,
"*archive signature missing or not trusted");
#endif
fu_engine_add_device(engine, device);
devices = fu_engine_get_devices(engine, &error);
g_assert_no_error(error);
g_assert_nonnull(devices);
g_assert_cmpint(devices->len, ==, 1);
#ifdef HAVE_POLKIT
g_assert_true(fu_device_has_flag(device, FWUPD_DEVICE_FLAG_SUPPORTED));
#endif
g_assert_true(fu_device_has_flag(device, FWUPD_DEVICE_FLAG_REGISTERED));
/* get the releases for one device */
releases = fu_engine_get_releases(engine, request, fu_device_get_id(device), &error);
g_assert_no_error(error);
g_assert_nonnull(releases);
g_assert_cmpint(releases->len, ==, 4);
/* no upgrades, as no firmware is approved */
releases_up = fu_engine_get_upgrades(engine, request, fu_device_get_id(device), &error);
g_assert_error(error, FWUPD_ERROR, FWUPD_ERROR_NOTHING_TO_DO);
g_assert_null(releases_up);
g_clear_error(&error);
/* retry with approved firmware set */
fu_engine_add_approved_firmware(engine, "deadbeefdeadbeefdeadbeefdeadbeef");
fu_engine_add_approved_firmware(engine, "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX");
/* upgrades */
releases_up = fu_engine_get_upgrades(engine, request, fu_device_get_id(device), &error);
g_assert_no_error(error);
g_assert_nonnull(releases_up);
g_assert_cmpint(releases_up->len, ==, 2);
/* ensure the list is sorted */
rel = FWUPD_RELEASE(g_ptr_array_index(releases_up, 0));
g_assert_cmpstr(fwupd_release_get_version(rel), ==, "1.2.5");
rel = FWUPD_RELEASE(g_ptr_array_index(releases_up, 1));
g_assert_cmpstr(fwupd_release_get_version(rel), ==, "1.2.4");
/* downgrades */
releases_dg = fu_engine_get_downgrades(engine, request, fu_device_get_id(device), &error);
g_assert_no_error(error);
g_assert_nonnull(releases_dg);
g_assert_cmpint(releases_dg->len, ==, 1);
rel = FWUPD_RELEASE(g_ptr_array_index(releases_dg, 0));
g_assert_cmpstr(fwupd_release_get_version(rel), ==, "1.2.2");
}
static void
fu_engine_install_duration_func(gconstpointer user_data)
{
FuTest *self = (FuTest *)user_data;
FwupdRelease *rel;
gboolean ret;
g_autoptr(FuDevice) device = fu_device_new_with_context(self->ctx);
g_autoptr(FuEngine) engine = fu_engine_new(FU_APP_FLAGS_NONE);
g_autoptr(FuEngineRequest) request = fu_engine_request_new(FU_ENGINE_REQUEST_KIND_ACTIVE);
g_autoptr(GError) error = NULL;
g_autoptr(GPtrArray) devices = NULL;
g_autoptr(GPtrArray) releases = NULL;
g_autoptr(XbSilo) silo_empty = xb_silo_new();
/* ensure empty tree */
fu_self_test_mkroot();
/* no metadata in daemon */
fu_engine_set_silo(engine, silo_empty);
/* write the main file */
ret = g_file_set_contents(
"/tmp/fwupd-self-test/stable.xml",
"<components>"
" <component type=\"firmware\">"
" <id>test</id>"
" <provides>"
" <firmware type=\"flashed\">aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee</firmware>"
" </provides>"
" <releases>"
" <release version=\"1.2.3\" date=\"2017-09-15\" install_duration=\"120\">"
" <location>https://test.org/foo.cab</location>"
" <checksum filename=\"foo.cab\" target=\"container\" "
"type=\"md5\">deadbeefdeadbeefdeadbeefdeadbeef</checksum>"
" <checksum filename=\"firmware.bin\" target=\"content\" "
"type=\"md5\">deadbeefdeadbeefdeadbeefdeadbeef</checksum>"
" </release>"
" </releases>"
" </component>"
"</components>",
-1,
&error);
g_assert_no_error(error);
g_assert_true(ret);
ret = fu_engine_load(engine,
FU_ENGINE_LOAD_FLAG_REMOTES | FU_ENGINE_LOAD_FLAG_NO_CACHE,
&error);
g_assert_no_error(error);
g_assert_true(ret);
/* add a device so we can get the install duration */
fu_device_set_version_format(device, FWUPD_VERSION_FORMAT_TRIPLET);
fu_device_set_version(device, "1.2.3");
fu_device_set_id(device, "test_device");
fu_device_add_vendor_id(device, "USB:FFFF");
fu_device_add_protocol(device, "com.acme");
fu_device_add_guid(device, "aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee");
fu_device_set_install_duration(device, 999);
fu_device_add_flag(device, FWUPD_DEVICE_FLAG_UPDATABLE);
fu_device_add_flag(device, FWUPD_DEVICE_FLAG_UNSIGNED_PAYLOAD);
#ifndef HAVE_POLKIT
g_test_expect_message("FuEngine",
G_LOG_LEVEL_WARNING,
"*archive signature missing or not trusted");
#endif
fu_engine_add_device(engine, device);
devices = fu_engine_get_devices(engine, &error);
g_assert_no_error(error);
g_assert_nonnull(devices);
g_assert_cmpint(devices->len, ==, 1);
#ifdef HAVE_POLKIT
g_assert_true(fu_device_has_flag(device, FWUPD_DEVICE_FLAG_SUPPORTED));
#endif
/* check the release install duration */
releases = fu_engine_get_releases(engine, request, fu_device_get_id(device), &error);
g_assert_no_error(error);
g_assert_nonnull(releases);
g_assert_cmpint(releases->len, ==, 1);
rel = FWUPD_RELEASE(g_ptr_array_index(releases, 0));
g_assert_cmpint(fwupd_release_get_install_duration(rel), ==, 120);
}
static void
fu_engine_history_func(gconstpointer user_data)
{
FuTest *self = (FuTest *)user_data;
gboolean ret;
g_autofree gchar *checksum = NULL;
g_autofree gchar *device_str_expected = NULL;
g_autofree gchar *device_str = NULL;
g_autofree gchar *filename = NULL;
g_autoptr(FuDevice) device2 = NULL;
g_autoptr(FuDevice) device = fu_device_new_with_context(self->ctx);
g_autoptr(FuEngine) engine = fu_engine_new(FU_APP_FLAGS_NONE);
g_autoptr(FuHistory) history = NULL;
g_autoptr(FuRelease) release = fu_release_new();
g_autoptr(FuProgress) progress = fu_progress_new(G_STRLOC);
g_autoptr(FwupdDevice) device3 = NULL;
g_autoptr(FwupdDevice) device4 = NULL;
g_autoptr(GBytes) blob_cab = NULL;
g_autoptr(GError) error = NULL;
g_autoptr(GPtrArray) devices = NULL;
g_autoptr(XbNode) component = NULL;
g_autoptr(XbSilo) silo_empty = xb_silo_new();
g_autoptr(XbSilo) silo = NULL;
/* ensure empty tree */
fu_self_test_mkroot();
/* no metadata in daemon */
fu_engine_set_silo(engine, silo_empty);
/* set up dummy plugin */
fu_engine_add_plugin(engine, self->plugin);
ret = fu_engine_load(engine, FU_ENGINE_LOAD_FLAG_NO_CACHE, &error);
g_assert_no_error(error);
g_assert_true(ret);
g_assert_cmpint(fu_engine_get_status(engine), ==, FWUPD_STATUS_IDLE);
/* add a device so we can get upgrade it */
fu_device_set_version_format(device, FWUPD_VERSION_FORMAT_TRIPLET);
fu_device_set_version(device, "1.2.2");
fu_device_set_id(device, "test_device");
fu_device_add_vendor_id(device, "USB:FFFF");
fu_device_add_protocol(device, "com.acme");
fu_device_set_name(device, "Test Device");
fu_device_set_plugin(device, "test");
fu_device_add_guid(device, "12345678-1234-1234-1234-123456789012");
fu_device_add_checksum(device, "0123456789abcdef0123456789abcdef01234567");
fu_device_add_flag(device, FWUPD_DEVICE_FLAG_UPDATABLE);
fu_device_add_flag(device, FWUPD_DEVICE_FLAG_UNSIGNED_PAYLOAD);
fu_device_set_created(device, 1515338000);
fu_engine_add_device(engine, device);
devices = fu_engine_get_devices(engine, &error);
g_assert_no_error(error);
g_assert_nonnull(devices);
g_assert_cmpint(devices->len, ==, 1);
g_assert_true(fu_device_has_flag(device, FWUPD_DEVICE_FLAG_REGISTERED));
filename =
g_test_build_filename(G_TEST_BUILT, "tests", "missing-hwid", "noreqs-1.2.3.cab", NULL);
blob_cab = fu_common_get_contents_bytes(filename, &error);
g_assert_no_error(error);
g_assert_nonnull(blob_cab);
silo = fu_engine_get_silo_from_blob(engine, blob_cab, &error);
g_assert_no_error(error);
g_assert_nonnull(silo);
/* get component */
component =
xb_silo_query_first(silo,
"components/component/id[text()='com.hughski.test.firmware']/..",
&error);
g_assert_no_error(error);
g_assert_nonnull(component);
/* set the counter */
g_setenv("FWUPD_PLUGIN_TEST", "another-write-required", TRUE);
fu_device_set_metadata_integer(device, "nr-update", 0);
/* install it */
fu_release_set_device(release, device);
ret = fu_release_load(release, component, NULL, FWUPD_INSTALL_FLAG_NONE, &error);
g_assert_no_error(error);
g_assert_true(ret);
ret = fu_engine_install_release(engine,
release,
blob_cab,
progress,
FWUPD_INSTALL_FLAG_NONE,
&error);
g_assert_no_error(error);
g_assert_true(ret);
/* check the write was done more than once */
g_assert_cmpint(fu_device_get_metadata_integer(device, "nr-update"), ==, 2);
/* check the history database */
history = fu_history_new();
device2 = fu_history_get_device_by_id(history, fu_device_get_id(device), &error);
if (g_error_matches(error, FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED)) {
g_test_skip("no sqlite support");
return;
}
g_assert_no_error(error);
g_assert_nonnull(device2);
g_assert_cmpint(fu_device_get_update_state(device2), ==, FWUPD_UPDATE_STATE_SUCCESS);
g_assert_cmpstr(fu_device_get_update_error(device2), ==, NULL);
fu_device_set_modified(device2, 1514338000);
g_hash_table_remove_all(fwupd_release_get_metadata(fu_device_get_release_default(device2)));
device_str = fu_device_to_string(device2);
checksum = g_compute_checksum_for_bytes(G_CHECKSUM_SHA1, blob_cab);
device_str_expected =
g_strdup_printf("FuDevice:\n"
" DeviceId: 894e8c17a29428b09d10cd90d1db74ea76fbcfe8\n"
" Name: Test Device\n"
" Guid: 12345678-1234-1234-1234-123456789012\n"
" Plugin: test\n"
" Flags: historical\n"
" Version: 1.2.2\n"
" Created: 2018-01-07\n"
" Modified: 2017-12-27\n"
" UpdateState: success\n"
" \n"
" [Release]\n"
" Version: 1.2.3\n"
" Checksum: SHA1(%s)\n"
" Flags: none\n",
checksum);
ret = fu_test_compare_lines(device_str, device_str_expected, &error);
g_assert_no_error(error);
g_assert_true(ret);
/* GetResults() */
device3 = fu_engine_get_results(engine, FWUPD_DEVICE_ID_ANY, &error);
g_assert_nonnull(device3);
g_assert_cmpstr(fu_device_get_id(device3), ==, "894e8c17a29428b09d10cd90d1db74ea76fbcfe8");
g_assert_cmpint(fu_device_get_update_state(device3), ==, FWUPD_UPDATE_STATE_SUCCESS);
g_assert_cmpstr(fu_device_get_update_error(device3), ==, NULL);
/* ClearResults() */
ret = fu_engine_clear_results(engine, FWUPD_DEVICE_ID_ANY, &error);
g_assert_no_error(error);
g_assert_true(ret);
/* GetResults() */
device4 = fu_engine_get_results(engine, FWUPD_DEVICE_ID_ANY, &error);
g_assert_null(device4);
g_assert_error(error, FWUPD_ERROR, FWUPD_ERROR_NOTHING_TO_DO);
}
static void
fu_engine_multiple_rels_func(gconstpointer user_data)
{
FuTest *self = (FuTest *)user_data;
gboolean ret;
g_autofree gchar *filename = NULL;
g_autoptr(FuDevice) device = fu_device_new_with_context(self->ctx);
g_autoptr(FuEngine) engine = fu_engine_new(FU_APP_FLAGS_NONE);
g_autoptr(FuProgress) progress = fu_progress_new(G_STRLOC);
g_autoptr(GBytes) blob_cab = NULL;
g_autoptr(GError) error = NULL;
g_autoptr(XbNode) component = NULL;
g_autoptr(XbSilo) silo_empty = xb_silo_new();
g_autoptr(XbSilo) silo = NULL;
g_autoptr(FuEngineRequest) request = fu_engine_request_new(FU_ENGINE_REQUEST_KIND_ACTIVE);
g_autoptr(GPtrArray) releases = NULL;
g_autoptr(GPtrArray) rels = NULL;
#if LIBXMLB_CHECK_VERSION(0, 2, 0)
g_autoptr(XbQuery) query = NULL;
#endif
/* ensure empty tree */
fu_self_test_mkroot();
/* no metadata in daemon */
fu_engine_set_silo(engine, silo_empty);
/* set up dummy plugin */
fu_engine_add_plugin(engine, self->plugin);
ret = fu_engine_load(engine, FU_ENGINE_LOAD_FLAG_NO_CACHE, &error);
g_assert_no_error(error);
g_assert_true(ret);
g_assert_cmpint(fu_engine_get_status(engine), ==, FWUPD_STATUS_IDLE);
/* add a device so we can get upgrade it */
fu_device_set_version_format(device, FWUPD_VERSION_FORMAT_TRIPLET);
fu_device_set_version(device, "1.2.2");
fu_device_set_id(device, "test_device");
fu_device_add_vendor_id(device, "USB:FFFF");
fu_device_add_protocol(device, "com.acme");
fu_device_set_name(device, "Test Device");
fu_device_set_plugin(device, "test");
fu_device_add_guid(device, "12345678-1234-1234-1234-123456789012");
fu_device_add_checksum(device, "0123456789abcdef0123456789abcdef01234567");
fu_device_add_flag(device, FWUPD_DEVICE_FLAG_UPDATABLE);
fu_device_add_flag(device, FWUPD_DEVICE_FLAG_UNSIGNED_PAYLOAD);
fu_device_add_flag(device, FWUPD_DEVICE_FLAG_INSTALL_ALL_RELEASES);
fu_device_set_created(device, 1515338000);
fu_engine_add_device(engine, device);
filename = g_test_build_filename(G_TEST_BUILT,
"tests",
"multiple-rels",
"multiple-rels-1.2.4.cab",
NULL);
blob_cab = fu_common_get_contents_bytes(filename, &error);
g_assert_no_error(error);
g_assert_nonnull(blob_cab);
silo = fu_engine_get_silo_from_blob(engine, blob_cab, &error);
g_assert_no_error(error);
g_assert_nonnull(silo);
/* get component */
component =
xb_silo_query_first(silo,
"components/component/id[text()='com.hughski.test.firmware']/..",
&error);
g_assert_no_error(error);
g_assert_nonnull(component);
/* set up counter */
fu_device_set_metadata_integer(device, "nr-update", 0);
/* get all */
#if LIBXMLB_CHECK_VERSION(0, 2, 0)
query = xb_query_new_full(xb_node_get_silo(component),
"releases/release",
XB_QUERY_FLAG_FORCE_NODE_CACHE,
&error);
g_assert_no_error(error);
g_assert_nonnull(query);
rels = xb_node_query_full(component, query, &error);
#else
rels = xb_node_query(component, "releases/release", 0, &error);
#endif
g_assert_no_error(error);
g_assert_nonnull(rels);
releases = g_ptr_array_new_with_free_func((GDestroyNotify)g_object_unref);
for (guint i = 0; i < rels->len; i++) {
XbNode *rel = g_ptr_array_index(rels, i);
g_autoptr(FuRelease) release = fu_release_new();
fu_release_set_device(release, device);
ret = fu_release_load(release, component, rel, FWUPD_INSTALL_FLAG_NONE, &error);
g_assert_no_error(error);
g_assert_true(ret);
g_ptr_array_add(releases, g_object_ref(release));
}
/* install them */
ret = fu_engine_install_releases(engine,
request,
releases,
blob_cab,
progress,
FWUPD_INSTALL_FLAG_NONE,
&error);
g_assert_no_error(error);
g_assert_true(ret);
/* check we did 1.2.2 -> 1.2.3 -> 1.2.4 */
g_assert_cmpint(fu_device_get_metadata_integer(device, "nr-update"), ==, 2);
g_assert_cmpstr(fu_device_get_version(device), ==, "1.2.4");
}
static void
fu_engine_history_inherit(gconstpointer user_data)
{
FuTest *self = (FuTest *)user_data;
gboolean ret;
g_autofree gchar *filename = NULL;
g_autofree gchar *localstatedir = NULL;
g_autofree gchar *history_db = NULL;
g_autoptr(FuDevice) device = fu_device_new_with_context(self->ctx);
g_autoptr(FuEngine) engine = fu_engine_new(FU_APP_FLAGS_NONE);
g_autoptr(FuRelease) release = fu_release_new();
g_autoptr(FuProgress) progress = fu_progress_new(G_STRLOC);
g_autoptr(GBytes) blob_cab = NULL;
g_autoptr(GError) error = NULL;
g_autoptr(GPtrArray) devices = NULL;
g_autoptr(XbNode) component = NULL;
g_autoptr(XbSilo) silo_empty = xb_silo_new();
g_autoptr(XbSilo) silo = NULL;
#ifndef HAVE_SQLITE
g_test_skip("no sqlite support");
return;
#endif
/* delete history */
localstatedir = fu_common_get_path(FU_PATH_KIND_LOCALSTATEDIR_PKG);
history_db = g_build_filename(localstatedir, "pending.db", NULL);
g_unlink(history_db);
/* no metadata in daemon */
fu_engine_set_silo(engine, silo_empty);
/* set up dummy plugin */
g_setenv("FWUPD_PLUGIN_TEST", "fail", TRUE);
fu_engine_add_plugin(engine, self->plugin);
ret = fu_engine_load(engine, FU_ENGINE_LOAD_FLAG_NO_CACHE, &error);
g_assert_no_error(error);
g_assert_true(ret);
g_assert_cmpint(fu_engine_get_status(engine), ==, FWUPD_STATUS_IDLE);
/* add a device so we can get upgrade it */
fu_device_set_version_format(device, FWUPD_VERSION_FORMAT_TRIPLET);
fu_device_set_version(device, "1.2.2");
fu_device_set_id(device, "test_device");
fu_device_add_vendor_id(device, "USB:FFFF");
fu_device_add_protocol(device, "com.acme");
fu_device_set_name(device, "Test Device");
fu_device_set_plugin(device, "test");
fu_device_add_guid(device, "12345678-1234-1234-1234-123456789012");
fu_device_add_flag(device, FWUPD_DEVICE_FLAG_UPDATABLE);
fu_device_add_flag(device, FWUPD_DEVICE_FLAG_UNSIGNED_PAYLOAD);
fu_device_set_created(device, 1515338000);
fu_engine_add_device(engine, device);
devices = fu_engine_get_devices(engine, &error);
g_assert_no_error(error);
g_assert_nonnull(devices);
g_assert_cmpint(devices->len, ==, 1);
g_assert_true(fu_device_has_flag(device, FWUPD_DEVICE_FLAG_REGISTERED));
filename =
g_test_build_filename(G_TEST_BUILT, "tests", "missing-hwid", "noreqs-1.2.3.cab", NULL);
blob_cab = fu_common_get_contents_bytes(filename, &error);
g_assert_no_error(error);
g_assert_nonnull(blob_cab);
silo = fu_engine_get_silo_from_blob(engine, blob_cab, &error);
g_assert_no_error(error);
g_assert_nonnull(silo);
/* get component */
component =
xb_silo_query_first(silo,
"components/component/id[text()='com.hughski.test.firmware']/..",
&error);
g_assert_no_error(error);
g_assert_nonnull(component);
/* install it */
g_setenv("FWUPD_PLUGIN_TEST", "requires-activation", TRUE);
fu_release_set_device(release, device);
ret = fu_release_load(release, component, NULL, FWUPD_INSTALL_FLAG_NONE, &error);
g_assert_no_error(error);
g_assert_true(ret);
ret = fu_engine_install_release(engine,
release,
blob_cab,
progress,
FWUPD_INSTALL_FLAG_NONE,
&error);
g_assert_no_error(error);
g_assert_true(ret);
/* check the device requires an activation */
g_assert_true(fu_device_has_flag(device, FWUPD_DEVICE_FLAG_NEEDS_ACTIVATION));
g_assert_cmpstr(fu_device_get_version(device), ==, "1.2.2");
/* activate the device */
fu_progress_reset(progress);
ret = fu_engine_activate(engine, fu_device_get_id(device), progress, &error);
g_assert_no_error(error);
g_assert_true(ret);
/* check the device no longer requires an activation */
g_assert_false(fu_device_has_flag(device, FWUPD_DEVICE_FLAG_NEEDS_ACTIVATION));
g_assert_cmpstr(fu_device_get_version(device), ==, "1.2.3");
/* emulate getting the flag for a fresh boot on old firmware */
fu_progress_reset(progress);
fu_device_set_version_format(device, FWUPD_VERSION_FORMAT_TRIPLET);
fu_device_set_version(device, "1.2.2");
ret = fu_engine_install_release(engine,
release,
blob_cab,
progress,
FWUPD_INSTALL_FLAG_NONE,
&error);
g_assert_no_error(error);
g_assert_true(ret);
g_object_unref(engine);
g_object_unref(device);
engine = fu_engine_new(FU_APP_FLAGS_NONE);
fu_engine_set_silo(engine, silo_empty);
fu_engine_add_plugin(engine, self->plugin);
device = fu_device_new_with_context(self->ctx);
fu_device_add_internal_flag(device, FU_DEVICE_INTERNAL_FLAG_INHERIT_ACTIVATION);
fu_device_set_id(device, "test_device");
fu_device_add_vendor_id(device, "USB:FFFF");
fu_device_add_protocol(device, "com.acme");
fu_device_set_name(device, "Test Device");
fu_device_add_guid(device, "12345678-1234-1234-1234-123456789012");
fu_device_set_version_format(device, FWUPD_VERSION_FORMAT_TRIPLET);
fu_device_set_version(device, "1.2.2");
fu_engine_add_device(engine, device);
g_assert_true(fu_device_has_flag(device, FWUPD_DEVICE_FLAG_NEEDS_ACTIVATION));
/* emulate not getting the flag */
g_object_unref(engine);
g_object_unref(device);
engine = fu_engine_new(FU_APP_FLAGS_NONE);
fu_engine_set_silo(engine, silo_empty);
fu_engine_add_plugin(engine, self->plugin);
device = fu_device_new_with_context(self->ctx);
fu_device_set_id(device, "test_device");
fu_device_add_vendor_id(device, "USB:FFFF");
fu_device_add_protocol(device, "com.acme");
fu_device_set_name(device, "Test Device");
fu_device_add_guid(device, "12345678-1234-1234-1234-123456789012");
fu_device_set_version_format(device, FWUPD_VERSION_FORMAT_TRIPLET);
fu_device_set_version(device, "1.2.2");
fu_engine_add_device(engine, device);
g_assert_false(fu_device_has_flag(device, FWUPD_DEVICE_FLAG_NEEDS_ACTIVATION));
}
static void
fu_engine_install_needs_reboot(gconstpointer user_data)
{
FuTest *self = (FuTest *)user_data;
gboolean ret;
g_autofree gchar *filename = NULL;
g_autoptr(FuDevice) device = fu_device_new_with_context(self->ctx);
g_autoptr(FuEngine) engine = fu_engine_new(FU_APP_FLAGS_NONE);
g_autoptr(FuRelease) release = fu_release_new();
g_autoptr(FuProgress) progress = fu_progress_new(G_STRLOC);
g_autoptr(GBytes) blob_cab = NULL;
g_autoptr(GError) error = NULL;
g_autoptr(GPtrArray) devices = NULL;
g_autoptr(XbNode) component = NULL;
g_autoptr(XbSilo) silo_empty = xb_silo_new();
g_autoptr(XbSilo) silo = NULL;
/* no metadata in daemon */
fu_engine_set_silo(engine, silo_empty);
/* set up dummy plugin */
g_setenv("FWUPD_PLUGIN_TEST", "fail", TRUE);
fu_engine_add_plugin(engine, self->plugin);
ret = fu_engine_load(engine, FU_ENGINE_LOAD_FLAG_NONE, &error);
g_assert_no_error(error);
g_assert_true(ret);
g_assert_cmpint(fu_engine_get_status(engine), ==, FWUPD_STATUS_IDLE);
/* add a device so we can get upgrade it */
fu_device_set_version_format(device, FWUPD_VERSION_FORMAT_TRIPLET);
fu_device_set_version(device, "1.2.2");
fu_device_set_id(device, "test_device");
fu_device_add_vendor_id(device, "USB:FFFF");
fu_device_add_protocol(device, "com.acme");
fu_device_set_name(device, "Test Device");
fu_device_set_plugin(device, "test");
fu_device_add_guid(device, "12345678-1234-1234-1234-123456789012");
fu_device_add_flag(device, FWUPD_DEVICE_FLAG_UPDATABLE);
fu_device_add_flag(device, FWUPD_DEVICE_FLAG_UNSIGNED_PAYLOAD);
fu_device_set_created(device, 1515338000);
fu_engine_add_device(engine, device);
devices = fu_engine_get_devices(engine, &error);
g_assert_no_error(error);
g_assert_nonnull(devices);
g_assert_cmpint(devices->len, ==, 1);
g_assert_true(fu_device_has_flag(device, FWUPD_DEVICE_FLAG_REGISTERED));
filename =
g_test_build_filename(G_TEST_BUILT, "tests", "missing-hwid", "noreqs-1.2.3.cab", NULL);
blob_cab = fu_common_get_contents_bytes(filename, &error);
g_assert_no_error(error);
g_assert_nonnull(blob_cab);
silo = fu_engine_get_silo_from_blob(engine, blob_cab, &error);
g_assert_no_error(error);
g_assert_nonnull(silo);
/* get component */
component =
xb_silo_query_first(silo,
"components/component/id[text()='com.hughski.test.firmware']/..",
&error);
g_assert_no_error(error);
g_assert_nonnull(component);
/* install it */
g_setenv("FWUPD_PLUGIN_TEST", "requires-reboot", TRUE);
fu_release_set_device(release, device);
ret = fu_release_load(release, component, NULL, FWUPD_INSTALL_FLAG_NONE, &error);
g_assert_no_error(error);
g_assert_true(ret);
ret = fu_engine_install_release(engine,
release,
blob_cab,
progress,
FWUPD_INSTALL_FLAG_NONE,
&error);
g_assert_no_error(error);
g_assert_true(ret);
/* check the device requires reboot */
g_assert_true(fu_device_has_flag(device, FWUPD_DEVICE_FLAG_NEEDS_REBOOT));
g_assert_cmpint(fu_device_get_update_state(device), ==, FWUPD_UPDATE_STATE_NEEDS_REBOOT);
g_assert_cmpstr(fu_device_get_version(device), ==, "1.2.2");
}
static void
fu_engine_history_error_func(gconstpointer user_data)
{
FuTest *self = (FuTest *)user_data;
gboolean ret;
g_autofree gchar *checksum = NULL;
g_autofree gchar *device_str_expected = NULL;
g_autofree gchar *device_str = NULL;
g_autofree gchar *filename = NULL;
g_autoptr(FuDevice) device2 = NULL;
g_autoptr(FuDevice) device = fu_device_new_with_context(self->ctx);
g_autoptr(FuEngine) engine = fu_engine_new(FU_APP_FLAGS_NONE);
g_autoptr(FuHistory) history = NULL;
g_autoptr(FuRelease) release = fu_release_new();
g_autoptr(FuProgress) progress = fu_progress_new(G_STRLOC);
g_autoptr(GBytes) blob_cab = NULL;
g_autoptr(GError) error2 = NULL;
g_autoptr(GError) error = NULL;
g_autoptr(GPtrArray) devices = NULL;
g_autoptr(XbNode) component = NULL;
g_autoptr(XbSilo) silo_empty = xb_silo_new();
g_autoptr(XbSilo) silo = NULL;
/* no metadata in daemon */
fu_engine_set_silo(engine, silo_empty);
/* set up dummy plugin */
g_setenv("FWUPD_PLUGIN_TEST", "fail", TRUE);
fu_engine_add_plugin(engine, self->plugin);
ret = fu_engine_load(engine, FU_ENGINE_LOAD_FLAG_NO_CACHE, &error);
g_assert_no_error(error);
g_assert_true(ret);
g_assert_cmpint(fu_engine_get_status(engine), ==, FWUPD_STATUS_IDLE);
/* add a device so we can get upgrade it */
fu_device_set_version_format(device, FWUPD_VERSION_FORMAT_TRIPLET);
fu_device_set_version(device, "1.2.2");
fu_device_set_id(device, "test_device");
fu_device_add_vendor_id(device, "USB:FFFF");
fu_device_add_protocol(device, "com.acme");
fu_device_set_name(device, "Test Device");
fu_device_set_plugin(device, "test");
fu_device_add_guid(device, "12345678-1234-1234-1234-123456789012");
fu_device_add_flag(device, FWUPD_DEVICE_FLAG_UPDATABLE);
fu_device_add_flag(device, FWUPD_DEVICE_FLAG_UNSIGNED_PAYLOAD);
fu_device_set_created(device, 1515338000);
fu_engine_add_device(engine, device);
devices = fu_engine_get_devices(engine, &error);
g_assert_no_error(error);
g_assert_nonnull(devices);
g_assert_cmpint(devices->len, ==, 1);
g_assert_true(fu_device_has_flag(device, FWUPD_DEVICE_FLAG_REGISTERED));
/* install the wrong thing */
filename =
g_test_build_filename(G_TEST_BUILT, "tests", "missing-hwid", "noreqs-1.2.3.cab", NULL);
blob_cab = fu_common_get_contents_bytes(filename, &error);
g_assert_no_error(error);
g_assert_nonnull(blob_cab);
silo = fu_engine_get_silo_from_blob(engine, blob_cab, &error);
g_assert_no_error(error);
g_assert_nonnull(silo);
component =
xb_silo_query_first(silo,
"components/component/id[text()='com.hughski.test.firmware']/..",
&error);
g_assert_no_error(error);
g_assert_nonnull(component);
fu_release_set_device(release, device);
ret = fu_release_load(release, component, NULL, FWUPD_INSTALL_FLAG_NONE, &error);
g_assert_no_error(error);
g_assert_true(ret);
ret = fu_engine_install_release(engine,
release,
blob_cab,
progress,
FWUPD_INSTALL_FLAG_NONE,
&error);
g_assert_error(error, FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED);
g_assert_cmpstr(error->message, ==, "device was not in supported mode");
g_assert_false(ret);
/* check the history database */
history = fu_history_new();
device2 = fu_history_get_device_by_id(history, fu_device_get_id(device), &error2);
if (g_error_matches(error2, FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED)) {
g_test_skip("no sqlite support");
return;
}
g_assert_no_error(error2);
g_assert_nonnull(device2);
g_assert_cmpint(fu_device_get_update_state(device2), ==, FWUPD_UPDATE_STATE_FAILED);
g_assert_cmpstr(fu_device_get_update_error(device2), ==, error->message);
g_clear_error(&error);
fu_device_set_modified(device2, 1514338000);
g_hash_table_remove_all(fwupd_release_get_metadata(fu_device_get_release_default(device2)));
device_str = fu_device_to_string(device2);
checksum = g_compute_checksum_for_bytes(G_CHECKSUM_SHA1, blob_cab);
device_str_expected =
g_strdup_printf("FuDevice:\n"
" DeviceId: 894e8c17a29428b09d10cd90d1db74ea76fbcfe8\n"
" Name: Test Device\n"
" Guid: 12345678-1234-1234-1234-123456789012\n"
" Plugin: test\n"
" Flags: updatable|historical\n"
" Version: 1.2.2\n"
" Created: 2018-01-07\n"
" Modified: 2017-12-27\n"
" UpdateState: failed\n"
" UpdateError: device was not in supported mode\n"
" \n"
" [Release]\n"
" Version: 1.2.3\n"
" Checksum: SHA1(%s)\n"
" Flags: none\n",
checksum);
ret = fu_test_compare_lines(device_str, device_str_expected, &error);
g_assert_no_error(error);
g_assert_true(ret);
}
static void
_device_list_count_cb(FuDeviceList *device_list, FuDevice *device, gpointer user_data)
{
guint *cnt = (guint *)user_data;
(*cnt)++;
}
static void
fu_device_list_no_auto_remove_children_func(gconstpointer user_data)
{
g_autoptr(FuDevice) child = fu_device_new();
g_autoptr(FuDevice) parent = fu_device_new();
g_autoptr(FuDeviceList) device_list = fu_device_list_new();
g_autoptr(GPtrArray) active1 = NULL;
g_autoptr(GPtrArray) active2 = NULL;
g_autoptr(GPtrArray) active3 = NULL;
/* normal behavior, remove child with parent */
fu_device_set_id(parent, "parent");
fu_device_set_id(child, "child");
fu_device_add_child(parent, child);
fu_device_list_add(device_list, parent);
fu_device_list_add(device_list, child);
fu_device_list_remove(device_list, parent);
active1 = fu_device_list_get_active(device_list);
g_assert_cmpint(active1->len, ==, 0);
/* new-style behavior, do not remove child */
fu_device_add_internal_flag(parent, FU_DEVICE_INTERNAL_FLAG_NO_AUTO_REMOVE_CHILDREN);
fu_device_list_add(device_list, parent);
fu_device_list_add(device_list, child);
fu_device_list_remove(device_list, parent);
active2 = fu_device_list_get_active(device_list);
g_assert_cmpint(active2->len, ==, 1);
fu_device_list_remove(device_list, child);
active3 = fu_device_list_get_active(device_list);
g_assert_cmpint(active3->len, ==, 0);
}
static void
fu_device_list_delay_func(gconstpointer user_data)
{
FuTest *self = (FuTest *)user_data;
g_autoptr(FuDevice) device1 = fu_device_new_with_context(self->ctx);
g_autoptr(FuDevice) device2 = fu_device_new_with_context(self->ctx);
g_autoptr(FuDeviceList) device_list = fu_device_list_new();
guint added_cnt = 0;
guint changed_cnt = 0;
guint removed_cnt = 0;
g_signal_connect(FU_DEVICE_LIST(device_list),
"added",
G_CALLBACK(_device_list_count_cb),
&added_cnt);
g_signal_connect(FU_DEVICE_LIST(device_list),
"removed",
G_CALLBACK(_device_list_count_cb),
&removed_cnt);
g_signal_connect(FU_DEVICE_LIST(device_list),
"changed",
G_CALLBACK(_device_list_count_cb),
&changed_cnt);
/* add one device */
fu_device_set_id(device1, "device1");
fu_device_add_instance_id(device1, "foobar");
fu_device_set_remove_delay(device1, 100);
fu_device_convert_instance_ids(device1);
fu_device_list_add(device_list, device1);
g_assert_cmpint(added_cnt, ==, 1);
g_assert_cmpint(removed_cnt, ==, 0);
g_assert_cmpint(changed_cnt, ==, 0);
/* add the same device again */
fu_device_list_add(device_list, device1);
g_assert_cmpint(added_cnt, ==, 1);
g_assert_cmpint(removed_cnt, ==, 0);
g_assert_cmpint(changed_cnt, ==, 1);
/* add a device with the same ID */
fu_device_set_id(device2, "device1");
fu_device_list_add(device_list, device2);
fu_device_set_remove_delay(device2, 100);
g_assert_cmpint(added_cnt, ==, 1);
g_assert_cmpint(removed_cnt, ==, 0);
g_assert_cmpint(changed_cnt, ==, 2);
/* spin a bit */
fu_test_loop_run_with_timeout(10);
fu_test_loop_quit();
/* verify only a changed event was generated */
added_cnt = removed_cnt = changed_cnt = 0;
fu_device_list_remove(device_list, device1);
fu_device_list_add(device_list, device1);
g_assert_cmpint(added_cnt, ==, 0);
g_assert_cmpint(removed_cnt, ==, 0);
g_assert_cmpint(changed_cnt, ==, 1);
}
typedef struct {
FuDevice *device_new;
FuDevice *device_old;
FuDeviceList *device_list;
} FuDeviceListReplugHelper;
static gboolean
fu_device_list_remove_cb(gpointer user_data)
{
FuDeviceListReplugHelper *helper = (FuDeviceListReplugHelper *)user_data;
fu_device_list_remove(helper->device_list, helper->device_old);
return FALSE;
}
static gboolean
fu_device_list_add_cb(gpointer user_data)
{
FuDeviceListReplugHelper *helper = (FuDeviceListReplugHelper *)user_data;
fu_device_list_add(helper->device_list, helper->device_new);
return FALSE;
}
static void
fu_device_list_replug_auto_func(gconstpointer user_data)
{
gboolean ret;
g_autoptr(FuDevice) device1 = fu_device_new();
g_autoptr(FuDevice) device2 = fu_device_new();
g_autoptr(FuDevice) parent = fu_device_new();
g_autoptr(FuDeviceList) device_list = fu_device_list_new();
g_autoptr(GError) error = NULL;
FuDeviceListReplugHelper helper;
/* parent */
fu_device_set_id(parent, "parent");
/* fake child devices */
fu_device_set_id(device1, "device1");
fu_device_add_internal_flag(device1, FU_DEVICE_INTERNAL_FLAG_REPLUG_MATCH_GUID);
fu_device_set_physical_id(device1, "ID");
fu_device_set_plugin(device1, "self-test");
fu_device_set_remove_delay(device1, FU_DEVICE_REMOVE_DELAY_RE_ENUMERATE);
fu_device_add_child(parent, device1);
fu_device_set_id(device2, "device2");
fu_device_add_internal_flag(device2, FU_DEVICE_INTERNAL_FLAG_REPLUG_MATCH_GUID);
fu_device_set_physical_id(device2, "ID"); /* matches */
fu_device_set_plugin(device2, "self-test");
fu_device_set_remove_delay(device2, FU_DEVICE_REMOVE_DELAY_RE_ENUMERATE);
/* not yet added */
ret = fu_device_list_wait_for_replug(device_list, &error);
g_assert_no_error(error);
g_assert_true(ret);
/* add device */
fu_device_list_add(device_list, device1);
/* not waiting */
ret = fu_device_list_wait_for_replug(device_list, &error);
g_assert_no_error(error);
g_assert_true(ret);
/* waiting */
helper.device_old = device1;
helper.device_new = device2;
helper.device_list = device_list;
g_timeout_add(100, fu_device_list_remove_cb, &helper);
g_timeout_add(200, fu_device_list_add_cb, &helper);
fu_device_add_flag(device1, FWUPD_DEVICE_FLAG_WAIT_FOR_REPLUG);
ret = fu_device_list_wait_for_replug(device_list, &error);
g_assert_no_error(error);
g_assert_true(ret);
g_assert_false(fu_device_has_flag(device1, FWUPD_DEVICE_FLAG_WAIT_FOR_REPLUG));
/* check device2 now has parent too */
g_assert_true(fu_device_get_parent(device2) == parent);
/* waiting, failed */
fu_device_add_flag(device2, FWUPD_DEVICE_FLAG_WAIT_FOR_REPLUG);
ret = fu_device_list_wait_for_replug(device_list, &error);
g_assert_error(error, FWUPD_ERROR, FWUPD_ERROR_NOT_FOUND);
g_assert_false(ret);
g_assert_false(fu_device_has_flag(device1, FWUPD_DEVICE_FLAG_WAIT_FOR_REPLUG));
}
static void
fu_device_list_replug_user_func(gconstpointer user_data)
{
FuTest *self = (FuTest *)user_data;
gboolean ret;
g_autoptr(FuDevice) device1 = fu_device_new_with_context(self->ctx);
g_autoptr(FuDevice) device2 = fu_device_new_with_context(self->ctx);
g_autoptr(FuDeviceList) device_list = fu_device_list_new();
g_autoptr(GError) error = NULL;
FuDeviceListReplugHelper helper;
/* fake devices */
fu_device_set_id(device1, "device1");
fu_device_set_name(device1, "device1");
fu_device_add_internal_flag(device1, FU_DEVICE_INTERNAL_FLAG_REPLUG_MATCH_GUID);
fu_device_add_instance_id(device1, "foo");
fu_device_add_instance_id(device1, "bar");
fu_device_set_plugin(device1, "self-test");
fu_device_set_remove_delay(device1, FU_DEVICE_REMOVE_DELAY_USER_REPLUG);
fu_device_convert_instance_ids(device1);
fu_device_set_id(device2, "device2");
fu_device_set_name(device2, "device2");
fu_device_add_internal_flag(device2, FU_DEVICE_INTERNAL_FLAG_REPLUG_MATCH_GUID);
fu_device_add_instance_id(device2, "baz");
fu_device_add_instance_id(device2, "bar"); /* matches */
fu_device_set_plugin(device2, "self-test");
fu_device_set_remove_delay(device2, FU_DEVICE_REMOVE_DELAY_USER_REPLUG);
fu_device_convert_instance_ids(device2);
/* not yet added */
ret = fu_device_list_wait_for_replug(device_list, &error);
g_assert_no_error(error);
g_assert_true(ret);
/* add device */
fu_device_list_add(device_list, device1);
/* add duplicate */
fu_device_add_flag(device1, FWUPD_DEVICE_FLAG_WAIT_FOR_REPLUG);
fu_device_list_add(device_list, device1);
g_assert_false(fu_device_has_flag(device1, FWUPD_DEVICE_FLAG_WAIT_FOR_REPLUG));
/* not waiting */
ret = fu_device_list_wait_for_replug(device_list, &error);
g_assert_no_error(error);
g_assert_true(ret);
/* waiting */
helper.device_old = device1;
helper.device_new = device2;
helper.device_list = device_list;
g_timeout_add(100, fu_device_list_remove_cb, &helper);
g_timeout_add(200, fu_device_list_add_cb, &helper);
fu_device_add_flag(device1, FWUPD_DEVICE_FLAG_WAIT_FOR_REPLUG);
ret = fu_device_list_wait_for_replug(device_list, &error);
g_assert_no_error(error);
g_assert_true(ret);
g_assert_false(fu_device_has_flag(device1, FWUPD_DEVICE_FLAG_WAIT_FOR_REPLUG));
/* should not be possible, but here we are */
fu_device_add_flag(device1, FWUPD_DEVICE_FLAG_WAIT_FOR_REPLUG);
fu_device_add_flag(device2, FWUPD_DEVICE_FLAG_WAIT_FOR_REPLUG);
fu_device_list_add(device_list, device1);
g_assert_false(fu_device_has_flag(device1, FWUPD_DEVICE_FLAG_WAIT_FOR_REPLUG));
g_assert_false(fu_device_has_flag(device2, FWUPD_DEVICE_FLAG_WAIT_FOR_REPLUG));
/* add back the old device */
fu_device_add_flag(device1, FWUPD_DEVICE_FLAG_WAIT_FOR_REPLUG);
fu_device_add_flag(device2, FWUPD_DEVICE_FLAG_WAIT_FOR_REPLUG);
fu_device_list_remove(device_list, device2);
fu_device_list_add(device_list, device1);
g_assert_false(fu_device_has_flag(device1, FWUPD_DEVICE_FLAG_WAIT_FOR_REPLUG));
g_assert_false(fu_device_has_flag(device2, FWUPD_DEVICE_FLAG_WAIT_FOR_REPLUG));
}
static void
fu_device_list_compatible_func(gconstpointer user_data)
{
FuTest *self = (FuTest *)user_data;
g_autoptr(FuDevice) device1 = fu_device_new_with_context(self->ctx);
g_autoptr(FuDevice) device2 = fu_device_new_with_context(self->ctx);
g_autoptr(FuDevice) device_old = NULL;
g_autoptr(FuDeviceList) device_list = fu_device_list_new();
g_autoptr(GPtrArray) devices_all = NULL;
g_autoptr(GPtrArray) devices_active = NULL;
FuDevice *device;
guint added_cnt = 0;
guint changed_cnt = 0;
guint removed_cnt = 0;
g_signal_connect(FU_DEVICE_LIST(device_list),
"added",
G_CALLBACK(_device_list_count_cb),
&added_cnt);
g_signal_connect(FU_DEVICE_LIST(device_list),
"removed",
G_CALLBACK(_device_list_count_cb),
&removed_cnt);
g_signal_connect(FU_DEVICE_LIST(device_list),
"changed",
G_CALLBACK(_device_list_count_cb),
&changed_cnt);
/* add one device in runtime mode */
fu_device_set_id(device1, "device1");
fu_device_set_plugin(device1, "plugin-for-runtime");
fu_device_add_vendor_id(device1, "USB:0x20A0");
fu_device_set_version_format(device1, FWUPD_VERSION_FORMAT_TRIPLET);
fu_device_set_version(device1, "1.2.3");
fu_device_add_internal_flag(device1, FU_DEVICE_INTERNAL_FLAG_REPLUG_MATCH_GUID);
fu_device_add_instance_id(device1, "foobar");
fu_device_add_instance_id(device1, "bootloader");
fu_device_set_remove_delay(device1, 100);
fu_device_convert_instance_ids(device1);
fu_device_list_add(device_list, device1);
g_assert_cmpint(added_cnt, ==, 1);
g_assert_cmpint(removed_cnt, ==, 0);
g_assert_cmpint(changed_cnt, ==, 0);
/* add another device in bootloader mode */
fu_device_set_id(device2, "device2");
fu_device_set_plugin(device2, "plugin-for-bootloader");
fu_device_add_instance_id(device2, "bootloader");
fu_device_add_internal_flag(device2, FU_DEVICE_INTERNAL_FLAG_REPLUG_MATCH_GUID);
fu_device_convert_instance_ids(device2);
/* verify only a changed event was generated */
added_cnt = removed_cnt = changed_cnt = 0;
fu_device_list_remove(device_list, device1);
fu_device_list_add(device_list, device2);
g_assert_cmpint(added_cnt, ==, 0);
g_assert_cmpint(removed_cnt, ==, 0);
g_assert_cmpint(changed_cnt, ==, 1);
/* device2 should inherit the vendor ID and version from device1 */
g_assert_true(fu_device_has_vendor_id(device2, "USB:0x20A0"));
g_assert_cmpstr(fu_device_get_version(device2), ==, "1.2.3");
/* one device is active */
devices_active = fu_device_list_get_active(device_list);
g_assert_cmpint(devices_active->len, ==, 1);
device = g_ptr_array_index(devices_active, 0);
g_assert_cmpstr(fu_device_get_id(device), ==, "1a8d0d9a96ad3e67ba76cf3033623625dc6d6882");
/* the list knows about both devices, list in order of active->old */
devices_all = fu_device_list_get_all(device_list);
g_assert_cmpint(devices_all->len, ==, 2);
device = g_ptr_array_index(devices_all, 0);
g_assert_cmpstr(fu_device_get_id(device), ==, "1a8d0d9a96ad3e67ba76cf3033623625dc6d6882");
device = g_ptr_array_index(devices_all, 1);
g_assert_cmpstr(fu_device_get_id(device), ==, "99249eb1bd9ef0b6e192b271a8cb6a3090cfec7a");
/* verify we can get the old device from the new device */
device_old = fu_device_list_get_old(device_list, device2);
g_assert_true(device_old == device1);
}
static void
fu_device_list_remove_chain_func(gconstpointer user_data)
{
FuTest *self = (FuTest *)user_data;
g_autoptr(FuDeviceList) device_list = fu_device_list_new();
g_autoptr(FuDevice) device_child = fu_device_new_with_context(self->ctx);
g_autoptr(FuDevice) device_parent = fu_device_new_with_context(self->ctx);
guint added_cnt = 0;
guint changed_cnt = 0;
guint removed_cnt = 0;
g_signal_connect(FU_DEVICE_LIST(device_list),
"added",
G_CALLBACK(_device_list_count_cb),
&added_cnt);
g_signal_connect(FU_DEVICE_LIST(device_list),
"removed",
G_CALLBACK(_device_list_count_cb),
&removed_cnt);
g_signal_connect(FU_DEVICE_LIST(device_list),
"changed",
G_CALLBACK(_device_list_count_cb),
&changed_cnt);
/* add child */
fu_device_set_id(device_child, "child");
fu_device_add_instance_id(device_child, "child-GUID-1");
fu_device_convert_instance_ids(device_child);
fu_device_list_add(device_list, device_child);
g_assert_cmpint(added_cnt, ==, 1);
g_assert_cmpint(removed_cnt, ==, 0);
g_assert_cmpint(changed_cnt, ==, 0);
/* add parent */
fu_device_set_id(device_parent, "parent");
fu_device_add_instance_id(device_parent, "parent-GUID-1");
fu_device_convert_instance_ids(device_parent);
fu_device_add_child(device_parent, device_child);
fu_device_list_add(device_list, device_parent);
g_assert_cmpint(added_cnt, ==, 2);
g_assert_cmpint(removed_cnt, ==, 0);
g_assert_cmpint(changed_cnt, ==, 0);
/* make sure that removing the parent causes both to go; but the child to go first */
fu_device_list_remove(device_list, device_parent);
g_assert_cmpint(added_cnt, ==, 2);
g_assert_cmpint(removed_cnt, ==, 2);
g_assert_cmpint(changed_cnt, ==, 0);
}
static void
fu_device_list_func(gconstpointer user_data)
{
FuTest *self = (FuTest *)user_data;
g_autoptr(FuDeviceList) device_list = fu_device_list_new();
g_autoptr(FuDevice) device1 = fu_device_new_with_context(self->ctx);
g_autoptr(FuDevice) device2 = fu_device_new_with_context(self->ctx);
g_autoptr(GPtrArray) devices = NULL;
g_autoptr(GPtrArray) devices2 = NULL;
g_autoptr(GError) error = NULL;
FuDevice *device;
guint added_cnt = 0;
guint changed_cnt = 0;
guint removed_cnt = 0;
g_signal_connect(FU_DEVICE_LIST(device_list),
"added",
G_CALLBACK(_device_list_count_cb),
&added_cnt);
g_signal_connect(FU_DEVICE_LIST(device_list),
"removed",
G_CALLBACK(_device_list_count_cb),
&removed_cnt);
g_signal_connect(FU_DEVICE_LIST(device_list),
"changed",
G_CALLBACK(_device_list_count_cb),
&changed_cnt);
/* add both */
fu_device_set_id(device1, "device1");
fu_device_add_instance_id(device1, "foobar");
fu_device_convert_instance_ids(device1);
fu_device_list_add(device_list, device1);
fu_device_set_id(device2, "device2");
fu_device_add_instance_id(device2, "baz");
fu_device_convert_instance_ids(device2);
fu_device_list_add(device_list, device2);
g_assert_cmpint(added_cnt, ==, 2);
g_assert_cmpint(removed_cnt, ==, 0);
g_assert_cmpint(changed_cnt, ==, 0);
/* get all */
devices = fu_device_list_get_all(device_list);
g_assert_cmpint(devices->len, ==, 2);
device = g_ptr_array_index(devices, 0);
g_assert_cmpstr(fu_device_get_id(device), ==, "99249eb1bd9ef0b6e192b271a8cb6a3090cfec7a");
/* find by ID */
device = fu_device_list_get_by_id(device_list,
"99249eb1bd9ef0b6e192b271a8cb6a3090cfec7a",
&error);
g_assert_no_error(error);
g_assert_nonnull(device);
g_assert_cmpstr(fu_device_get_id(device), ==, "99249eb1bd9ef0b6e192b271a8cb6a3090cfec7a");
g_clear_object(&device);
/* find by GUID */
device =
fu_device_list_get_by_guid(device_list, "579a3b1c-d1db-5bdc-b6b9-e2c1b28d5b8a", &error);
g_assert_no_error(error);
g_assert_nonnull(device);
g_assert_cmpstr(fu_device_get_id(device), ==, "1a8d0d9a96ad3e67ba76cf3033623625dc6d6882");
g_clear_object(&device);
/* find by missing GUID */
device = fu_device_list_get_by_guid(device_list, "notfound", &error);
g_assert_error(error, FWUPD_ERROR, FWUPD_ERROR_NOT_FOUND);
g_assert_null(device);
/* remove device */
added_cnt = removed_cnt = changed_cnt = 0;
fu_device_list_remove(device_list, device1);
g_assert_cmpint(added_cnt, ==, 0);
g_assert_cmpint(removed_cnt, ==, 1);
g_assert_cmpint(changed_cnt, ==, 0);
devices2 = fu_device_list_get_all(device_list);
g_assert_cmpint(devices2->len, ==, 1);
device = g_ptr_array_index(devices2, 0);
g_assert_cmpstr(fu_device_get_id(device), ==, "1a8d0d9a96ad3e67ba76cf3033623625dc6d6882");
}
static void
fu_plugin_list_func(gconstpointer user_data)
{
GPtrArray *plugins;
FuPlugin *plugin;
g_autoptr(FuPluginList) plugin_list = fu_plugin_list_new();
g_autoptr(FuPlugin) plugin1 = fu_plugin_new(NULL);
g_autoptr(FuPlugin) plugin2 = fu_plugin_new(NULL);
g_autoptr(GError) error = NULL;
fu_plugin_set_name(plugin1, "plugin1");
fu_plugin_set_name(plugin2, "plugin2");
/* get all the plugins */
fu_plugin_list_add(plugin_list, plugin1);
fu_plugin_list_add(plugin_list, plugin2);
plugins = fu_plugin_list_get_all(plugin_list);
g_assert_cmpint(plugins->len, ==, 2);
/* get a single plugin */
plugin = fu_plugin_list_find_by_name(plugin_list, "plugin1", &error);
g_assert_no_error(error);
g_assert_nonnull(plugin);
g_assert_cmpstr(fu_plugin_get_name(plugin), ==, "plugin1");
/* does not exist */
plugin = fu_plugin_list_find_by_name(plugin_list, "nope", &error);
g_assert_error(error, FWUPD_ERROR, FWUPD_ERROR_NOT_FOUND);
g_assert_null(plugin);
}
static void
fu_plugin_list_depsolve_func(gconstpointer user_data)
{
GPtrArray *plugins;
FuPlugin *plugin;
gboolean ret;
g_autoptr(FuPluginList) plugin_list = fu_plugin_list_new();
g_autoptr(FuPlugin) plugin1 = fu_plugin_new(NULL);
g_autoptr(FuPlugin) plugin2 = fu_plugin_new(NULL);
g_autoptr(GError) error = NULL;
fu_plugin_set_name(plugin1, "plugin1");
fu_plugin_set_name(plugin2, "plugin2");
/* add rule then depsolve */
fu_plugin_list_add(plugin_list, plugin1);
fu_plugin_list_add(plugin_list, plugin2);
fu_plugin_add_rule(plugin1, FU_PLUGIN_RULE_RUN_AFTER, "plugin2");
ret = fu_plugin_list_depsolve(plugin_list, &error);
g_assert_no_error(error);
g_assert_true(ret);
plugins = fu_plugin_list_get_all(plugin_list);
g_assert_cmpint(plugins->len, ==, 2);
plugin = g_ptr_array_index(plugins, 0);
g_assert_cmpstr(fu_plugin_get_name(plugin), ==, "plugin2");
g_assert_cmpint(fu_plugin_get_order(plugin), ==, 0);
g_assert_false(fu_plugin_has_flag(plugin, FWUPD_PLUGIN_FLAG_DISABLED));
/* add another rule, then re-depsolve */
fu_plugin_add_rule(plugin1, FU_PLUGIN_RULE_CONFLICTS, "plugin2");
ret = fu_plugin_list_depsolve(plugin_list, &error);
g_assert_no_error(error);
g_assert_true(ret);
plugin = fu_plugin_list_find_by_name(plugin_list, "plugin1", &error);
g_assert_no_error(error);
g_assert_nonnull(plugin);
g_assert_false(fu_plugin_has_flag(plugin, FWUPD_PLUGIN_FLAG_DISABLED));
plugin = fu_plugin_list_find_by_name(plugin_list, "plugin2", &error);
g_assert_no_error(error);
g_assert_nonnull(plugin);
g_assert_true(fu_plugin_has_flag(plugin, FWUPD_PLUGIN_FLAG_DISABLED));
}
static void
fu_history_migrate_func(gconstpointer user_data)
{
gboolean ret;
g_autoptr(GError) error = NULL;
g_autoptr(GFile) file_dst = NULL;
g_autoptr(GFile) file_src = NULL;
g_autoptr(FuDevice) device = NULL;
g_autoptr(FuHistory) history = NULL;
g_autofree gchar *filename = NULL;
#ifndef HAVE_SQLITE
g_test_skip("no sqlite support");
return;
#endif
/* load old version */
filename = g_test_build_filename(G_TEST_DIST, "tests", "history_v1.db", NULL);
file_src = g_file_new_for_path(filename);
file_dst = g_file_new_for_path("/tmp/fwupd-self-test/var/lib/fwupd/pending.db");
ret = g_file_copy(file_src, file_dst, G_FILE_COPY_OVERWRITE, NULL, NULL, NULL, &error);
g_assert_no_error(error);
g_assert_true(ret);
/* create, migrating as required */
history = fu_history_new();
g_assert_nonnull(history);
/* get device */
device = fu_history_get_device_by_id(history,
"2ba16d10df45823dd4494ff10a0bfccfef512c9d",
&error);
g_assert_no_error(error);
g_assert_nonnull(device);
g_assert_cmpstr(fu_device_get_id(device), ==, "2ba16d10df45823dd4494ff10a0bfccfef512c9d");
}
static void
_plugin_status_changed_cb(FuDevice *device, FwupdStatus status, gpointer user_data)
{
guint *cnt = (guint *)user_data;
g_debug("status now %s", fwupd_status_to_string(status));
(*cnt)++;
fu_test_loop_quit();
}
static void
_plugin_device_added_cb(FuPlugin *plugin, FuDevice *device, gpointer user_data)
{
FuDevice **dev = (FuDevice **)user_data;
*dev = g_object_ref(device);
fu_test_loop_quit();
}
static void
_plugin_device_register_cb(FuPlugin *plugin, FuDevice *device, gpointer user_data)
{
/* fake being a daemon */
fu_plugin_runner_device_register(plugin, device);
}
static void
fu_plugin_module_func(gconstpointer user_data)
{
FuTest *self = (FuTest *)user_data;
GError *error = NULL;
FuDevice *device_tmp;
FwupdRelease *release;
gboolean ret;
guint cnt = 0;
g_autofree gchar *localstatedir = NULL;
g_autofree gchar *mapped_file_fn = NULL;
g_autofree gchar *pending_cap = NULL;
g_autofree gchar *history_db = NULL;
g_autoptr(FuDevice) device = NULL;
g_autoptr(FuDevice) device2 = NULL;
g_autoptr(FuDevice) device3 = NULL;
g_autoptr(FuEngine) engine = fu_engine_new(FU_APP_FLAGS_NONE);
g_autoptr(FuHistory) history = NULL;
g_autoptr(FuProgress) progress = fu_progress_new(G_STRLOC);
g_autoptr(GBytes) blob_cab = NULL;
g_autoptr(GMappedFile) mapped_file = NULL;
g_autoptr(XbSilo) silo_empty = xb_silo_new();
/* no metadata in daemon */
fu_engine_set_silo(engine, silo_empty);
/* create a fake device */
g_setenv("FWUPD_PLUGIN_TEST", "registration", TRUE);
ret = fu_plugin_runner_startup(self->plugin, &error);
g_assert_no_error(error);
g_assert_true(ret);
g_signal_connect(FU_PLUGIN(self->plugin),
"device-added",
G_CALLBACK(_plugin_device_added_cb),
&device);
g_signal_connect(FU_PLUGIN(self->plugin),
"device-register",
G_CALLBACK(_plugin_device_register_cb),
&device);
ret = fu_plugin_runner_coldplug(self->plugin, &error);
g_assert_no_error(error);
g_assert_true(ret);
/* check we did the right thing */
g_assert_nonnull(device);
g_assert_cmpstr(fu_device_get_id(device), ==, "08d460be0f1f9f128413f816022a6439e0078018");
g_assert_cmpstr(fu_device_get_version_lowest(device), ==, "1.2.0");
g_assert_cmpstr(fu_device_get_version(device), ==, "1.2.2");
g_assert_cmpstr(fu_device_get_version_bootloader(device), ==, "0.1.2");
g_assert_cmpstr(fu_device_get_guid_default(device),
==,
"b585990a-003e-5270-89d5-3705a17f9a43");
g_assert_cmpstr(fu_device_get_name(device), ==, "Integrated Webcam™");
g_signal_handlers_disconnect_by_data(self->plugin, &device);
#ifndef HAVE_FWUPDOFFLINE
g_test_skip("No offline update support on Windows");
return;
#endif
/* schedule an offline update */
g_signal_connect(FU_PROGRESS(progress),
"status-changed",
G_CALLBACK(_plugin_status_changed_cb),
&cnt);
mapped_file_fn =
g_test_build_filename(G_TEST_DIST, "tests", "colorhug", "firmware.bin", NULL);
mapped_file = g_mapped_file_new(mapped_file_fn, FALSE, &error);
g_assert_no_error(error);
g_assert_nonnull(mapped_file);
blob_cab = g_mapped_file_get_bytes(mapped_file);
release = fu_device_get_release_default(device);
fwupd_release_set_version(release, "1.2.3");
ret = fu_engine_schedule_update(engine,
device,
release,
blob_cab,
FWUPD_INSTALL_FLAG_NONE,
&error);
g_assert_no_error(error);
g_assert_true(ret);
/* set on the current device */
g_assert_true(fu_device_has_flag(device, FWUPD_DEVICE_FLAG_NEEDS_REBOOT));
/* lets check the history */
history = fu_history_new();
device2 = fu_history_get_device_by_id(history, fu_device_get_id(device), &error);
g_assert_no_error(error);
g_assert_nonnull(device2);
g_assert_cmpint(fu_device_get_update_state(device2), ==, FWUPD_UPDATE_STATE_PENDING);
g_assert_cmpstr(fu_device_get_update_error(device2), ==, NULL);
g_assert_true(fu_device_has_flag(device2, FWUPD_DEVICE_FLAG_NEEDS_REBOOT));
release = fu_device_get_release_default(device2);
g_assert_nonnull(release);
g_assert_cmpstr(fwupd_release_get_filename(release), !=, NULL);
g_assert_cmpstr(fwupd_release_get_version(release), ==, "1.2.3");
/* save this; we'll need to delete it later */
pending_cap = g_strdup(fwupd_release_get_filename(release));
/* lets do this online */
fu_engine_add_device(engine, device);
fu_engine_add_plugin(engine, self->plugin);
ret = fu_engine_install_blob(engine,
device,
blob_cab,
progress,
FWUPD_INSTALL_FLAG_NONE,
FWUPD_FEATURE_FLAG_NONE,
&error);
g_assert_no_error(error);
g_assert_true(ret);
g_assert_cmpint(cnt, ==, 8);
/* check the new version */
g_assert_cmpstr(fu_device_get_version(device), ==, "1.2.3");
g_assert_cmpstr(fu_device_get_version_bootloader(device), ==, "0.1.2");
/* lets check the history */
device3 = fu_history_get_device_by_id(history, fu_device_get_id(device), &error);
g_assert_no_error(error);
g_assert_nonnull(device3);
g_assert_cmpint(fu_device_get_update_state(device3), ==, FWUPD_UPDATE_STATE_SUCCESS);
g_assert_cmpstr(fu_device_get_update_error(device3), ==, NULL);
/* get the status */
device_tmp = fu_device_new();
fu_device_set_id(device_tmp, "FakeDevice");
ret = fu_plugin_runner_get_results(self->plugin, device_tmp, &error);
g_assert_no_error(error);
g_assert_true(ret);
g_assert_cmpint(fu_device_get_update_state(device_tmp), ==, FWUPD_UPDATE_STATE_SUCCESS);
g_assert_cmpstr(fu_device_get_update_error(device_tmp), ==, NULL);
/* clear */
ret = fu_plugin_runner_clear_results(self->plugin, device_tmp, &error);
g_assert_no_error(error);
g_assert_true(ret);
g_object_unref(device_tmp);
g_clear_error(&error);
/* delete files */
localstatedir = fu_common_get_path(FU_PATH_KIND_LOCALSTATEDIR_PKG);
history_db = g_build_filename(localstatedir, "pending.db", NULL);
g_unlink(history_db);
g_unlink(pending_cap);
}
static void
fu_history_func(gconstpointer user_data)
{
FuTest *self = (FuTest *)user_data;
GError *error = NULL;
GPtrArray *checksums;
gboolean ret;
FuDevice *device;
FwupdRelease *release;
g_autoptr(FuDevice) device_found = NULL;
g_autoptr(FuHistory) history = NULL;
g_autoptr(GPtrArray) approved_firmware = NULL;
g_autofree gchar *dirname = NULL;
g_autofree gchar *filename = NULL;
#ifndef HAVE_SQLITE
g_test_skip("no sqlite support");
return;
#endif
/* create */
history = fu_history_new();
g_assert_nonnull(history);
/* delete the database */
dirname = fu_common_get_path(FU_PATH_KIND_LOCALSTATEDIR_PKG);
if (!g_file_test(dirname, G_FILE_TEST_IS_DIR))
return;
filename = g_build_filename(dirname, "pending.db", NULL);
g_unlink(filename);
/* add a device */
device = fu_device_new_with_context(self->ctx);
fu_device_set_id(device, "self-test");
fu_device_set_name(device, "ColorHug"),
fu_device_set_version_format(device, FWUPD_VERSION_FORMAT_TRIPLET);
fu_device_set_version(device, "3.0.1"),
fu_device_set_update_state(device, FWUPD_UPDATE_STATE_FAILED);
fu_device_set_update_error(device, "word");
fu_device_add_guid(device, "827edddd-9bb6-5632-889f-2c01255503da");
fu_device_add_flag(device, FWUPD_DEVICE_FLAG_INTERNAL);
fu_device_set_created(device, 123);
fu_device_set_modified(device, 456);
release = fwupd_release_new();
fwupd_release_set_filename(release, "/var/lib/dave.cap"),
fwupd_release_add_checksum(release, "abcdef");
fwupd_release_set_version(release, "3.0.2");
fwupd_release_add_metadata_item(release, "FwupdVersion", VERSION);
ret = fu_history_add_device(history, device, release, &error);
g_assert_no_error(error);
g_assert_true(ret);
g_object_unref(release);
/* ensure database was created */
g_assert_true(g_file_test(filename, G_FILE_TEST_EXISTS));
g_object_unref(device);
/* get device */
device = fu_history_get_device_by_id(history,
"2ba16d10df45823dd4494ff10a0bfccfef512c9d",
&error);
g_assert_no_error(error);
g_assert_nonnull(device);
g_assert_cmpstr(fu_device_get_id(device), ==, "2ba16d10df45823dd4494ff10a0bfccfef512c9d");
g_assert_cmpstr(fu_device_get_name(device), ==, "ColorHug");
g_assert_cmpstr(fu_device_get_version(device), ==, "3.0.1");
g_assert_cmpint(fu_device_get_update_state(device), ==, FWUPD_UPDATE_STATE_FAILED);
g_assert_cmpstr(fu_device_get_update_error(device), ==, "word");
g_assert_cmpstr(fu_device_get_guid_default(device),
==,
"827edddd-9bb6-5632-889f-2c01255503da");
g_assert_cmpint(fu_device_get_flags(device),
==,
FWUPD_DEVICE_FLAG_INTERNAL | FWUPD_DEVICE_FLAG_HISTORICAL);
g_assert_cmpint(fu_device_get_created(device), ==, 123);
g_assert_cmpint(fu_device_get_modified(device), ==, 456);
release = fu_device_get_release_default(device);
g_assert_nonnull(release);
g_assert_cmpstr(fwupd_release_get_version(release), ==, "3.0.2");
g_assert_cmpstr(fwupd_release_get_filename(release), ==, "/var/lib/dave.cap");
g_assert_cmpstr(fwupd_release_get_metadata_item(release, "FwupdVersion"), ==, VERSION);
checksums = fwupd_release_get_checksums(release);
g_assert_nonnull(checksums);
g_assert_cmpint(checksums->len, ==, 1);
g_assert_cmpstr(fwupd_checksum_get_by_kind(checksums, G_CHECKSUM_SHA1), ==, "abcdef");
ret = fu_history_add_device(history, device, release, &error);
g_assert_no_error(error);
g_assert_true(ret);
/* get device that does not exist */
device_found = fu_history_get_device_by_id(history, "XXXXXXXXXXXXX", &error);
g_assert_error(error, FWUPD_ERROR, FWUPD_ERROR_NOT_FOUND);
g_assert_null(device_found);
g_clear_error(&error);
/* get device that does exist */
device_found = fu_history_get_device_by_id(history,
"2ba16d10df45823dd4494ff10a0bfccfef512c9d",
&error);
g_assert_no_error(error);
g_assert_nonnull(device_found);
g_object_unref(device_found);
/* remove device */
ret = fu_history_remove_device(history, device, &error);
g_assert_no_error(error);
g_assert_true(ret);
g_object_unref(device);
/* get device that does not exist */
device_found = fu_history_get_device_by_id(history,
"2ba16d10df45823dd4494ff10a0bfccfef512c9d",
&error);
g_assert_error(error, FWUPD_ERROR, FWUPD_ERROR_NOT_FOUND);
g_assert_null(device_found);
g_clear_error(&error);
/* approved firmware */
ret = fu_history_clear_approved_firmware(history, &error);
g_assert_no_error(error);
g_assert_true(ret);
ret = fu_history_add_approved_firmware(history, "foo", &error);
g_assert_no_error(error);
g_assert_true(ret);
ret = fu_history_add_approved_firmware(history, "bar", &error);
g_assert_no_error(error);
g_assert_true(ret);
approved_firmware = fu_history_get_approved_firmware(history, &error);
g_assert_no_error(error);
g_assert_nonnull(approved_firmware);
g_assert_cmpint(approved_firmware->len, ==, 2);
g_assert_cmpstr(g_ptr_array_index(approved_firmware, 0), ==, "foo");
g_assert_cmpstr(g_ptr_array_index(approved_firmware, 1), ==, "bar");
}
static GBytes *
_build_cab(GCabCompression compression, ...)
{
gboolean ret;
va_list args;
g_autoptr(GCabCabinet) cabinet = NULL;
g_autoptr(GCabFolder) cabfolder = NULL;
g_autoptr(GError) error = NULL;
g_autoptr(GOutputStream) op = NULL;
/* create a new archive */
cabinet = gcab_cabinet_new();
cabfolder = gcab_folder_new(compression);
ret = gcab_cabinet_add_folder(cabinet, cabfolder, &error);
g_assert_no_error(error);
g_assert_true(ret);
/* add each file */
va_start(args, compression);
do {
const gchar *fn;
const gchar *text;
g_autoptr(GCabFile) cabfile = NULL;
g_autoptr(GBytes) blob = NULL;
/* get filename */
fn = va_arg(args, const gchar *);
if (fn == NULL)
break;
/* get contents */
text = va_arg(args, const gchar *);
if (text == NULL)
break;
g_debug("creating %s with %s", fn, text);
/* add a GCabFile to the cabinet */
blob = g_bytes_new_static(text, strlen(text));
cabfile = gcab_file_new_with_bytes(fn, blob);
ret = gcab_folder_add_file(cabfolder, cabfile, FALSE, NULL, &error);
g_assert_no_error(error);
g_assert_true(ret);
} while (TRUE);
va_end(args);
/* write the archive to a blob */
op = g_memory_output_stream_new_resizable();
ret = gcab_cabinet_write_simple(cabinet, op, NULL, NULL, NULL, &error);
g_assert_no_error(error);
g_assert_true(ret);
ret = g_output_stream_close(op, NULL, &error);
g_assert_no_error(error);
g_assert_true(ret);
return g_memory_output_stream_steal_as_bytes(G_MEMORY_OUTPUT_STREAM(op));
}
static void
_plugin_composite_device_added_cb(FuPlugin *plugin, FuDevice *device, gpointer user_data)
{
GPtrArray *devices = (GPtrArray *)user_data;
g_ptr_array_add(devices, g_object_ref(device));
}
static void
fu_plugin_composite_func(gconstpointer user_data)
{
FuTest *self = (FuTest *)user_data;
GError *error = NULL;
gboolean ret;
g_autoptr(FuEngine) engine = fu_engine_new(FU_APP_FLAGS_NONE);
g_autoptr(FuEngineRequest) request = fu_engine_request_new(FU_ENGINE_REQUEST_KIND_ACTIVE);
g_autoptr(GBytes) blob = NULL;
g_autoptr(GPtrArray) components = NULL;
g_autoptr(GPtrArray) devices = NULL;
g_autoptr(GPtrArray) releases =
g_ptr_array_new_with_free_func((GDestroyNotify)g_object_unref);
g_autoptr(FuProgress) progress = fu_progress_new(G_STRLOC);
g_autoptr(XbSilo) silo_empty = xb_silo_new();
g_autoptr(XbSilo) silo = NULL;
/* no metadata in daemon */
fu_engine_set_silo(engine, silo_empty);
/* create CAB file */
blob = _build_cab(
GCAB_COMPRESSION_NONE,
"acme.metainfo.xml",
"<component type=\"firmware\">\n"
" <id>com.acme.example.firmware</id>\n"
" <provides>\n"
" <firmware type=\"flashed\">b585990a-003e-5270-89d5-3705a17f9a43</firmware>\n"
" </provides>\n"
" <releases>\n"
" <release version=\"1.2.3\"/>\n"
" </releases>\n"
"</component>",
"acme.module1.metainfo.xml",
"<component type=\"firmware\">\n"
" <id>com.acme.example.firmware.module1</id>\n"
" <provides>\n"
" <firmware type=\"flashed\">7fddead7-12b5-4fb9-9fa0-6d30305df755</firmware>\n"
" </provides>\n"
" <releases>\n"
" <release version=\"2\"/>\n"
" </releases>\n"
" <custom>\n"
" <value key=\"LVFS::VersionFormat\">plain</value>\n"
" </custom>\n"
"</component>",
"acme.module2.metainfo.xml",
"<component type=\"firmware\">\n"
" <id>com.acme.example.firmware.module2</id>\n"
" <provides>\n"
" <firmware type=\"flashed\">b8fe6b45-8702-4bcd-8120-ef236caac76f</firmware>\n"
" </provides>\n"
" <releases>\n"
" <release version=\"11\"/>\n"
" </releases>\n"
" <custom>\n"
" <value key=\"LVFS::VersionFormat\">plain</value>\n"
" </custom>\n"
"</component>",
"firmware.bin",
"world",
NULL);
silo = fu_common_cab_build_silo(blob, 10240, &error);
g_assert_no_error(error);
g_assert_nonnull(silo);
components = xb_silo_query(silo, "components/component", 0, &error);
g_assert_no_error(error);
g_assert_nonnull(components);
g_assert_cmpint(components->len, ==, 3);
/* set up dummy plugin */
g_setenv("FWUPD_PLUGIN_TEST", "composite", TRUE);
fu_engine_add_plugin(engine, self->plugin);
ret = fu_plugin_runner_startup(self->plugin, &error);
g_assert_no_error(error);
g_assert_true(ret);
devices = g_ptr_array_new_with_free_func((GDestroyNotify)g_object_unref);
g_signal_connect(FU_PLUGIN(self->plugin),
"device-added",
G_CALLBACK(_plugin_composite_device_added_cb),
devices);
ret = fu_plugin_runner_coldplug(self->plugin, &error);
g_assert_no_error(error);
g_assert_true(ret);
/* check we found all composite devices */
g_assert_cmpint(devices->len, ==, 3);
for (guint i = 0; i < devices->len; i++) {
FuDevice *device = g_ptr_array_index(devices, i);
fu_engine_add_device(engine, device);
if (g_strcmp0(fu_device_get_id(device),
"08d460be0f1f9f128413f816022a6439e0078018") == 0) {
g_assert_cmpstr(fu_device_get_version(device), ==, "1.2.2");
} else if (g_strcmp0(fu_device_get_id(device),
"c0a0a4aa6480ac28eea1ce164fbb466ca934e1ff") == 0) {
g_assert_cmpstr(fu_device_get_version(device), ==, "1");
g_assert_nonnull(fu_device_get_parent(device));
} else if (g_strcmp0(fu_device_get_id(device),
"bf455e9f371d2608d1cb67660fd2b335d3f6ef73") == 0) {
g_assert_cmpstr(fu_device_get_version(device), ==, "10");
g_assert_nonnull(fu_device_get_parent(device));
}
}
/* produce install tasks */
for (guint i = 0; i < components->len; i++) {
XbNode *component = g_ptr_array_index(components, i);
/* do any devices pass the requirements */
for (guint j = 0; j < devices->len; j++) {
FuDevice *device = g_ptr_array_index(devices, j);
g_autoptr(FuRelease) release = fu_release_new();
g_autoptr(GError) error_local = NULL;
/* is this component valid for the device */
fu_release_set_device(release, device);
fu_release_set_request(release, request);
if (!fu_release_load(release,
component,
NULL,
FWUPD_INSTALL_FLAG_NONE,
&error_local)) {
g_debug("requirement on %s:%s failed: %s",
fu_device_get_id(device),
xb_node_query_text(component, "id", NULL),
error_local->message);
continue;
}
g_ptr_array_add(releases, g_steal_pointer(&release));
}
}
g_assert_cmpint(releases->len, ==, 3);
/* install the cab */
ret = fu_engine_install_releases(engine,
request,
releases,
blob,
progress,
FWUPD_DEVICE_FLAG_NONE,
&error);
g_assert_no_error(error);
g_assert_true(ret);
/* verify everything upgraded */
for (guint i = 0; i < devices->len; i++) {
FuDevice *device = g_ptr_array_index(devices, i);
const gchar *metadata;
if (g_strcmp0(fu_device_get_id(device),
"08d460be0f1f9f128413f816022a6439e0078018") == 0) {
g_assert_cmpstr(fu_device_get_version(device), ==, "1.2.3");
} else if (g_strcmp0(fu_device_get_id(device),
"c0a0a4aa6480ac28eea1ce164fbb466ca934e1ff") == 0) {
g_assert_cmpstr(fu_device_get_version(device), ==, "2");
} else if (g_strcmp0(fu_device_get_id(device),
"bf455e9f371d2608d1cb67660fd2b335d3f6ef73") == 0) {
g_assert_cmpstr(fu_device_get_version(device), ==, "11");
}
/* verify prepare and cleanup ran on all devices */
metadata = fu_device_get_metadata(device, "frimbulator");
g_assert_cmpstr(metadata, ==, "1");
metadata = fu_device_get_metadata(device, "frombulator");
g_assert_cmpstr(metadata, ==, "1");
}
}
static void
fu_security_attrs_func(gconstpointer user_data)
{
FwupdSecurityAttr *attr_tmp;
g_autoptr(FuSecurityAttrs) attrs1 = fu_security_attrs_new();
g_autoptr(FuSecurityAttrs) attrs2 = fu_security_attrs_new();
g_autoptr(FwupdSecurityAttr) attr1 = fwupd_security_attr_new("org.fwupd.hsi.foo");
g_autoptr(FwupdSecurityAttr) attr2 = fwupd_security_attr_new("org.fwupd.hsi.bar");
g_autoptr(FwupdSecurityAttr) attr3 = fwupd_security_attr_new("org.fwupd.hsi.baz");
g_autoptr(FwupdSecurityAttr) attr4 = fwupd_security_attr_new("org.fwupd.hsi.baz");
g_autoptr(GPtrArray) results = NULL;
/* attrs1 has foo and baz(enabled) */
fwupd_security_attr_set_plugin(attr1, "foo");
fwupd_security_attr_set_created(attr1, 0);
fwupd_security_attr_set_result(attr1, FWUPD_SECURITY_ATTR_RESULT_ENCRYPTED);
fu_security_attrs_append(attrs1, attr1);
fwupd_security_attr_set_plugin(attr3, "baz");
fwupd_security_attr_set_created(attr3, 0);
fwupd_security_attr_set_result(attr3, FWUPD_SECURITY_ATTR_RESULT_ENABLED);
fu_security_attrs_append(attrs1, attr3);
/* attrs2 has bar and baz(~enabled) */
fwupd_security_attr_set_plugin(attr2, "bar");
fwupd_security_attr_set_created(attr2, 0);
fwupd_security_attr_set_result(attr2, FWUPD_SECURITY_ATTR_RESULT_LOCKED);
fu_security_attrs_append(attrs2, attr2);
fwupd_security_attr_set_plugin(attr4, "baz");
fwupd_security_attr_set_created(attr4, 0);
fwupd_security_attr_set_result(attr4, FWUPD_SECURITY_ATTR_RESULT_NOT_ENABLED);
fu_security_attrs_append(attrs2, attr4);
results = fu_security_attrs_compare(attrs1, attrs2);
g_assert_cmpint(results->len, ==, 3);
attr_tmp = g_ptr_array_index(results, 0);
g_assert_cmpstr(fwupd_security_attr_get_appstream_id(attr_tmp), ==, "org.fwupd.hsi.bar");
g_assert_cmpint(fwupd_security_attr_get_result_fallback(attr_tmp),
==,
FWUPD_SECURITY_ATTR_RESULT_UNKNOWN);
g_assert_cmpint(fwupd_security_attr_get_result(attr_tmp),
==,
FWUPD_SECURITY_ATTR_RESULT_LOCKED);
attr_tmp = g_ptr_array_index(results, 1);
g_assert_cmpstr(fwupd_security_attr_get_appstream_id(attr_tmp), ==, "org.fwupd.hsi.foo");
g_assert_cmpint(fwupd_security_attr_get_result_fallback(attr_tmp),
==,
FWUPD_SECURITY_ATTR_RESULT_ENCRYPTED);
g_assert_cmpint(fwupd_security_attr_get_result(attr_tmp),
==,
FWUPD_SECURITY_ATTR_RESULT_UNKNOWN);
attr_tmp = g_ptr_array_index(results, 2);
g_assert_cmpstr(fwupd_security_attr_get_appstream_id(attr_tmp), ==, "org.fwupd.hsi.baz");
g_assert_cmpint(fwupd_security_attr_get_result_fallback(attr_tmp),
==,
FWUPD_SECURITY_ATTR_RESULT_ENABLED);
g_assert_cmpint(fwupd_security_attr_get_result(attr_tmp),
==,
FWUPD_SECURITY_ATTR_RESULT_NOT_ENABLED);
g_assert_true(fu_security_attrs_equal(attrs1, attrs1));
g_assert_false(fu_security_attrs_equal(attrs1, attrs2));
g_assert_false(fu_security_attrs_equal(attrs2, attrs1));
}
static void
fu_security_attr_func(gconstpointer user_data)
{
gboolean ret;
g_autofree gchar *json1 = NULL;
g_autofree gchar *json2 = NULL;
g_autoptr(FuSecurityAttrs) attrs1 = fu_security_attrs_new();
g_autoptr(FuSecurityAttrs) attrs2 = fu_security_attrs_new();
g_autoptr(FwupdSecurityAttr) attr1 = fwupd_security_attr_new("org.fwupd.hsi.foo");
g_autoptr(FwupdSecurityAttr) attr2 = fwupd_security_attr_new("org.fwupd.hsi.bar");
g_autoptr(GError) error = NULL;
g_autoptr(JsonParser) parser = json_parser_new();
fwupd_security_attr_set_plugin(attr1, "foo");
fwupd_security_attr_set_created(attr1, 0);
fwupd_security_attr_set_plugin(attr2, "bar");
fwupd_security_attr_set_created(attr2, 0);
fu_security_attrs_append(attrs1, attr1);
fu_security_attrs_append(attrs1, attr2);
json1 = fu_security_attrs_to_json_string(attrs1, &error);
g_assert_no_error(error);
g_assert_nonnull(json1);
ret = fu_test_compare_lines(
json1,
"{\n"
" \"SecurityAttributes\" : [\n"
" {\n"
" \"AppstreamId\" : \"org.fwupd.hsi.foo\",\n"
" \"HsiLevel\" : 0,\n"
" \"Plugin\" : \"foo\",\n"
" \"Uri\" : "
"\"https://fwupd.github.io/libfwupdplugin/hsi.html#org.fwupd.hsi.foo\"\n"
" },\n"
" {\n"
" \"AppstreamId\" : \"org.fwupd.hsi.bar\",\n"
" \"HsiLevel\" : 0,\n"
" \"Plugin\" : \"bar\",\n"
" \"Uri\" : "
"\"https://fwupd.github.io/libfwupdplugin/hsi.html#org.fwupd.hsi.bar\"\n"
" }\n"
" ]\n"
"}",
&error);
g_assert_no_error(error);
g_assert_true(ret);
ret = json_parser_load_from_data(parser, json1, -1, &error);
g_assert_no_error(error);
g_assert_true(ret);
ret = fu_security_attrs_from_json(attrs2, json_parser_get_root(parser), &error);
if (g_error_matches(error, FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED)) {
g_test_skip(error->message);
return;
}
g_assert_no_error(error);
g_assert_true(ret);
json2 = fu_security_attrs_to_json_string(attrs2, &error);
g_assert_no_error(error);
g_assert_nonnull(json2);
ret = fu_test_compare_lines(json2, json1, &error);
g_assert_no_error(error);
g_assert_true(ret);
}
static void
fu_memcpy_func(gconstpointer user_data)
{
const guint8 src[] = {'a', 'b', 'c', 'd', 'e'};
gboolean ret;
guint8 dst[4];
g_autoptr(GError) error = NULL;
/* copy entire buffer */
ret = fu_memcpy_safe(dst, sizeof(dst), 0x0, src, sizeof(src), 0x0, 4, &error);
g_assert_no_error(error);
g_assert_true(ret);
g_assert_cmpint(memcmp(src, dst, 4), ==, 0);
/* copy first char */
ret = fu_memcpy_safe(dst, sizeof(dst), 0x0, src, sizeof(src), 0x0, 1, &error);
g_assert_no_error(error);
g_assert_true(ret);
g_assert_cmpint(dst[0], ==, 'a');
/* copy last char */
ret = fu_memcpy_safe(dst, sizeof(dst), 0x0, src, sizeof(src), 0x4, 1, &error);
g_assert_no_error(error);
g_assert_true(ret);
g_assert_cmpint(dst[0], ==, 'e');
/* copy nothing */
ret = fu_memcpy_safe(dst, sizeof(dst), 0x0, src, sizeof(src), 0x0, 0, &error);
g_assert_no_error(error);
g_assert_true(ret);
/* write past the end of dst */
ret = fu_memcpy_safe(dst, sizeof(dst), 0x0, src, sizeof(src), 0x0, 5, &error);
g_assert_error(error, FWUPD_ERROR, FWUPD_ERROR_WRITE);
g_assert_false(ret);
g_clear_error(&error);
/* write past the end of dst with offset */
ret = fu_memcpy_safe(dst, sizeof(dst), 0x1, src, sizeof(src), 0x0, 4, &error);
g_assert_error(error, FWUPD_ERROR, FWUPD_ERROR_WRITE);
g_assert_false(ret);
g_clear_error(&error);
/* read past past the end of dst */
ret = fu_memcpy_safe(dst, sizeof(dst), 0x0, src, sizeof(src), 0x0, 6, &error);
g_assert_error(error, FWUPD_ERROR, FWUPD_ERROR_READ);
g_assert_false(ret);
g_clear_error(&error);
/* read past the end of src with offset */
ret = fu_memcpy_safe(dst, sizeof(dst), 0x0, src, sizeof(src), 0x4, 4, &error);
g_assert_error(error, FWUPD_ERROR, FWUPD_ERROR_READ);
g_assert_false(ret);
g_clear_error(&error);
}
static void
fu_progressbar_func(gconstpointer user_data)
{
g_autoptr(FuProgressbar) progressbar = fu_progressbar_new();
fu_progressbar_set_length_status(progressbar, 20);
fu_progressbar_set_length_percentage(progressbar, 50);
g_print("\n");
for (guint i = 0; i < 100; i++) {
fu_progressbar_update(progressbar, FWUPD_STATUS_DECOMPRESSING, i);
g_usleep(10000);
}
fu_progressbar_update(progressbar, FWUPD_STATUS_IDLE, 0);
for (guint i = 0; i < 100; i++) {
guint pc = (i > 25 && i < 75) ? 0 : i;
fu_progressbar_update(progressbar, FWUPD_STATUS_LOADING, pc);
g_usleep(10000);
}
fu_progressbar_update(progressbar, FWUPD_STATUS_IDLE, 0);
for (guint i = 0; i < 5000; i++) {
fu_progressbar_update(progressbar, FWUPD_STATUS_LOADING, 0);
g_usleep(1000);
}
fu_progressbar_update(progressbar, FWUPD_STATUS_IDLE, 0);
}
static gint
fu_release_compare_func_cb(gconstpointer a, gconstpointer b)
{
FuRelease *release1 = *((FuRelease **)a);
FuRelease *release2 = *((FuRelease **)b);
return fu_release_compare(release1, release2);
}
static void
fu_release_compare_func(gconstpointer user_data)
{
FuDevice *device_tmp;
g_autoptr(GPtrArray) releases = g_ptr_array_new();
g_autoptr(FuDevice) device1 = fu_device_new();
g_autoptr(FuDevice) device2 = fu_device_new();
g_autoptr(FuDevice) device3 = fu_device_new();
g_autoptr(FuRelease) release1 = fu_release_new();
g_autoptr(FuRelease) release2 = fu_release_new();
g_autoptr(FuRelease) release3 = fu_release_new();
fu_device_set_order(device1, 99);
fu_release_set_device(release1, device1);
g_ptr_array_add(releases, release1);
fu_device_set_order(device2, 11);
fu_release_set_device(release2, device2);
g_ptr_array_add(releases, release2);
fu_device_set_order(device3, 33);
fu_release_set_device(release3, device3);
g_ptr_array_add(releases, release3);
/* order the install tasks */
g_ptr_array_sort(releases, fu_release_compare_func_cb);
g_assert_cmpint(releases->len, ==, 3);
device_tmp = fu_release_get_device(g_ptr_array_index(releases, 0));
g_assert_cmpint(fu_device_get_order(device_tmp), ==, 11);
device_tmp = fu_release_get_device(g_ptr_array_index(releases, 1));
g_assert_cmpint(fu_device_get_order(device_tmp), ==, 33);
device_tmp = fu_release_get_device(g_ptr_array_index(releases, 2));
g_assert_cmpint(fu_device_get_order(device_tmp), ==, 99);
}
int
main(int argc, char **argv)
{
gboolean ret;
g_autofree gchar *pluginfn = NULL;
g_autofree gchar *testdatadir = NULL;
g_autoptr(GError) error = NULL;
g_autoptr(FuTest) self = g_new0(FuTest, 1);
g_test_init(&argc, &argv, NULL);
/* only critical and error are fatal */
g_log_set_fatal_mask(NULL, G_LOG_LEVEL_ERROR | G_LOG_LEVEL_CRITICAL);
g_setenv("G_MESSAGES_DEBUG", "all", TRUE);
g_setenv("FWUPD_DEVICE_LIST_VERBOSE", "1", TRUE);
testdatadir = g_test_build_filename(G_TEST_DIST, "tests", NULL);
g_setenv("FWUPD_DATADIR", testdatadir, TRUE);
g_setenv("FWUPD_PLUGINDIR", testdatadir, TRUE);
g_setenv("FWUPD_SYSCONFDIR", testdatadir, TRUE);
g_setenv("FWUPD_SYSFSFWDIR", testdatadir, TRUE);
g_setenv("CONFIGURATION_DIRECTORY", testdatadir, TRUE);
g_setenv("FWUPD_OFFLINE_TRIGGER", "/tmp/fwupd-self-test/system-update", TRUE);
g_setenv("FWUPD_LOCALSTATEDIR", "/tmp/fwupd-self-test/var", TRUE);
/* ensure empty tree */
fu_self_test_mkroot();
/* do not save silo */
self->ctx = fu_context_new();
ret = fu_context_load_quirks(self->ctx, FU_QUIRKS_LOAD_FLAG_NO_CACHE, &error);
g_assert_no_error(error);
g_assert_true(ret);
/* load the test plugin */
self->plugin = fu_plugin_new(self->ctx);
pluginfn = g_test_build_filename(G_TEST_BUILT,
"..",
"plugins",
"test",
"libfu_plugin_test." G_MODULE_SUFFIX,
NULL);
ret = fu_plugin_open(self->plugin, pluginfn, &error);
g_assert_no_error(error);
g_assert_true(ret);
/* tests go here */
if (g_test_slow()) {
g_test_add_data_func("/fwupd/progressbar", self, fu_progressbar_func);
}
g_test_add_data_func("/fwupd/plugin{build-hash}", self, fu_plugin_hash_func);
g_test_add_data_func("/fwupd/plugin{module}", self, fu_plugin_module_func);
g_test_add_data_func("/fwupd/memcpy", self, fu_memcpy_func);
g_test_add_data_func("/fwupd/security-attr", self, fu_security_attr_func);
g_test_add_data_func("/fwupd/security-attrs", self, fu_security_attrs_func);
g_test_add_data_func("/fwupd/device-list", self, fu_device_list_func);
g_test_add_data_func("/fwupd/device-list{delay}", self, fu_device_list_delay_func);
g_test_add_data_func("/fwupd/device-list{no-auto-remove-children}",
self,
fu_device_list_no_auto_remove_children_func);
g_test_add_data_func("/fwupd/device-list{compatible}",
self,
fu_device_list_compatible_func);
g_test_add_data_func("/fwupd/device-list{remove-chain}",
self,
fu_device_list_remove_chain_func);
g_test_add_data_func("/fwupd/release{compare}", self, fu_release_compare_func);
g_test_add_data_func("/fwupd/engine{device-unlock}", self, fu_engine_device_unlock_func);
g_test_add_data_func("/fwupd/engine{multiple-releases}",
self,
fu_engine_multiple_rels_func);
g_test_add_data_func("/fwupd/engine{history-success}", self, fu_engine_history_func);
g_test_add_data_func("/fwupd/engine{history-error}", self, fu_engine_history_error_func);
if (g_test_slow()) {
g_test_add_data_func("/fwupd/device-list{replug-auto}",
self,
fu_device_list_replug_auto_func);
}
g_test_add_data_func("/fwupd/device-list{replug-user}",
self,
fu_device_list_replug_user_func);
g_test_add_data_func("/fwupd/engine{require-hwid}", self, fu_engine_require_hwid_func);
g_test_add_data_func("/fwupd/engine{requires-reboot}",
self,
fu_engine_install_needs_reboot);
g_test_add_data_func("/fwupd/engine{history-inherit}", self, fu_engine_history_inherit);
g_test_add_data_func("/fwupd/engine{partial-hash}", self, fu_engine_partial_hash_func);
g_test_add_data_func("/fwupd/engine{downgrade}", self, fu_engine_downgrade_func);
g_test_add_data_func("/fwupd/engine{requirements-success}",
self,
fu_engine_requirements_func);
g_test_add_data_func("/fwupd/engine{requirements-soft}",
self,
fu_engine_requirements_soft_func);
g_test_add_data_func("/fwupd/engine{requirements-missing}",
self,
fu_engine_requirements_missing_func);
g_test_add_data_func("/fwupd/engine{requirements-client-fail}",
self,
fu_engine_requirements_client_fail_func);
g_test_add_data_func("/fwupd/engine{requirements-client-invalid}",
self,
fu_engine_requirements_client_invalid_func);
g_test_add_data_func("/fwupd/engine{requirements-client-pass}",
self,
fu_engine_requirements_client_pass_func);
g_test_add_data_func("/fwupd/engine{requirements-version-require}",
self,
fu_engine_requirements_version_require_func);
g_test_add_data_func("/fwupd/engine{requirements-version-lowest}",
self,
fu_engine_requirements_version_lowest_func);
g_test_add_data_func("/fwupd/engine{requirements-parent-device}",
self,
fu_engine_requirements_parent_device_func);
g_test_add_data_func("/fwupd/engine{requirements_protocol_check_func}",
self,
fu_engine_requirements_protocol_check_func);
g_test_add_data_func("/fwupd/engine{requirements-not-child}",
self,
fu_engine_requirements_child_func);
g_test_add_data_func("/fwupd/engine{requirements-not-child-fail}",
self,
fu_engine_requirements_child_fail_func);
g_test_add_data_func("/fwupd/engine{requirements-unsupported}",
self,
fu_engine_requirements_unsupported_func);
g_test_add_data_func("/fwupd/engine{requirements-device}",
self,
fu_engine_requirements_device_func);
g_test_add_data_func("/fwupd/engine{requirements-device-plain}",
self,
fu_engine_requirements_device_plain_func);
g_test_add_data_func("/fwupd/engine{requirements-version-format}",
self,
fu_engine_requirements_version_format_func);
g_test_add_data_func("/fwupd/engine{requirements-only-upgrade}",
self,
fu_engine_requirements_only_upgrade_func);
g_test_add_data_func("/fwupd/engine{device-auto-parent-id}",
self,
fu_engine_device_parent_id_func);
g_test_add_data_func("/fwupd/engine{device-auto-parent-guid}",
self,
fu_engine_device_parent_guid_func);
g_test_add_data_func("/fwupd/engine{install-duration}",
self,
fu_engine_install_duration_func);
g_test_add_data_func("/fwupd/engine{generate-md}", self, fu_engine_generate_md_func);
g_test_add_data_func("/fwupd/engine{requirements-other-device}",
self,
fu_engine_requirements_other_device_func);
g_test_add_data_func("/fwupd/engine{fu_engine_requirements_sibling_device_func}",
self,
fu_engine_requirements_sibling_device_func);
g_test_add_data_func("/fwupd/plugin{composite}", self, fu_plugin_composite_func);
g_test_add_data_func("/fwupd/history", self, fu_history_func);
g_test_add_data_func("/fwupd/history{migrate}", self, fu_history_migrate_func);
g_test_add_data_func("/fwupd/plugin-list", self, fu_plugin_list_func);
g_test_add_data_func("/fwupd/plugin-list{depsolve}", self, fu_plugin_list_depsolve_func);
return g_test_run();
}