Browse Source

Adding json writing checks to test cased for string, int, double, bool and tuple.
Fixing a bug where strings were not escaped when writing them out.
Fixing a bug where doubles would be written in scientific notation, causing lossy behavior. Note that with numbers that cannot be precisely rendered in binary, loss of precision can still occur.

Samuel Jaffe 8 years ago
parent
commit
6547fa3510

+ 2 - 2
json/json_direct_scalar_binder.hpp

@@ -62,7 +62,7 @@ namespace json { namespace binder {
     }
     
     virtual void write(T const& val, std::ostream & data) const override {
-      data << val.*ptr;
+      data << std::fixed << val.*ptr;
     }
   private:
     N T::*ptr;
@@ -79,7 +79,7 @@ namespace json { namespace binder {
     }
     
     virtual void write(T const& val, std::ostream & data) const override {
-      data << "\"" << val.*ptr << "\"";
+      data << "\"" << json::helper::replace_all(val.*ptr, "\"", "\\\"") << "\"";
     }
   private:
     std::string T::*ptr;

+ 3 - 3
json_binder.hpp

@@ -72,9 +72,9 @@ namespace json {
       binder_impl<T>& b;
     };
     
-    template <typename T>
-    visitor<T> bind(T& object, binder_impl<T>& b) {
-      return visitor<T>{object, b};
+    template <typename T, typename S>
+    visitor<T, S> bind(S& object, binder_impl<T>& b) {
+      return visitor<T, S>{object, b};
     }
   }
   

+ 19 - 1
json_binder_test_bool.t.h

@@ -38,4 +38,22 @@ public:
     TS_ASSERT_THROWS(parse(bind(out, binder), data, allow_all),
                      json::malformed_json_exception);
   }
-};
+  
+  void test_write_bool_true() {
+    std::string expected = "true";
+    std::stringstream ss;
+    bool const in = true;
+    value_binder<bool> binder{};
+    write(bind(in, binder), ss);
+    TS_ASSERT_EQUALS(ss.str(), expected);
+  }
+  
+  void test_write_bool_false() {
+    std::string expected = "false";
+    std::stringstream ss;
+    bool const in = false;
+    value_binder<bool> binder{};
+    write(bind(in, binder), ss);
+    TS_ASSERT_EQUALS(ss.str(), expected);
+  }
+};

+ 32 - 0
json_binder_tuple.t.h

@@ -13,6 +13,11 @@
 using namespace json::binder;
 using namespace json::parser;
 
+struct point { int x; int y; };
+bool operator==(point const & lhs, point const & rhs) {
+  return lhs.x == rhs.x && lhs.y == rhs.y;
+}
+
 class json_binder_tuple_TestSuite : public CxxTest::TestSuite {
 public:
   void test_bind_to_tuple() {
@@ -68,4 +73,31 @@ public:
     TS_ASSERT_THROWS_NOTHING(parse(json::binder::bind(out, binder), data, allow_all));
     TS_ASSERT_EQUALS(out, std::make_tuple(1, 0.5, "hello"));
   }
+  
+  void test_write_tuple() {
+    std::string expected = "[1,\"word\"]";
+    std::stringstream ss;
+    using tuple = std::tuple<int, std::string>;
+    tuple const in = std::make_tuple(1, "word");
+    auto binder = make_default_tuple_binder<int, std::string>();
+    write(json::binder::bind(in, binder), ss);
+    TS_ASSERT_EQUALS(ss.str(), expected);
+  }
+  
+  void test_parse_struct_tuple_binding() {
+    char data[] = "[ 10, 5 ]";
+    point out{0, 0};
+    auto binder = tuple_binder<point>()(&point::x)(&point::y);
+    TS_ASSERT_THROWS_NOTHING(parse(json::binder::bind(out, binder), data, allow_all));
+    TS_ASSERT_EQUALS(out, (point{10, 5}));
+  }
+  
+  void test_write_struct_tuple_binding() {
+    std::string expected = "[10,5]";
+    std::stringstream ss;
+    point const in{10, 5};
+    auto binder = tuple_binder<point>()(&point::x)(&point::y);
+    TS_ASSERT_THROWS_NOTHING(write(json::binder::bind(in, binder), ss));
+    TS_ASSERT_EQUALS(ss.str(), expected);
+  }
 };

+ 55 - 1
json_binder_value_double.t.h

@@ -103,4 +103,58 @@ public:
     TS_ASSERT_THROWS(parse(bind(out, binder), data, allow_all),
                      json::json_numeric_width_exception);
   }
