[Concept,05/30] test: Add a test for FIT image printing

Message ID 20251120025614.2215587-6-sjg@u-boot.org
State New
Headers
Series fit: Improve and test the code to print FIT info |

Commit Message

Simon Glass Nov. 20, 2025, 2:55 a.m. UTC
  From: Simon Glass <simon.glass@canonical.com>

The code for printing FITs is quite messy, with lots of separate
printf() calls, an indentation string, etc.

It also has no tests.

In preparation for refactoring this code, add a test. Use Python code
to create the test image and C code to test it.

The test covers FIT description, image details (type, architecture, OS,
addresses), and configuration details.

Co-developed-by: Claude <noreply@anthropic.com>
Signed-off-by: Simon Glass <simon.glass@canonical.com>
---

 test/boot/Makefile              |   1 +
 test/boot/fit_print.c           |  93 +++++++++++++++++++++++++
 test/py/tests/test_fit_print.py | 120 ++++++++++++++++++++++++++++++++
 3 files changed, 214 insertions(+)
 create mode 100644 test/boot/fit_print.c
 create mode 100644 test/py/tests/test_fit_print.py
  

Patch

diff --git a/test/boot/Makefile b/test/boot/Makefile
index 71c482f8d24..70e15bf63fa 100644
--- a/test/boot/Makefile
+++ b/test/boot/Makefile
@@ -5,6 +5,7 @@ 
 ifdef CONFIG_UT_BOOTSTD
 obj-$(CONFIG_BOOTSTD) += bootdev.o bootstd_common.o bootflow.o bootmeth.o
 obj-$(CONFIG_FIT) += image.o
+obj-$(CONFIG_$(PHASE_)FIT_PRINT) += fit_print.o
 obj-$(CONFIG_BLK_LUKS) += luks.o
 
 obj-$(CONFIG_EXPO) += expo.o expo_common.o
