common: handle negative unsigneds in config

refs #4489

Change-Id: Ibeab0aad9ac95bef1ef2f17178b795cde4a79fea
diff --git a/daemon/common/config-file.hpp b/daemon/common/config-file.hpp
index 20e778b..09ac1a2 100644
--- a/daemon/common/config-file.hpp
+++ b/daemon/common/config-file.hpp
@@ -1,6 +1,6 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
 /*
- * Copyright (c) 2014-2019,  Regents of the University of California,
+ * Copyright (c) 2014-2020,  Regents of the University of California,
  *                           Arizona Board of Regents,
  *                           Colorado State University,
  *                           University Pierre & Marie Curie, Sorbonne University,
@@ -108,7 +108,9 @@
     static_assert(std::is_arithmetic<T>::value, "T must be an arithmetic type");
 
     boost::optional<T> value = node.get_value_optional<T>();
-    if (value) {
+    // Unsigned logic is workaround for https://redmine.named-data.net/issues/4489
+    if (value &&
+        (std::is_signed<T>() || node.get_value<std::string>().find("-") == std::string::npos)) {
       return *value;
     }
     NDN_THROW(Error("Invalid value '" + node.get_value<std::string>() +
diff --git a/tests/daemon/common/config-file.t.cpp b/tests/daemon/common/config-file.t.cpp
index 8eae4fb..2a2cff4 100644
--- a/tests/daemon/common/config-file.t.cpp
+++ b/tests/daemon/common/config-file.t.cpp
@@ -1,6 +1,6 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
 /*
- * Copyright (c) 2014-2019,  Regents of the University of California,
+ * Copyright (c) 2014-2020,  Regents of the University of California,
  *                           Arizona Board of Regents,
  *                           Colorado State University,
  *                           University Pierre & Marie Curie, Sorbonne University,
@@ -325,6 +325,56 @@
   BOOST_CHECK(subB.allCallbacksFired());
 }
 
+BOOST_AUTO_TEST_CASE(ParseNumber)
+{
+  std::istringstream input(R"CONFIG(
+    a -125
+    b 156
+    c 100000
+    d efg
+    e 123.456e-10
+    f 123abc
+    g ""
+    h " -1"
+  )CONFIG");
+  ConfigSection section;
+  boost::property_tree::read_info(input, section);
+
+  // Read various types and ensure they return the correct value
+  BOOST_CHECK_EQUAL(ConfigFile::parseNumber<short>(section.get_child("a"), "a", "section"), -125);
+  BOOST_CHECK_EQUAL(ConfigFile::parseNumber<int>(section.get_child("a"), "a", "section"), -125);
+  BOOST_CHECK_EQUAL(ConfigFile::parseNumber<long>(section.get_child("a"), "a", "section"), -125);
+  BOOST_CHECK_EQUAL(ConfigFile::parseNumber<double>(section.get_child("a"), "a", "section"), -125.0);
+  BOOST_CHECK_EQUAL(ConfigFile::parseNumber<float>(section.get_child("a"), "a", "section"), -125.0);
+  BOOST_CHECK_EQUAL(ConfigFile::parseNumber<short>(section.get_child("b"), "b", "section"), 156);
+  BOOST_CHECK_EQUAL(ConfigFile::parseNumber<int>(section.get_child("b"), "b", "section"), 156);
+  BOOST_CHECK_EQUAL(ConfigFile::parseNumber<long>(section.get_child("b"), "b", "section"), 156);
+  BOOST_CHECK_CLOSE(ConfigFile::parseNumber<double>(section.get_child("e"), "e", "section"), 123.456e-10, 0.0001);
+  BOOST_CHECK_CLOSE(ConfigFile::parseNumber<float>(section.get_child("e"), "e", "section"), 123.456e-10, 0.0001);
+  BOOST_CHECK_EQUAL(ConfigFile::parseNumber<int>(section.get_child("h"), "h", "section"), -1);
+
+  // Check throw on out of range
+  BOOST_CHECK_THROW(ConfigFile::parseNumber<int16_t>(section.get_child("c"), "c", "section"),
+                    ConfigFile::Error);
+
+  // Check throw on non-integer for integer types
+  BOOST_CHECK_THROW(ConfigFile::parseNumber<int>(section.get_child("d"), "d", "section"),
+                    ConfigFile::Error);
+  BOOST_CHECK_THROW(ConfigFile::parseNumber<int>(section.get_child("e"), "e", "section"),
+                    ConfigFile::Error);
+  BOOST_CHECK_THROW(ConfigFile::parseNumber<int>(section.get_child("f"), "f", "section"),
+                    ConfigFile::Error);
+  BOOST_CHECK_THROW(ConfigFile::parseNumber<int>(section.get_child("g"), "g", "section"),
+                    ConfigFile::Error);
+
+  // Should throw exception if try to read unsigned from negative value
+  BOOST_CHECK_THROW(ConfigFile::parseNumber<uint16_t>(section.get_child("a"), "a", "section"),
+                    ConfigFile::Error);
+  BOOST_CHECK_EQUAL(ConfigFile::parseNumber<uint16_t>(section.get_child("b"), "b", "section"), 156);
+  BOOST_CHECK_THROW(ConfigFile::parseNumber<uint32_t>(section.get_child("h"), "h", "section"),
+                    ConfigFile::Error);
+}
+
 BOOST_AUTO_TEST_SUITE_END() // TestConfigFile
 
 } // namespace tests
diff --git a/tests/daemon/face/ethernet-factory.t.cpp b/tests/daemon/face/ethernet-factory.t.cpp
index e204cee..605d3e2 100644
--- a/tests/daemon/face/ethernet-factory.t.cpp
+++ b/tests/daemon/face/ethernet-factory.t.cpp
@@ -1,6 +1,6 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
 /*
- * Copyright (c) 2014-2019,  Regents of the University of California,
+ * Copyright (c) 2014-2020,  Regents of the University of California,
  *                           Arizona Board of Regents,
  *                           Colorado State University,
  *                           University Pierre & Marie Curie, Sorbonne University,
@@ -334,7 +334,6 @@
   BOOST_CHECK_THROW(parseConfig(CONFIG, false), ConfigFile::Error);
 }
 
-BOOST_AUTO_TEST_CASE_EXPECTED_FAILURES(BadIdleTimeout, 2) // Bug #4489
 BOOST_AUTO_TEST_CASE(BadIdleTimeout)
 {
   // not a number
diff --git a/tests/daemon/face/tcp-factory.t.cpp b/tests/daemon/face/tcp-factory.t.cpp
index 3c00134..95e6c2b 100644
--- a/tests/daemon/face/tcp-factory.t.cpp
+++ b/tests/daemon/face/tcp-factory.t.cpp
@@ -1,6 +1,6 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
 /*
- * Copyright (c) 2014-2019,  Regents of the University of California,
+ * Copyright (c) 2014-2020,  Regents of the University of California,
  *                           Arizona Board of Regents,
  *                           Colorado State University,
  *                           University Pierre & Marie Curie, Sorbonne University,
@@ -267,7 +267,6 @@
   BOOST_CHECK_THROW(parseConfig(CONFIG, false), ConfigFile::Error);
 }
 
-BOOST_AUTO_TEST_CASE_EXPECTED_FAILURES(BadPort, 2) // Bug #4489
 BOOST_AUTO_TEST_CASE(BadPort)
 {
   // not a number
diff --git a/tests/daemon/face/udp-factory.t.cpp b/tests/daemon/face/udp-factory.t.cpp
index 0c10276..c09fe62 100644
--- a/tests/daemon/face/udp-factory.t.cpp
+++ b/tests/daemon/face/udp-factory.t.cpp
@@ -1,6 +1,6 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
 /*
- * Copyright (c) 2014-2019,  Regents of the University of California,
+ * Copyright (c) 2014-2020,  Regents of the University of California,
  *                           Arizona Board of Regents,
  *                           Colorado State University,
  *                           University Pierre & Marie Curie, Sorbonne University,
@@ -579,7 +579,6 @@
   BOOST_CHECK_THROW(parseConfig(CONFIG, false), ConfigFile::Error);
 }
 
-BOOST_AUTO_TEST_CASE_EXPECTED_FAILURES(BadPort, 2) // Bug #4489
 BOOST_AUTO_TEST_CASE(BadPort)
 {
   // not a number
@@ -625,7 +624,6 @@
   BOOST_CHECK_THROW(parseConfig(CONFIG3, false), ConfigFile::Error);
 }
 
-BOOST_AUTO_TEST_CASE_EXPECTED_FAILURES(BadIdleTimeout, 2) // Bug #4489
 BOOST_AUTO_TEST_CASE(BadIdleTimeout)
 {
   // not a number
diff --git a/tests/daemon/face/websocket-factory.t.cpp b/tests/daemon/face/websocket-factory.t.cpp
index 1234737..b684cee 100644
--- a/tests/daemon/face/websocket-factory.t.cpp
+++ b/tests/daemon/face/websocket-factory.t.cpp
@@ -1,6 +1,6 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
 /*
- * Copyright (c) 2014-2018,  Regents of the University of California,
+ * Copyright (c) 2014-2020,  Regents of the University of California,
  *                           Arizona Board of Regents,
  *                           Colorado State University,
  *                           University Pierre & Marie Curie, Sorbonne University,
@@ -203,7 +203,6 @@
   BOOST_CHECK_THROW(parseConfig(CONFIG, false), ConfigFile::Error);
 }
 
-BOOST_AUTO_TEST_CASE_EXPECTED_FAILURES(BadPort, 2) // Bug #4489
 BOOST_AUTO_TEST_CASE(BadPort)
 {
   // not a number