fwupd/plugins/redfish/fu-self-test.c

399 lines
12 KiB
C

/*
* Copyright (C) 2017 Richard Hughes <richard@hughsie.com>
*
* SPDX-License-Identifier: LGPL-2.1+
*/
#include "config.h"
#include <fwupdplugin.h>
#include "fu-context-private.h"
#include "fu-device-private.h"
#ifdef HAVE_LINUX_IPMI_H
#include "fu-ipmi-device.h"
#endif
#include "fu-plugin-private.h"
#include "fu-redfish-common.h"
#include "fu-redfish-network.h"
typedef struct {
FuPlugin *plugin;
} FuTest;
static gboolean
fu_test_is_installed_test(void)
{
const gchar *builddir = g_getenv("G_TEST_BUILDDIR");
if (builddir == NULL)
return FALSE;
return g_str_has_prefix(builddir, FWUPD_PREFIX);
}
static void
fu_test_self_init(FuTest *self)
{
gboolean ret;
g_autoptr(FuContext) ctx = fu_context_new();
g_autoptr(FuProgress) progress = fu_progress_new(G_STRLOC);
g_autoptr(GError) error = NULL;
g_autofree gchar *pluginfn = NULL;
ret = fu_context_load_quirks(ctx,
FU_QUIRKS_LOAD_FLAG_NO_CACHE | FU_QUIRKS_LOAD_FLAG_NO_VERIFY,
&error);
g_assert_no_error(error);
g_assert_true(ret);
self->plugin = fu_plugin_new(ctx);
/* running as an installed test */
if (fu_test_is_installed_test()) {
g_autofree gchar *plugindir = fu_path_from_kind(FU_PATH_KIND_PLUGINDIR_PKG);
pluginfn =
g_build_filename(plugindir, "libfu_plugin_redfish." G_MODULE_SUFFIX, NULL);
} else {
pluginfn = g_test_build_filename(G_TEST_BUILT,
"libfu_plugin_redfish." G_MODULE_SUFFIX,
NULL);
}
ret = fu_plugin_open(self->plugin, pluginfn, &error);
g_assert_no_error(error);
g_assert_true(ret);
ret = fu_plugin_runner_startup(self->plugin, progress, &error);
if (g_error_matches(error, FWUPD_ERROR, FWUPD_ERROR_INVALID_FILE)) {
g_test_skip("no redfish.py running");
return;
}
g_assert_no_error(error);
g_assert_true(ret);
ret = fu_plugin_runner_coldplug(self->plugin, progress, &error);
g_assert_no_error(error);
g_assert_true(ret);
}
static void
fu_test_redfish_ipmi_func(void)
{
#ifdef HAVE_LINUX_IPMI_H
gboolean ret;
g_autoptr(FuIpmiDevice) device = fu_ipmi_device_new(NULL);
g_autoptr(FuDeviceLocker) locker = NULL;
g_autoptr(GError) error = NULL;
g_autofree gchar *username = NULL;
g_autofree gchar *str = NULL;
/* sanity check */
if (!g_file_test("/dev/ipmi0", G_FILE_TEST_EXISTS)) {
g_test_skip("no IPMI hardware");
return;
}
/* create device */
locker = fu_device_locker_new(device, &error);
if (g_error_matches(error, G_IO_ERROR, G_IO_ERROR_PERMISSION_DENIED)) {
g_test_skip("permission denied for access to IPMI hardware");
return;
}
g_assert_no_error(error);
g_assert_nonnull(locker);
str = fu_device_to_string(FU_DEVICE(device));
g_debug("%s", str);
/* add user that can do redfish commands */
if (g_getenv("FWUPD_REDFISH_SELF_TEST") == NULL) {
g_test_skip("not doing destructive tests");
return;
}
ret = fu_ipmi_device_set_user_name(device, 0x04, "fwupd", &error);
g_assert_no_error(error);
g_assert_true(ret);
username = fu_ipmi_device_get_user_password(device, 0x04, &error);
g_assert_no_error(error);
g_assert_nonnull(username);
g_debug("username=%s", username);
ret = fu_ipmi_device_set_user_enable(device, 0x04, TRUE, &error);
g_assert_no_error(error);
g_assert_true(ret);
ret = fu_ipmi_device_set_user_priv(device, 0x04, 0x4, 1, &error);
g_assert_no_error(error);
g_assert_true(ret);
ret = fu_ipmi_device_set_user_password(device, 0x04, "Passw0rd123", &error);
g_assert_no_error(error);
g_assert_true(ret);
#else
g_test_skip("no linux/ipmi.h, so skipping");
#endif
}
static void
fu_test_redfish_common_func(void)
{
const guint8 buf[16] = {0x00,
0x01,
0x02,
0x03,
0x04,
0x05,
0x06,
0x07,
0x08,
0x09,
0x0a,
0x0b,
0x0c,
0x0d,
0x0e,
0x0f};
g_autofree gchar *ipv4 = NULL;
g_autofree gchar *ipv6 = NULL;
g_autofree gchar *maca = NULL;
ipv4 = fu_redfish_common_buffer_to_ipv4(buf);
g_assert_cmpstr(ipv4, ==, "0.1.2.3");
ipv6 = fu_redfish_common_buffer_to_ipv6(buf);
g_assert_cmpstr(ipv6, ==, "00010203:04050607:08090a0b:0c0d0e0f");
maca = fu_redfish_common_buffer_to_mac(buf);
g_assert_cmpstr(maca, ==, "00:01:02:03:04:05");
}
static void
fu_test_redfish_common_version_func(void)
{
struct {
const gchar *in;
const gchar *op;
} strs[] = {{"1.2.3", "1.2.3"},
{"P50 v1.2.3 PROD", "1.2.3"},
{"P50 1.2.3 DEV", "1.2.3"},
{NULL, NULL}};
for (guint i = 0; strs[i].in != NULL; i++) {
g_autofree gchar *tmp = fu_redfish_common_fix_version(strs[i].in);
g_assert_cmpstr(tmp, ==, strs[i].op);
}
}
static void
fu_test_redfish_common_lenovo_func(void)
{
struct {
const gchar *in;
gboolean ret;
const gchar *build;
const gchar *version;
} values[] = {{"11A-1.02", TRUE, "11A", "1.02"},
{"11A-0.00", TRUE, "11A", "0.00"},
{"99Z-9.99", TRUE, "99Z", "9.99"},
{"9-9-9.99", FALSE, NULL, NULL},
{"999-9.99", FALSE, NULL, NULL},
{"ACB-9.99", FALSE, NULL, NULL},
{NULL, FALSE, NULL, NULL}};
for (guint i = 0; values[i].in != NULL; i++) {
gboolean ret;
g_autofree gchar *build = NULL;
g_autofree gchar *version = NULL;
ret = fu_redfish_common_parse_version_lenovo(values[i].in, &build, &version, NULL);
g_assert_cmpint(ret, ==, values[i].ret);
g_assert_cmpstr(build, ==, values[i].build);
g_assert_cmpstr(version, ==, values[i].version);
}
}
static void
fu_test_redfish_network_mac_addr_func(void)
{
FuRedfishNetworkDeviceState state = FU_REDFISH_NETWORK_DEVICE_STATE_UNKNOWN;
gboolean ret;
g_autofree gchar *ip_addr = NULL;
g_autoptr(FuRedfishNetworkDevice) device = NULL;
g_autoptr(GError) error = NULL;
device = fu_redfish_network_device_for_mac_addr("00:13:F7:29:C2:D8", &error);
if (device == NULL && g_error_matches(error, FWUPD_ERROR, FWUPD_ERROR_NOT_FOUND)) {
g_test_skip("no hardware");
return;
}
if (device == NULL && g_error_matches(error, FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED)) {
g_autofree gchar *str = g_strdup_printf("not supported: %s", error->message);
g_test_skip(str);
return;
}
g_assert_no_error(error);
g_assert_nonnull(device);
ret = fu_redfish_network_device_get_state(device, &state, &error);
g_assert_no_error(error);
g_assert_true(ret);
if (state == FU_REDFISH_NETWORK_DEVICE_STATE_DISCONNECTED) {
ret = fu_redfish_network_device_connect(device, &error);
g_assert_no_error(error);
g_assert_true(ret);
}
ip_addr = fu_redfish_network_device_get_address(device, &error);
g_assert_no_error(error);
g_assert_nonnull(ip_addr);
}
static void
fu_test_redfish_network_vid_pid_func(void)
{
g_autofree gchar *ip_addr = NULL;
g_autoptr(FuRedfishNetworkDevice) device = NULL;
g_autoptr(GError) error = NULL;
device = fu_redfish_network_device_for_vid_pid(0x0707, 0x0201, &error);
if (device == NULL && g_error_matches(error, FWUPD_ERROR, FWUPD_ERROR_NOT_FOUND)) {
g_test_skip("no hardware");
return;
}
if (device == NULL && g_error_matches(error, FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED)) {
g_autofree gchar *str = g_strdup_printf("not supported: %s", error->message);
g_test_skip(str);
return;
}
g_assert_no_error(error);
g_assert_nonnull(device);
ip_addr = fu_redfish_network_device_get_address(device, &error);
g_assert_no_error(error);
g_assert_nonnull(ip_addr);
}
static void
fu_test_redfish_devices_func(gconstpointer user_data)
{
FuDevice *dev;
FuTest *self = (FuTest *)user_data;
GPtrArray *devices;
g_autofree gchar *devstr0 = NULL;
g_autofree gchar *devstr1 = NULL;
devices = fu_plugin_get_devices(self->plugin);
g_assert_nonnull(devices);
if (devices->len == 0) {
g_test_skip("no redfish support");
return;
}
g_assert_cmpint(devices->len, ==, 2);
/* BMC */
dev = g_ptr_array_index(devices, 1);
devstr0 = fu_device_to_string(dev);
g_debug("%s", devstr0);
g_assert_cmpstr(fu_device_get_id(dev), ==, "62c1cd95692c5225826cf8568a460427ea3b1827");
g_assert_cmpstr(fu_device_get_name(dev), ==, "BMC Firmware");
g_assert_cmpstr(fu_device_get_vendor(dev), ==, "Lenovo");
g_assert_cmpstr(fu_device_get_version(dev), ==, "1.02");
g_assert_cmpstr(fu_device_get_version_lowest(dev), ==, "0.12");
g_assert_cmpint(fu_device_get_version_format(dev), ==, FWUPD_VERSION_FORMAT_PAIR);
g_assert_cmpint(fu_device_get_version_build_date(dev), ==, 1552608000);
g_assert_true(fu_device_has_flag(dev, FWUPD_DEVICE_FLAG_UPDATABLE));
g_assert_true(fu_device_has_protocol(dev, "org.dmtf.redfish"));
g_assert_true(
fu_device_has_guid(dev, "REDFISH\\VENDOR_Lenovo&SOFTWAREID_UEFI-AFE1-6&TYPE_UNSIGNED"));
g_assert_true(fu_device_has_vendor_id(dev, "REDFISH:LENOVO"));
/* BIOS */
dev = g_ptr_array_index(devices, 0);
devstr1 = fu_device_to_string(dev);
g_debug("%s", devstr1);
g_assert_cmpstr(fu_device_get_id(dev), ==, "562313e34c756a05a2e878861377765582bbf971");
g_assert_cmpstr(fu_device_get_name(dev), ==, "BIOS Firmware");
g_assert_cmpstr(fu_device_get_vendor(dev), ==, "Contoso");
g_assert_cmpstr(fu_device_get_version(dev), ==, "1.45");
g_assert_cmpstr(fu_device_get_serial(dev), ==, "12345");
g_assert_cmpstr(fu_device_get_version_lowest(dev), ==, "1.10");
g_assert_cmpint(fu_device_get_version_format(dev), ==, FWUPD_VERSION_FORMAT_PAIR);
g_assert_cmpint(fu_device_get_version_build_date(dev), ==, 1552608000);
g_assert_true(fu_device_has_flag(dev, FWUPD_DEVICE_FLAG_UPDATABLE));
g_assert_true(fu_device_has_icon(dev, "network-wired"));
g_assert_true(fu_device_has_protocol(dev, "org.dmtf.redfish"));
g_assert_true(fu_device_has_guid(dev, "fee82a67-6ce2-4625-9f44-237ad2402c28"));
g_assert_true(fu_device_has_guid(dev, "a6d3294e-37e5-50aa-ae2f-c0c457af16f3"));
g_assert_true(fu_device_has_vendor_id(dev, "REDFISH:CONTOSO"));
}
static void
fu_test_redfish_update_func(gconstpointer user_data)
{
FuDevice *dev;
FuTest *self = (FuTest *)user_data;
GPtrArray *devices;
gboolean ret;
g_autoptr(GError) error = NULL;
g_autoptr(GBytes) blob_fw = NULL;
g_autoptr(FuProgress) progress = fu_progress_new(G_STRLOC);
devices = fu_plugin_get_devices(self->plugin);
g_assert_nonnull(devices);
if (devices->len == 0) {
g_test_skip("no redfish support");
return;
}
g_assert_cmpint(devices->len, ==, 2);
/* BMC */
dev = g_ptr_array_index(devices, 1);
blob_fw = g_bytes_new_static("hello", 5);
ret = fu_plugin_runner_write_firmware(self->plugin,
dev,
blob_fw,
progress,
FWUPD_INSTALL_FLAG_NONE,
&error);
g_assert_no_error(error);
g_assert_true(ret);
g_assert_true(fu_device_has_flag(dev, FWUPD_DEVICE_FLAG_NEEDS_REBOOT));
/* try again */
ret = fu_plugin_runner_write_firmware(self->plugin,
dev,
blob_fw,
progress,
FWUPD_INSTALL_FLAG_NONE,
&error);
g_assert_error(error, FWUPD_ERROR, FWUPD_ERROR_WRITE);
g_assert_false(ret);
}
static void
fu_test_self_free(FuTest *self)
{
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_self_free)
#pragma clang diagnostic pop
int
main(int argc, char **argv)
{
g_autoptr(FuTest) self = g_new0(FuTest, 1);
g_autofree gchar *smbios_data_fn = NULL;
g_autofree gchar *testdatadir = NULL;
g_test_init(&argc, &argv, NULL);
(void)g_setenv("FWUPD_REDFISH_VERBOSE", "1", TRUE);
testdatadir = g_test_build_filename(G_TEST_DIST, "tests", NULL);
smbios_data_fn = g_build_filename(testdatadir, "redfish-smbios.bin", NULL);
(void)g_setenv("FWUPD_REDFISH_SMBIOS_DATA", smbios_data_fn, TRUE);
(void)g_setenv("FWUPD_SYSFSFWDIR", testdatadir, TRUE);
(void)g_setenv("CONFIGURATION_DIRECTORY", testdatadir, TRUE);
g_log_set_fatal_mask(NULL, G_LOG_LEVEL_ERROR | G_LOG_LEVEL_CRITICAL);
fu_test_self_init(self);
g_test_add_func("/redfish/ipmi", fu_test_redfish_ipmi_func);
g_test_add_func("/redfish/common", fu_test_redfish_common_func);
g_test_add_func("/redfish/common{version}", fu_test_redfish_common_version_func);
g_test_add_func("/redfish/common{lenovo}", fu_test_redfish_common_lenovo_func);
g_test_add_func("/redfish/network{mac_addr}", fu_test_redfish_network_mac_addr_func);
g_test_add_func("/redfish/network{vid_pid}", fu_test_redfish_network_vid_pid_func);
g_test_add_data_func("/redfish/plugin{devices}", self, fu_test_redfish_devices_func);
g_test_add_data_func("/redfish/plugin{update}", self, fu_test_redfish_update_func);
return g_test_run();
}