瀏覽代碼

Adding direct-to-value binder scheme, primarily for testing purposes.
Adding test cases for integers (incl a shortening test for short) and strings.
Adding first few test cases for doubles

Samuel Jaffe 8 年之前
父節點
當前提交
522100365c

+ 60 - 13
json.xcodeproj/project.pbxproj

@@ -7,7 +7,6 @@
 	objects = {
 
 /* Begin PBXBuildFile section */
-		CD217D911CCAD587007C50C6 /* json_test.cpp in Sources */ = {isa = PBXBuildFile; fileRef = CD217D8F1CCAD587007C50C6 /* json_test.cpp */; };
 		CD3C80C61D6A711000ACC795 /* json_binder_discard.cpp in Sources */ = {isa = PBXBuildFile; fileRef = CD17473E1D4C1DFD000C344B /* json_binder_discard.cpp */; };
 		CD3C80C71D6A711000ACC795 /* json_common.cpp in Sources */ = {isa = PBXBuildFile; fileRef = CD472C751CCC1ABD0084C8D6 /* json_common.cpp */; };
 		CD3C80CA1D6A711000ACC795 /* json.hpp in Headers */ = {isa = PBXBuildFile; fileRef = CDB2F7421C5D48090067C2EC /* json.hpp */; };
@@ -24,6 +23,7 @@
 		CD3C80E51D6A731D00ACC795 /* libjson.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = CDB2F7331C5D47F70067C2EC /* libjson.dylib */; };
 		CD472C761CCC1ABD0084C8D6 /* json_common.cpp in Sources */ = {isa = PBXBuildFile; fileRef = CD472C751CCC1ABD0084C8D6 /* json_common.cpp */; };
 		CD472C801CCDA4B00084C8D6 /* json_parser.cpp in Sources */ = {isa = PBXBuildFile; fileRef = CD472C7F1CCDA4B00084C8D6 /* json_parser.cpp */; };
+		CD679D7A1E6126CA00F9F843 /* json_tc.cpp in Sources */ = {isa = PBXBuildFile; fileRef = CD679D781E6126C700F9F843 /* json_tc.cpp */; };
 		CDB2F7431C5D48090067C2EC /* json.cpp in Sources */ = {isa = PBXBuildFile; fileRef = CDB2F7411C5D48090067C2EC /* json.cpp */; };
 		CDB2F7441C5D48090067C2EC /* json.hpp in Headers */ = {isa = PBXBuildFile; fileRef = CDB2F7421C5D48090067C2EC /* json.hpp */; };
 /* End PBXBuildFile section */
@@ -55,12 +55,16 @@
 		CD472C7D1CCC1E120084C8D6 /* json_direct_scalar_binder.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = json_direct_scalar_binder.hpp; sourceTree = "<group>"; };
 		CD472C7E1CCC498C0084C8D6 /* json_direct_binder.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = json_direct_binder.hpp; sourceTree = "<group>"; };
 		CD472C7F1CCDA4B00084C8D6 /* json_parser.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = json_parser.cpp; sourceTree = "<group>"; };
+		CD679D761E61267300F9F843 /* json_binder_value_int.t.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = json_binder_value_int.t.h; sourceTree = "<group>"; };
+		CD679D781E6126C700F9F843 /* json_tc.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = json_tc.cpp; sourceTree = "<group>"; };
+		CD679D7B1E61E26000F9F843 /* json_binder_value_string.t.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = json_binder_value_string.t.h; sourceTree = "<group>"; };
+		CD679D7C1E6273DB00F9F843 /* json_binder_value_double.t.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = json_binder_value_double.t.h; sourceTree = "<group>"; };
 		CDB2F7331C5D47F70067C2EC /* libjson.dylib */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.dylib"; includeInIndex = 0; path = libjson.dylib; sourceTree = BUILT_PRODUCTS_DIR; };
 		CDB2F7411C5D48090067C2EC /* json.cpp */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 2; lastKnownFileType = sourcecode.cpp.cpp; path = json.cpp; sourceTree = "<group>"; tabWidth = 2; };
 		CDB2F7421C5D48090067C2EC /* json.hpp */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 2; lastKnownFileType = sourcecode.cpp.h; path = json.hpp; sourceTree = "<group>"; tabWidth = 2; };
 		CDB2F7451C5E9BEB0067C2EC /* json_binder_parser.hpp */ = {isa = PBXFileReference; indentWidth = 2; lastKnownFileType = sourcecode.cpp.h; path = json_binder_parser.hpp; sourceTree = "<group>"; tabWidth = 2; };
 		CDB2F7461C5EA2E80067C2EC /* json_binder.hpp */ = {isa = PBXFileReference; indentWidth = 2; lastKnownFileType = sourcecode.cpp.h; path = json_binder.hpp; sourceTree = SOURCE_ROOT; tabWidth = 2; };
