regulators: Make TmpFile class common

A TmpFile class was implemented in two different test cases to
automatically create and delete temporary files.

Moved the TmpFile class into a standalone file so that the code could be
common among test cases.  Improved the error handling.

Signed-off-by: Shawn McCarney <shawnmm@us.ibm.com>
Change-Id: Ifa8ddd3b442bcf8fbf0dc4d0a39dd9fc5f233e3f
diff --git a/phosphor-regulators/test/config_file_parser_tests.cpp b/phosphor-regulators/test/config_file_parser_tests.cpp
index 490cac7..83d5699 100644
--- a/phosphor-regulators/test/config_file_parser_tests.cpp
+++ b/phosphor-regulators/test/config_file_parser_tests.cpp
@@ -20,10 +20,9 @@
 #include "pmbus_utils.hpp"
 #include "pmbus_write_vout_command_action.hpp"
 #include "rule.hpp"
+#include "tmp_file.hpp"
 
-#include <stdlib.h>
-#include <sys/stat.h>
-#include <unistd.h>
+#include <sys/stat.h> // for chmod()
 
 #include <nlohmann/json.hpp>
 
@@ -46,41 +45,6 @@
 using namespace phosphor::power::regulators::config_file_parser::internal;
 using json = nlohmann::json;
 
-/**
- * @class TmpFile
- *
- * Temporary file.
- *
- * File is deleted automatically by the destructor when the object goes out of
- * scope.
- */
-class TmpFile
-{
-  public:
-    TmpFile()
-    {
-        int fd = mkstemp(fileName);
-        if (fd == -1)
-        {
-            throw std::runtime_error{"Unable to create temporary file"};
-        }
-        close(fd);
-    }
-
-    std::string getName()
-    {
-        return fileName;
-    }
-
-    ~TmpFile()
-    {
-        unlink(fileName);
-    }
-
-  private:
-    char fileName[17] = "/tmp/temp-XXXXXX";
-};
-
 void writeConfigFile(const std::filesystem::path& pathName,
                      const std::string& contents)
 {
diff --git a/phosphor-regulators/test/tmp_file.hpp b/phosphor-regulators/test/tmp_file.hpp
new file mode 100644
index 0000000..dd0a66c
--- /dev/null
+++ b/phosphor-regulators/test/tmp_file.hpp
@@ -0,0 +1,99 @@
+/**
+ * Copyright © 2020 IBM Corporation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#pragma once
+
+#include <errno.h>  // for errno
+#include <stdio.h>  // for perror()
+#include <stdlib.h> // for mkstemp()
+#include <string.h> // for strerror()
+#include <unistd.h> // for close(), unlink()
+
+#include <stdexcept>
+#include <string>
+
+namespace phosphor::power::regulators
+{
+
+/**
+ * @class TmpFile
+ *
+ * Temporary file.
+ *
+ * File is created by the constructor and deleted by the destructor.  The file
+ * name can be obtained from getName().
+ */
+class TmpFile
+{
+  public:
+    // Specify which compiler-generated methods we want
+    TmpFile(const TmpFile&) = delete;
+    TmpFile(TmpFile&&) = delete;
+    TmpFile& operator=(const TmpFile&) = delete;
+    TmpFile& operator=(TmpFile&&) = delete;
+
+    /**
+     * Constructor.
+     *
+     * Creates the temporary file.
+     */
+    TmpFile()
+    {
+        // Generate unique file name, create file, and open it.  The XXXXXX
+        // characters are replaced by mkstemp() to make the file name unique.
+        char fileNameTemplate[17] = "/tmp/temp-XXXXXX";
+        int fd = mkstemp(fileNameTemplate);
+        if (fd == -1)
+        {
+            // If mkstemp() fails, throw an exception.  No temporary file has
+            // been created and calling getName() would not work.
+            throw std::runtime_error{"Unable to create temporary file: " +
+                                     std::string{strerror(errno)}};
+        }
+
+        // Close file
+        if (close(fd) == -1)
+        {
+            // If close() fails, write a message to standard error but do not
+            // throw an exception.  If an exception is thrown, the destructor
+            // will not be run and the temporary file will not be deleted.
+            perror("Unable to close temporary file");
+        }
+
+        // Save file name
+        fileName = fileNameTemplate;
+    }
+
+    const std::string& getName()
+    {
+        return fileName;
+    }
+
+    ~TmpFile()
+    {
+        // Delete temporary file
+        if (unlink(fileName.c_str()) == -1)
+        {
+            // If unlink() fails, write a message to standard error but do not
+            // throw an exception.  Destructors must not throw exceptions.
+            perror("Unable to delete temporary file");
+        }
+    }
+
+  private:
+    std::string fileName{};
+};
+
+} // namespace phosphor::power::regulators
diff --git a/phosphor-regulators/test/validate-regulators-config_tests.cpp b/phosphor-regulators/test/validate-regulators-config_tests.cpp
index 2b915b9..987a492 100644
--- a/phosphor-regulators/test/validate-regulators-config_tests.cpp
+++ b/phosphor-regulators/test/validate-regulators-config_tests.cpp
@@ -13,14 +13,15 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-#include <errno.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <sys/stat.h>
-#include <sys/wait.h>
+#include "tmp_file.hpp"
+
+#include <stdio.h>    // for popen(), pclose(), fgets()
+#include <sys/stat.h> // for chmod()
+#include <sys/wait.h> // for WEXITSTATUS
 
 #include <nlohmann/json.hpp>
 
+#include <cstdio>
 #include <fstream>
 
 #include <gtest/gtest.h>
@@ -35,6 +36,7 @@
     expectJsonInvalid(configFileJson, expectedErrorMessage,                    \
                       expectedOutputMessage)
 
+using namespace phosphor::power::regulators;
 using json = nlohmann::json;
 
 const json validConfigFile = R"(
@@ -103,33 +105,6 @@
     }
 )"_json;
 
-class TmpFile
-{
-  public:
-    TmpFile()
-    {
-        int fd = mkstemp(fileName);
-        if (fd == -1)
-        {
-            perror("Can't create temporary file");
-        }
-        close(fd);
-    }
-
-    std::string getName()
-    {
-        return fileName;
-    }
-
-    ~TmpFile()
-    {
-        unlink(fileName);
-    }
-
-  private:
-    char fileName[17] = "/tmp/temp-XXXXXX";
-};
-
 std::string getValidationToolCommand(const std::string& configFileName)
 {
     std::string command =