diff --git a/test/boot/fit_print.c b/test/boot/fit_print.c
new file mode 100644
index 00000000000..ef1e86800ee
--- /dev/null
+++ b/test/boot/fit_print.c
@@ -0,0 +1,93 @@ 
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Test for FIT image printing
+ *
+ * Copyright 2025 Canonical Ltd
+ * Written by Simon Glass <simon.glass@canonical.com>
+ */
+
+#include <image.h>
+#include <mapmem.h>
+#include <os.h>
+#include <test/ut.h>
+#include <linux/libfdt.h>
+#include "bootstd_common.h"
+
+/* Test fit_print_contents() output */
+static int test_fit_print_norun(struct unit_test_state *uts)
+{
+	char fname[256];
+	void *fit;
+	void *buf;
+	ulong addr;
+	int size;
+
+	/* Load the FIT created by the Python test */
+	ut_assertok(os_persistent_file(fname, sizeof(fname), "test-fit.fit"));
+	ut_assertok(os_read_file(fname, &buf, &size));
+
+	/* Copy to address 0x10000 and print from there */
+	addr = 0x10000;
+	fit = map_sysmem(addr, size);
+	memcpy(fit, buf, size);
+
+	/* Print it and check output line by line */
+	console_record_reset_enable();
+	fit_print_contents(fit);
+
+	/* Check every line of output */
+	ut_assert_nextline("   FIT description: Test FIT image for printing");
+	ut_assert_nextline("   Created:         2009-02-13  23:31:30 UTC");
+	ut_assert_nextline("    Image 0 (kernel)");
+	ut_assert_nextline("     Description:  Test kernel");
+	ut_assert_nextline("     Created:      2009-02-13  23:31:30 UTC");
+	ut_assert_nextline("     Type:         Kernel Image");
+	ut_assert_nextline("     Compression:  gzip compressed");
+	ut_assert_nextline("     Data Start:   0x000100c4");
+	ut_assert_nextline("     Data Size:    327 Bytes = 327 Bytes");
+	ut_assert_nextline("     Architecture: Sandbox");
+	ut_assert_nextline("     OS:           Linux");
+	ut_assert_nextline("     Load Address: 0x01000000");
+	ut_assert_nextline("     Entry Point:  0x01000000");
+	ut_assert_nextline("     Hash algo:    sha256");
+	ut_assert_nextline("     Hash value:   fad998b94ef12fdac0c347915d8b9b6069a4011399e1a2097638a2cb33244cee");
+	ut_assert_nextline("    Image 1 (ramdisk)");
+	ut_assert_nextline("     Description:  Test ramdisk");
+	ut_assert_nextline("     Created:      2009-02-13  23:31:30 UTC");
+	ut_assert_nextline("     Type:         RAMDisk Image");
+	ut_assert_nextline("     Compression:  uncompressed");
+	ut_assert_nextline("     Data Start:   0x00010304");
+	ut_assert_nextline("     Data Size:    301 Bytes = 301 Bytes");
+	ut_assert_nextline("     Architecture: Sandbox");
+	ut_assert_nextline("     OS:           Linux");
+	ut_assert_nextline("     Load Address: 0x02000000");
+	ut_assert_nextline("     Entry Point:  unavailable");
+	ut_assert_nextline("     Hash algo:    sha256");
+	ut_assert_nextline("     Hash value:   53e2a65d92ad890dcd89d83a1f95ad6b8206e0e4889548b035062fc494e7f655");
+	ut_assert_nextline("    Image 2 (fdt)");
+	ut_assert_nextline("     Description:  Test FDT");
+	ut_assert_nextline("     Created:      2009-02-13  23:31:30 UTC");
+	ut_assert_nextline("     Type:         Flat Device Tree");
+	ut_assert_nextline("     Compression:  uncompressed");
+	ut_assert_nextline("     Data Start:   0x00010514");
+	ut_assert_nextline("     Data Size:    157 Bytes = 157 Bytes");
+	ut_assert_nextline("     Architecture: Sandbox");
+	ut_assert_nextline("     Hash algo:    sha256");
+	ut_assert_nextline("     Hash value:   51918524b06745cae06331047c7e566909431bf71338e5f703dffba1823274f4");
+	ut_assert_nextline("    Default Configuration: 'conf-1'");
+	ut_assert_nextline("    Configuration 0 (conf-1)");
+	ut_assert_nextline("     Description:  Test configuration");
+	ut_assert_nextline("     Kernel:       kernel");
+	ut_assert_nextline("     Init Ramdisk: ramdisk");
+	ut_assert_nextline("     FDT:          fdt");
+	ut_assert_nextline("    Configuration 1 (conf-2)");
+	ut_assert_nextline("     Description:  Alternate configuration");
+	ut_assert_nextline("     Kernel:       kernel");
+	ut_assert_nextline("     FDT:          fdt");
+	ut_assert_console_end();
+
+	os_free(buf);
+
+	return 0;
+}
+BOOTSTD_TEST(test_fit_print_norun, UTF_CONSOLE | UTF_MANUAL);
diff --git a/test/py/tests/test_fit_print.py b/test/py/tests/test_fit_print.py
new file mode 100644
index 00000000000..4960ce503b4
--- /dev/null
+++ b/test/py/tests/test_fit_print.py
@@ -0,0 +1,120 @@ 
+# SPDX-License-Identifier: GPL-2.0+
+# Copyright 2025 Canonical Ltd
+# Written by Simon Glass <simon.glass@canonical.com>
+
+"""Test for FIT image printing"""
+
+import os
+
+import pytest
+
+import fit_util
+import utils
+
+# ITS for testing FIT printing with hashes, ramdisk, and multiple configs
+PRINT_ITS = '''
+/dts-v1/;
+
+/ {
+	description = "Test FIT image for printing";
+	#address-cells = <1>;
+
+	images {
+		kernel {
+			description = "Test kernel";
+			data = /incbin/("%(kernel)s");
+			type = "kernel";
+			arch = "sandbox";
+			os = "linux";
+			compression = "gzip";
+			load = <0x1000000>;
+			entry = <0x1000000>;
+			hash-1 {
+				algo = "sha256";
+			};
+		};
+		ramdisk {
+			description = "Test ramdisk";
+			data = /incbin/("%(ramdisk)s");
+			type = "ramdisk";
+			arch = "sandbox";
+			os = "linux";
+			compression = "none";
+			load = <0x2000000>;
+			hash-1 {
+				algo = "sha256";
+			};
+		};
+		fdt {
+			description = "Test FDT";
+			data = /incbin/("%(fdt)s");
+			type = "flat_dt";
+			arch = "sandbox";
+			compression = "none";
+			hash-1 {
+				algo = "sha256";
+			};
+		};
+	};
+	configurations {
+		default = "conf-1";
+		conf-1 {
+			description = "Test configuration";
+			kernel = "kernel";
+			fdt = "fdt";
+			ramdisk = "ramdisk";
+		};
+		conf-2 {
+			description = "Alternate configuration";
+			kernel = "kernel";
+			fdt = "fdt";
+		};
+	};
+};
+'''
+
+@pytest.mark.boardspec('sandbox')
+@pytest.mark.buildconfigspec('fit_print')
+@pytest.mark.requiredtool('dtc')
+def test_fit_print(ubman):
+    """Test fit_print_contents() via C unit test"""
+    mkimage = os.path.join(ubman.config.build_dir, 'tools/mkimage')
+
+    # Create test files (make kernel ~6.3K)
+    kernel = fit_util.make_kernel(ubman, 'test-kernel.bin',
+                                  'kernel with some extra test data')
+
+    # Compress the kernel (with -n to avoid timestamps for reproducibility)
+    kernel_gz = kernel + '.gz'
+    utils.run_and_log(ubman, ['gzip', '-f', '-n', '-k', kernel])
+
+    fdt = fit_util.make_dtb(ubman, '''
+/dts-v1/;
+/ {
+	#address-cells = <1>;
+	#size-cells = <0>;
+	model = "Test";
+};
+''', 'test-fdt')
+    ramdisk = fit_util.make_kernel(ubman, 'test-ramdisk.bin', 'ramdisk')
+
+    # Compress the ramdisk (with -n to avoid timestamps for reproducibility)
+    ramdisk_gz = ramdisk + '.gz'
+    utils.run_and_log(ubman, ['gzip', '-f', '-n', '-k', ramdisk])
+
+    # Create FIT image with fixed timestamp for reproducible output
+    params = {
+        'kernel': kernel_gz,
+        'fdt': fdt,
+        'ramdisk': ramdisk_gz,
+    }
+    env = os.environ.copy()
+    env['SOURCE_DATE_EPOCH'] = '1234567890'  # 2009-02-13 23:31:30 UTC
+    fit = os.path.join(ubman.config.persistent_data_dir, 'test-fit.fit')
+    its = fit_util.make_its(ubman, PRINT_ITS, params)
+    utils.run_and_log(ubman, [mkimage, '-f', its, fit], env=env)
+
+    # Run the C test which will load and verify this FIT
+    ubman.run_command('ut -f bootstd test_fit_print_norun')
+    result = ubman.run_command('echo $?')
+    assert '0' == result