Repository: hadoop
Updated Branches:
  refs/heads/HDFS-8707 8b7769557 -> 8c6a84fdd


HDFS-9117.  Config file reader / options classes for libhdfs++.  Contributed by 
Bob Hansen.


Project: http://git-wip-us.apache.org/repos/asf/hadoop/repo
Commit: http://git-wip-us.apache.org/repos/asf/hadoop/commit/8c6a84fd
Tree: http://git-wip-us.apache.org/repos/asf/hadoop/tree/8c6a84fd
Diff: http://git-wip-us.apache.org/repos/asf/hadoop/diff/8c6a84fd

Branch: refs/heads/HDFS-8707
Commit: 8c6a84fdd55350791268bb4624b1f2b3163bc90b
Parents: 8b77695
Author: James <j...@apache.org>
Authored: Wed Nov 25 12:09:13 2015 -0500
Committer: James <j...@apache.org>
Committed: Wed Nov 25 12:09:13 2015 -0500

----------------------------------------------------------------------
 .../src/main/native/libhdfspp/CMakeLists.txt    |   2 +
 .../native/libhdfspp/lib/common/CMakeLists.txt  |   2 +-
 .../libhdfspp/lib/common/configuration.cc       | 231 ++++++++++++
 .../native/libhdfspp/lib/common/configuration.h |  94 +++++
 .../main/native/libhdfspp/tests/CMakeLists.txt  |   4 +
 .../libhdfspp/tests/configuration_test.cc       | 363 +++++++++++++++++++
 .../native/libhdfspp/tests/configuration_test.h |  73 ++++
 7 files changed, 768 insertions(+), 1 deletion(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/hadoop/blob/8c6a84fd/hadoop-hdfs-project/hadoop-hdfs-native-client/src/main/native/libhdfspp/CMakeLists.txt
----------------------------------------------------------------------
diff --git 
a/hadoop-hdfs-project/hadoop-hdfs-native-client/src/main/native/libhdfspp/CMakeLists.txt
 
b/hadoop-hdfs-project/hadoop-hdfs-native-client/src/main/native/libhdfspp/CMakeLists.txt
index 18e4cb0..d1b60be 100644
--- 
a/hadoop-hdfs-project/hadoop-hdfs-native-client/src/main/native/libhdfspp/CMakeLists.txt
+++ 
b/hadoop-hdfs-project/hadoop-hdfs-native-client/src/main/native/libhdfspp/CMakeLists.txt
@@ -49,7 +49,9 @@ include_directories(
   lib
   ${PROJECT_BINARY_DIR}/lib/proto
   third_party/asio-1.10.2/include
+  third_party/rapidxml-1.13
   third_party/gmock-1.7.0
+  third_party/tr2
   ${OPENSSL_INCLUDE_DIR}
   ../libhdfs/include
 )

http://git-wip-us.apache.org/repos/asf/hadoop/blob/8c6a84fd/hadoop-hdfs-project/hadoop-hdfs-native-client/src/main/native/libhdfspp/lib/common/CMakeLists.txt
----------------------------------------------------------------------
diff --git 
a/hadoop-hdfs-project/hadoop-hdfs-native-client/src/main/native/libhdfspp/lib/common/CMakeLists.txt
 
b/hadoop-hdfs-project/hadoop-hdfs-native-client/src/main/native/libhdfspp/lib/common/CMakeLists.txt
index b03f00b..8dbcd03 100644
--- 
a/hadoop-hdfs-project/hadoop-hdfs-native-client/src/main/native/libhdfspp/lib/common/CMakeLists.txt
+++ 
b/hadoop-hdfs-project/hadoop-hdfs-native-client/src/main/native/libhdfspp/lib/common/CMakeLists.txt
@@ -1 +1 @@
-add_library(common base64.cc options.cc status.cc sasl_digest_md5.cc 
hdfs_public_api.cc)
+add_library(common base64.cc status.cc sasl_digest_md5.cc hdfs_public_api.cc 
options.cc configuration.cc)

http://git-wip-us.apache.org/repos/asf/hadoop/blob/8c6a84fd/hadoop-hdfs-project/hadoop-hdfs-native-client/src/main/native/libhdfspp/lib/common/configuration.cc
----------------------------------------------------------------------
diff --git 
a/hadoop-hdfs-project/hadoop-hdfs-native-client/src/main/native/libhdfspp/lib/common/configuration.cc
 
b/hadoop-hdfs-project/hadoop-hdfs-native-client/src/main/native/libhdfspp/lib/common/configuration.cc
new file mode 100644
index 0000000..2baf84b
--- /dev/null
+++ 
b/hadoop-hdfs-project/hadoop-hdfs-native-client/src/main/native/libhdfspp/lib/common/configuration.cc
@@ -0,0 +1,231 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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.
+ */
+
+/*
+ * The following features are not currently implemented
+ * - Deprecated values
+ * - Make filename and config file contents unicode-safe
+ * - Config redirection/environment substitution
+ *
+ * - getInts (comma separated))
+ * - getStrings (comma separated))
+ * - getIntegerRange
+ * - getSocketAddr
+ * - getTimeDuration
+ * - getBytes (e.g. 1M or 1G)
+ * - hex values
+ */
+
+#include "configuration.h"
+
+#include <strings.h>
+#include <sstream>
+#include <map>
+#include <rapidxml/rapidxml.hpp>
+#include <rapidxml/rapidxml_utils.hpp>
+
+namespace hdfs {
+
+/*
+ * Configuration class
+ */
+
+Configuration::Configuration() {}
+
+bool is_valid_bool(const std::string& raw) {
+  if (!strcasecmp(raw.c_str(), "true")) {
+    return true;
+  }
+  if (!strcasecmp(raw.c_str(), "false")) {
+    return true;
+  }
+  return false;
+}
+
+bool str_to_bool(const std::string& raw) {
+  if (!strcasecmp(raw.c_str(), "true")) {
+    return true;
+  }
+
+  return false;
+}
+
+optional<Configuration> Configuration::Load(const std::string& xmlData) {
+  Configuration result;
+  return result.OverlayResourceString(xmlData);
+}
+
+optional<Configuration> Configuration::OverlayResourceString(
+    const std::string& xmlData) const {
+  if (xmlData.size() == 0) {
+    return optional<Configuration>();
+  }
+
+  int length = xmlData.size();
+  std::vector<char> raw_bytes;
+  raw_bytes.reserve(length + 1);
+  std::copy(xmlData.begin(), xmlData.end(), std::back_inserter(raw_bytes));
+  raw_bytes.push_back('\0');
+
+  ConfigMap map(raw_values_);
+  bool success = UpdateMapWithResource(map, raw_bytes);
+
+  if (success) {
+    return optional<Configuration>(Configuration(map));
+  } else {
+    return optional<Configuration>();
+  }
+}
+
+bool Configuration::UpdateMapWithResource(ConfigMap& map,
+                                          std::vector<char>& raw_bytes) {
+  rapidxml::xml_document<> dom;
+  dom.parse<rapidxml::parse_trim_whitespace>(&raw_bytes[0]);
+
+  /* File must contain a single <configuration> stanza */
+  auto config_node = dom.first_node("configuration", 0, false);
+  if (!config_node) {
+    return false;
+  }
+
+  /* Walk all of the <property> nodes, ignoring the rest */
+  for (auto property_node = config_node->first_node("property", 0, false);
+       property_node;
+       property_node = property_node->next_sibling("property", 0, false)) {
+    auto name_node = property_node->first_node("name", 0, false);
+    auto value_node = property_node->first_node("value", 0, false);
+
+    if (name_node && value_node) {
+      auto mapValue = map.find(name_node->value());
+      if (mapValue != map.end() && mapValue->second.final) {
+        continue;
+      }
+
+      map[name_node->value()] = value_node->value();
+      auto final_node = property_node->first_node("final", 0, false);
+      if (final_node && is_valid_bool(final_node->value())) {
+        map[name_node->value()].final = str_to_bool(final_node->value());
+      }
+    }
+
+    auto name_attr = property_node->first_attribute("name", 0, false);
+    auto value_attr = property_node->first_attribute("value", 0, false);
+
+    if (name_attr && value_attr) {
+      auto mapValue = map.find(name_attr->value());
+      if (mapValue != map.end() && mapValue->second.final) {
+        continue;
+      }
+
+      map[name_attr->value()] = value_attr->value();
+      auto final_attr = property_node->first_attribute("final", 0, false);
+      if (final_attr && is_valid_bool(final_attr->value())) {
+        map[name_attr->value()].final = str_to_bool(final_attr->value());
+      }
+    }
+  }
+
+  return true;
+}
+
+optional<std::string> Configuration::Get(const std::string& key) const {
+  auto found = raw_values_.find(key);
+  if (found != raw_values_.end()) {
+    return std::experimental::make_optional(found->second.value);
+  } else {
+    return optional<std::string>();
+  }
+}
+
+std::string Configuration::GetWithDefault(
+    const std::string& key, const std::string& default_value) const {
+  return Get(key).value_or(default_value);
+}
+
+optional<int64_t> Configuration::GetInt(const std::string& key) const {
+  auto raw = Get(key);
+  if (raw) {
+    errno = 0;
+    char* end = nullptr;
+    auto result =
+        std::experimental::make_optional(strtol(raw->c_str(), &end, 10));
+    if (end == raw->c_str()) {
+      /* strtoll will set end to input if no conversion was done */
+      return optional<int64_t>();
+    }
+    if (errno == ERANGE) {
+      return optional<int64_t>();
+    }
+
+    return result;
+  } else {
+    return optional<int64_t>();
+  }
+}
+
+int64_t Configuration::GetIntWithDefault(const std::string& key,
+                                         int64_t default_value) const {
+  return GetInt(key).value_or(default_value);
+}
+
+optional<double> Configuration::GetDouble(const std::string& key) const {
+  auto raw = Get(key);
+  if (raw) {
+    errno = 0;
+    char* end = nullptr;
+    auto result = std::experimental::make_optional(strtod(raw->c_str(), &end));
+    if (end == raw->c_str()) {
+      /* strtod will set end to input if no conversion was done */
+      return optional<double>();
+    }
+    if (errno == ERANGE) {
+      return optional<double>();
+    }
+
+    return result;
+  } else {
+    return optional<double>();
+  }
+}
+
+double Configuration::GetDoubleWithDefault(const std::string& key,
+                                           double default_value) const {
+  return GetDouble(key).value_or(default_value);
+}
+
+optional<bool> Configuration::GetBool(const std::string& key) const {
+  auto raw = Get(key);
+  if (!raw) {
+    return optional<bool>();
+  }
+
+  if (!strcasecmp(raw->c_str(), "true")) {
+    return std::experimental::make_optional(true);
+  }
+  if (!strcasecmp(raw->c_str(), "false")) {
+    return std::experimental::make_optional(false);
+  }
+
+  return optional<bool>();
+}
+
+bool Configuration::GetBoolWithDefault(const std::string& key,
+                                       bool default_value) const {
+  return GetBool(key).value_or(default_value);
+}
+}

http://git-wip-us.apache.org/repos/asf/hadoop/blob/8c6a84fd/hadoop-hdfs-project/hadoop-hdfs-native-client/src/main/native/libhdfspp/lib/common/configuration.h
----------------------------------------------------------------------
diff --git 
a/hadoop-hdfs-project/hadoop-hdfs-native-client/src/main/native/libhdfspp/lib/common/configuration.h
 
b/hadoop-hdfs-project/hadoop-hdfs-native-client/src/main/native/libhdfspp/lib/common/configuration.h
new file mode 100644
index 0000000..54ada94
--- /dev/null
+++ 
b/hadoop-hdfs-project/hadoop-hdfs-native-client/src/main/native/libhdfspp/lib/common/configuration.h
@@ -0,0 +1,94 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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.
+ */
+
+#ifndef COMMON_CONFIGURATION_H_
+#define COMMON_CONFIGURATION_H_
+
+#include <string>
+#include <map>
+#include <vector>
+#include <set>
+#include <istream>
+#include <stdint.h>
+#include <optional.hpp>
+
+namespace hdfs {
+
+template <class T>
+using optional = std::experimental::optional<T>;
+
+/**
+ * Configuration class that parses XML.
+ *
+ * Files should be an XML file of the form
+ * <configuration>
+ *  <property>
+ *    <name>Name</name>
+ *    <value>Value</value>
+ *  </property>
+ * <configuration>
+ *
+ * This class is not thread-safe.
+ */
+class Configuration {
+ public:
+  /* Creates a new Configuration from input xml */
+  static optional<Configuration> Load(const std::string &xml_data);
+
+  /* Constructs a configuration with no resources loaded */
+  Configuration();
+
+  /* Loads resources from a file or a stream */
+  optional<Configuration> OverlayResourceString(
+      const std::string &xml_data) const;
+
+  // Gets values
+  std::string GetWithDefault(const std::string &key,
+                             const std::string &default_value) const;
+  optional<std::string> Get(const std::string &key) const;
+  int64_t GetIntWithDefault(const std::string &key, int64_t default_value) 
const;
+  optional<int64_t> GetInt(const std::string &key) const;
+  double GetDoubleWithDefault(const std::string &key,
+                              double default_value) const;
+  optional<double> GetDouble(const std::string &key) const;
+  bool GetBoolWithDefault(const std::string &key, bool default_value) const;
+  optional<bool> GetBool(const std::string &key) const;
+
+ private:
+  /* Transparent data holder for property values */
+  struct ConfigData {
+    std::string value;
+    bool final;
+    ConfigData() : final(false){};
+    ConfigData(const std::string &value) : value(value), final(false) {}
+    void operator=(const std::string &new_value) {
+      value = new_value;
+      final = false;
+    }
+  };
+  typedef std::map<std::string, ConfigData> ConfigMap;
+
+  Configuration(ConfigMap &src_map) : raw_values_(src_map){};
+  static bool UpdateMapWithResource(ConfigMap &map,
+                                    std::vector<char> &raw_bytes);
+
+  const ConfigMap raw_values_;
+};
+}
+
+#endif

http://git-wip-us.apache.org/repos/asf/hadoop/blob/8c6a84fd/hadoop-hdfs-project/hadoop-hdfs-native-client/src/main/native/libhdfspp/tests/CMakeLists.txt
----------------------------------------------------------------------
diff --git 
a/hadoop-hdfs-project/hadoop-hdfs-native-client/src/main/native/libhdfspp/tests/CMakeLists.txt
 
b/hadoop-hdfs-project/hadoop-hdfs-native-client/src/main/native/libhdfspp/tests/CMakeLists.txt
index abd3858..7d06141 100644
--- 
a/hadoop-hdfs-project/hadoop-hdfs-native-client/src/main/native/libhdfspp/tests/CMakeLists.txt
+++ 
b/hadoop-hdfs-project/hadoop-hdfs-native-client/src/main/native/libhdfspp/tests/CMakeLists.txt
@@ -68,6 +68,10 @@ add_executable(node_exclusion_test node_exclusion_test.cc)
 target_link_libraries(node_exclusion_test fs gmock_main common 
${PROTOBUF_LIBRARIES} ${OPENSSL_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT})
 add_test(node_exclusion node_exclusion_test)
 
+add_executable(configuration_test configuration_test.cc)
+target_link_libraries(configuration_test common gmock_main 
${CMAKE_THREAD_LIBS_INIT})
+add_test(configuration configuration_test)
+
 build_libhdfs_test(libhdfs_threaded hdfspp_test_shim_static expect.c 
test_libhdfs_threaded.c ${OS_DIR}/thread.c)
 link_libhdfs_test(libhdfs_threaded hdfspp_test_shim_static fs reader rpc proto 
common ${PROTOBUF_LIBRARIES} ${OPENSSL_LIBRARIES} native_mini_dfs 
${JAVA_JVM_LIBRARY})
 add_libhdfs_test(libhdfs_threaded hdfspp_test_shim_static)

http://git-wip-us.apache.org/repos/asf/hadoop/blob/8c6a84fd/hadoop-hdfs-project/hadoop-hdfs-native-client/src/main/native/libhdfspp/tests/configuration_test.cc
----------------------------------------------------------------------
diff --git 
a/hadoop-hdfs-project/hadoop-hdfs-native-client/src/main/native/libhdfspp/tests/configuration_test.cc
 
b/hadoop-hdfs-project/hadoop-hdfs-native-client/src/main/native/libhdfspp/tests/configuration_test.cc
new file mode 100644
index 0000000..067fb8f
--- /dev/null
+++ 
b/hadoop-hdfs-project/hadoop-hdfs-native-client/src/main/native/libhdfspp/tests/configuration_test.cc
@@ -0,0 +1,363 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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.
+ */
+
+#include "configuration_test.h"
+#include "common/configuration.h"
+#include <gmock/gmock.h>
+#include <cstdio>
+#include <fstream>
+
+using ::testing::_;
+
+using namespace hdfs;
+
+namespace hdfs {
+
+TEST(ConfigurationTest, TestDegenerateInputs) {
+  /* Completely empty stream */
+  {
+    std::stringstream stream;
+    optional<Configuration> config = Configuration::Load("");
+    EXPECT_FALSE(config && "Empty stream");
+  }
+
+  /* No values */
+  {
+    std::string data = "<configuration></configuration>";
+    optional<Configuration> config = Configuration::Load(data);
+    EXPECT_TRUE(config && "Blank config");
+  }
+
+  /* Extraneous values */
+  {
+    std::string data = "<configuration><spam></spam></configuration>";
+    optional<Configuration> config = Configuration::Load(data);
+    EXPECT_TRUE(config && "Extraneous values");
+  }
+}
+
+TEST(ConfigurationTest, TestBasicOperations) {
+  /* Single value */
+  {
+    std::stringstream stream;
+    simpleConfigStream(stream, "key1", "value1");
+    optional<Configuration> config = Configuration::Load(stream.str());
+    EXPECT_TRUE(config && "Parse single value");
+    EXPECT_EQ("value1", config->GetWithDefault("key1", ""));
+  }
+
+  /* Multiple values */
+  {
+    optional<Configuration> config =
+        simpleConfig("key1", "value1", "key2", "value2");
+    EXPECT_EQ("value1", config->GetWithDefault("key1", ""));
+    EXPECT_EQ("value2", config->GetWithDefault("key2", ""));
+  }
+
+  /* No defaults */
+  {
+    std::stringstream stream;
+    simpleConfigStream(stream, "key1", "value1");
+    optional<Configuration> config = Configuration::Load(stream.str());
+    EXPECT_TRUE(config && "Parse single value");
+    optional<std::string> value = config->Get("key1");
+    EXPECT_TRUE((bool)value);
+    EXPECT_EQ("value1", *value);
+    EXPECT_FALSE(config->Get("key2"));
+  }
+}
+
+TEST(ConfigurationTest, TestCompactValues) {
+  {
+    std::stringstream stream;
+    stream << "<configuration><property name=\"key1\" "
+              "value=\"value1\"/></configuration>";
+    optional<Configuration> config = Configuration::Load(stream.str());
+    EXPECT_TRUE(config && "Compact value parse");
+    EXPECT_EQ("value1", config->GetWithDefault("key1", ""));
+  }
+}
+
+TEST(ConfigurationTest, TestMultipleResources) {
+  /* Single value */
+  {
+    std::stringstream stream;
+    simpleConfigStream(stream, "key1", "value1");
+    optional<Configuration> config = Configuration::Load(stream.str());
+    EXPECT_TRUE(config && "Parse first stream");
+    EXPECT_EQ("value1", config->GetWithDefault("key1", ""));
+
+    std::stringstream stream2;
+    simpleConfigStream(stream2, "key2", "value2");
+    optional<Configuration> config2 =
+        config->OverlayResourceString(stream2.str());
+    EXPECT_TRUE(config2 && "Parse second stream");
+    EXPECT_EQ("value1", config2->GetWithDefault("key1", ""));
+    EXPECT_EQ("value2", config2->GetWithDefault("key2", ""));
+  }
+}
+
+TEST(ConfigurationTest, TestStringResource) {
+  /* Single value */
+  {
+    std::stringstream stream;
+    simpleConfigStream(stream, "key1", "value1");
+    std::string str = stream.str();
+
+    optional<Configuration> config = Configuration::Load(stream.str());
+    EXPECT_TRUE(config && "Parse single value");
+    EXPECT_EQ("value1", config->GetWithDefault("key1", ""));
+  }
+}
+
+TEST(ConfigurationTest, TestFinal) {
+  {
+    /* Explicitly non-final non-compact value */
+    std::stringstream stream;
+    stream << "<configuration><property><name>key1</name><value>value1</"
+              "value><final>false</final></property></configuration>";
+    optional<Configuration> config = Configuration::Load(stream.str());
+    EXPECT_TRUE(config && "Parse first stream");
+    EXPECT_EQ("value1", config->GetWithDefault("key1", ""));
+
+    std::stringstream stream2;
+    simpleConfigStream(stream2, "key1", "value2");
+    optional<Configuration> config2 =
+        config->OverlayResourceString(stream2.str());
+    EXPECT_TRUE(config2 && "Parse second stream");
+    EXPECT_EQ("value2", config2->GetWithDefault("key1", ""));
+  }
+  {
+    /* Explicitly final non-compact value */
+    std::stringstream stream;
+    stream << "<configuration><property><name>key1</name><value>value1</"
+              "value><final>true</final></property></configuration>";
+    optional<Configuration> config = Configuration::Load(stream.str());
+    EXPECT_TRUE(config && "Parse first stream");
+    EXPECT_EQ("value1", config->GetWithDefault("key1", ""));
+
+    std::stringstream stream2;
+    simpleConfigStream(stream2, "key1", "value2");
+    optional<Configuration> config2 =
+        config->OverlayResourceString(stream2.str());
+    EXPECT_TRUE(config2 && "Parse second stream");
+    EXPECT_EQ("value1", config2->GetWithDefault("key1", ""));
+  }
+  {
+    /* Explicitly non-final compact value */
+    std::stringstream stream;
+    stream << "<configuration><property name=\"key1\" value=\"value1\" "
+              "final=\"false\"/></configuration>";
+    optional<Configuration> config = Configuration::Load(stream.str());
+    EXPECT_TRUE(config && "Parse first stream");
+    EXPECT_EQ("value1", config->GetWithDefault("key1", ""));
+
+    std::stringstream stream2;
+    simpleConfigStream(stream2, "key1", "value2");
+    optional<Configuration> config2 =
+        config->OverlayResourceString(stream2.str());
+    EXPECT_TRUE(config2 && "Parse second stream");
+    EXPECT_EQ("value2", config2->GetWithDefault("key1", ""));
+  }
+  {
+    /* Explicitly final compact value */
+    std::stringstream stream;
+    stream << "<configuration><property name=\"key1\" value=\"value1\" "
+              "final=\"true\"/></configuration>";
+    optional<Configuration> config = Configuration::Load(stream.str());
+    EXPECT_TRUE(config && "Parse first stream");
+    EXPECT_EQ("value1", config->GetWithDefault("key1", ""));
+
+    std::stringstream stream2;
+    simpleConfigStream(stream2, "key1", "value2");
+    optional<Configuration> config2 =
+        config->OverlayResourceString(stream2.str());
+    EXPECT_TRUE(config2 && "Parse second stream");
+    EXPECT_EQ("value1", config2->GetWithDefault("key1", ""));
+  }
+  {
+    /* Bogus final value */
+    std::stringstream stream;
+    stream << "<configuration><property><name>key1</name><value>value1</"
+              "value><final>spam</final></property></configuration>";
+    optional<Configuration> config = Configuration::Load(stream.str());
+    EXPECT_TRUE(config && "Parse first stream");
+    EXPECT_EQ("value1", config->GetWithDefault("key1", ""));
+
+    std::stringstream stream2;
+    simpleConfigStream(stream2, "key1", "value2");
+    optional<Configuration> config2 =
+        config->OverlayResourceString(stream2.str());
+    EXPECT_TRUE(config2 && "Parse second stream");
+    EXPECT_EQ("value2", config2->GetWithDefault("key1", ""));
+  }
+  {
+    /* Blank final value */
+    std::stringstream stream;
+    stream << "<configuration><property><name>key1</name><value>value1</"
+              "value><final></final></property></configuration>";
+    optional<Configuration> config = Configuration::Load(stream.str());
+    EXPECT_TRUE(config && "Parse first stream");
+    EXPECT_EQ("value1", config->GetWithDefault("key1", ""));
+
+    std::stringstream stream2;
+    simpleConfigStream(stream2, "key1", "value2");
+    optional<Configuration> config2 =
+        config->OverlayResourceString(stream2.str());
+    EXPECT_TRUE(config2 && "Parse second stream");
+    EXPECT_EQ("value2", config2->GetWithDefault("key1", ""));
+  }
+}
+TEST(ConfigurationTest, TestIntConversions) {
+  /* No defaults */
+  {
+    std::stringstream stream;
+    simpleConfigStream(stream, "key1", "1");
+    optional<Configuration> config = Configuration::Load(stream.str());
+    EXPECT_TRUE(config && "Parse single value");
+    optional<int64_t> value = config->GetInt("key1");
+    EXPECT_TRUE((bool)value);
+    EXPECT_EQ(1, *value);
+    EXPECT_FALSE(config->GetInt("key2"));
+  }
+
+  {
+    optional<Configuration> config = simpleConfig("key1", "1");
+    EXPECT_EQ(1, config->GetIntWithDefault("key1", -1));
+  }
+  {
+    optional<Configuration> config = simpleConfig("key1", "-100");
+    EXPECT_EQ(-100, config->GetIntWithDefault("key1", -1));
+  }
+  {
+    optional<Configuration> config = simpleConfig("key1", " 1 ");
+    EXPECT_EQ(1, config->GetIntWithDefault("key1", -1));
+  }
+  {
+    optional<Configuration> config = simpleConfig("key1", "");
+    EXPECT_EQ(-1, config->GetIntWithDefault("key1", -1));
+  }
+  {
+    optional<Configuration> config = simpleConfig("key1", "spam");
+    EXPECT_EQ(-1, config->GetIntWithDefault("key1", -1));
+  }
+  {
+    optional<Configuration> config = simpleConfig("key2", "");
+    EXPECT_EQ(-1, config->GetIntWithDefault("key1", -1));
+  }
+}
+
+TEST(ConfigurationTest, TestDoubleConversions) {
+  /* No defaults */
+  {
+    std::stringstream stream;
+    simpleConfigStream(stream, "key1", "1");
+    optional<Configuration> config = Configuration::Load(stream.str());
+    EXPECT_TRUE(config && "Parse single value");
+    optional<double> value = config->GetDouble("key1");
+    EXPECT_TRUE((bool)value);
+    EXPECT_EQ(1, *value);
+    EXPECT_FALSE(config->GetDouble("key2"));
+  }
+
+  {
+    optional<Configuration> config = simpleConfig("key1", "1");
+    EXPECT_EQ(1, config->GetDoubleWithDefault("key1", -1));
+  }
+  {
+    optional<Configuration> config = simpleConfig("key1", "-100");
+    EXPECT_EQ(-100, config->GetDoubleWithDefault("key1", -1));
+  }
+  {
+    optional<Configuration> config = simpleConfig("key1", " 1 ");
+    EXPECT_EQ(1, config->GetDoubleWithDefault("key1", -1));
+  }
+  {
+    optional<Configuration> config = simpleConfig("key1", "");
+    EXPECT_EQ(-1, config->GetDoubleWithDefault("key1", -1));
+  }
+  {
+    optional<Configuration> config = simpleConfig("key1", "spam");
+    EXPECT_EQ(-1, config->GetDoubleWithDefault("key1", -1));
+  }
+  {
+    optional<Configuration> config = simpleConfig("key2", "");
+    EXPECT_EQ(-1, config->GetDoubleWithDefault("key1", -1));
+  }
+  { /* Out of range */
+    optional<Configuration> config = simpleConfig("key2", "1e9999");
+    EXPECT_EQ(-1, config->GetDoubleWithDefault("key1", -1));
+  }
+}
+
+TEST(ConfigurationTest, TestBoolConversions) {
+  /* No defaults */
+  {
+    std::stringstream stream;
+    simpleConfigStream(stream, "key1", "true");
+    optional<Configuration> config = Configuration::Load(stream.str());
+    EXPECT_TRUE(config && "Parse single value");
+    optional<bool> value = config->GetBool("key1");
+    EXPECT_TRUE((bool)value);
+    EXPECT_EQ(true, *value);
+    EXPECT_FALSE(config->GetBool("key2"));
+  }
+
+  {
+    optional<Configuration> config = simpleConfig("key1", "true");
+    EXPECT_EQ(true, config->GetBoolWithDefault("key1", false));
+  }
+  {
+    optional<Configuration> config = simpleConfig("key1", "tRuE");
+    EXPECT_EQ(true, config->GetBoolWithDefault("key1", false));
+  }
+  {
+    optional<Configuration> config = simpleConfig("key1", "false");
+    EXPECT_FALSE(config->GetBoolWithDefault("key1", true));
+  }
+  {
+    optional<Configuration> config = simpleConfig("key1", "FaLsE");
+    EXPECT_FALSE(config->GetBoolWithDefault("key1", true));
+  }
+  {
+    optional<Configuration> config = simpleConfig("key1", " FaLsE ");
+    EXPECT_FALSE(config->GetBoolWithDefault("key1", true));
+  }
+  {
+    optional<Configuration> config = simpleConfig("key1", "");
+    EXPECT_EQ(true, config->GetBoolWithDefault("key1", true));
+  }
+  {
+    optional<Configuration> config = simpleConfig("key1", "spam");
+    EXPECT_EQ(true, config->GetBoolWithDefault("key1", true));
+  }
+  {
+    optional<Configuration> config = simpleConfig("key1", "");
+    EXPECT_EQ(true, config->GetBoolWithDefault("key2", true));
+  }
+}
+
+int main(int argc, char *argv[]) {
+  /*
+   *  The following line must be executed to initialize Google Mock
+   * (and Google Test) before running the tests.
+   */
+  ::testing::InitGoogleMock(&argc, argv);
+  return RUN_ALL_TESTS();
+}
+}

http://git-wip-us.apache.org/repos/asf/hadoop/blob/8c6a84fd/hadoop-hdfs-project/hadoop-hdfs-native-client/src/main/native/libhdfspp/tests/configuration_test.h
----------------------------------------------------------------------
diff --git 
a/hadoop-hdfs-project/hadoop-hdfs-native-client/src/main/native/libhdfspp/tests/configuration_test.h
 
b/hadoop-hdfs-project/hadoop-hdfs-native-client/src/main/native/libhdfspp/tests/configuration_test.h
new file mode 100644
index 0000000..ec04ca4
--- /dev/null
+++ 
b/hadoop-hdfs-project/hadoop-hdfs-native-client/src/main/native/libhdfspp/tests/configuration_test.h
@@ -0,0 +1,73 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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.
+ */
+#ifndef TESTS_CONFIGURATION_H_
+#define TESTS_CONFIGURATION_H_
+
+#include "common/configuration.h"
+#include <cstdio>
+#include <fstream>
+#include <istream>
+#include <gmock/gmock.h>
+
+namespace hdfs {
+
+template <typename T, typename U>
+void simpleConfigStreamProperty(std::stringstream& out, T key, U value) {
+  out << "<property>"
+      << "<name>" << key << "</name>"
+      << "<value>" << value << "</value>"
+      << "</property>";
+}
+
+template <typename T, typename U, typename... Args>
+void simpleConfigStreamProperty(std::stringstream& out, T key, U value,
+                                Args... args) {
+  simpleConfigStreamProperty(out, key, value);
+  simpleConfigStreamProperty(out, args...);
+}
+
+template <typename... Args>
+void simpleConfigStream(std::stringstream& out, Args... args) {
+  out << "<configuration>";
+  simpleConfigStreamProperty(out, args...);
+  out << "</configuration>";
+}
+
+template <typename... Args>
+optional<Configuration> simpleConfig(Args... args) {
+  Configuration result;
+  std::stringstream stream;
+  simpleConfigStream(stream, args...);
+  optional<Configuration> parse = result.Load(stream.str());
+  EXPECT_TRUE((bool)parse);
+
+  return parse;
+}
+
+template <typename... Args>
+void writeSimpleConfig(const std::string& filename, Args... args) {
+  std::stringstream stream;
+  simpleConfigStream(stream, args...);
+
+  std::ofstream out;
+  out.open(filename);
+  out << stream.rdbuf();
+}
+}
+
+#endif

Reply via email to