manifest_test: Build DTs programatically

Manifest unit tests used to come with binary blobs of DTs to test.
This results in having the refresh all the blobs whenever a new required
property is added to the manifest. This patch adds a DTBuilder class
for programatically constructing a DT and compiling it with `dtc`.

Bug: 117551352
Change-Id: Ic6962ad494c200d612988cf25648dd23b9d76c93
diff --git a/build/image/dtc.py b/build/image/dtc.py
old mode 100644
new mode 100755
index 49eeeb7..28a75c3
--- a/build/image/dtc.py
+++ b/build/image/dtc.py
@@ -25,17 +25,22 @@
 
 def main():
     parser = argparse.ArgumentParser()
-    parser.add_argument("input_file")
-    parser.add_argument("output_file")
+    parser.add_argument("-i", "--input-file")
+    parser.add_argument("-o", "--output-file")
     args = parser.parse_args()
 
-    return subprocess.call([
-        DTC,
-        "-I", "dts", "-O", "dtb",
-        "-o", args.output_file,
-        "--out-version", "17",
-        args.input_file
-        ])
+    dtc_args = [
+            DTC,
+            "-I", "dts", "-O", "dtb",
+            "--out-version", "17",
+        ]
+
+    if args.output_file:
+        dtc_args += [ "-o", args.output_file ]
+    if args.input_file:
+        dtc_args += [ args.input_file ]
+
+    return subprocess.call(dtc_args)
 
 if __name__ == "__main__":
     sys.exit(main())
diff --git a/build/image/image.gni b/build/image/image.gni
index bed73df..6c8ea17 100644
--- a/build/image/image.gni
+++ b/build/image/image.gni
@@ -164,7 +164,9 @@
       dtb_file,
     ]
     args = [
+      "-i",
       "{{source}}",
+      "-o",
       rebase_path(dtb_file),
     ]
   }
diff --git a/src/manifest_test.cc b/src/manifest_test.cc
index 3a86a7a..0a23745 100644
--- a/src/manifest_test.cc
+++ b/src/manifest_test.cc
@@ -14,6 +14,10 @@
  * limitations under the License.
  */
 
