Rework envMock

Instead of mocking all the functions, only mock std::getenv.
Now each test only needs to provide an EnvImpl that delegate calls to
mockEnv in order to inject dependencies on std::getenv. This for several
reasons:
1. Any call to env::getEnv() will be calling the real implementation of
the C++ code, and testing real code is better than testing mocks.
2. It is easier to write a fake class that takes a config string which
can greatly simplify test cases.
3. We can now write unit tests that ensure the number of times
std::getenv gets called (should be once, but multiple times right now).

Tested: unit tests still pass

Signed-off-by: Kun Yi <kunyi731@gmail.com>
Change-Id: I3e5aff7fa5d025de1b8ae798af43b97d31151ab9
diff --git a/env.hpp b/env.hpp
index b3ddff5..b8c3815 100644
--- a/env.hpp
+++ b/env.hpp
@@ -2,20 +2,48 @@
 
 #include "sensorset.hpp"
 
+#include <fstream>
 #include <string>
 
 namespace env
 {
 
+/** @class Env
+ *  @brief Overridable std::getenv interface
+ */
+struct Env
+{
+    virtual ~Env() = default;
+
+    virtual const char* get(const char* key) const = 0;
+};
+
+/** @class EnvImpl
+ *  @brief Concrete implementation that calls std::getenv
+ */
+struct EnvImpl : public Env
+{
+    const char* get(const char* key) const override;
+};
+
+/** @brief Default instantiation of Env */
+extern EnvImpl env_impl;
+
 /** @brief Reads an environment variable
  *
  *  Reads the environment for that key
  *
  *  @param[in] key - the key
+ *  @param[in] env - env interface that defaults to calling std::getenv
  *
  *  @return string - the env var value
  */
-std::string getEnv(const char* key);
+inline std::string getEnv(const char* key, const Env* env = &env_impl)
+{
+    // Be aware value could be nullptr
+    auto value = env->get(key);
+    return (value) ? std::string(value) : std::string();
+}
 
 /** @brief Reads an environment variable
  *
@@ -23,21 +51,38 @@
  *
  *  @param[in] prefix - the variable prefix
  *  @param[in] sensor - Sensor details
+ *  @param[in] env - env interface that defaults to calling std::getenv
  *
  *  @return string - the env var value
  */
-std::string getEnv(const char* prefix, const SensorSet::key_type& sensor);
+inline std::string getEnv(const char* prefix, const SensorSet::key_type& sensor,
+                          const Env* env = &env_impl)
+{
+    std::string key;
+
+    key.assign(prefix);
+    key.append(1, '_');
+    key.append(sensor.first);
+    key.append(sensor.second);
+
+    return getEnv(key.c_str(), env);
+}
 
 /** @brief Reads an environment variable, and takes type and id separately
  *
  *  @param[in] prefix - the variable prefix
  *  @param[in] type - sensor type, like 'temp'
  *  @param[in] id - sensor ID, like '5'
+ *  @param[in] env - env interface that defaults to calling std::getenv
  *
  *  @return string - the env var value
  */
-std::string getEnv(const char* prefix, const std::string& type,
-                   const std::string& id);
+inline std::string getEnv(const char* prefix, const std::string& type,
+                          const std::string& id, const Env* env = &env_impl)
+{
+    SensorSet::key_type sensor{type, id};
+    return getEnv(prefix, sensor, env);
+}
 
 /** @brief Gets the ID for the sensor with a level of indirection
  *
@@ -48,7 +93,31 @@
  *  @param[in] fileSuffix - The file suffix
  *  @param[in] sensor - Sensor details
  */
-std::string getIndirectID(std::string path, const std::string& fileSuffix,
-                          const SensorSet::key_type& sensor);
+inline std::string getIndirectID(std::string path,
+                                 const std::string& fileSuffix,
+                                 const SensorSet::key_type& sensor)
+{
+    std::string content;
+
+    path.append(sensor.first);
+    path.append(sensor.second);
+    path.append(1, '_');
+    path.append(fileSuffix);
+
+    std::ifstream handle(path.c_str());
+    if (!handle.fail())
+    {
+        content.assign((std::istreambuf_iterator<char>(handle)),
+                       (std::istreambuf_iterator<char>()));
+
+        if (!content.empty())
+        {
+            // remove the newline
+            content.pop_back();
+        }
+    }
+
+    return content;
+}
 
 } // namespace env