[PATCH 1/3] tests/qtest/fdc-test: give each test case its own QEMU instance

Christian Quante <[email protected]>
Newsgroups org.nongnu.qemu-devel
Message-ID <[email protected]>
The test cases share one QEMU instance and, with it, the state of the
floppy controller.  Several of them therefore only pass in the order they
happen to be registered in: test_read_no_dma_1, _18 and _19 read the medium
that test_media_insert leaves in the drive, so running any of them on its
own fails.  fuzz-registers is worse -- it writes 1000 random values to the
eight I/O ports at FLOPPY_BASE and restores none of them, which is harmless
only because it is the last test case to run in the shared instance; the
two registered after it start one of their own.  Moving it anywhere else
leaves an unacknowledged interrupt behind and the next test dies in
send_seek().

Register the test cases from a table instead, through a wrapper that starts
a QEMU instance, runs one test and stops it again.  The test functions are
unchanged: they reach the current instance through global_qtest, which
qtest_start() reassigns for every test.  The three read_no_dma tests now
insert their own medium, and test_read_id no longer ejects at the end -- it
did that only to leave the drive as the next test expected to find it.

The two CVE test cases keep qtest_add_func(); they already run a QEMU
instance of their own and do not use global_qtest.

The same pattern is used by tests/qtest/boot-serial-test.c.

Every test case now passes on its own, and the suite passes with the
registration order reversed.

Signed-off-by: Christian Quante <[email protected]>
---
 tests/qtest/fdc-test.c | 71 ++++++++++++++++++++++++++++++------------
 1 file changed, 51 insertions(+), 20 deletions(-)

diff --git a/tests/qtest/fdc-test.c b/tests/qtest/fdc-test.c
index 1e1dd8659d..fd38475bf7 100644
--- a/tests/qtest/fdc-test.c
+++ b/tests/qtest/fdc-test.c
@@ -510,9 +510,6 @@ static void test_read_id(void)
     g_assert_cmpint(cyl, ==, 8);
     g_assert_cmpint(head, ==, 1);
     g_assert_cmpint(st0, ==, head << 2);
-
-    /* Leave the drive empty, the way the machine starts up. */
-    media_eject();
 }
 
 /*
@@ -552,6 +549,7 @@ static void test_read_no_dma_1(void)
 {
     uint8_t ret;
 
+    media_insert();
     outb(FLOPPY_BASE + reg_dor, inb(FLOPPY_BASE + reg_dor) & ~0x08);
     send_seek(0);
     ret = send_read_no_dma_command(1, 0x04);
@@ -562,6 +560,7 @@ static void test_read_no_dma_18(void)
 {
     uint8_t ret;
 
+    media_insert();
     outb(FLOPPY_BASE + reg_dor, inb(FLOPPY_BASE + reg_dor) & ~0x08);
     send_seek(0);
     ret = send_read_no_dma_command(18, 0x04);
@@ -572,6 +571,7 @@ static void test_read_no_dma_19(void)
 {
     uint8_t ret;
 
+    media_insert();
     outb(FLOPPY_BASE + reg_dor, inb(FLOPPY_BASE + reg_dor) & ~0x08);
     send_seek(0);
     ret = send_read_no_dma_command(19, 0x20);
@@ -656,10 +656,53 @@ static void test_cve_2021_3507(void)
     qtest_quit(s);
 }
 
+/*
+ * Each test gets its own QEMU instance, so that none of them depends on the
+ * state another one left behind.  Without this, several tests only pass in
+ * the order they happen to be registered in: the read_no_dma tests need the
+ * medium that test_media_insert leaves in the drive, and fuzz-registers
+ * writes random values to the eight I/O ports at FLOPPY_BASE and restores
+ * none of them.
+ */
+typedef struct {
+    const char *path;
+    void (*fn)(void);
+} FDCTest;
+
+static const FDCTest fdc_tests[] = {
+    { "/fdc/cmos",              test_cmos },
+    { "/fdc/no_media_on_start", test_no_media_on_start },
+    { "/fdc/read_without_media", test_read_without_media },
+    { "/fdc/media_change",      test_media_change },
+    { "/fdc/sense_interrupt",   test_sense_interrupt },
+    { "/fdc/relative_seek",     test_relative_seek },
+    { "/fdc/read_id",           test_read_id },
+    { "/fdc/read_id_no_media",  test_read_id_no_media },
+    { "/fdc/verify",            test_verify },
+    { "/fdc/media_insert",      test_media_insert },
+    { "/fdc/read_no_dma_1",     test_read_no_dma_1 },
+    { "/fdc/read_no_dma_18",    test_read_no_dma_18 },
+    { "/fdc/read_no_dma_19",    test_read_no_dma_19 },
+    { "/fdc/fuzz-registers",    fuzz_registers },
+};
+
+static void run_isolated(const void *data)
+{
+    const FDCTest *test = data;
+
+    qtest_start("-machine pc -device floppy,id=floppy0");
+    qtest_irq_intercept_in(global_qtest, "ioapic");
+
+    test->fn();
+
+    qtest_end();
+}
+
 int main(int argc, char **argv)
 {
     int fd;
     int ret;
+    size_t i;
 
     /* Create a temporary raw image */
     fd = g_file_open_tmp("qtest.XXXXXX", &test_image, NULL);
@@ -671,29 +714,17 @@ int main(int argc, char **argv)
     /* Run the tests */
     g_test_init(&argc, &argv, NULL);
 
-    qtest_start("-machine pc -device floppy,id=floppy0");
-    qtest_irq_intercept_in(global_qtest, "ioapic");
-    qtest_add_func("/fdc/cmos", test_cmos);
-    qtest_add_func("/fdc/no_media_on_start", test_no_media_on_start);
-    qtest_add_func("/fdc/read_without_media", test_read_without_media);
-    qtest_add_func("/fdc/media_change", test_media_change);
-    qtest_add_func("/fdc/sense_interrupt", test_sense_interrupt);
-    qtest_add_func("/fdc/relative_seek", test_relative_seek);
-    qtest_add_func("/fdc/read_id", test_read_id);
-    qtest_add_func("/fdc/read_id_no_media", test_read_id_no_media);
-    qtest_add_func("/fdc/verify", test_verify);
-    qtest_add_func("/fdc/media_insert", test_media_insert);
-    qtest_add_func("/fdc/read_no_dma_1", test_read_no_dma_1);
-    qtest_add_func("/fdc/read_no_dma_18", test_read_no_dma_18);
-    qtest_add_func("/fdc/read_no_dma_19", test_read_no_dma_19);
-    qtest_add_func("/fdc/fuzz-registers", fuzz_registers);
+    for (i = 0; i < ARRAY_SIZE(fdc_tests); i++) {
+        qtest_add_data_func(fdc_tests[i].path, &fdc_tests[i], run_isolated);
+    }
+
+    /* These use a QTestState of their own instead of global_qtest */
     qtest_add_func("/fdc/fuzz/cve_2021_20196", test_cve_2021_20196);
     qtest_add_func("/fdc/fuzz/cve_2021_3507", test_cve_2021_3507);
 
     ret = g_test_run();
 
     /* Cleanup */
-    qtest_end();
     unlink(test_image);
     g_free(test_image);
 
-- 
2.53.0
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.