test: tmpd: Replace createVpnorRoot() with VpnorRoot class

The VpnorRoot class prepares a temporary directory for use as a VPNOR
backing store. Implementing it as a class allows us to use RAII to get
it to clean up after itself.

Change-Id: Ia5a839e751f8dc2126a4c0b474e9a7b8593cfd57
Signed-off-by: Andrew Jeffery <andrew@aj.id.au>
diff --git a/test/vpnor/Makefile.am.include b/test/vpnor/Makefile.am.include
index 0b5b1c9..5dfccaa 100644
--- a/test/vpnor/Makefile.am.include
+++ b/test/vpnor/Makefile.am.include
@@ -1,4 +1,5 @@
-TEST_MBOX_VPNOR_SRCS = common.c pnor_partition_table.cpp
+TEST_MBOX_VPNOR_SRCS = common.c pnor_partition_table.cpp \
+		       %reldir%/tmpd.cpp
 
 test_vpnor_create_pnor_partition_table_SOURCES = \
 	$(TEST_MBOX_VPNOR_SRCS) \
diff --git a/test/vpnor/create_pnor_partition_table.cpp b/test/vpnor/create_pnor_partition_table.cpp
index 1d4b126..fb371cb 100644
--- a/test/vpnor/create_pnor_partition_table.cpp
+++ b/test/vpnor/create_pnor_partition_table.cpp
@@ -19,18 +19,14 @@
 constexpr auto partitionName = "HBB";
 
 namespace fs = std::experimental::filesystem;
+namespace test = openpower::virtual_pnor::test;
 
 int main()
 {
-    char tmplt[] = "/tmp/vpnor_partitions.XXXXXX";
-    char* tmpdir = mkdtemp(tmplt);
-    assert(tmpdir != nullptr);
-    fs::path root{tmpdir};
-
-    openpower::virtual_pnor::test::createVpnorTree(root, toc, BLOCK_SIZE);
+    test::VpnorRoot root(toc, BLOCK_SIZE);
 
     const openpower::virtual_pnor::partition::Table table(
-        fs::path{tmpdir}, BLOCK_SIZE, PNOR_SIZE);
+        fs::path{root.path()}, BLOCK_SIZE, PNOR_SIZE);
 
     pnor_partition_table expectedTable{};
     expectedTable.data.magic = PARTITION_HEADER_MAGIC;
@@ -61,8 +57,6 @@
 
     const pnor_partition_table& result = table.getNativeTable();
 
-    fs::remove_all(fs::path{tmpdir});
-
     auto rc = memcmp(&expectedTable, &result, sizeof(pnor_partition_table));
     assert(rc == 0);
 
diff --git a/test/vpnor/tmpd.cpp b/test/vpnor/tmpd.cpp
new file mode 100644
index 0000000..16a4c2b
--- /dev/null
+++ b/test/vpnor/tmpd.cpp
@@ -0,0 +1,33 @@
+// SPDX-License-Identifier: Apache-2.0
+// Copyright (C) 2018 IBM Corp.
+
+#include "test/vpnor/tmpd.hpp"
+
+namespace openpower
+{
+namespace virtual_pnor
+{
+namespace test
+{
+
+namespace fs = std::experimental::filesystem;
+
+size_t VpnorRoot::write(const std::string &name, const void *data, size_t len)
+{
+    fs::path path{root};
+    path /= name;
+
+    if (!fs::exists(path))
+        /* It's not in the ToC */
+        throw std::invalid_argument(name);
+
+    std::ofstream partitionFile(path.c_str());
+    partitionFile.write((const char *)data, len);
+    partitionFile.close();
+
+    return len;
+}
+
+} // test
+} // virtual_pnor
+} // openpower
diff --git a/test/vpnor/tmpd.hpp b/test/vpnor/tmpd.hpp
index ab27377..d684706 100644
--- a/test/vpnor/tmpd.hpp
+++ b/test/vpnor/tmpd.hpp
@@ -10,8 +10,6 @@
 #include "config.h"
 #include "pnor_partition_table.hpp"
 
-namespace fs = std::experimental::filesystem;
-
 namespace openpower
 {
 namespace virtual_pnor
@@ -19,35 +17,62 @@
 namespace test
 {
 
-template <std::size_t N>
-void createVpnorTree(fs::path &root, const std::string (&toc)[N],
-                     size_t blockSize)
+namespace fs = std::experimental::filesystem;
+
+class VpnorRoot
 {
-    fs::path tocFilePath{root};
-    tocFilePath /= PARTITION_TOC_FILE;
-    std::ofstream tocFile(tocFilePath.c_str());
-
-    for (const std::string &line : toc)
+  public:
+    template <std::size_t N>
+    VpnorRoot(const std::string (&toc)[N], size_t blockSize)
     {
-        pnor_partition part;
+        char tmplt[] = "/tmp/vpnor_root.XXXXXX";
+        char* tmpdir = mkdtemp(tmplt);
+        root = fs::path{tmpdir};
 
-        openpower::virtual_pnor::parseTocLine(line, blockSize, part);
+        fs::path tocFilePath{root};
+        tocFilePath /= PARTITION_TOC_FILE;
+        std::ofstream tocFile(tocFilePath.c_str());
 
-        /* Populate the partition in the tree */
-        fs::path partitionFilePath{root};
-        partitionFilePath /= part.data.name;
-        std::ofstream partitionFile(partitionFilePath.c_str());
-        std::vector<char> empty(part.data.size, 0);
-        partitionFile.write(empty.data(), empty.size());
-        partitionFile.close();
+        for (const std::string& line : toc)
+        {
+            pnor_partition part;
 
-        /* Update the ToC if the partition file was created */
-        tocFile.write(line.c_str(), line.length());
-        tocFile.write("\n", 1);
+            openpower::virtual_pnor::parseTocLine(line, blockSize, part);
+
+            /* Populate the partition in the tree */
+            fs::path partitionFilePath{root};
+            partitionFilePath /= part.data.name;
+            std::ofstream partitionFile(partitionFilePath.c_str());
+            std::vector<char> empty(part.data.size, 0);
+            partitionFile.write(empty.data(), empty.size());
+            partitionFile.close();
+
+            /* Update the ToC if the partition file was created */
+            tocFile.write(line.c_str(), line.length());
+            tocFile.write("\n", 1);
+        }
+
+        tocFile.close();
     }
 
-    tocFile.close();
-}
+    VpnorRoot(const VpnorRoot&) = delete;
+    VpnorRoot& operator=(const VpnorRoot&) = delete;
+    VpnorRoot(VpnorRoot&&) = delete;
+    VpnorRoot& operator=(VpnorRoot&&) = delete;
+
+    ~VpnorRoot()
+    {
+        fs::remove_all(root);
+    }
+    const fs::path& path()
+    {
+        return root;
+    }
+    size_t write(const std::string& name, const void* data, size_t len);
+
+  private:
+    fs::path root;
+};
 
 } // test
 } // virtual_pnor