-};
+  
+  void test_double_number_written_is_equal_exact_binary() {
+    std::stringstream ss;
+    double in = 500., out = 0;
+    value_binder<double> binder{};
+    write(bind(in, binder), ss);
+    parse(bind(out, binder), ss.str().c_str(), allow_all);
+    TS_ASSERT_EQUALS(out, in);
+  }
+  
+  void test_double_number_written_is_equal_inexact_binary() {
+    std::stringstream ss;
+    double in = 0.3, out = 0;
+    value_binder<double> binder{};
+    write(bind(in, binder), ss);
+    parse(bind(out, binder), ss.str().c_str(), allow_all);
+    TS_ASSERT_EQUALS(out, in);
+  }
+  
+  void test_double_number_written_is_equal_at_high_absolute_value() {
+    std::stringstream ss;
+    double in = 1.34e300, out = 0.0;
+    value_binder<double> binder{};
+    write(bind(in, binder), ss);
+    parse(bind(out, binder), ss.str().c_str(), allow_all);
+    TS_ASSERT_EQUALS(out, in);
+  }
+
+  void test_double_number_written_is_equal_at_low_absolute_value() {
+    std::stringstream ss;
+    double in = 1.34e-300, out = 0.0;
+    value_binder<double> binder{};
+    write(bind(in, binder), ss);
+    parse(bind(out, binder), ss.str().c_str(), allow_all);
+    TS_ASSERT_DELTA(out, in, 1e-299);
+  }
+  
+  void test_double_number_written_is_equal_at_high_absolute_value_nonscientific() {
+    std::stringstream ss;
+    double in = 52450012.25, out = 0.0;
+    value_binder<double> binder{};
+    write(bind(in, binder), ss);
+    parse(bind(out, binder), ss.str().c_str(), allow_all);
+    TS_ASSERT_EQUALS(out, in);
+  }
+
+  void test_double_number_written_is_equal_at_low_absolute_value_nonscientific() {
+    std::stringstream ss;
+    double in = 0.0002517867, out = 0.0;
+    value_binder<double> binder{};
+    write(bind(in, binder), ss);
+    parse(bind(out, binder), ss.str().c_str(), allow_all);
+    TS_ASSERT_DELTA(out, in, 0.000001);
+  }
+};

+ 26 - 10
json_binder_value_int.t.h

@@ -23,15 +23,6 @@ public:
     TS_ASSERT_EQUALS(out, 100);
   }
   
-  void test_output_from_integer_type() {
-    std::string expected = "100";
-    std::stringstream ss;
-    int in = 100;
-    value_binder<int> binder{};
-    write(bind(in, binder), ss);
-    TS_ASSERT_EQUALS(ss.str(), expected);
-  }
-  
   void test_negative_into_unsigned_throws() {
     char data[] = "-1";
     unsigned int out = 0;
@@ -114,7 +105,6 @@ public:
                      json::json_numeric_width_exception);
   }
 
-
   void test_double_to_int_throws() {
     char data[] = "2.0";
     int out = 0;
@@ -156,4 +146,30 @@ public:
                      json::json_numeric_exception);
   }
 
+  void test_output_from_integer_type() {
+    std::string expected = "100";
+    std::stringstream ss;
+    int const in = 100;
+    value_binder<int> binder{};
+    write(bind(in, binder), ss);
+    TS_ASSERT_EQUALS(ss.str(), expected);
+  }
+  
+  void test_unsigned_integer_output() {
+    std::string expected = "2147483648";
+    std::stringstream ss;
+    unsigned int const in = 2147483648;
+    value_binder<unsigned int> binder{};
+    write(bind(in, binder), ss);
+    TS_ASSERT_EQUALS(ss.str(), expected);
+  }
+  
+  void test_signed_integer_output() {
+    std::string expected = "-2147483648";
+    std::stringstream ss;
+    int const in = -2147483648;
+    value_binder<int> binder{};
+    write(bind(in, binder), ss);
+    TS_ASSERT_EQUALS(ss.str(), expected);
+  }
 };

+ 19 - 1
json_binder_value_string.t.h

@@ -47,5 +47,23 @@ public:
     TS_ASSERT_THROWS(parse(json::binder::bind(out, binder), data, allow_all),
                      json::unterminated_json_exception);
     TS_ASSERT_EQUALS(out, "");
-  }  
+  }
+  
+  void test_write_string() {
+    std::string expected = "\"This is a string\"";
+    std::stringstream ss;
+    std::string const in = "This is a string";
+    value_binder<std::string> binder{};
+    write(json::binder::bind(in, binder), ss);
+    TS_ASSERT_EQUALS(ss.str(), expected);
+  }
+  
+  void test_write_string_with_quotes() {
+    std::string expected = "\"This is a \\\"string\\\"\"";
+    std::stringstream ss;
+    std::string const in = "This is a \"string\"";
+    value_binder<std::string> binder{};
+    write(json::binder::bind(in, binder), ss);
+    TS_ASSERT_EQUALS(ss.str(), expected);
+  }
 };

+ 1 - 1
json_common.cpp

@@ -120,7 +120,7 @@ namespace json {
       return i;
     }
     
-    std::string replace_all(std::string && str, std::string const & from, std::string const & to) {
+    std::string replace_all(std::string str, std::string const & from, std::string const & to) {
       std::string::size_type start_pos = 0;
       while((start_pos = str.find(from, start_pos)) != std::string::npos) {
         str.replace(start_pos, from.length(), to);

+ 1 - 0
json_common.hpp

@@ -100,6 +100,7 @@ namespace json { namespace helper {
    * @throws json::malformed_json_exception
    */
   std::string parse_string(char const * & data);
+  std::string replace_all(std::string str, std::string const & from, std::string const & to);
   
   template <typename T>
   void parse_string(T& json, char const * & data) {