+#include <array>
+#include <cstdio>
+#include <sstream>
+
 #include <gmock/gmock.h>
 
 extern "C" {
@@ -25,261 +29,276 @@
 using ::testing::Eq;
 using ::testing::NotNull;
 
-/*
- * DTB files compiled with:
- *   $ dtc -I dts -O dtb --out-version 17 test.dts | xxd -i
- */
+template <typename T>
+void exec(const char *program, char *const args[], const T &stdin,
+	  std::vector<char> *stdout)
+{
+	/* Create two pipes, one for stdin and one for stdout. */
+	int pipes[2][2];
+	pipe(pipes[0]);
+	pipe(pipes[1]);
 
-/*
- * /dts-v1/;
- *
- * / {
- * };
- *
- */
-constexpr uint8_t dtb_empty_root[] = {
-	0xd0, 0x0d, 0xfe, 0xed, 0x00, 0x00, 0x00, 0x48, 0x00, 0x00, 0x00, 0x38,
-	0x00, 0x00, 0x00, 0x48, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x11,
-	0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-	0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
-	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x09};
+	/* Assign FDs for reading/writing by the parent/child. */
+	int parent_read_fd = pipes[1][0];  /* stdout pipe, read FD */
+	int parent_write_fd = pipes[0][1]; /* stdin pipe, write FD */
+	int child_read_fd = pipes[0][0];   /* stdin pipe, read FD */
+	int child_write_fd = pipes[1][1];  /* stdout pipe, write FD */
 
-TEST(manifest, empty_root)
+	if (fork()) {
+		/* Parent process. */
+		std::array<char, 128> buf;
+		ssize_t res;
+
+		/* Close child FDs which won't be used. */
+		close(child_read_fd);
+		close(child_write_fd);
+
+		/* Write to stdin. */
+		for (size_t count = 0; count < stdin.size();) {
+			res = write(parent_write_fd, stdin.data() + count,
+				    stdin.size() - count);
+			if (res < 0) {
+				std::cerr << "IO error" << std::endl;
+				exit(1);
+			}
+			count += res;
+		}
+		close(parent_write_fd);
+
+		/* Read from stdout. */
+		while (true) {
+			res = read(parent_read_fd, buf.data(), buf.size());
+			if (res == 0) {
+				/* EOF */
+				break;
+			} else if (res < 0) {
+				std::cerr << "IO error" << std::endl;
+				exit(1);
+			}
+			stdout->insert(stdout->end(), buf.begin(),
+				       buf.begin() + res);
+		}
+		close(parent_read_fd);
+	} else {
+		/* Child process. */
+
+		/* Redirect stdin/stdout to read/write FDs. */
+		dup2(child_read_fd, STDIN_FILENO);
+		dup2(child_write_fd, STDOUT_FILENO);
+
+		/* Close all FDs which are now unused. */
+		close(child_read_fd);
+		close(child_write_fd);
+		close(parent_read_fd);
+		close(parent_write_fd);
+
+		/* Execute the given program. */
+		execv(program, args);
+	}
+}
+
+/**
+ * Class for programatically building a Device Tree.
+ *
+ * Usage:
+ *   std::vector<char> dtb = ManifestDtBuilder()
+ *       .Command1()
+ *       .Command2()
+ *       ...
+ *       .CommandN()
+ *       .Build();
+ */
+class ManifestDtBuilder
+{
+       public:
+	ManifestDtBuilder()
+	{
+		dts_ << "/dts-v1/;" << std::endl;
+		dts_ << std::endl;
+
+		/* Start root node. */
+		StartChild("/");
+	}
+
+	std::vector<char> Build()
+	{
+		char *const dtc_args[] = {NULL};
+		std::vector<char> dtc_stdout;
+
+		/* Finish root node. */
+		EndChild();
+
+		exec("./build/image/dtc.py", dtc_args, dts_.str(), &dtc_stdout);
+		return dtc_stdout;
+	}
+
+	ManifestDtBuilder &StartChild(const std::string_view &name)
+	{
+		dts_ << name << " {" << std::endl;
+		return *this;
+	}
+
+	ManifestDtBuilder &EndChild()
+	{
+		dts_ << "};" << std::endl;
+		return *this;
+	}
+
+	ManifestDtBuilder &DebugName(const std::string_view &value)
+	{
+		return StringProperty("debug_name", value);
+	}
+
+	ManifestDtBuilder &KernelFilename(const std::string_view &value)
+	{
+		return StringProperty("kernel_filename", value);
+	}
+
+	ManifestDtBuilder &VcpuCount(uint64_t value)
+	{
+		return IntegerProperty("vcpu_count", value);
+	}
+
+	ManifestDtBuilder &MemSize(uint64_t value)
+	{
+		return IntegerProperty("mem_size", value);
+	}
+
+       private:
+	ManifestDtBuilder &StringProperty(const std::string_view &name,
+					  const std::string_view &value)
+	{
+		dts_ << name << " = \"" << value << "\";" << std::endl;
+		return *this;
+	}
+
+	ManifestDtBuilder &IntegerProperty(const std::string_view &name,
+					   uint64_t value)
+	{
+		dts_ << name << " = <" << value << ">;" << std::endl;
+		return *this;
+	}
+
+	std::stringstream dts_;
+};
+
+TEST(manifest, no_hypervisor_node)
 {
 	struct manifest m;
 	struct memiter it;
+	std::vector<char> dtb = ManifestDtBuilder().Build();
 
-	memiter_init(&it, dtb_empty_root, sizeof(dtb_empty_root));
+	memiter_init(&it, dtb.data(), dtb.size());
 	ASSERT_EQ(manifest_init(&m, &it),
 		  MANIFEST_ERROR_NO_HYPERVISOR_FDT_NODE);
 }
 
-/*
- * /dts-v1/;
- *
- * / {
- * 	hypervisor {
- * 	};
- * };
- *
- */
-constexpr uint8_t dtb_no_vms[] = {
-	0xd0, 0x0d, 0xfe, 0xed, 0x00, 0x00, 0x00, 0x5c, 0x00, 0x00, 0x00, 0x38,
-	0x00, 0x00, 0x00, 0x5c, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x11,
-	0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-	0x00, 0x00, 0x00, 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
-	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x68, 0x79, 0x70, 0x65,
-	0x72, 0x76, 0x69, 0x73, 0x6f, 0x72, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02,
-	0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x09};
-
 TEST(manifest, no_vms)
 {
 	struct manifest m;
 	struct memiter it;
 
-	memiter_init(&it, dtb_no_vms, sizeof(dtb_no_vms));
+	/* clang-format off */
+	std::vector<char> dtb = ManifestDtBuilder()
+		.StartChild("hypervisor")
+		.EndChild()
+		.Build();
+	/* clang-format on */
+
+	memiter_init(&it, dtb.data(), dtb.size());
 	ASSERT_EQ(manifest_init(&m, &it), MANIFEST_ERROR_NO_PRIMARY_VM);
 }
 
-/*
- * /dts-v1/;
- *
- * / {
- * 	hypervisor {
- * 		vm1 {
- * 			debug_name = "primary_vm";
- * 		};
- * 		vm0 {
- * 			debug_name = "reserved_vm";
- * 			vcpu_count = <1>;
- * 			mem_size = <4096>;
- * 			kernel_filename = "kernel";
- * 		};
- * 	};
- * };
- *
- */
-constexpr uint8_t dtb_reserved_vmid[] = {
-	0xd0, 0x0d, 0xfe, 0xed, 0x00, 0x00, 0x01, 0x07, 0x00, 0x00, 0x00, 0x38,
-	0x00, 0x00, 0x00, 0xd8, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x11,
-	0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2f,
-	0x00, 0x00, 0x00, 0xa0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
-	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x68, 0x79, 0x70, 0x65,
-	0x72, 0x76, 0x69, 0x73, 0x6f, 0x72, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
-	0x76, 0x6d, 0x31, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x0b,
-	0x00, 0x00, 0x00, 0x00, 0x70, 0x72, 0x69, 0x6d, 0x61, 0x72, 0x79, 0x5f,
-	0x76, 0x6d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01,
-	0x76, 0x6d, 0x30, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x0c,
-	0x00, 0x00, 0x00, 0x00, 0x72, 0x65, 0x73, 0x65, 0x72, 0x76, 0x65, 0x64,
-	0x5f, 0x76, 0x6d, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x04,
-	0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x03,
-	0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x10, 0x00,
-	0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x1f,
-	0x6b, 0x65, 0x72, 0x6e, 0x65, 0x6c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02,
-	0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x09,
-	0x64, 0x65, 0x62, 0x75, 0x67, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x00, 0x76,
-	0x63, 0x70, 0x75, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x00, 0x6d, 0x65,
-	0x6d, 0x5f, 0x73, 0x69, 0x7a, 0x65, 0x00, 0x6b, 0x65, 0x72, 0x6e, 0x65,
-	0x6c, 0x5f, 0x66, 0x69, 0x6c, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x00};
-
 TEST(manifest, reserved_vmid)
 {
 	struct manifest m;
 	struct memiter it;
 
-	memiter_init(&it, dtb_reserved_vmid, sizeof(dtb_reserved_vmid));
+	/* clang-format off */
+	std::vector<char> dtb = ManifestDtBuilder()
+		.StartChild("hypervisor")
+			.StartChild("vm1")
+				.DebugName("primary_vm")
+			.EndChild()
+			.StartChild("vm0")
+				.DebugName("reserved_vm")
+				.VcpuCount(1)
+				.MemSize(0x1000)
+				.KernelFilename("kernel")
+			.EndChild()
+		.EndChild()
+		.Build();
+	/* clang-format on */
+
+	memiter_init(&it, dtb.data(), dtb.size());
 	ASSERT_EQ(manifest_init(&m, &it), MANIFEST_ERROR_RESERVED_VM_ID);
 }
 
-/*
- * /dts-v1/;
- *
- * / {
- * 	hypervisor {
- * 		vm1 {
- * 			debug_name = "";
- * 		};
- * 		vm2 {
- * 			debug_name = "";
- * 			vcpu_count = <65535>;
- * 			mem_size = <0>;
- * 			kernel_filename = "";
- * 		};
- * 	};
- * };
- *
- */
-constexpr uint8_t dtb_last_valid_vcpu_count[] = {
-	0xd0, 0x0d, 0xfe, 0xed, 0x00, 0x00, 0x00, 0xf3, 0x00, 0x00, 0x00, 0x38,
-	0x00, 0x00, 0x00, 0xc4, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x11,
-	0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2f,
-	0x00, 0x00, 0x00, 0x8c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
-	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x68, 0x79, 0x70, 0x65,
-	0x72, 0x76, 0x69, 0x73, 0x6f, 0x72, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
-	0x76, 0x6d, 0x31, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x01,
-	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02,
-	0x00, 0x00, 0x00, 0x01, 0x76, 0x6d, 0x32, 0x00, 0x00, 0x00, 0x00, 0x03,
-	0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-	0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x0b,
-	0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x04,
-	0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03,
-	0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00,
-	0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02,
-	0x00, 0x00, 0x00, 0x09, 0x64, 0x65, 0x62, 0x75, 0x67, 0x5f, 0x6e, 0x61,
-	0x6d, 0x65, 0x00, 0x76, 0x63, 0x70, 0x75, 0x5f, 0x63, 0x6f, 0x75, 0x6e,
-	0x74, 0x00, 0x6d, 0x65, 0x6d, 0x5f, 0x73, 0x69, 0x7a, 0x65, 0x00, 0x6b,
-	0x65, 0x72, 0x6e, 0x65, 0x6c, 0x5f, 0x66, 0x69, 0x6c, 0x65, 0x6e, 0x61,
-	0x6d, 0x65, 0x00};
-
-/* Same as above, set "vcpu_count" to 65536. */
-constexpr uint8_t dtb_first_invalid_vcpu_count[] = {
-	0xd0, 0x0d, 0xfe, 0xed, 0x00, 0x00, 0x00, 0xf3, 0x00, 0x00, 0x00, 0x38,
-	0x00, 0x00, 0x00, 0xc4, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x11,
-	0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2f,
-	0x00, 0x00, 0x00, 0x8c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
-	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x68, 0x79, 0x70, 0x65,
-	0x72, 0x76, 0x69, 0x73, 0x6f, 0x72, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
-	0x76, 0x6d, 0x31, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x01,
-	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02,
-	0x00, 0x00, 0x00, 0x01, 0x76, 0x6d, 0x32, 0x00, 0x00, 0x00, 0x00, 0x03,
-	0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-	0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x0b,
-	0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x04,
-	0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03,
-	0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00,
-	0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02,
-	0x00, 0x00, 0x00, 0x09, 0x64, 0x65, 0x62, 0x75, 0x67, 0x5f, 0x6e, 0x61,
-	0x6d, 0x65, 0x00, 0x76, 0x63, 0x70, 0x75, 0x5f, 0x63, 0x6f, 0x75, 0x6e,
-	0x74, 0x00, 0x6d, 0x65, 0x6d, 0x5f, 0x73, 0x69, 0x7a, 0x65, 0x00, 0x6b,
-	0x65, 0x72, 0x6e, 0x65, 0x6c, 0x5f, 0x66, 0x69, 0x6c, 0x65, 0x6e, 0x61,
-	0x6d, 0x65, 0x00};
+static std::vector<char> gen_vcpu_count_limit_dtb(uint64_t vcpu_count)
+{
+	/* clang-format off */
+	return ManifestDtBuilder()
+		.StartChild("hypervisor")
+			.StartChild("vm1")
+				.DebugName("primary_vm")
+			.EndChild()
+			.StartChild("vm2")
+				.DebugName("secondary_vm")
+				.VcpuCount(vcpu_count)
+				.MemSize(0x1000)
+				.KernelFilename("kernel")
+			.EndChild()
+		.EndChild()
+		.Build();
+	/* clang-format on */
+}
 
 TEST(manifest, vcpu_count_limit)
 {
 	struct manifest m;
 	struct memiter it;
+	std::vector<char> dtb_last_valid = gen_vcpu_count_limit_dtb(UINT16_MAX);
+	std::vector<char> dtb_first_invalid =
+		gen_vcpu_count_limit_dtb(UINT16_MAX + 1);
 
-	memiter_init(&it, dtb_last_valid_vcpu_count,
-		     sizeof(dtb_last_valid_vcpu_count));
+	memiter_init(&it, dtb_last_valid.data(), dtb_last_valid.size());
 	ASSERT_EQ(manifest_init(&m, &it), MANIFEST_SUCCESS);
 	ASSERT_EQ(m.num_vms, 2);
 	ASSERT_EQ(m.vm[1].secondary.vcpu_count, UINT16_MAX);
 
-	memiter_init(&it, dtb_first_invalid_vcpu_count,
-		     sizeof(dtb_first_invalid_vcpu_count));
+	memiter_init(&it, dtb_first_invalid.data(), dtb_first_invalid.size());
 	ASSERT_EQ(manifest_init(&m, &it), MANIFEST_ERROR_INTEGER_OVERFLOW);
 }
 
-/*
- * /dts-v1/;
- *
- * / {
- * 	hypervisor {
- * 		vm1 {
- * 			debug_name = "primary_vm";
- * 		};
- * 		vm3 {
- * 			debug_name = "second_secondary_vm";
- * 			vcpu_count = <43>;
- * 			mem_size = <0x12345>;
- * 			kernel_filename = "second_kernel";
- * 		};
- * 		vm2 {
- * 			debug_name = "first_secondary_vm";
- * 			vcpu_count = <42>;
- * 			mem_size = <12345>;
- * 			kernel_filename = "first_kernel";
- * 		};
- * 	};
- * };
- *
- */
-constexpr uint8_t dtb_valid[] = {
-	0xd0, 0x0d, 0xfe, 0xed, 0x00, 0x00, 0x01, 0x7f, 0x00, 0x00, 0x00, 0x38,
-	0x00, 0x00, 0x01, 0x50, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x11,
-	0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2f,
-	0x00, 0x00, 0x01, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
-	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x68, 0x79, 0x70, 0x65,
-	0x72, 0x76, 0x69, 0x73, 0x6f, 0x72, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
-	0x76, 0x6d, 0x31, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x0b,
-	0x00, 0x00, 0x00, 0x00, 0x70, 0x72, 0x69, 0x6d, 0x61, 0x72, 0x79, 0x5f,
-	0x76, 0x6d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01,
-	0x76, 0x6d, 0x33, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x14,
-	0x00, 0x00, 0x00, 0x00, 0x73, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x5f, 0x73,
-	0x65, 0x63, 0x6f, 0x6e, 0x64, 0x61, 0x72, 0x79, 0x5f, 0x76, 0x6d, 0x00,
-	0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x0b,
-	0x00, 0x00, 0x00, 0x2b, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x04,
-	0x00, 0x00, 0x00, 0x16, 0x00, 0x01, 0x23, 0x45, 0x00, 0x00, 0x00, 0x03,
-	0x00, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x1f, 0x73, 0x65, 0x63, 0x6f,
-	0x6e, 0x64, 0x5f, 0x6b, 0x65, 0x72, 0x6e, 0x65, 0x6c, 0x00, 0x00, 0x00,
-	0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x76, 0x6d, 0x32, 0x00,
-	0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x13, 0x00, 0x00, 0x00, 0x00,
-	0x66, 0x69, 0x72, 0x73, 0x74, 0x5f, 0x73, 0x65, 0x63, 0x6f, 0x6e, 0x64,
-	0x61, 0x72, 0x79, 0x5f, 0x76, 0x6d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03,
-	0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x2a,
-	0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x16,
-	0x00, 0x00, 0x30, 0x39, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x0d,
-	0x00, 0x00, 0x00, 0x1f, 0x66, 0x69, 0x72, 0x73, 0x74, 0x5f, 0x6b, 0x65,
-	0x72, 0x6e, 0x65, 0x6c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02,
-	0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x09,
-	0x64, 0x65, 0x62, 0x75, 0x67, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x00, 0x76,
-	0x63, 0x70, 0x75, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x00, 0x6d, 0x65,
-	0x6d, 0x5f, 0x73, 0x69, 0x7a, 0x65, 0x00, 0x6b, 0x65, 0x72, 0x6e, 0x65,
-	0x6c, 0x5f, 0x66, 0x69, 0x6c, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x00};
-
 TEST(manifest, valid)
 {
 	struct manifest m;
 	struct manifest_vm *vm;
 	struct memiter it;
 
-	memiter_init(&it, dtb_valid, sizeof(dtb_valid));
+	/* clang-format off */
+	std::vector<char> dtb = ManifestDtBuilder()
+		.StartChild("hypervisor")
+			.StartChild("vm1")
+				.DebugName("primary_vm")
+			.EndChild()
+			.StartChild("vm3")
+				.DebugName("second_secondary_vm")
+				.VcpuCount(43)
+				.MemSize(0x12345)
+				.KernelFilename("second_kernel")
+			.EndChild()
+			.StartChild("vm2")
+				.DebugName("first_secondary_vm")
+				.VcpuCount(42)
+				.MemSize(12345)
+				.KernelFilename("first_kernel")
+			.EndChild()
+		.EndChild()
+		.Build();
+	/* clang-format on */
+
+	memiter_init(&it, dtb.data(), dtb.size());
 
 	ASSERT_EQ(manifest_init(&m, &it), MANIFEST_SUCCESS);
 	ASSERT_EQ(m.num_vms, 3);