-		CDF643321C6E9A8B0016A475 /* json-test */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = "json-test"; sourceTree = BUILT_PRODUCTS_DIR; };
+		CDF643321C6E9A8B0016A475 /* json_tc */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = json_tc; sourceTree = BUILT_PRODUCTS_DIR; };
 /* End PBXFileReference section */
 
 /* Begin PBXFrameworksBuildPhase section */
@@ -126,15 +130,35 @@
 			name = json;
 			sourceTree = "<group>";
 		};
-		CDB2F72A1C5D47F70067C2EC = {
+		CD679D741E61265F00F9F843 /* src */ = {
 			isa = PBXGroup;
 			children = (
-				CD1747431D4C216B000C344B /* example.json */,
-				CD217D8F1CCAD587007C50C6 /* json_test.cpp */,
 				CD217D921CCAD885007C50C6 /* json_common.hpp */,
 				CD472C751CCC1ABD0084C8D6 /* json_common.cpp */,
 				CD472C831CCDA5990084C8D6 /* json */,
 				CD472C821CCDA5830084C8D6 /* binder */,
+			);
+			name = src;
+			sourceTree = "<group>";
+		};
+		CD679D751E61266300F9F843 /* test */ = {
+			isa = PBXGroup;
+			children = (
+				CD679D761E61267300F9F843 /* json_binder_value_int.t.h */,
+				CD679D7B1E61E26000F9F843 /* json_binder_value_string.t.h */,
+				CD679D7C1E6273DB00F9F843 /* json_binder_value_double.t.h */,
+				CD679D781E6126C700F9F843 /* json_tc.cpp */,
+			);
+			name = test;
+			sourceTree = "<group>";
+		};
+		CDB2F72A1C5D47F70067C2EC = {
+			isa = PBXGroup;
+			children = (
+				CD1747431D4C216B000C344B /* example.json */,
+				CD217D8F1CCAD587007C50C6 /* json_test.cpp */,
+				CD679D741E61265F00F9F843 /* src */,
+				CD679D751E61266300F9F843 /* test */,
 				CDB2F7341C5D47F70067C2EC /* Products */,
 			);
 			sourceTree = "<group>";
@@ -143,7 +167,7 @@
 			isa = PBXGroup;
 			children = (
 				CDB2F7331C5D47F70067C2EC /* libjson.dylib */,
-				CDF643321C6E9A8B0016A475 /* json-test */,
+				CDF643321C6E9A8B0016A475 /* json_tc */,
 				CD3C80CF1D6A711000ACC795 /* libjson-direct.dylib */,
 			);
 			name = Products;
@@ -214,10 +238,11 @@
 			productReference = CDB2F7331C5D47F70067C2EC /* libjson.dylib */;
 			productType = "com.apple.product-type.library.dynamic";
 		};
-		CDF643311C6E9A8B0016A475 /* json-test */ = {
+		CDF643311C6E9A8B0016A475 /* json_tc */ = {
 			isa = PBXNativeTarget;
-			buildConfigurationList = CDF643381C6E9A8B0016A475 /* Build configuration list for PBXNativeTarget "json-test" */;
+			buildConfigurationList = CDF643381C6E9A8B0016A475 /* Build configuration list for PBXNativeTarget "json_tc" */;
 			buildPhases = (
+				CD679D771E61269500F9F843 /* ShellScript */,
 				CDF6432E1C6E9A8B0016A475 /* Sources */,
 				CDF6432F1C6E9A8B0016A475 /* Frameworks */,
 				CDF643301C6E9A8B0016A475 /* CopyFiles */,
@@ -226,9 +251,9 @@
 			);
 			dependencies = (
 			);
-			name = "json-test";
+			name = json_tc;
 			productName = "json-test";
-			productReference = CDF643321C6E9A8B0016A475 /* json-test */;
+			productReference = CDF643321C6E9A8B0016A475 /* json_tc */;
 			productType = "com.apple.product-type.tool";
 		};
 /* End PBXNativeTarget section */
@@ -262,11 +287,31 @@
 			targets = (
 				CDB2F7321C5D47F70067C2EC /* json */,
 				CD3C80C21D6A711000ACC795 /* json-direct */,
-				CDF643311C6E9A8B0016A475 /* json-test */,
+				CDF643311C6E9A8B0016A475 /* json_tc */,
 			);
 		};
 /* End PBXProject section */
 
+/* Begin PBXShellScriptBuildPhase section */
+		CD679D771E61269500F9F843 /* ShellScript */ = {
+			isa = PBXShellScriptBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+			);
+			inputPaths = (
+				"$(SRCROOT)/json_binder_value_int.t.h",
+				"$(SRCROOT)/json_binder_value_string.t.h",
+				"$(SRCROOT)/json_binder_value_double.t.h",
+			);
+			outputPaths = (
+				"$(SRCDIR)/json_tc.cpp",
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+			shellPath = /bin/sh;
+			shellScript = "cxxtestgen --error-printer -o json_tc.cpp json_binder_value_int.t.h json_binder_value_string.t.h json_binder_value_double.t.h";
+		};
+/* End PBXShellScriptBuildPhase section */
+
 /* Begin PBXSourcesBuildPhase section */
 		CD3C80C31D6A711000ACC795 /* Sources */ = {
 			isa = PBXSourcesBuildPhase;
@@ -291,7 +336,7 @@
 			isa = PBXSourcesBuildPhase;
 			buildActionMask = 2147483647;
 			files = (
-				CD217D911CCAD587007C50C6 /* json_test.cpp in Sources */,
+				CD679D7A1E6126CA00F9F843 /* json_tc.cpp in Sources */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};
@@ -438,6 +483,7 @@
 			buildSettings = {
 				GCC_TREAT_WARNINGS_AS_ERRORS = YES;
 				GCC_WARN_PEDANTIC = YES;
+				HEADER_SEARCH_PATHS = /usr/local/include/;
 				PRODUCT_NAME = "$(TARGET_NAME)";
 				USER_HEADER_SEARCH_PATHS = "../**";
 			};
@@ -448,6 +494,7 @@
 			buildSettings = {
 				GCC_TREAT_WARNINGS_AS_ERRORS = YES;
 				GCC_WARN_PEDANTIC = YES;
+				HEADER_SEARCH_PATHS = /usr/local/include/;
 				PRODUCT_NAME = "$(TARGET_NAME)";
 				USER_HEADER_SEARCH_PATHS = "../**";
 			};
@@ -483,7 +530,7 @@
 			defaultConfigurationIsVisible = 0;
 			defaultConfigurationName = Release;
 		};
-		CDF643381C6E9A8B0016A475 /* Build configuration list for PBXNativeTarget "json-test" */ = {
+		CDF643381C6E9A8B0016A475 /* Build configuration list for PBXNativeTarget "json_tc" */ = {
 			isa = XCConfigurationList;
 			buildConfigurations = (
 				CDF643361C6E9A8B0016A475 /* Debug */,

+ 4 - 4
json.xcodeproj/xcuserdata/samjaffe.xcuserdatad/xcschemes/xcschememanagement.plist

@@ -9,15 +9,15 @@
 			<key>orderHint</key>
 			<integer>7</integer>
 		</dict>
-		<key>json-test.xcscheme</key>
+		<key>json.xcscheme</key>
 		<dict>
 			<key>orderHint</key>
-			<integer>4</integer>
+			<integer>3</integer>
 		</dict>
-		<key>json.xcscheme</key>
+		<key>json_tc.xcscheme</key>
 		<dict>
 			<key>orderHint</key>
-			<integer>3</integer>
+			<integer>4</integer>
 		</dict>
 	</dict>
 	<key>SuppressBuildableAutocreation</key>

+ 26 - 0
json/json_direct_binder.hpp

@@ -30,4 +30,30 @@ namespace json { namespace binder {
     E T::*ptr;
     binder<E> impl;
   };
+  
+  template <typename T>
+  class value_binder : public binder_impl<T> {
+  private:
+    struct detail {
+      T value;
+    };
+  public:
+    value_binder() : impl(&detail::value) {}
+    
+    virtual binder_impl<T>* clone() const override {
+      return new value_binder(*this);
+    }
+    
+    virtual void parse(T & val, char const *&data, parser::options opts) const override {
+      detail tmp;
+      impl.parse(tmp, data, opts);
+      val = std::move(tmp.value);
+    }
+    
+    virtual void write(T const& val, std::ostream & data) const override {
+      impl.write({val}, data);
+    }
+  private:
+    direct_binder<detail, T> impl;
+  };
 } }

+ 32 - 0
json_binder_value_double.t.h

@@ -0,0 +1,32 @@
+//
+//  json_binder_value_double.t.h
+//  json
+//
+//  Created by Sam Jaffe on 2/25/17.
+//
+
+#pragma once
+
+#include <cxxtest/TestSuite.h>
+#include "json_binder.hpp"
+
+using namespace json::binder;
+using namespace json::parser;
+
+class json_binder_value_float_TestSuite : public CxxTest::TestSuite {
+public:
+  void test_bind_to_double_type() {
+    char data[] = "0.5";
+    double out = 0.0;
+    value_binder<double> binder{};
+    parse(bind(out, binder), data, allow_all);
+    TS_ASSERT_EQUALS(out, 0.5);
+  }
+  
+  void test_parse_out_scientific_notation() {
+    char data[] = "2e4";
+    double out = 0.0;
+    value_binder<double> binder{};
+    TS_ASSERT_THROWS_NOTHING(parse(bind(out, binder), data, allow_all));
+  }
+};

+ 118 - 0
json_binder_value_int.t.h

@@ -0,0 +1,118 @@
+//
+//  json_binder_value_int.t.h
+//  json
+//
+//  Created by Sam Jaffe on 2/24/17.
+//
+
+#pragma once
+
+#include <cxxtest/TestSuite.h>
+#include "json_binder.hpp"
+
+using namespace json::binder;
+using namespace json::parser;
+
+class json_binder_value_int_TestSuite : public CxxTest::TestSuite {
+public:
+  void test_bind_to_integer_type() {
+    char data[] = "100";
+    int out = 0;
+    value_binder<int> binder{};
+    parse(bind(out, binder), data, allow_all);
+    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_empty_buffer_to_int_throws() {
+    char data[] = "";
+    int out = 0;
+    value_binder<int> binder{};
+    TS_ASSERT_THROWS(parse(bind(out, binder), data, allow_all),
+                     json::unterminated_json_exception);
+    TS_ASSERT_EQUALS(out, 0);
+  }
+  
+  void test_non_integral_to_int_throws() {
+    char data[] = "one";
+    int out = 0;
+    value_binder<int> binder{};
+    TS_ASSERT_THROWS(parse(bind(out, binder), data, allow_all),
+                     json::json_numeric_exception);
+  }
+  
+  void test_oversized_int_throws() {
+    char data[] = "1000000000000";
+    int out = 0;
+    value_binder<int> binder{};
+    TS_ASSERT_THROWS(parse(bind(out, binder), data, allow_all),
+                     json::json_numeric_exception);
+    TS_ASSERT_EQUALS(out, 0);
+  }
+  
+  void test_unsigned_int_downcast_to_int_throws() {
+    char data[] = "2147483648";
+    int out = 0;
+    value_binder<int> binder{};
+    TS_ASSERT_THROWS(parse(bind(out, binder), data, allow_all),
+                     json::json_numeric_width_exception);
+    TS_ASSERT_EQUALS(out, 0);
+  }
+
+  void test_signed_int_max_parses() {
+    char data[] = "2147483647";
+    int out = 0;
+    value_binder<int> binder{};
+    TS_ASSERT_THROWS_NOTHING(parse(bind(out, binder), data, allow_all));
+  }
+
+  void test_signed_int_min_parses() {
+    char data[] = "-2147483648";
+    int out = 0;
+    value_binder<int> binder{};
+    TS_ASSERT_THROWS_NOTHING(parse(bind(out, binder), data, allow_all));
+  }
+  
+  void test_parsing_short_will_error_over_narrowing() {
+    char data[] = "100000";
+    short out = 0;
+    value_binder<short> binder{};
+    TS_ASSERT_THROWS(parse(bind(out, binder), data, allow_all),
+                     json::json_numeric_width_exception);
+    TS_ASSERT_EQUALS(out, 0);
+  }
+
+  void test_double_to_int_throws() {
+    char data[] = "2.0";
+    int out = 0;
+    value_binder<int> binder{};
+    TS_ASSERT_THROWS(parse(bind(out, binder), data, allow_all),
+                     json::json_numeric_exception);
+    TS_ASSERT_EQUALS(out, 0);
+  }
+  
+  void test_whitespace_breaks_parsing_numeric_token() {
+    char data[] = "10 0";
+    int out = 0;
+    value_binder<int> binder{};
+    TS_ASSERT_THROWS_NOTHING(parse(bind(out, binder), data, allow_all));
+    TS_ASSERT_EQUALS(out, 10);
+  }
+  
+  void test_unterminated_input_causes_exception_when_flagged() {
+    char data[] = "10 0";
+    int out = 0;
+    value_binder<int> binder{};
+    TS_ASSERT_THROWS(parse(bind(out, binder), data,
+                           disable_concatenated_json_bodies),
+                     json::malformed_json_exception);
+  }  
+};

+ 84 - 0
json_binder_value_string.t.h

@@ -0,0 +1,84 @@
+//
+//  json_binder_value_string.t.h
+//  json
+//
+//  Created by Sam Jaffe on 2/25/17.
+//
+#pragma once
+
+#include <cxxtest/TestSuite.h>
+
+#include "json_binder.hpp"
+
+using namespace json::binder;
+using namespace json::parser;
+
+class json_binder_value_string_TestSuite : public CxxTest::TestSuite {
+public:
+  void test_bind_to_string_type() {
+    char data[] = "\"This is a string\"";
+    std::string out = "";
+    value_binder<std::string> binder{};
+    parse(json::binder::bind(out, binder), data, allow_all);
+    TS_ASSERT_EQUALS(out, "This is a string");
+  }
+  
+  void test_bind_to_string_with_quote_recieves_whole_string() {
+    char data[] = "\"This is a \\\"string\\\"\"";
+    std::string out = "";
+    value_binder<std::string> binder{};
+    parse(json::binder::bind(out, binder), data, allow_all);
+    TS_ASSERT_EQUALS(out, "This is a \"string\"");
+  }
+  
+  void test_cannot_bind_raw_string() {
+    char data[] = "This is a string";
+    std::string out = "";
+    value_binder<std::string> binder{};
+    TS_ASSERT_THROWS(parse(json::binder::bind(out, binder), data, allow_all),
+                     json::malformed_json_exception);
+    TS_ASSERT_EQUALS(out, "");
+  }
+  
+  void test_throws_error_binding_unterminated_string() {
+    char data[] = "\"This is a string";
+    std::string out = "";
+    value_binder<std::string> binder{};
+    TS_ASSERT_THROWS(parse(json::binder::bind(out, binder), data, allow_all),
+                     json::unterminated_json_exception);
+    TS_ASSERT_EQUALS(out, "");
+  }
+  
+  void test_does_not_crash_if_terminates_early() {
+    char data[] = "\"This is a \"string";
+    std::string out = "";
+    value_binder<std::string> binder{};
+    TS_ASSERT_THROWS_NOTHING(parse(json::binder::bind(out, binder), data, allow_all));
+  }
+  
+  void test_will_crash_if_terminates_with_remaining_with_option() {
+    char data[] = "\"This is a \"string";
+    std::string out = "";
+    value_binder<std::string> binder{};
+    TS_ASSERT_THROWS(parse(json::binder::bind(out, binder), data,
+                           disable_concatenated_json_bodies),
+                     json::malformed_json_exception);
+  }
+  
+  void test_will_not_crash_if_terminates_with_remaining_only_whitespace() {
+    char data[] = "\"This is a \"    ";
+    std::string out = "";
+    value_binder<std::string> binder{};
+    TS_ASSERT_THROWS_NOTHING(parse(json::binder::bind(out, binder), data,
+                                   disable_concatenated_json_bodies));
+  }
+  
+  void test_will_crash_if_terminates_with_remaining_buffered_by_whitespace() {
+    char data[] = "\"This is a \"    string";
+    std::string out = "";
+    value_binder<std::string> binder{};
+    TS_ASSERT_THROWS(parse(json::binder::bind(out, binder), data,
+                           disable_concatenated_json_bodies),
+                     json::malformed_json_exception);
+  }
+};