Browse Source

Convert from using cxxtest to gtest

Sam Jaffe 7 years ago
parent
commit
8980a74ae7

+ 1 - 0
include/json/json_binder.hpp

@@ -16,6 +16,7 @@
 #include <string>
 #include <sstream>
 #include <vector>
+#include <list>
 
 namespace json {
   namespace binder {

+ 22 - 0
json-direct-test/Info.plist

@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+	<key>CFBundleDevelopmentRegion</key>
+	<string>$(DEVELOPMENT_LANGUAGE)</string>
+	<key>CFBundleExecutable</key>
+	<string>$(EXECUTABLE_NAME)</string>
+	<key>CFBundleIdentifier</key>
+	<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
+	<key>CFBundleInfoDictionaryVersion</key>
+	<string>6.0</string>
+	<key>CFBundleName</key>
+	<string>$(PRODUCT_NAME)</string>
+	<key>CFBundlePackageType</key>
+	<string>BNDL</string>
+	<key>CFBundleShortVersionString</key>
+	<string>1.0</string>
+	<key>CFBundleVersion</key>
+	<string>1</string>
+</dict>
+</plist>

+ 258 - 288
json.xcodeproj/project.pbxproj

@@ -12,51 +12,87 @@
 		CD3C80C71D6A711000ACC795 /* json_common.cpp in Sources */ = {isa = PBXBuildFile; fileRef = CD472C751CCC1ABD0084C8D6 /* json_common.cpp */; };
 		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 */; };
+		CD6DC44F219F8A360052CD08 /* libjson-direct.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = CD3C80CF1D6A711000ACC795 /* libjson-direct.dylib */; };
+		CD6DC458219F8A670052CD08 /* GoogleMock.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = CD6DC43F219F89E90052CD08 /* GoogleMock.framework */; };
+		CD6DC459219F8B210052CD08 /* json_binder_test_bool_test.cxx in Sources */ = {isa = PBXBuildFile; fileRef = CDECC7D51E6504C800BEE842 /* json_binder_test_bool_test.cxx */; };
+		CD6DC45A219FA1AC0052CD08 /* json_binder_value_int_test.cxx in Sources */ = {isa = PBXBuildFile; fileRef = CD679D761E61267300F9F843 /* json_binder_value_int_test.cxx */; };
+		CD6DC45B219FA20E0052CD08 /* json_binder_value_string_test.cxx in Sources */ = {isa = PBXBuildFile; fileRef = CD679D7B1E61E26000F9F843 /* json_binder_value_string_test.cxx */; };
+		CD6DC45C219FA2140052CD08 /* json_binder_value_double_test.cxx in Sources */ = {isa = PBXBuildFile; fileRef = CD679D7C1E6273DB00F9F843 /* json_binder_value_double_test.cxx */; };
+		CD6DC45D219FA2180052CD08 /* json_binder_tuple_test.cxx in Sources */ = {isa = PBXBuildFile; fileRef = CD2B098B1E63839A00D6D23A /* json_binder_tuple_test.cxx */; };
+		CD6DC45E219FA21A0052CD08 /* json_binder_object_test.cxx in Sources */ = {isa = PBXBuildFile; fileRef = CDECC7D61E65073E00BEE842 /* json_binder_object_test.cxx */; };
+		CD6DC45F219FA21E0052CD08 /* json_binder_collection_test.cxx in Sources */ = {isa = PBXBuildFile; fileRef = CDECC7D41E64E6A900BEE842 /* json_binder_collection_test.cxx */; };
+		CD6DC460219FA2200052CD08 /* json_binder_terminate_test.cxx in Sources */ = {isa = PBXBuildFile; fileRef = CD2B09881E6374F300D6D23A /* json_binder_terminate_test.cxx */; };
+		CD6DC461219FA2230052CD08 /* json_binder_custom_test.cxx in Sources */ = {isa = PBXBuildFile; fileRef = CD84C4EB1F68908F002014D3 /* json_binder_custom_test.cxx */; };
+		CD6DC462219FA2420052CD08 /* json_binder_polymorphic_test.cxx in Sources */ = {isa = PBXBuildFile; fileRef = CD31E1E9219CE85A001C2AF1 /* json_binder_polymorphic_test.cxx */; };
 		CDB2F7431C5D48090067C2EC /* json.cpp in Sources */ = {isa = PBXBuildFile; fileRef = CDB2F7411C5D48090067C2EC /* json.cpp */; };
-		CDE6CC0E1EF9E977005E745A /* json_binder_discard.cpp in Sources */ = {isa = PBXBuildFile; fileRef = CD17473E1D4C1DFD000C344B /* json_binder_discard.cpp */; };
-		CDE6CC0F1EF9E977005E745A /* json_common.cpp in Sources */ = {isa = PBXBuildFile; fileRef = CD472C751CCC1ABD0084C8D6 /* json_common.cpp */; };
-		CDE6CC221EF9E984005E745A /* json_parser.cpp in Sources */ = {isa = PBXBuildFile; fileRef = CD472C7F1CCDA4B00084C8D6 /* json_parser.cpp */; };
-		CDE6CC231EF9E984005E745A /* json.cpp in Sources */ = {isa = PBXBuildFile; fileRef = CDB2F7411C5D48090067C2EC /* json.cpp */; };
-		CDE6CC241EF9E984005E745A /* json_common.cpp in Sources */ = {isa = PBXBuildFile; fileRef = CD472C751CCC1ABD0084C8D6 /* json_common.cpp */; };
-		CDE6CC2C1EF9E994005E745A /* libjson_s.a in Frameworks */ = {isa = PBXBuildFile; fileRef = CDE6CC2B1EF9E984005E745A /* libjson_s.a */; };
-		CDE6CC2D1EF9E994005E745A /* libjson-direct_s.a in Frameworks */ = {isa = PBXBuildFile; fileRef = CDE6CC1F1EF9E977005E745A /* libjson-direct_s.a */; };
 /* End PBXBuildFile section */
 
-/* Begin PBXCopyFilesBuildPhase section */
-		CDF643301C6E9A8B0016A475 /* CopyFiles */ = {
-			isa = PBXCopyFilesBuildPhase;
-			buildActionMask = 2147483647;
-			dstPath = /usr/share/man/man1/;
-			dstSubfolderSpec = 0;
-			files = (
-			);
-			runOnlyForDeploymentPostprocessing = 1;
-		};
-/* End PBXCopyFilesBuildPhase section */
+/* Begin PBXContainerItemProxy section */
+		CD6DC43E219F89E90052CD08 /* PBXContainerItemProxy */ = {
+			isa = PBXContainerItemProxy;
+			containerPortal = CD6DC437219F89E90052CD08 /* GoogleMock.xcodeproj */;
+			proxyType = 2;
+			remoteGlobalIDString = 05818F861A685AEA0072A469;
+			remoteInfo = GoogleMock;
+		};
+		CD6DC440219F89E90052CD08 /* PBXContainerItemProxy */ = {
+			isa = PBXContainerItemProxy;
+			containerPortal = CD6DC437219F89E90052CD08 /* GoogleMock.xcodeproj */;
+			proxyType = 2;
+			remoteGlobalIDString = 05E96ABD1A68600C00204102;
+			remoteInfo = gmock;
+		};
+		CD6DC442219F89E90052CD08 /* PBXContainerItemProxy */ = {
+			isa = PBXContainerItemProxy;
+			containerPortal = CD6DC437219F89E90052CD08 /* GoogleMock.xcodeproj */;
+			proxyType = 2;
+			remoteGlobalIDString = 05E96B1F1A68634900204102;
+			remoteInfo = gtest;
+		};
+		CD6DC444219F89E90052CD08 /* PBXContainerItemProxy */ = {
+			isa = PBXContainerItemProxy;
+			containerPortal = CD6DC437219F89E90052CD08 /* GoogleMock.xcodeproj */;
+			proxyType = 2;
+			remoteGlobalIDString = 05818F901A685AEA0072A469;
+			remoteInfo = GoogleMockTests;
+		};
+		CD6DC450219F8A360052CD08 /* PBXContainerItemProxy */ = {
+			isa = PBXContainerItemProxy;
+			containerPortal = CDB2F72B1C5D47F70067C2EC /* Project object */;
+			proxyType = 1;
+			remoteGlobalIDString = CD3C80C21D6A711000ACC795;
+			remoteInfo = "json-direct";
+		};
+		CD6DC455219F8A630052CD08 /* PBXContainerItemProxy */ = {
+			isa = PBXContainerItemProxy;
+			containerPortal = CD6DC437219F89E90052CD08 /* GoogleMock.xcodeproj */;
+			proxyType = 1;
+			remoteGlobalIDString = 05818F851A685AEA0072A469;
+			remoteInfo = GoogleMock;
+		};
+/* End PBXContainerItemProxy section */
 
 /* Begin PBXFileReference section */
 		CD17473E1D4C1DFD000C344B /* json_binder_discard.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = json_binder_discard.cpp; sourceTree = "<group>"; };
 		CD1AD69D219F7CFF00AB8959 /* json */ = {isa = PBXFileReference; lastKnownFileType = folder; name = json; path = include/json; sourceTree = "<group>"; };
-		CD2B09881E6374F300D6D23A /* json_binder_terminate.t.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = json_binder_terminate.t.h; sourceTree = "<group>"; };
-		CD2B098B1E63839A00D6D23A /* json_binder_tuple.t.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = json_binder_tuple.t.h; sourceTree = "<group>"; };
-		CD31E1E9219CE85A001C2AF1 /* json_binder_polymorphic.t.h */ = {isa = PBXFileReference; explicitFileType = sourcecode.cpp.h; path = json_binder_polymorphic.t.h; sourceTree = "<group>"; };
+		CD2B09881E6374F300D6D23A /* json_binder_terminate_test.cxx */ = {isa = PBXFileReference; explicitFileType = sourcecode.cpp.cpp; path = json_binder_terminate_test.cxx; sourceTree = "<group>"; };
+		CD2B098B1E63839A00D6D23A /* json_binder_tuple_test.cxx */ = {isa = PBXFileReference; explicitFileType = sourcecode.cpp.cpp; path = json_binder_tuple_test.cxx; sourceTree = "<group>"; };
+		CD31E1E9219CE85A001C2AF1 /* json_binder_polymorphic_test.cxx */ = {isa = PBXFileReference; explicitFileType = sourcecode.cpp.cpp; path = json_binder_polymorphic_test.cxx; sourceTree = "<group>"; };
 		CD3C80CF1D6A711000ACC795 /* libjson-direct.dylib */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.dylib"; includeInIndex = 0; path = "libjson-direct.dylib"; sourceTree = BUILT_PRODUCTS_DIR; };
 		CD472C751CCC1ABD0084C8D6 /* json_common.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = json_common.cpp; 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>"; };
-		CD84C4EB1F68908F002014D3 /* json_binder_custom.t.h */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 2; lastKnownFileType = sourcecode.c.h; path = json_binder_custom.t.h; sourceTree = "<group>"; tabWidth = 2; };
+		CD679D761E61267300F9F843 /* json_binder_value_int_test.cxx */ = {isa = PBXFileReference; explicitFileType = sourcecode.cpp.cpp; path = json_binder_value_int_test.cxx; sourceTree = "<group>"; };
+		CD679D7B1E61E26000F9F843 /* json_binder_value_string_test.cxx */ = {isa = PBXFileReference; explicitFileType = sourcecode.cpp.cpp; path = json_binder_value_string_test.cxx; sourceTree = "<group>"; };
+		CD679D7C1E6273DB00F9F843 /* json_binder_value_double_test.cxx */ = {isa = PBXFileReference; explicitFileType = sourcecode.cpp.cpp; path = json_binder_value_double_test.cxx; sourceTree = "<group>"; };
+		CD6DC437219F89E90052CD08 /* GoogleMock.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = GoogleMock.xcodeproj; path = "../../../gmock-xcode-master/GoogleMock.xcodeproj"; sourceTree = "<group>"; };
+		CD6DC44A219F8A360052CD08 /* json-direct-test.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "json-direct-test.xctest"; sourceTree = BUILT_PRODUCTS_DIR; };
+		CD6DC44E219F8A360052CD08 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
+		CD84C4EB1F68908F002014D3 /* json_binder_custom_test.cxx */ = {isa = PBXFileReference; explicitFileType = sourcecode.cpp.cpp; fileEncoding = 4; indentWidth = 2; path = json_binder_custom_test.cxx; sourceTree = "<group>"; tabWidth = 2; };
 		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; };
-		CDE6CC1F1EF9E977005E745A /* libjson-direct_s.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libjson-direct_s.a"; sourceTree = BUILT_PRODUCTS_DIR; };
-		CDE6CC2B1EF9E984005E745A /* libjson_s.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libjson_s.a; sourceTree = BUILT_PRODUCTS_DIR; };
-		CDECC7D41E64E6A900BEE842 /* json_binder_collection.t.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = json_binder_collection.t.h; sourceTree = "<group>"; };
-		CDECC7D51E6504C800BEE842 /* json_binder_test_bool.t.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = json_binder_test_bool.t.h; sourceTree = "<group>"; };
-		CDECC7D61E65073E00BEE842 /* json_binder_object.t.h */ = {isa = PBXFileReference; indentWidth = 2; lastKnownFileType = sourcecode.c.h; path = json_binder_object.t.h; sourceTree = "<group>"; tabWidth = 2; };
-		CDF643321C6E9A8B0016A475 /* json_tc */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = json_tc; sourceTree = BUILT_PRODUCTS_DIR; };
+		CDECC7D41E64E6A900BEE842 /* json_binder_collection_test.cxx */ = {isa = PBXFileReference; explicitFileType = sourcecode.cpp.cpp; fileEncoding = 4; path = json_binder_collection_test.cxx; sourceTree = "<group>"; };
+		CDECC7D51E6504C800BEE842 /* json_binder_test_bool_test.cxx */ = {isa = PBXFileReference; explicitFileType = sourcecode.cpp.cpp; path = json_binder_test_bool_test.cxx; sourceTree = "<group>"; };
+		CDECC7D61E65073E00BEE842 /* json_binder_object_test.cxx */ = {isa = PBXFileReference; explicitFileType = sourcecode.cpp.cpp; indentWidth = 2; path = json_binder_object_test.cxx; sourceTree = "<group>"; tabWidth = 2; };
 /* End PBXFileReference section */
 
 /* Begin PBXFrameworksBuildPhase section */
@@ -67,33 +103,19 @@
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};
-		CDB2F7301C5D47F70067C2EC /* Frameworks */ = {
-			isa = PBXFrameworksBuildPhase;
-			buildActionMask = 2147483647;
-			files = (
-			);
-			runOnlyForDeploymentPostprocessing = 0;
-		};
-		CDE6CC101EF9E977005E745A /* Frameworks */ = {
-			isa = PBXFrameworksBuildPhase;
-			buildActionMask = 2147483647;
-			files = (
-			);
-			runOnlyForDeploymentPostprocessing = 0;
-		};
-		CDE6CC251EF9E984005E745A /* Frameworks */ = {
+		CD6DC447219F8A360052CD08 /* Frameworks */ = {
 			isa = PBXFrameworksBuildPhase;
 			buildActionMask = 2147483647;
 			files = (
+				CD6DC458219F8A670052CD08 /* GoogleMock.framework in Frameworks */,
+				CD6DC44F219F8A360052CD08 /* libjson-direct.dylib in Frameworks */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};
-		CDF6432F1C6E9A8B0016A475 /* Frameworks */ = {
+		CDB2F7301C5D47F70067C2EC /* Frameworks */ = {
 			isa = PBXFrameworksBuildPhase;
 			buildActionMask = 2147483647;
 			files = (
-				CDE6CC2C1EF9E994005E745A /* libjson_s.a in Frameworks */,
-				CDE6CC2D1EF9E994005E745A /* libjson-direct_s.a in Frameworks */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};
@@ -114,28 +136,56 @@
 		CD679D751E61266300F9F843 /* test */ = {
 			isa = PBXGroup;
 			children = (
-				CDECC7D51E6504C800BEE842 /* json_binder_test_bool.t.h */,
-				CD679D761E61267300F9F843 /* json_binder_value_int.t.h */,
-				CD679D7B1E61E26000F9F843 /* json_binder_value_string.t.h */,
-				CD679D7C1E6273DB00F9F843 /* json_binder_value_double.t.h */,
-				CD2B098B1E63839A00D6D23A /* json_binder_tuple.t.h */,
-				CDECC7D61E65073E00BEE842 /* json_binder_object.t.h */,
-				CDECC7D41E64E6A900BEE842 /* json_binder_collection.t.h */,
-				CD2B09881E6374F300D6D23A /* json_binder_terminate.t.h */,
-				CD84C4EB1F68908F002014D3 /* json_binder_custom.t.h */,
-				CD31E1E9219CE85A001C2AF1 /* json_binder_polymorphic.t.h */,
-				CD679D781E6126C700F9F843 /* json_tc.cpp */,
+				CDECC7D51E6504C800BEE842 /* json_binder_test_bool_test.cxx */,
+				CD679D761E61267300F9F843 /* json_binder_value_int_test.cxx */,
+				CD679D7B1E61E26000F9F843 /* json_binder_value_string_test.cxx */,
+				CD679D7C1E6273DB00F9F843 /* json_binder_value_double_test.cxx */,
+				CD2B098B1E63839A00D6D23A /* json_binder_tuple_test.cxx */,
+				CDECC7D61E65073E00BEE842 /* json_binder_object_test.cxx */,
+				CDECC7D41E64E6A900BEE842 /* json_binder_collection_test.cxx */,
+				CD2B09881E6374F300D6D23A /* json_binder_terminate_test.cxx */,
+				CD84C4EB1F68908F002014D3 /* json_binder_custom_test.cxx */,
+				CD31E1E9219CE85A001C2AF1 /* json_binder_polymorphic_test.cxx */,
 			);
 			path = test;
 			sourceTree = "<group>";
 		};
+		CD6DC438219F89E90052CD08 /* Products */ = {
+			isa = PBXGroup;
+			children = (
+				CD6DC43F219F89E90052CD08 /* GoogleMock.framework */,
+				CD6DC441219F89E90052CD08 /* gmock.framework */,
+				CD6DC443219F89E90052CD08 /* gtest.framework */,
+				CD6DC445219F89E90052CD08 /* GoogleMockTests.xctest */,
+			);
+			name = Products;
+			sourceTree = "<group>";
+		};
+		CD6DC44B219F8A360052CD08 /* json-direct-test */ = {
+			isa = PBXGroup;
+			children = (
+				CD6DC44E219F8A360052CD08 /* Info.plist */,
+			);
+			path = "json-direct-test";
+			sourceTree = "<group>";
+		};
+		CD6DC457219F8A670052CD08 /* Frameworks */ = {
+			isa = PBXGroup;
+			children = (
+			);
+			name = Frameworks;
+			sourceTree = "<group>";
+		};
 		CDB2F72A1C5D47F70067C2EC = {
 			isa = PBXGroup;
 			children = (
+				CD6DC437219F89E90052CD08 /* GoogleMock.xcodeproj */,
 				CD1AD69D219F7CFF00AB8959 /* json */,
 				CD1AD69A219F7A2F00AB8959 /* src */,
 				CD679D751E61266300F9F843 /* test */,
+				CD6DC44B219F8A360052CD08 /* json-direct-test */,
 				CDB2F7341C5D47F70067C2EC /* Products */,
+				CD6DC457219F8A670052CD08 /* Frameworks */,
 			);
 			sourceTree = "<group>";
 		};
@@ -143,10 +193,8 @@
 			isa = PBXGroup;
 			children = (
 				CDB2F7331C5D47F70067C2EC /* libjson.dylib */,
-				CDF643321C6E9A8B0016A475 /* json_tc */,
 				CD3C80CF1D6A711000ACC795 /* libjson-direct.dylib */,
-				CDE6CC1F1EF9E977005E745A /* libjson-direct_s.a */,
-				CDE6CC2B1EF9E984005E745A /* libjson_s.a */,
+				CD6DC44A219F8A360052CD08 /* json-direct-test.xctest */,
 			);
 			name = Products;
 			sourceTree = "<group>";
@@ -169,20 +217,6 @@
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};
-		CDE6CC111EF9E977005E745A /* Headers */ = {
-			isa = PBXHeadersBuildPhase;
-			buildActionMask = 2147483647;
-			files = (
-			);
-			runOnlyForDeploymentPostprocessing = 0;
-		};
-		CDE6CC261EF9E984005E745A /* Headers */ = {
-			isa = PBXHeadersBuildPhase;
-			buildActionMask = 2147483647;
-			files = (
-			);
-			runOnlyForDeploymentPostprocessing = 0;
-		};
 /* End PBXHeadersBuildPhase section */
 
 /* Begin PBXNativeTarget section */
@@ -203,74 +237,41 @@
 			productReference = CD3C80CF1D6A711000ACC795 /* libjson-direct.dylib */;
 			productType = "com.apple.product-type.library.dynamic";
 		};
-		CDB2F7321C5D47F70067C2EC /* json */ = {
-			isa = PBXNativeTarget;
-			buildConfigurationList = CDB2F73E1C5D47F70067C2EC /* Build configuration list for PBXNativeTarget "json" */;
-			buildPhases = (
-				CDB2F72F1C5D47F70067C2EC /* Sources */,
-				CDB2F7301C5D47F70067C2EC /* Frameworks */,
-				CDB2F7311C5D47F70067C2EC /* Headers */,
-			);
-			buildRules = (
-			);
-			dependencies = (
-			);
-			name = json;
-			productName = json;
-			productReference = CDB2F7331C5D47F70067C2EC /* libjson.dylib */;
-			productType = "com.apple.product-type.library.dynamic";
-		};
-		CDE6CC0C1EF9E977005E745A /* json-direct_s */ = {
+		CD6DC449219F8A360052CD08 /* json-direct-test */ = {
 			isa = PBXNativeTarget;
-			buildConfigurationList = CDE6CC1C1EF9E977005E745A /* Build configuration list for PBXNativeTarget "json-direct_s" */;
+			buildConfigurationList = CD6DC452219F8A360052CD08 /* Build configuration list for PBXNativeTarget "json-direct-test" */;
 			buildPhases = (
-				CDE6CC0D1EF9E977005E745A /* Sources */,
-				CDE6CC101EF9E977005E745A /* Frameworks */,
-				CDE6CC111EF9E977005E745A /* Headers */,
+				CD6DC446219F8A360052CD08 /* Sources */,
+				CD6DC447219F8A360052CD08 /* Frameworks */,
+				CD6DC448219F8A360052CD08 /* Resources */,
 			);
 			buildRules = (
 			);
 			dependencies = (
+				CD6DC456219F8A630052CD08 /* PBXTargetDependency */,
+				CD6DC451219F8A360052CD08 /* PBXTargetDependency */,
 			);
-			name = "json-direct_s";
-			productName = json;
-			productReference = CDE6CC1F1EF9E977005E745A /* libjson-direct_s.a */;
-			productType = "com.apple.product-type.library.static";
+			name = "json-direct-test";
+			productName = "json-direct-test";
+			productReference = CD6DC44A219F8A360052CD08 /* json-direct-test.xctest */;
+			productType = "com.apple.product-type.bundle.unit-test";
 		};
-		CDE6CC201EF9E984005E745A /* json_s */ = {
+		CDB2F7321C5D47F70067C2EC /* json */ = {
 			isa = PBXNativeTarget;
-			buildConfigurationList = CDE6CC281EF9E984005E745A /* Build configuration list for PBXNativeTarget "json_s" */;
+			buildConfigurationList = CDB2F73E1C5D47F70067C2EC /* Build configuration list for PBXNativeTarget "json" */;
 			buildPhases = (
-				CDE6CC211EF9E984005E745A /* Sources */,
-				CDE6CC251EF9E984005E745A /* Frameworks */,
-				CDE6CC261EF9E984005E745A /* Headers */,
+				CDB2F72F1C5D47F70067C2EC /* Sources */,
+				CDB2F7301C5D47F70067C2EC /* Frameworks */,
+				CDB2F7311C5D47F70067C2EC /* Headers */,
 			);
 			buildRules = (
 			);
 			dependencies = (
 			);
-			name = json_s;
+			name = json;
 			productName = json;
-			productReference = CDE6CC2B1EF9E984005E745A /* libjson_s.a */;
-			productType = "com.apple.product-type.library.static";
-		};
-		CDF643311C6E9A8B0016A475 /* json_tc */ = {
-			isa = PBXNativeTarget;
-			buildConfigurationList = CDF643381C6E9A8B0016A475 /* Build configuration list for PBXNativeTarget "json_tc" */;
-			buildPhases = (
-				CD679D771E61269500F9F843 /* ShellScript */,
-				CDF6432E1C6E9A8B0016A475 /* Sources */,
-				CDF6432F1C6E9A8B0016A475 /* Frameworks */,
-				CDF643301C6E9A8B0016A475 /* CopyFiles */,
-			);
-			buildRules = (
-			);
-			dependencies = (
-			);
-			name = json_tc;
-			productName = "json-test";
-			productReference = CDF643321C6E9A8B0016A475 /* json_tc */;
-			productType = "com.apple.product-type.tool";
+			productReference = CDB2F7331C5D47F70067C2EC /* libjson.dylib */;
+			productType = "com.apple.product-type.library.dynamic";
 		};
 /* End PBXNativeTarget section */
 
@@ -281,12 +282,13 @@
 				LastUpgradeCheck = 1000;
 				ORGANIZATIONNAME = "Sam Jaffe";
 				TargetAttributes = {
+					CD6DC449219F8A360052CD08 = {
+						CreatedOnToolsVersion = 10.1;
+						ProvisioningStyle = Automatic;
+					};
 					CDB2F7321C5D47F70067C2EC = {
 						CreatedOnToolsVersion = 7.2;
 					};
-					CDF643311C6E9A8B0016A475 = {
-						CreatedOnToolsVersion = 7.2.1;
-					};
 				};
 			};
 			buildConfigurationList = CDB2F72E1C5D47F70067C2EC /* Build configuration list for PBXProject "json" */;
@@ -299,43 +301,61 @@
 			mainGroup = CDB2F72A1C5D47F70067C2EC;
 			productRefGroup = CDB2F7341C5D47F70067C2EC /* Products */;
 			projectDirPath = "";
+			projectReferences = (
+				{
+					ProductGroup = CD6DC438219F89E90052CD08 /* Products */;
+					ProjectRef = CD6DC437219F89E90052CD08 /* GoogleMock.xcodeproj */;
+				},
+			);
 			projectRoot = "";
 			targets = (
 				CDB2F7321C5D47F70067C2EC /* json */,
 				CD3C80C21D6A711000ACC795 /* json-direct */,
-				CDF643311C6E9A8B0016A475 /* json_tc */,
-				CDE6CC0C1EF9E977005E745A /* json-direct_s */,
-				CDE6CC201EF9E984005E745A /* json_s */,
+				CD6DC449219F8A360052CD08 /* json-direct-test */,
 			);
 		};
 /* End PBXProject section */
 
-/* Begin PBXShellScriptBuildPhase section */
-		CD679D771E61269500F9F843 /* ShellScript */ = {
-			isa = PBXShellScriptBuildPhase;
+/* Begin PBXReferenceProxy section */
+		CD6DC43F219F89E90052CD08 /* GoogleMock.framework */ = {
+			isa = PBXReferenceProxy;
+			fileType = wrapper.framework;
+			path = GoogleMock.framework;
+			remoteRef = CD6DC43E219F89E90052CD08 /* PBXContainerItemProxy */;
+			sourceTree = BUILT_PRODUCTS_DIR;
+		};
+		CD6DC441219F89E90052CD08 /* gmock.framework */ = {
+			isa = PBXReferenceProxy;
+			fileType = wrapper.framework;
+			path = gmock.framework;
+			remoteRef = CD6DC440219F89E90052CD08 /* PBXContainerItemProxy */;
+			sourceTree = BUILT_PRODUCTS_DIR;
+		};
+		CD6DC443219F89E90052CD08 /* gtest.framework */ = {
+			isa = PBXReferenceProxy;
+			fileType = wrapper.framework;
+			path = gtest.framework;
+			remoteRef = CD6DC442219F89E90052CD08 /* PBXContainerItemProxy */;
+			sourceTree = BUILT_PRODUCTS_DIR;
+		};
+		CD6DC445219F89E90052CD08 /* GoogleMockTests.xctest */ = {
+			isa = PBXReferenceProxy;
+			fileType = wrapper.cfbundle;
+			path = GoogleMockTests.xctest;
+			remoteRef = CD6DC444219F89E90052CD08 /* PBXContainerItemProxy */;
+			sourceTree = BUILT_PRODUCTS_DIR;
+		};
+/* End PBXReferenceProxy section */
+
+/* Begin PBXResourcesBuildPhase section */
+		CD6DC448219F8A360052CD08 /* Resources */ = {
+			isa = PBXResourcesBuildPhase;
 			buildActionMask = 2147483647;
 			files = (
 			);
-			inputPaths = (
-				"$(SRCROOT)/test/json_binder_value_int.t.h",
-				"$(SRCROOT)/test/json_binder_value_string.t.h",
-				"$(SRCROOT)/test/json_binder_value_double.t.h",
-				"$(SRCROOT)/test/json_binder_terminate.t.h",
-				"$(SRCROOT)/test/json_binder_tuple.t.h",
-				"$(SRCROOT)/test/json_binder_collection.t.h",
-				"$(SRCROOT)/test/json_binder_test_bool.t.h",
-				"$(SRCROOT)/test/json_binder_object.t.h",
-				"$(SRCROOT)/test/json_binder_custom.t.h",
-				"$(SRCROOT)/test/json_binder_polymorphic.t.h",
-			);
-			outputPaths = (
-				"$(SRCDIR)/test/json_tc.cpp",
-			);
 			runOnlyForDeploymentPostprocessing = 0;
-			shellPath = /bin/sh;
-			shellScript = "cd test\ncxxtestgen --error-printer -o json_tc.cpp \\\n    json_binder_value_int.t.h json_binder_value_string.t.h \\\n    json_binder_value_double.t.h json_binder_terminate.t.h \\\n    json_binder_tuple.t.h json_binder_collection.t.h \\\n    json_binder_test_bool.t.h json_binder_object.t.h \\\n    json_binder_custom.t.h json_binder_polymorphic.t.h\n";
 		};
-/* End PBXShellScriptBuildPhase section */
+/* End PBXResourcesBuildPhase section */
 
 /* Begin PBXSourcesBuildPhase section */
 		CD3C80C31D6A711000ACC795 /* Sources */ = {
@@ -347,45 +367,48 @@
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};
-		CDB2F72F1C5D47F70067C2EC /* Sources */ = {
-			isa = PBXSourcesBuildPhase;
-			buildActionMask = 2147483647;
-			files = (
-				CD472C801CCDA4B00084C8D6 /* json_parser.cpp in Sources */,
-				CDB2F7431C5D48090067C2EC /* json.cpp in Sources */,
-				CD472C761CCC1ABD0084C8D6 /* json_common.cpp in Sources */,
-			);
-			runOnlyForDeploymentPostprocessing = 0;
-		};
-		CDE6CC0D1EF9E977005E745A /* Sources */ = {
+		CD6DC446219F8A360052CD08 /* Sources */ = {
 			isa = PBXSourcesBuildPhase;
 			buildActionMask = 2147483647;
 			files = (
-				CDE6CC0E1EF9E977005E745A /* json_binder_discard.cpp in Sources */,
-				CDE6CC0F1EF9E977005E745A /* json_common.cpp in Sources */,
+				CD6DC45E219FA21A0052CD08 /* json_binder_object_test.cxx in Sources */,
+				CD6DC45C219FA2140052CD08 /* json_binder_value_double_test.cxx in Sources */,
+				CD6DC45F219FA21E0052CD08 /* json_binder_collection_test.cxx in Sources */,
+				CD6DC460219FA2200052CD08 /* json_binder_terminate_test.cxx in Sources */,
+				CD6DC459219F8B210052CD08 /* json_binder_test_bool_test.cxx in Sources */,
+				CD6DC45A219FA1AC0052CD08 /* json_binder_value_int_test.cxx in Sources */,
+				CD6DC45B219FA20E0052CD08 /* json_binder_value_string_test.cxx in Sources */,
+				CD6DC45D219FA2180052CD08 /* json_binder_tuple_test.cxx in Sources */,
+				CD6DC461219FA2230052CD08 /* json_binder_custom_test.cxx in Sources */,
+				CD6DC462219FA2420052CD08 /* json_binder_polymorphic_test.cxx in Sources */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};
-		CDE6CC211EF9E984005E745A /* Sources */ = {
-			isa = PBXSourcesBuildPhase;
-			buildActionMask = 2147483647;
-			files = (
-				CDE6CC221EF9E984005E745A /* json_parser.cpp in Sources */,
-				CDE6CC231EF9E984005E745A /* json.cpp in Sources */,
-				CDE6CC241EF9E984005E745A /* json_common.cpp in Sources */,
-			);
-			runOnlyForDeploymentPostprocessing = 0;
-		};
-		CDF6432E1C6E9A8B0016A475 /* Sources */ = {
+		CDB2F72F1C5D47F70067C2EC /* Sources */ = {
 			isa = PBXSourcesBuildPhase;
 			buildActionMask = 2147483647;
 			files = (
-				CD679D7A1E6126CA00F9F843 /* json_tc.cpp in Sources */,
+				CD472C801CCDA4B00084C8D6 /* json_parser.cpp in Sources */,
+				CDB2F7431C5D48090067C2EC /* json.cpp in Sources */,
+				CD472C761CCC1ABD0084C8D6 /* json_common.cpp in Sources */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};
 /* End PBXSourcesBuildPhase section */
 
+/* Begin PBXTargetDependency section */
+		CD6DC451219F8A360052CD08 /* PBXTargetDependency */ = {
+			isa = PBXTargetDependency;
+			target = CD3C80C21D6A711000ACC795 /* json-direct */;
+			targetProxy = CD6DC450219F8A360052CD08 /* PBXContainerItemProxy */;
+		};
+		CD6DC456219F8A630052CD08 /* PBXTargetDependency */ = {
+			isa = PBXTargetDependency;
+			name = GoogleMock;
+			targetProxy = CD6DC455219F8A630052CD08 /* PBXContainerItemProxy */;
+		};
+/* End PBXTargetDependency section */
+
 /* Begin XCBuildConfiguration section */
 		CD3C80CD1D6A711000ACC795 /* Debug */ = {
 			isa = XCBuildConfiguration;
@@ -413,6 +436,49 @@
 			};
 			name = Release;
 		};
+		CD6DC453219F8A360052CD08 /* Debug */ = {
+			isa = XCBuildConfiguration;
+			buildSettings = {
+				CLANG_ANALYZER_NONNULL = YES;
+				CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
+				CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
+				CLANG_ENABLE_OBJC_WEAK = YES;
+				CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
+				CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
+				CODE_SIGN_STYLE = Automatic;
+				COMBINE_HIDPI_IMAGES = YES;
+				GCC_C_LANGUAGE_STANDARD = gnu11;
+				INFOPLIST_FILE = "json-direct-test/Info.plist";
+				LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks";
+				MACOSX_DEPLOYMENT_TARGET = 10.13;
+				MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
+				MTL_FAST_MATH = YES;
+				PRODUCT_BUNDLE_IDENTIFIER = "leumasjaffe.json-direct-test";
+				PRODUCT_NAME = "$(TARGET_NAME)";
+			};
+			name = Debug;
+		};
+		CD6DC454219F8A360052CD08 /* Release */ = {
+			isa = XCBuildConfiguration;
+			buildSettings = {
+				CLANG_ANALYZER_NONNULL = YES;
+				CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
+				CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
+				CLANG_ENABLE_OBJC_WEAK = YES;
+				CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
+				CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
+				CODE_SIGN_STYLE = Automatic;
+				COMBINE_HIDPI_IMAGES = YES;
+				GCC_C_LANGUAGE_STANDARD = gnu11;
+				INFOPLIST_FILE = "json-direct-test/Info.plist";
+				LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks";
+				MACOSX_DEPLOYMENT_TARGET = 10.13;
+				MTL_FAST_MATH = YES;
+				PRODUCT_BUNDLE_IDENTIFIER = "leumasjaffe.json-direct-test";
+				PRODUCT_NAME = "$(TARGET_NAME)";
+			};
+			name = Release;
+		};
 		CDB2F73C1C5D47F70067C2EC /* Debug */ = {
 			isa = XCBuildConfiguration;
 			buildSettings = {
@@ -542,84 +608,6 @@
 			};
 			name = Release;
 		};
-		CDE6CC1D1EF9E977005E745A /* Debug */ = {
-			isa = XCBuildConfiguration;
-			buildSettings = {
-				DYLIB_COMPATIBILITY_VERSION = 1;
-				DYLIB_CURRENT_VERSION = 1;
-				EXECUTABLE_PREFIX = lib;
-				GCC_ENABLE_CPP_EXCEPTIONS = YES;
-				GCC_ENABLE_CPP_RTTI = YES;
-				GCC_SYMBOLS_PRIVATE_EXTERN = YES;
-				PRODUCT_NAME = "$(TARGET_NAME)";
-				USER_HEADER_SEARCH_PATHS = "../**";
-			};
-			name = Debug;
-		};
-		CDE6CC1E1EF9E977005E745A /* Release */ = {
-			isa = XCBuildConfiguration;
-			buildSettings = {
-				DYLIB_COMPATIBILITY_VERSION = 1;
-				DYLIB_CURRENT_VERSION = 1;
-				EXECUTABLE_PREFIX = lib;
-				GCC_ENABLE_CPP_EXCEPTIONS = YES;
-				GCC_ENABLE_CPP_RTTI = YES;
-				GCC_SYMBOLS_PRIVATE_EXTERN = YES;
-				PRODUCT_NAME = "$(TARGET_NAME)";
-				USER_HEADER_SEARCH_PATHS = "../**";
-			};
-			name = Release;
-		};
-		CDE6CC291EF9E984005E745A /* Debug */ = {
-			isa = XCBuildConfiguration;
-			buildSettings = {
-				DYLIB_COMPATIBILITY_VERSION = 1;
-				DYLIB_CURRENT_VERSION = 1;
-				EXECUTABLE_PREFIX = lib;
-				GCC_ENABLE_CPP_EXCEPTIONS = YES;
-				GCC_ENABLE_CPP_RTTI = YES;
-				GCC_SYMBOLS_PRIVATE_EXTERN = YES;
-				PRODUCT_NAME = "$(TARGET_NAME)";
-				USER_HEADER_SEARCH_PATHS = "../**";
-			};
-			name = Debug;
-		};
-		CDE6CC2A1EF9E984005E745A /* Release */ = {
-			isa = XCBuildConfiguration;
-			buildSettings = {
-				DYLIB_COMPATIBILITY_VERSION = 1;
-				DYLIB_CURRENT_VERSION = 1;
-				EXECUTABLE_PREFIX = lib;
-				GCC_ENABLE_CPP_EXCEPTIONS = YES;
-				GCC_ENABLE_CPP_RTTI = YES;
-				GCC_SYMBOLS_PRIVATE_EXTERN = YES;
-				PRODUCT_NAME = "$(TARGET_NAME)";
-				USER_HEADER_SEARCH_PATHS = "../**";
-			};
-			name = Release;
-		};
-		CDF643361C6E9A8B0016A475 /* Debug */ = {
-			isa = XCBuildConfiguration;
-			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 = "../**";
-			};
-			name = Debug;
-		};
-		CDF643371C6E9A8B0016A475 /* Release */ = {
-			isa = XCBuildConfiguration;
-			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 = "../**";
-			};
-			name = Release;
-		};
 /* End XCBuildConfiguration section */
 
 /* Begin XCConfigurationList section */
@@ -632,47 +620,29 @@
 			defaultConfigurationIsVisible = 0;
 			defaultConfigurationName = Release;
 		};
-		CDB2F72E1C5D47F70067C2EC /* Build configuration list for PBXProject "json" */ = {
-			isa = XCConfigurationList;
-			buildConfigurations = (
-				CDB2F73C1C5D47F70067C2EC /* Debug */,
-				CDB2F73D1C5D47F70067C2EC /* Release */,
-			);
-			defaultConfigurationIsVisible = 0;
-			defaultConfigurationName = Release;
-		};
-		CDB2F73E1C5D47F70067C2EC /* Build configuration list for PBXNativeTarget "json" */ = {
-			isa = XCConfigurationList;
-			buildConfigurations = (
-				CDB2F73F1C5D47F70067C2EC /* Debug */,
-				CDB2F7401C5D47F70067C2EC /* Release */,
-			);
-			defaultConfigurationIsVisible = 0;
-			defaultConfigurationName = Release;
-		};
-		CDE6CC1C1EF9E977005E745A /* Build configuration list for PBXNativeTarget "json-direct_s" */ = {
+		CD6DC452219F8A360052CD08 /* Build configuration list for PBXNativeTarget "json-direct-test" */ = {
 			isa = XCConfigurationList;
 			buildConfigurations = (
-				CDE6CC1D1EF9E977005E745A /* Debug */,
-				CDE6CC1E1EF9E977005E745A /* Release */,
+				CD6DC453219F8A360052CD08 /* Debug */,
+				CD6DC454219F8A360052CD08 /* Release */,
 			);
 			defaultConfigurationIsVisible = 0;
 			defaultConfigurationName = Release;
 		};
-		CDE6CC281EF9E984005E745A /* Build configuration list for PBXNativeTarget "json_s" */ = {
+		CDB2F72E1C5D47F70067C2EC /* Build configuration list for PBXProject "json" */ = {
 			isa = XCConfigurationList;
 			buildConfigurations = (
-				CDE6CC291EF9E984005E745A /* Debug */,
-				CDE6CC2A1EF9E984005E745A /* Release */,
+				CDB2F73C1C5D47F70067C2EC /* Debug */,
+				CDB2F73D1C5D47F70067C2EC /* Release */,
 			);
 			defaultConfigurationIsVisible = 0;
 			defaultConfigurationName = Release;
 		};
-		CDF643381C6E9A8B0016A475 /* Build configuration list for PBXNativeTarget "json_tc" */ = {
+		CDB2F73E1C5D47F70067C2EC /* Build configuration list for PBXNativeTarget "json" */ = {
 			isa = XCConfigurationList;
 			buildConfigurations = (
-				CDF643361C6E9A8B0016A475 /* Debug */,
-				CDF643371C6E9A8B0016A475 /* Release */,
+				CDB2F73F1C5D47F70067C2EC /* Debug */,
+				CDB2F7401C5D47F70067C2EC /* Release */,
 			);
 			defaultConfigurationIsVisible = 0;
 			defaultConfigurationName = Release;

+ 74 - 0
json.xcodeproj/xcshareddata/xcschemes/json-direct-test.xcscheme

@@ -0,0 +1,74 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<Scheme
+   LastUpgradeVersion = "1010"
+   version = "1.3">
+   <BuildAction
+      parallelizeBuildables = "YES"
+      buildImplicitDependencies = "YES">
+   </BuildAction>
+   <TestAction
+      buildConfiguration = "Debug"
+      selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
+      selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
+      codeCoverageEnabled = "YES"
+      onlyGenerateCoverageForSpecifiedTargets = "YES"
+      shouldUseLaunchSchemeArgsEnv = "YES">
+      <CodeCoverageTargets>
+         <BuildableReference
+            BuildableIdentifier = "primary"
+            BlueprintIdentifier = "CD3C80C21D6A711000ACC795"
+            BuildableName = "libjson-direct.dylib"
+            BlueprintName = "json-direct"
+            ReferencedContainer = "container:json.xcodeproj">
+         </BuildableReference>
+         <BuildableReference
+            BuildableIdentifier = "primary"
+            BlueprintIdentifier = "CD6DC449219F8A360052CD08"
+            BuildableName = "json-direct-test.xctest"
+            BlueprintName = "json-direct-test"
+            ReferencedContainer = "container:json.xcodeproj">
+         </BuildableReference>
+      </CodeCoverageTargets>
+      <Testables>
+         <TestableReference
+            skipped = "NO">
+            <BuildableReference
+               BuildableIdentifier = "primary"
+               BlueprintIdentifier = "CD6DC449219F8A360052CD08"
+               BuildableName = "json-direct-test.xctest"
+               BlueprintName = "json-direct-test"
+               ReferencedContainer = "container:json.xcodeproj">
+            </BuildableReference>
+         </TestableReference>
+      </Testables>
+      <AdditionalOptions>
+      </AdditionalOptions>
+   </TestAction>
+   <LaunchAction
+      buildConfiguration = "Debug"
+      selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
+      selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
+      launchStyle = "0"
+      useCustomWorkingDirectory = "NO"
+      ignoresPersistentStateOnLaunch = "NO"
+      debugDocumentVersioning = "YES"
+      debugServiceExtension = "internal"
+      allowLocationSimulation = "YES">
+      <AdditionalOptions>
+      </AdditionalOptions>
+   </LaunchAction>
+   <ProfileAction
+      buildConfiguration = "Release"
+      shouldUseLaunchSchemeArgsEnv = "YES"
+      savedToolIdentifier = ""
+      useCustomWorkingDirectory = "NO"
+      debugDocumentVersioning = "YES">
+   </ProfileAction>
+   <AnalyzeAction
+      buildConfiguration = "Debug">
+   </AnalyzeAction>
+   <ArchiveAction
+      buildConfiguration = "Release"
+      revealArchiveInOrganizer = "YES">
+   </ArchiveAction>
+</Scheme>

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

@@ -4,10 +4,15 @@
 <dict>
 	<key>SchemeUserState</key>
 	<dict>
+		<key>json-direct-test.xcscheme_^#shared#^_</key>
+		<dict>
+			<key>orderHint</key>
+			<integer>47</integer>
+		</dict>
 		<key>json-direct.xcscheme</key>
 		<dict>
 			<key>isShown</key>
-			<true/>
+			<false/>
 			<key>orderHint</key>
 			<integer>8</integer>
 		</dict>
@@ -34,6 +39,8 @@
 		</dict>
 		<key>json_tc.xcscheme</key>
 		<dict>
+			<key>isShown</key>
+			<false/>
 			<key>orderHint</key>
 			<integer>7</integer>
 		</dict>
@@ -45,6 +52,11 @@
 			<key>primary</key>
 			<true/>
 		</dict>
+		<key>CD6DC449219F8A360052CD08</key>
+		<dict>
+			<key>primary</key>
+			<true/>
+		</dict>
 		<key>CDB2F7321C5D47F70067C2EC</key>
 		<dict>
 			<key>primary</key>

+ 0 - 101
test/json_binder_collection.t.h

@@ -1,101 +0,0 @@
-//
-//  json_binder_collection.t.h
-//  json
-//
-//  Created by Sam Jaffe on 2/27/17.
-//
-
-#pragma once
-
-#include <cxxtest/TestSuite.h>
-#include "json/json_binder.hpp"
-
-using namespace json::binder;
-using namespace json::parser;
-
-class json_binder_collection_TestSuite : public CxxTest::TestSuite {
-public:
-  void test_vector_parsing() {
-    char data[] = "[ 0, 1, 2 ]";
-    using collect = std::vector<int>;
-    collect out;
-    value_binder<collect> binder{};
-    TS_ASSERT_THROWS_NOTHING(parse(json::binder::bind(out, binder), data, allow_all));
-    TS_ASSERT_EQUALS(out, collect({0, 1, 2}));
-  }
-  
-  void test_vector_throws_if_array_unterminated() {
-    char data[] = "[ 0, 1";
-    using collect = std::vector<int>;
-    collect out;
-    value_binder<collect> binder{};
-    TS_ASSERT_THROWS(parse(json::binder::bind(out, binder), data, allow_all),
-                     json::unterminated_json_exception);
-  }
-  
-  void test_vector_throws_if_array_unstarted() {
-    char data[] = "0, 1";
-    using collect = std::vector<int>;
-    collect out;
-    value_binder<collect> binder{};
-    TS_ASSERT_THROWS(parse(json::binder::bind(out, binder), data, allow_all),
-                     json::malformed_json_exception);
-  }
-
-  
-  void test_map_parsing() {
-    char data[] = "{ \"a\" : 1 , \"b\" : 2 }";
-    using collect = std::map<std::string, int>;
-    collect out;
-    value_binder<collect> binder{};
-    TS_ASSERT_THROWS_NOTHING(parse(json::binder::bind(out, binder), data, allow_all));
-    TS_ASSERT_EQUALS(out, collect({{"a", 1}, {"b", 2}}));
-  }
-  
-  void test_map_throws_if_object_unterminated() {
-    char data[] = "{ \"a\" : 1 , \"b\" : 2";
-    using collect = std::map<std::string, int>;
-    collect out;
-    value_binder<collect> binder{};
-    TS_ASSERT_THROWS(parse(json::binder::bind(out, binder), data, allow_all),
-                     json::unterminated_json_exception);
-  }
-  
-  void test_map_throws_if_object_missing_associative_token() {
-    char data[] = "{ \"a\" : 1 , \"b\" }";
-    using collect = std::map<std::string, int>;
-    collect out;
-    value_binder<collect> binder{};
-    TS_ASSERT_THROWS(parse(json::binder::bind(out, binder), data, allow_all),
-                     json::malformed_json_exception);
-  }
-
-  void test_map_throws_if_object_unstarted() {
-    char data[] = "\"a\" : 1 , \"b\" : 2";
-    using collect = std::map<std::string, int>;
-    collect out;
-    value_binder<collect> binder{};
-    TS_ASSERT_THROWS(parse(json::binder::bind(out, binder), data, allow_all),
-                     json::malformed_json_exception);
-  }
-  
-  void test_write_vector() {
-    std::string const expected = "[5,4,7]";
-    std::stringstream ss;
-    using collect = std::vector<int>;
-    collect const in{ 5, 4, 7 };
-    value_binder<collect> binder{};
-    TS_ASSERT_THROWS_NOTHING(write(json::binder::bind(in, binder), ss));
-    TS_ASSERT_EQUALS(ss.str(), expected);
-  }
-  
-  void test_write_map() {
-    std::string const expected = "{\"a\":1,\"b\":2}";
-    std::stringstream ss;
-    using collect = std::map<std::string, int>;
-    collect const in{ { "a", 1 }, { "b", 2 } };
-    value_binder<collect> binder{};
-    TS_ASSERT_THROWS_NOTHING(write(json::binder::bind(in, binder), ss));
-    TS_ASSERT_EQUALS(ss.str(), expected);
-  }
-};

+ 96 - 0
test/json_binder_collection_test.cxx

@@ -0,0 +1,96 @@
+//
+//  json_binder_collection.t.h
+//  json
+//
+//  Created by Sam Jaffe on 2/27/17.
+//
+
+#include <gmock/gmock.h>
+#include "json/json_binder.hpp"
+
+using namespace json::binder;
+using namespace json::parser;
+
+TEST(JsonBinderArrayTest, ParsesSuccessfully) {
+  char data[] = "[ 0, 1, 2 ]";
+  using collect = std::vector<int>;
+  collect out;
+  value_binder<collect> binder{};
+  EXPECT_NO_THROW(parse(json::binder::bind(out, binder), data, allow_all));
+  EXPECT_THAT(out, collect({0, 1, 2}));
+}
+
+TEST(JsonBinderArrayTest, ThrowsOnMissingEndToken) {
+  char data[] = "[ 0, 1";
+  using collect = std::vector<int>;
+  collect out;
+  value_binder<collect> binder{};
+  EXPECT_THROW(parse(json::binder::bind(out, binder), data, allow_all),
+                   json::unterminated_json_exception);
+}
+
+TEST(JsonBinderArrayTest, ThrowsOnMissingStartToken) {
+  char data[] = "0, 1";
+  using collect = std::vector<int>;
+  collect out;
+  value_binder<collect> binder{};
+  EXPECT_THROW(parse(json::binder::bind(out, binder), data, allow_all),
+                   json::malformed_json_exception);
+}
+
+
+TEST(JsonBinderMapTest, ParsesSuccessfully) {
+  char data[] = "{ \"a\" : 1 , \"b\" : 2 }";
+  using collect = std::map<std::string, int>;
+  collect out;
+  value_binder<collect> binder{};
+  EXPECT_NO_THROW(parse(json::binder::bind(out, binder), data, allow_all));
+  EXPECT_THAT(out, collect({{"a", 1}, {"b", 2}}));
+}
+
+TEST(JsonBinderMapTest, ThrowsOnMissingEndToken) {
+  char data[] = "{ \"a\" : 1 , \"b\" : 2";
+  using collect = std::map<std::string, int>;
+  collect out;
+  value_binder<collect> binder{};
+  EXPECT_THROW(parse(json::binder::bind(out, binder), data, allow_all),
+                   json::unterminated_json_exception);
+}
+
+TEST(JsonBinderMapTest, ThrowsOnMissingValueForKey) {
+  char data[] = "{ \"a\" : 1 , \"b\" }";
+  using collect = std::map<std::string, int>;
+  collect out;
+  value_binder<collect> binder{};
+  EXPECT_THROW(parse(json::binder::bind(out, binder), data, allow_all),
+                   json::malformed_json_exception);
+}
+
+TEST(JsonBinderMapTest, ThrowsOnMissingStartToken) {
+  char data[] = "\"a\" : 1 , \"b\" : 2";
+  using collect = std::map<std::string, int>;
+  collect out;
+  value_binder<collect> binder{};
+  EXPECT_THROW(parse(json::binder::bind(out, binder), data, allow_all),
+                   json::malformed_json_exception);
+}
+
+TEST(JsonBinderArrayTest, WritesDataWithoutWhitespace) {
+  std::string const expected = "[5,4,7]";
+  std::stringstream ss;
+  using collect = std::vector<int>;
+  collect const in{ 5, 4, 7 };
+  value_binder<collect> binder{};
+  EXPECT_NO_THROW(write(json::binder::bind(in, binder), ss));
+  EXPECT_THAT(ss.str(), expected);
+}
+
+TEST(JsonBinderMapTest, WritesDataWithoutWhitespace) {
+  std::string const expected = "{\"a\":1,\"b\":2}";
+  std::stringstream ss;
+  using collect = std::map<std::string, int>;
+  collect const in{ { "a", 1 }, { "b", 2 } };
+  value_binder<collect> binder{};
+  EXPECT_NO_THROW(write(json::binder::bind(in, binder), ss));
+  EXPECT_THAT(ss.str(), expected);
+}

+ 0 - 71
test/json_binder_custom.t.h

@@ -1,71 +0,0 @@
-//
-//  json_binder_custom.t.h
-//  json
-//
-//  Created by Sam Jaffe on 9/12/17.
-//
-
-#pragma once
-
-#include <cxxtest/TestSuite.h>
-#include "json/json_binder.hpp"
-
-#include <iomanip>
-
-using namespace json::binder;
-using namespace json::parser;
-
-class precision_binder : public binder_impl<double> {
-public:
-  precision_binder(int d) : digits(d), trunc(std::pow(10, d)) {}
-  virtual ~precision_binder() = default;
-  
-  binder_impl<double> * clone() const override {
-    return new precision_binder{*this};
-  }
-  
-  void parse(double & value, char const * & str, options) const override {
-    std::sscanf(str, "%lf", &value);
-    value = static_cast<long long>(value * trunc) / trunc;
-    json::parse_discard_token(str); // Advance to the boundary
-  }
-  
-  void write(double const & value, std::ostream & out) const override {
-    out << std::fixed << std::setprecision(digits) << value;
-  }
-private:
-  int digits;
-  double trunc;
-};
-
-class json_binder_custom_TestSuite : public CxxTest::TestSuite {
-public:
-  struct TestObject {
-    double A; // four_digits
-    double B; // two_digits
-    double C;
-    bool operator==(TestObject const & other) const {
-      return A == other.A && B == other.B && C == other.C;
-    }
-  };
-  
-  static object_binder<TestObject> GetImpl() {
-    return object_binder<TestObject>()
-    ("A", &TestObject::A, precision_binder(4))
-    ("B", &TestObject::B, precision_binder(2))
-    ("C", &TestObject::C);
-  }
-  
-  static object_binder<TestObject> & Get() {
-    static object_binder<TestObject> val = GetImpl();
-    return val;
-  }
-  
-  void test_() {
-    char data[] = "{\"A\":1.00008, \"B\":1.0, \"C\":1.523211102}";
-    TestObject out{0.0, 0.0, 0.0};
-    TestObject const expected{1.0, 1.0, 1.523211102};
-    TS_ASSERT_THROWS_NOTHING(parse(json::binder::bind(out, Get()), data));
-    TS_ASSERT_EQUALS(out, expected);
-  }
-};

+ 67 - 0
test/json_binder_custom_test.cxx

@@ -0,0 +1,67 @@
+//
+//  json_binder_custom.t.h
+//  json
+//
+//  Created by Sam Jaffe on 9/12/17.
+//
+
+#include <gmock/gmock.h>
+#include "json/json_binder.hpp"
+
+#include <iomanip>
+#include <cmath>
+
+using namespace json::binder;
+using namespace json::parser;
+
+class precision_binder : public binder_impl<double> {
+public:
+  precision_binder(int d) : digits(d), trunc(std::pow(10, d)) {}
+  virtual ~precision_binder() = default;
+  
+  binder_impl<double> * clone() const override {
+    return new precision_binder{*this};
+  }
+  
+  void parse(double & value, char const * & str, options) const override {
+    std::sscanf(str, "%lf", &value);
+    value = static_cast<long long>(value * trunc) / trunc;
+    json::parse_discard_token(str); // Advance to the boundary
+  }
+  
+  void write(double const & value, std::ostream & out) const override {
+    out << std::fixed << std::setprecision(digits) << value;
+  }
+private:
+  int digits;
+  double trunc;
+};
+
+struct CustomObject {
+  double A; // four_digits
+  double B; // two_digits
+  double C;
+};
+
+bool operator==(CustomObject const & lhs, CustomObject const & rhs) {
+  return lhs.A == rhs.A && lhs.B == rhs.B && lhs.C == rhs.C;
+}
+
+class JsonBinderCustomTest : public ::testing::Test {
+protected:
+  static object_binder<CustomObject> & GetBinder() {
+    static object_binder<CustomObject> val = object_binder<CustomObject>()
+    ("A", &CustomObject::A, precision_binder(4))
+    ("B", &CustomObject::B, precision_binder(2))
+    ("C", &CustomObject::C);
+    return val;
+  }
+};
+
+TEST_F(JsonBinderCustomTest, CustomBindersWillParse) {
+  char data[] = "{\"A\":1.00008, \"B\":1.0, \"C\":1.523211102}";
+  CustomObject out{0.0, 0.0, 0.0};
+  CustomObject const expected{1.0, 1.0, 1.523211102};
+  EXPECT_NO_THROW(parse(json::binder::bind(out, GetBinder()), data));
+  EXPECT_THAT(out, expected);
+}

+ 0 - 137
test/json_binder_object.t.h

@@ -1,137 +0,0 @@
-//
-//  json_binder_object.t.h
-//  json
-//
-//  Created by Sam Jaffe on 2/27/17.
-//
-
-#pragma once
-
-#include <cxxtest/TestSuite.h>
-#include "json/json_binder.hpp"
-
-using namespace json::binder;
-using namespace json::parser;
-
-class json_binder_object_TestSuite : public CxxTest::TestSuite {
-public:
-  using vec_t = std::vector<int>;
-  using map_t = std::map<std::string, vec_t>;
-  struct TestObject {
-    int count;
-    double average;
-    std::map<std::string, std::vector<int>> data;
-    bool operator==(TestObject const & other) const {
-      return count == other.count && average == other.average && data == other.data;
-    }
-  };
-  
-  static object_binder<TestObject> GetImpl() {
-    return object_binder<TestObject>()
-      ("count", &TestObject::count)
-      ("average", &TestObject::average)
-      ("data", direct_binder<TestObject, map_t>(&TestObject::data));
-  }
-  
-  static object_binder<TestObject> & Get() {
-    static object_binder<TestObject> val = GetImpl();
-    return val;
-  }
-  
-  void test_construct_object_from_json() {
-    char data[] = "{ \"count\":10, \"average\":1.0, \"data\":{ \"key1\":[1, 2], \"key2\":[3, 4] }}";
-    TestObject out = { 0, 0.0, {} };
-    TestObject expected = { 10, 1.0, {{"key1", {1, 2}},{"key2", {3, 4}}} };
-    TS_ASSERT_THROWS_NOTHING(parse(json::binder::bind(out, Get()), data, allow_all));
-    TS_ASSERT_EQUALS(out, expected);
-  }
-  
-  void test_permits_extra_keys() {
-    char data[] = "{ \"count\":10, \"average\":1.0, \"data\":{ \"key1\":[1, 2], \"key2\":[3, 4] }, \"lemon\":true}";
-    TestObject out = { 0, 0.0, {} };
-    TestObject expected = { 10, 1.0, {{"key1", {1, 2}},{"key2", {3, 4}}} };
-    TS_ASSERT_THROWS_NOTHING(parse(json::binder::bind(out, Get()), data, allow_all));
-    TS_ASSERT_EQUALS(out, expected);
-  }
-  
-  void test_throws_on_extra_keys_with_flag() {
-    char data[] = "{ \"count\":10, \"average\":1.0, \"data\":{ \"key1\":[1, 2], \"key2\":[3, 4] }, \"lemon\":true}";
-    TestObject out = { 0, 0.0, {} };
-    TS_ASSERT_THROWS(parse(json::binder::bind(out, Get()), data,
-                           disable_unknown_keys),
-                     json::malformed_json_exception);
-  }
-  
-  void test_permits_missing_keys() {
-    char data[] = "{ \"count\":10, \"average\":1.0 }";
-    TestObject out = { 0, 0.0, {} };
-    TestObject expected = { 10, 1.0, {} };
-    TS_ASSERT_THROWS_NOTHING(parse(json::binder::bind(out, Get()), data, allow_all));
-    TS_ASSERT_EQUALS(out, expected);
-  }
-
-  void test_throws_if_missing_keys_with_flag() {
-    char data[] = "{ \"count\":10, \"average\":1.0 }";
-    TestObject out = { 0, 0.0, {} };
-    TS_ASSERT_THROWS(parse(json::binder::bind(out, Get()), data,
-                           disable_missing_keys),
-                     json::malformed_json_exception);
-  }
-  
-  void test_throws_if_object_unstarted() {
-    char data[] = "\"count\":10, \"average\":1.0, \"data\":{ \"key1\":[1, 2], \"key2\":[3, 4] } }";
-    TestObject out = { 0, 0.0, {} };
-    TS_ASSERT_THROWS(parse(json::binder::bind(out, Get()), data, allow_all),
-                     json::malformed_json_exception);
-  }
-  
-  void test_throws_if_object_missing_associative_token() {
-    char data[] = "{ \"count\":10, \"average\":1.0, \"data\": }";
-    TestObject out = { 0, 0.0, {} };
-    TS_ASSERT_THROWS(parse(json::binder::bind(out, Get()), data, allow_all),
-                     json::malformed_json_exception);
-  }
-
-  void test_throws_if_object_unterminated() {
-    char data[] = "{ \"count\":10, \"average\":1.0, \"data\":{ \"key1\":[1, 2], \"key2\":[3, 4] }";
-    TestObject out = { 0, 0.0, {} };
-    TS_ASSERT_THROWS(parse(json::binder::bind(out, Get()), data, allow_all),
-                     json::unterminated_json_exception);
-  }
-  
-  void test_throws_if_object_missing_associative_token_discard_t() {
-    char data[] = "{ \"count\":10, \"average\":1.0, \"data\":{ \"key1\":[1, 2], \"key2\":[3, 4] }, \"lemon\":{\"key\": } }";
-    TestObject out = { 0, 0.0, {} };
-    TS_ASSERT_THROWS(parse(json::binder::bind(out, Get()), data, allow_all),
-                     json::malformed_json_exception);
-  }
-  
-  void test_throws_if_object_unterminated_array_discard_t() {
-    char data[] = "{ \"count\":10, \"average\":1.0, \"data\":{ \"key1\":[1, 2], \"key2\":[3, 4] }, \"lemon\":[1.0 }";
-    TestObject out = { 0, 0.0, {} };
-    TS_ASSERT_THROWS(parse(json::binder::bind(out, Get()), data, allow_all),
-                     json::unterminated_json_exception);
-  }
-
-  void test_throws_if_object_unterminated_object_discard_t() {
-    char data[] = "{ \"count\":10, \"average\":1.0, \"data\":{ \"key1\":[1, 2], \"key2\":[3, 4] }, \"lemon\":{\"key\":false }";
-    TestObject out = { 0, 0.0, {} };
-    TS_ASSERT_THROWS(parse(json::binder::bind(out, Get()), data, allow_all),
-                     json::unterminated_json_exception);
-  }
-  
-  std::string ify(double value) {
-    std::stringstream ss;
-    ss << std::fixed << value;
-    return ss.str();
-  }
-  
-  void test_write_object_is_composed_of_correct_data() {
-    std::string const dbl = ify(1.0);
-    std::string const expected = "{\"average\":"+dbl+",\"count\":10,\"data\":{\"key1\":[1,2],\"key2\":[3,4]}}";
-    std::stringstream ss;
-    TestObject const in = { 10, 1.0, { { "key1", {1, 2} }, { "key2", {3, 4} } } };
-    TS_ASSERT_THROWS_NOTHING(write(json::binder::bind(in, Get()), ss));
-    TS_ASSERT_EQUALS(ss.str(), expected);
-  }
-};

+ 133 - 0
test/json_binder_object_test.cxx

@@ -0,0 +1,133 @@
+//
+//  json_binder_object.t.h
+//  json
+//
+//  Created by Sam Jaffe on 2/27/17.
+//
+
+#include <gmock/gmock.h>
+#include "json/json_binder.hpp"
+
+using namespace json::binder;
+using namespace json::parser;
+
+using vec_t = std::vector<int>;
+using map_t = std::map<std::string, vec_t>;
+
+struct TestObject {
+  int count;
+  double average;
+  map_t data;
+};
+
+bool operator==(TestObject const & lhs, TestObject const & rhs) {
+  return lhs.count == rhs.count && lhs.average == rhs.average && lhs.data == rhs.data;
+}
+
+class JsonBinderObjectTest : public ::testing::Test {
+protected:
+  static object_binder<TestObject> & GetBinder() {
+    static object_binder<TestObject> val = object_binder<TestObject>()
+    ("count", &TestObject::count)
+    ("average", &TestObject::average)
+    ("data", direct_binder<TestObject, map_t>(&TestObject::data));
+    return val;
+  }
+};
+
+TEST_F(JsonBinderObjectTest, ParsesSuccessfully) {
+  char data[] = "{ \"count\":10, \"average\":1.0, \"data\":{ \"key1\":[1, 2], \"key2\":[3, 4] }}";
+  TestObject out = { 0, 0.0, {} };
+  TestObject expected = { 10, 1.0, {{"key1", {1, 2}},{"key2", {3, 4}}} };
+  EXPECT_NO_THROW(parse(json::binder::bind(out, GetBinder()), data, allow_all));
+  EXPECT_THAT(out, expected);
+}
+
+TEST_F(JsonBinderObjectTest, IgnoresUnknownKeysByDefault) {
+  char data[] = "{ \"count\":10, \"average\":1.0, \"data\":{ \"key1\":[1, 2], \"key2\":[3, 4] }, \"lemon\":true}";
+  TestObject out = { 0, 0.0, {} };
+  TestObject expected = { 10, 1.0, {{"key1", {1, 2}},{"key2", {3, 4}}} };
+  EXPECT_NO_THROW(parse(json::binder::bind(out, GetBinder()), data, allow_all));
+  EXPECT_THAT(out, expected);
+}
+
+TEST_F(JsonBinderObjectTest, ThrowsOnUnknownKeyWithSetting) {
+  char data[] = "{ \"count\":10, \"average\":1.0, \"data\":{ \"key1\":[1, 2], \"key2\":[3, 4] }, \"lemon\":true}";
+  TestObject out = { 0, 0.0, {} };
+  EXPECT_THROW(parse(json::binder::bind(out, GetBinder()), data,
+                     disable_unknown_keys),
+               json::malformed_json_exception);
+}
+
+TEST_F(JsonBinderObjectTest, AllowsMissingExpectedKeysByDefault) {
+  char data[] = "{ \"count\":10, \"average\":1.0 }";
+  TestObject out = { 0, 0.0, {} };
+  TestObject expected = { 10, 1.0, {} };
+  EXPECT_NO_THROW(parse(json::binder::bind(out, GetBinder()), data, allow_all));
+  EXPECT_THAT(out, expected);
+}
+
+TEST_F(JsonBinderObjectTest, ThrowsOnMissingExpectedKeyWithSetting) {
+  char data[] = "{ \"count\":10, \"average\":1.0 }";
+  TestObject out = { 0, 0.0, {} };
+  EXPECT_THROW(parse(json::binder::bind(out, GetBinder()), data,
+                     disable_missing_keys),
+               json::malformed_json_exception);
+}
+
+TEST_F(JsonBinderObjectTest, ThrowsOnMissingStartToken) {
+  char data[] = "\"count\":10, \"average\":1.0, \"data\":{ \"key1\":[1, 2], \"key2\":[3, 4] } }";
+  TestObject out = { 0, 0.0, {} };
+  EXPECT_THROW(parse(json::binder::bind(out, GetBinder()), data, allow_all),
+               json::malformed_json_exception);
+}
+
+TEST_F(JsonBinderObjectTest, ThrowsIfMissingValueForKey) {
+  char data[] = "{ \"count\":10, \"average\":1.0, \"data\": }";
+  TestObject out = { 0, 0.0, {} };
+  EXPECT_THROW(parse(json::binder::bind(out, GetBinder()), data, allow_all),
+               json::malformed_json_exception);
+}
+
+TEST_F(JsonBinderObjectTest, ThrowsOnMissingEndToken) {
+  char data[] = "{ \"count\":10, \"average\":1.0, \"data\":{ \"key1\":[1, 2], \"key2\":[3, 4] }";
+  TestObject out = { 0, 0.0, {} };
+  EXPECT_THROW(parse(json::binder::bind(out, GetBinder()), data, allow_all),
+                   json::unterminated_json_exception);
+}
+
+TEST_F(JsonBinderObjectTest, ThrowsOnMissingValueForKeyEvenForUnknownKey) {
+  char data[] = "{ \"count\":10, \"average\":1.0, \"data\":{ \"key1\":[1, 2], \"key2\":[3, 4] }, \"lemon\":{\"key\": } }";
+  TestObject out = { 0, 0.0, {} };
+  EXPECT_THROW(parse(json::binder::bind(out, GetBinder()), data, allow_all),
+               json::malformed_json_exception);
+}
+
+TEST_F(JsonBinderObjectTest, ThrowsOnMissingEndTokenEvenForUnknownKey) {
+  char data[] = "{ \"count\":10, \"average\":1.0, \"data\":{ \"key1\":[1, 2], \"key2\":[3, 4] }, \"lemon\":[1.0 }";
+  TestObject out = { 0, 0.0, {} };
+  EXPECT_THROW(parse(json::binder::bind(out, GetBinder()), data, allow_all),
+                   json::unterminated_json_exception);
+}
+
+TEST_F(JsonBinderObjectTest, ThrowsOnMissingEndTokenEvenForUnknownKey2) {
+  char data[] = "{ \"count\":10, \"average\":1.0, \"data\":{ \"key1\":[1, 2], \"key2\":[3, 4] }, \"lemon\":{\"key\":false }";
+  TestObject out = { 0, 0.0, {} };
+  EXPECT_THROW(parse(json::binder::bind(out, GetBinder()), data, allow_all),
+                   json::unterminated_json_exception);
+}
+
+std::string ify(double value) {
+  std::stringstream ss;
+  ss << std::fixed << value;
+  return ss.str();
+}
+
+TEST_F(JsonBinderObjectTest, WritesBackOriginalObject) {
+  std::string const dbl = ify(1.0);
+  std::string const expected = "{\"average\":"+dbl+",\"count\":10,\"data\":{\"key1\":[1,2],\"key2\":[3,4]}}";
+  std::stringstream ss;
+  TestObject const in = { 10, 1.0, { { "key1", {1, 2} }, { "key2", {3, 4} } } };
+  EXPECT_NO_THROW(write(json::binder::bind(in, GetBinder()), ss));
+  EXPECT_THAT(ss.str(), expected);
+}

+ 0 - 84
test/json_binder_polymorphic.t.h

@@ -1,84 +0,0 @@
-//
-//  json_binder_polymorphic.t.h
-//  json
-//
-//  Created by Sam Jaffe on 11/14/18.
-//  Copyright © 2018 Sam Jaffe. All rights reserved.
-//
-
-#pragma once
-
-#include <cxxtest/TestSuite.h>
-#include "json/json_binder.hpp"
-
-struct Base {
-  virtual ~Base() = default;
-  virtual void run() const = 0;
-};
-
-struct Left : public Base {
-  int a;
-  void run() const override {}
-};
-
-struct Right : public Base {
-  std::string a;
-  void run() const override {}
-};
-
-bool operator==(Left const & lhs, Left const & rhs) {
-  return lhs.a == rhs.a;
-}
-
-bool operator==(Right const & lhs, Right const & rhs) {
-  return lhs.a == rhs.a;
-}
-
-using namespace json;
-using namespace json::binder;
-object_binder<Left> GetLeftBinder() {
-  return object_binder<Left>()
-  ("a", &Left::a);
-}
-
-object_binder<Right> GetRightBinder() {
-  return object_binder<Right>()
-  ("a", &Right::a);
-}
-
-using pBase = std::unique_ptr<Base>;
-
-polymorphic_binder<pBase> GetBinder() {
-  return polymorphic_binder<pBase>()
-  ("Left", GetLeftBinder())
-  ("Right", GetRightBinder());
-}
-
-polymorphic_binder<pBase> & Get() {
-  static polymorphic_binder<pBase> _ = GetBinder();
-  return _;
-}
-
-using namespace json::parser;
-class json_binder_polymorphic_TestSuite : public CxxTest::TestSuite {
-  public:
-  void testCanHitLeftPolymorph() {
-    char data[] = "{\"@id\":\"Left\", \"@value\":{\"a\":1}}";
-    pBase out;
-    Left expected; expected.a = 1;
-    TS_ASSERT_THROWS_NOTHING(parse(json::binder::bind(out, Get()), data));
-    TS_ASSERT_DIFFERS(&*out, nullptr);
-    TS_ASSERT(dynamic_cast<Left*>(&*out));
-    TS_ASSERT_EQUALS(dynamic_cast<Left&>(*out), expected);
-  }
-
-  void testCanHitRightPolymorph() {
-    char data[] = "{\"@id\":\"Right\", \"@value\":{\"a\":\"hello\"}}";
-    pBase out;
-    Right expected; expected.a = "hello";
-    TS_ASSERT_THROWS_NOTHING(parse(json::binder::bind(out, Get()), data));
-    TS_ASSERT_DIFFERS(&*out, nullptr);
-    TS_ASSERT(dynamic_cast<Right*>(&*out));
-    TS_ASSERT_EQUALS(dynamic_cast<Right&>(*out), expected);
-  }
-};

+ 70 - 0
test/json_binder_polymorphic_test.cxx

@@ -0,0 +1,70 @@
+//
+//  json_binder_polymorphic.t.h
+//  json
+//
+//  Created by Sam Jaffe on 11/14/18.
+//  Copyright © 2018 Sam Jaffe. All rights reserved.
+//
+
+#include <gmock/gmock.h>
+#include "json/json_binder.hpp"
+
+struct Base {
+  virtual ~Base() = default;
+  virtual void run() const = 0;
+};
+
+struct Left : public Base {
+  int a;
+  void run() const override {}
+};
+
+struct Right : public Base {
+  std::string a;
+  void run() const override {}
+};
+
+bool operator==(Left const & lhs, Left const & rhs) {
+  return lhs.a == rhs.a;
+}
+
+bool operator==(Right const & lhs, Right const & rhs) {
+  return lhs.a == rhs.a;
+}
+
+using namespace json::binder;
+using namespace json::parser;
+
+class JsonBinderPolymorphicTest : public ::testing::Test {
+protected:
+  using pBase = std::unique_ptr<Base>;
+
+  polymorphic_binder<pBase> & GetBinder() {
+    static polymorphic_binder<pBase> val = polymorphic_binder<pBase>()
+    ("Left", object_binder<Left>()("a", &Left::a))
+    ("Right", object_binder<Right>()("a", &Right::a));
+    return val;
+  }
+};
+
+using namespace ::testing;
+
+TEST_F(JsonBinderPolymorphicTest, CanHitLeftPolymorph) {
+  char data[] = "{\"@id\":\"Left\", \"@value\":{\"a\":1}}";
+  pBase out;
+  Left expected; expected.a = 1;
+  EXPECT_NO_THROW(parse(json::binder::bind(out, GetBinder()), data));
+  EXPECT_THAT(&*out, Not(IsNull()));
+  EXPECT_THAT(dynamic_cast<Left*>(&*out), Not(IsNull()));
+  EXPECT_THAT(dynamic_cast<Left&>(*out), expected);
+}
+
+TEST_F(JsonBinderPolymorphicTest, CanHitRightPolymorph) {
+  char data[] = "{\"@id\":\"Right\", \"@value\":{\"a\":\"hello\"}}";
+  pBase out;
+  Right expected; expected.a = "hello";
+  EXPECT_NO_THROW(parse(json::binder::bind(out, GetBinder()), data));
+  EXPECT_THAT(&*out, Not(IsNull()));
+  EXPECT_THAT(dynamic_cast<Right*>(&*out), Not(IsNull()));
+  EXPECT_THAT(dynamic_cast<Right&>(*out), expected);
+}

+ 0 - 68
test/json_binder_terminate.t.h

@@ -1,68 +0,0 @@
-//
-//  json_binder_test_terminate.t.h
-//  json
-//
-//  Created by Sam Jaffe on 2/26/17.
-//
-
-#pragma once
-
-#include <cxxtest/TestSuite.h>
-#include "json/json_binder.hpp"
-
-using namespace json::binder;
-using namespace json::parser;
-
-class json_binder_terminate_TestSuite : public CxxTest::TestSuite {
-public:
-  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);
-  }
-  
-  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));
-    TS_ASSERT_EQUALS(out, "This is a ");
-  }
-  
-  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);
-  }
-};

+ 63 - 0
test/json_binder_terminate_test.cxx

@@ -0,0 +1,63 @@
+//
+//  json_binder_test_terminate.t.h
+//  json
+//
+//  Created by Sam Jaffe on 2/26/17.
+//
+
+#include <gmock/gmock.h>
+#include "json/json_binder.hpp"
+
+using namespace json::binder;
+using namespace json::parser;
+
+TEST(JsonBinderEndOfStreamTest, WhitespaceDelimitsTokens) {
+  char data[] = "10 0";
+  int out = 0;
+  value_binder<int> binder{};
+  EXPECT_NO_THROW(parse(bind(out, binder), data, allow_all));
+  EXPECT_THAT(out, 10);
+}
+
+TEST(JsonBinderEndOfStreamTest, ExpectsEoBToOccurAfterAllParsed) {
+  char data[] = "10 0";
+  int out = 0;
+  value_binder<int> binder{};
+  EXPECT_THROW(parse(bind(out, binder), data,
+                     disable_concatenated_json_bodies),
+               json::malformed_json_exception);
+}
+
+TEST(JsonBinderEndOfStreamTest, QuotationFormsEndOfStringToken) {
+  char data[] = "\"This is a \"string";
+  std::string out = "";
+  value_binder<std::string> binder{};
+  EXPECT_NO_THROW(parse(json::binder::bind(out, binder), data, allow_all));
+  EXPECT_THAT(out, "This is a ");
+}
+
+TEST(JsonBinderEndOfStreamTest, ExpectsEoBToOccurAfterAllParsedString) {
+  char data[] = "\"This is a \"string";
+  std::string out = "";
+  value_binder<std::string> binder{};
+  EXPECT_THROW(parse(json::binder::bind(out, binder), data,
+                     disable_concatenated_json_bodies),
+               json::malformed_json_exception);
+}
+
+TEST(JsonBinderEndOfStreamTest, WhitespaceCountsAsSuccessfulEoB) {
+  char data[] = "\"This is a \"    ";
+  std::string out = "";
+  value_binder<std::string> binder{};
+  EXPECT_NO_THROW(parse(json::binder::bind(out, binder), data,
+                        disable_concatenated_json_bodies));
+}
+
+TEST(JsonBinderEndOfStreamTest, NoAmountOfWhitespaceHidesConcatToken) {
+  char data[] = "\"This is a \"    string";
+  std::string out = "";
+  value_binder<std::string> binder{};
+  EXPECT_THROW(parse(json::binder::bind(out, binder), data,
+                     disable_concatenated_json_bodies),
+               json::malformed_json_exception);
+}

+ 0 - 59
test/json_binder_test_bool.t.h

@@ -1,59 +0,0 @@
-//
-//  json_binder_test_bool.t.h
-//  json
-//
-//  Created by Sam Jaffe on 2/27/17.
-//
-
-#pragma once
-
-#include <cxxtest/TestSuite.h>
-#include "json/json_binder.hpp"
-
-using namespace json::binder;
-using namespace json::parser;
-
-class json_binder_value_bool_TestSuite : public CxxTest::TestSuite {
-public:
-  void test_parse_bool_true() {
-    char data[] = "true";
-    bool out = false;
-    value_binder<bool> binder{};
-    parse(bind(out, binder), data, allow_all);
-    TS_ASSERT_EQUALS( out, true );
-  }
-  
-  void test_parse_bool_false() {
-    char data[] = "false";
-    bool out = true;
-    value_binder<bool> binder{};
-    parse(bind(out, binder), data, allow_all);
-    TS_ASSERT_EQUALS( out, false );
-  }
-
-  void test_parse_nonbool_throws() {
-    char data[] = "YES"; // Obj-C
-    bool out = false;
-    value_binder<bool> binder{};
-    TS_ASSERT_THROWS(parse(bind(out, binder), data, allow_all),
-                     json::malformed_json_exception);
-  }
-  
-  void test_write_bool_true() {
-    std::string const 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 const expected = "false";
-    std::stringstream ss;
-    bool const in = false;
-    value_binder<bool> binder{};
-    write(bind(in, binder), ss);
-    TS_ASSERT_EQUALS(ss.str(), expected);
-  }
-};

+ 55 - 0
test/json_binder_test_bool_test.cxx

@@ -0,0 +1,55 @@
+//
+//  json_binder_test_bool.t.h
+//  json
+//
+//  Created by Sam Jaffe on 2/27/17.
+//
+
+#include <gmock/gmock.h>
+#include "json/json_binder.hpp"
+
+using namespace json::binder;
+using namespace json::parser;
+using namespace ::testing;
+
+TEST(JsonBinderBoolTest, ParsesTrue) {
+  char data[] = "true";
+  bool out = false;
+  value_binder<bool> binder{};
+  parse(bind(out, binder), data, allow_all);
+  EXPECT_THAT( out, true );
+}
+
+TEST(JsonBinderBoolTest, ParsesFalse) {
+  char data[] = "false";
+  bool out = true;
+  value_binder<bool> binder{};
+  parse(bind(out, binder), data, allow_all);
+  EXPECT_THAT( out, false );
+}
+
+TEST(JsonBinderBoolTest, ThrowsOnBadData) {
+  char data[] = "YES"; // Obj-C
+  bool out = false;
+  value_binder<bool> binder{};
+  EXPECT_THROW(parse(bind(out, binder), data, allow_all),
+               json::malformed_json_exception);
+}
+
+TEST(JsonBinderBoolTest, WritesTrue) {
+  std::string const expected = "true";
+  std::stringstream ss;
+  bool const in = true;
+  value_binder<bool> binder{};
+  write(bind(in, binder), ss);
+  EXPECT_THAT(ss.str(), expected);
+}
+
+TEST(JsonBinderBoolTest, WritesFalse) {
+  std::string const expected = "false";
+  std::stringstream ss;
+  bool const in = false;
+  value_binder<bool> binder{};
+  write(bind(in, binder), ss);
+  EXPECT_THAT(ss.str(), expected);
+}

+ 0 - 103
test/json_binder_tuple.t.h

@@ -1,103 +0,0 @@
-//
-//  json_binder_tuple.t.h
-//  json
-//
-//  Created by Sam Jaffe on 2/26/17.
-//
-
-#pragma once
-
-#include <cxxtest/TestSuite.h>
-#include "json/json_binder.hpp"
-
-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() {
-    char data[] = "[ 1, 2 ]";
-    using tuple = std::tuple<int, int>;
-    tuple out = std::make_tuple(0, 0);
-    auto binder = make_default_tuple_binder<int, int>();
-    parse(json::binder::bind(out, binder), data, allow_all);
-    TS_ASSERT_EQUALS(out, std::make_tuple(1, 2));
-  }
-  
-  void test_bind_to_tuple_throws_if_missing_entry() {
-    char data[] = "[ 1 ]";
-    using tuple = std::tuple<int, int>;
-    tuple out = std::make_tuple(0, 0);
-    auto binder = make_default_tuple_binder<int, int>();
-    TS_ASSERT_THROWS(parse(json::binder::bind(out, binder), data, allow_all),
-                     json::malformed_json_exception);
-  }
-
-  void test_bind_to_tuple_throws_if_too_many_entries() {
-    char data[] = "[ 1, 2, 3 ]";
-    using tuple = std::tuple<int, int>;
-    tuple out = std::make_tuple(0, 0);
-    auto binder = make_default_tuple_binder<int, int>();
-    TS_ASSERT_THROWS(parse(json::binder::bind(out, binder), data, allow_all),
-                     json::malformed_json_exception);
-  }
-  
-  void test_bind_to_tuple_throws_if_unterminated() {
-    char data[] = "[ 1, 2 ";
-    using tuple = std::tuple<int, int>;
-    tuple out = std::make_tuple(0, 0);
-    auto binder = make_default_tuple_binder<int, int>();
-    TS_ASSERT_THROWS(parse(json::binder::bind(out, binder), data, allow_all),
-                     json::unterminated_json_exception);
-  }
-  
-  void test_bind_to_tuple_throws_if_unstarted() {
-    char data[] = "1, 2 ]";
-    using tuple = std::tuple<int, int>;
-    tuple out = std::make_tuple(0, 0);
-    auto binder = make_default_tuple_binder<int, int>();
-    TS_ASSERT_THROWS(parse(json::binder::bind(out, binder), data, allow_all),
-                     json::malformed_json_exception);
-  }
-  
-  void test_bind_to_tuple_with_multiple_types() {
-    char data[] = "[ 1, 0.5, \"hello\" ]";
-    using tuple = std::tuple<int, double, std::string>;
-    tuple out = std::make_tuple(0, 0.0, "");
-    auto binder = make_default_tuple_binder<int, double, std::string>();
-    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 const 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);
-  }
-};

+ 98 - 0
test/json_binder_tuple_test.cxx

@@ -0,0 +1,98 @@
+//
+//  json_binder_tuple.t.h
+//  json
+//
+//  Created by Sam Jaffe on 2/26/17.
+//
+
+#include <gmock/gmock.h>
+#include "json/json_binder.hpp"
+
+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;
+}
+
+TEST(JsonBinderTupleTest, ParsesTuple) {
+  char data[] = "[ 1, 2 ]";
+  using tuple = std::tuple<int, int>;
+  tuple out = std::make_tuple(0, 0);
+  auto binder = make_default_tuple_binder<int, int>();
+  parse(json::binder::bind(out, binder), data, allow_all);
+  EXPECT_THAT(out, std::make_tuple(1, 2));
+}
+
+TEST(JsonBinderTupleTest, ThrowsIfMissingData) {
+  char data[] = "[ 1 ]";
+  using tuple = std::tuple<int, int>;
+  tuple out = std::make_tuple(0, 0);
+  auto binder = make_default_tuple_binder<int, int>();
+  EXPECT_THROW(parse(json::binder::bind(out, binder), data, allow_all),
+               json::malformed_json_exception);
+}
+
+TEST(JsonBinderTupleTest, ThrowsIfDataRemaining) {
+  char data[] = "[ 1, 2, 3 ]";
+  using tuple = std::tuple<int, int>;
+  tuple out = std::make_tuple(0, 0);
+  auto binder = make_default_tuple_binder<int, int>();
+  EXPECT_THROW(parse(json::binder::bind(out, binder), data, allow_all),
+               json::malformed_json_exception);
+}
+
+TEST(JsonBinderTupleTest, ThrowsIfEOB) {
+  char data[] = "[ 1, 2 ";
+  using tuple = std::tuple<int, int>;
+  tuple out = std::make_tuple(0, 0);
+  auto binder = make_default_tuple_binder<int, int>();
+  EXPECT_THROW(parse(json::binder::bind(out, binder), data, allow_all),
+               json::unterminated_json_exception);
+}
+
+TEST(JsonBinderTupleTest, ThrowsIfMissingStartToken) {
+  char data[] = "1, 2 ]";
+  using tuple = std::tuple<int, int>;
+  tuple out = std::make_tuple(0, 0);
+  auto binder = make_default_tuple_binder<int, int>();
+  EXPECT_THROW(parse(json::binder::bind(out, binder), data, allow_all),
+               json::malformed_json_exception);
+}
+
+TEST(JsonBinderTupleTest, InnerTypeIsIndependant) {
+  char data[] = "[ 1, 0.5, \"hello\" ]";
+  using tuple = std::tuple<int, double, std::string>;
+  tuple out = std::make_tuple(0, 0.0, "");
+  auto binder = make_default_tuple_binder<int, double, std::string>();
+  EXPECT_NO_THROW(parse(json::binder::bind(out, binder), data, allow_all));
+  EXPECT_THAT(out, std::make_tuple(1, 0.5, "hello"));
+}
+
+TEST(JsonBinderTupleTest, WritesTupleWithoutWhitespace) {
+  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);
+  EXPECT_THAT(ss.str(), expected);
+}
+
+TEST(JsonBinderTupleTest, CanParseTupleAsStruct) {
+  char data[] = "[ 10, 5 ]";
+  point out{0, 0};
+  auto binder = tuple_binder<point>()(&point::x)(&point::y);
+  EXPECT_NO_THROW(parse(json::binder::bind(out, binder), data, allow_all));
+  EXPECT_THAT(out, (point{10, 5}));
+}
+
+TEST(JsonBinderTupleTest, WritesTupleAsStructWithoutWhitespace) {
+  std::string const expected = "[10,5]";
+  std::stringstream ss;
+  point const in{10, 5};
+  auto binder = tuple_binder<point>()(&point::x)(&point::y);
+  EXPECT_NO_THROW(write(json::binder::bind(in, binder), ss));
+  EXPECT_THAT(ss.str(), expected);
+}

+ 0 - 160
test/json_binder_value_double.t.h

@@ -1,160 +0,0 @@
-//
-//  json_binder_value_double.t.h
-//  json
-//
-//  Created by Sam Jaffe on 2/25/17.
-//
-
-#pragma once
-
-#include <cxxtest/TestSuite.h>
-#include "json/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));
-  }
-  
-  void test_parse_throws_on_no_data() {
-    char data[] = "";
-    double out = 0.0;
-    value_binder<double> binder{};
-    TS_ASSERT_THROWS(parse(bind(out, binder), data, allow_all),
-                     json::json_numeric_exception);
-  }
-
-  void test_parse_throws_on_non_numeric_data() {
-    char data[] = "one half";
-    double out = 0.0;
-    value_binder<double> binder{};
-    TS_ASSERT_THROWS(parse(bind(out, binder), data, allow_all),
-                     json::json_numeric_exception);
-  }
-  
-  void test_parse_throws_on_incorrectly_terminated_data_with_flag() {
-    char data[] = "5.0boo";
-    double out = 0.0;
-    value_binder<double> binder{};
-    TS_ASSERT_THROWS(parse(bind(out, binder), data,
-                           disable_concatenated_json_bodies),
-                     json::malformed_json_exception);
-    TS_ASSERT_EQUALS(out, 5.0);
-  }
-  
-  void test_parses_double_max_scientific_within_five_decimal_places() {
-    char data[] = "1.79769e+308";
-    double out = 0.0;
-    value_binder<double> binder{};
-    TS_ASSERT_THROWS_NOTHING(parse(bind(out, binder), data, allow_all));
-    TS_ASSERT_DELTA(out, std::numeric_limits<double>::max(), 1E303);
-  }
-
-  void test_throws_exception_on_number_out_of_range_max() {
-    char data[] = "1.8e+308";
-    double out = 0.0;
-    value_binder<double> binder{};
-    TS_ASSERT_THROWS(parse(bind(out, binder), data, allow_all),
-                     json::json_numeric_width_exception);
-  }
-
-  void test_parses_double_lowest_scientific_within_five_decimal_places() {
-    char data[] = "-1.79769e+308";
-    double out = 0.0;
-    value_binder<double> binder{};
-    TS_ASSERT_THROWS_NOTHING(parse(bind(out, binder), data, allow_all));
-    TS_ASSERT_DELTA(out, std::numeric_limits<double>::lowest(), 1E303);
-  }
-  
-  void test_throws_exception_on_number_out_of_range_lowest() {
-    char data[] = "-1.8e+308";
-    double out = 0.0;
-    value_binder<double> binder{};
-    TS_ASSERT_THROWS(parse(bind(out, binder), data, allow_all),
-                     json::json_numeric_width_exception);
-  }
-  
-  void test_parses_double_min_scientific_within_five_decimal_places() {
-    char data[] = "2.22508e-308"; // 2.22507e-308 is min according to cppreference
-    double out = 0.0;
-    value_binder<double> binder{};
-    TS_ASSERT_THROWS_NOTHING(parse(bind(out, binder), data, allow_all));
-    TS_ASSERT_DELTA(out, std::numeric_limits<double>::min(), 1E-303);
-  }
-  
-  void test_throws_exception_on_number_out_of_range_min() {
-    char data[] = "2e-308";
-    double out = 0.0;
-    value_binder<double> binder{};
-    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);
-  }
-};

+ 156 - 0
test/json_binder_value_double_test.cxx

@@ -0,0 +1,156 @@
+//
+//  json_binder_value_double.t.h
+//  json
+//
+//  Created by Sam Jaffe on 2/25/17.
+//
+
+#include <gmock/gmock.h>
+#include "json/json_binder.hpp"
+
+using namespace json::binder;
+using namespace json::parser;
+using namespace ::testing;
+
+TEST(JsonBinderDoubleTest, ParsesDouble) {
+  char data[] = "0.5";
+  double out = 0.0;
+  value_binder<double> binder{};
+  parse(bind(out, binder), data, allow_all);
+  EXPECT_THAT(out, 0.5);
+}
+
+TEST(JsonBinderDoubleTest, ParsesScientific) {
+  char data[] = "2e4";
+  double out = 0.0;
+  value_binder<double> binder{};
+  EXPECT_NO_THROW(parse(bind(out, binder), data, allow_all));
+}
+
+TEST(JsonBinderDoubleTest, ThrowsOnEOB) {
+  char data[] = "";
+  double out = 0.0;
+  value_binder<double> binder{};
+  EXPECT_THROW(parse(bind(out, binder), data, allow_all),
+               json::json_numeric_exception);
+}
+
+TEST(JsonBinderDoubleTest, ThrowsOnNaN) {
+  char data[] = "one half";
+  double out = 0.0;
+  value_binder<double> binder{};
+  EXPECT_THROW(parse(bind(out, binder), data, allow_all),
+               json::json_numeric_exception);
+}
+
+TEST(JsonBinderDoubleTest, ThrowsOnTokenConcat) {
+  char data[] = "5.0boo";
+  double out = 0.0;
+  value_binder<double> binder{};
+  EXPECT_THROW(parse(bind(out, binder), data,
+                     disable_concatenated_json_bodies),
+               json::malformed_json_exception);
+  EXPECT_THAT(out, 5.0);
+}
+
+TEST(JsonBinderDoubleTest, OutputIsEssentiallyEqualDblMax) {
+  char data[] = "1.79769e+308";
+  double out = 0.0;
+  value_binder<double> binder{};
+  EXPECT_NO_THROW(parse(bind(out, binder), data, allow_all));
+  EXPECT_THAT(out, DoubleNear(std::numeric_limits<double>::max(), 1E303));
+}
+
+TEST(JsonBinderDoubleTest, ThrowsOnOOBMax) {
+  char data[] = "1.8e+308";
+  double out = 0.0;
+  value_binder<double> binder{};
+  EXPECT_THROW(parse(bind(out, binder), data, allow_all),
+               json::json_numeric_width_exception);
+}
+
+TEST(JsonBinderDoubleTest, OutputIsEssentiallyEqualDblLowest) {
+  char data[] = "-1.79769e+308";
+  double out = 0.0;
+  value_binder<double> binder{};
+  EXPECT_NO_THROW(parse(bind(out, binder), data, allow_all));
+  EXPECT_THAT(out, DoubleNear(std::numeric_limits<double>::lowest(), 1E303));
+}
+
+TEST(JsonBinderDoubleTest, ThrowsOnOOBLowest) {
+  char data[] = "-1.8e+308";
+  double out = 0.0;
+  value_binder<double> binder{};
+  EXPECT_THROW(parse(bind(out, binder), data, allow_all),
+               json::json_numeric_width_exception);
+}
+
+TEST(JsonBinderDoubleTest, OutputIsEssentiallyEqualDblMin) {
+  char data[] = "2.22508e-308"; // 2.22507e-308 is min according to cppreference
+  double out = 0.0;
+  value_binder<double> binder{};
+  EXPECT_NO_THROW(parse(bind(out, binder), data, allow_all));
+  EXPECT_THAT(out, DoubleNear(std::numeric_limits<double>::min(), 1E-303));
+}
+
+TEST(JsonBinderDoubleTest, ThrowsOnOOBMin) {
+  char data[] = "2e-308";
+  double out = 0.0;
+  value_binder<double> binder{};
+  EXPECT_THROW(parse(bind(out, binder), data, allow_all),
+               json::json_numeric_width_exception);
+}
+
+TEST(JsonBinderDoubleTest, ParseNoLossWithExactRep) {
+  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);
+  EXPECT_THAT(out, in);
+}
+
+TEST(JsonBinderDoubleTest, ParseNoLossWithInexactRep) {
+  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);
+  EXPECT_THAT(out, in);
+}
+
+TEST(JsonBinderDoubleTest, ParseNoLossWithLargeNumberSci) {
+  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);
+  EXPECT_THAT(out, in);
+}
+
+TEST(JsonBinderDoubleTest, ParseNoLossWithLargeNegativeNumberSci) {
+  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);
+  EXPECT_THAT(out, DoubleNear(in, 1e-299));
+}
+
+TEST(JsonBinderDoubleTest, ParseNoLossWithLargeNumber) {
+  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);
+  EXPECT_THAT(out, in);
+}
+
+TEST(JsonBinderDoubleTest, ParseEssentiallyEqualAtSeveralDecimalPlaces) {
+  std::stringstream ss;
+  double in = 0.0002517867, out = 0.0;
+  value_binder<double> binder{};
+  write(bind(in, binder), ss);
+  EXPECT_NO_THROW(parse(bind(out, binder), ss.str().c_str(), allow_all));
+  EXPECT_THAT(out, DoubleNear(in, 0.000001));
+}

+ 0 - 175
test/json_binder_value_int.t.h

@@ -1,175 +0,0 @@
-//
-//  json_binder_value_int.t.h
-//  json
-//
-//  Created by Sam Jaffe on 2/24/17.
-//
-
-#pragma once
-
-#include <cxxtest/TestSuite.h>
-#include "json/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_negative_into_unsigned_throws() {
-    char data[] = "-1";
-    unsigned int out = 0;
-    value_binder<unsigned int> binder{};
-    TS_ASSERT_THROWS(parse(bind(out, binder), data, allow_all),
-                     json::json_numeric_exception);
-  }
-  
-  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_signed_int_under_min_throws() {
-    char data[] = "-2147483649";
-    int out = 0;
-    value_binder<int> binder{};
-    TS_ASSERT_THROWS(parse(bind(out, binder), data, allow_all),
-                     json::json_numeric_width_exception);
-  }
-
-  void test_parsing_short_will_error_over_narrowing_max() {
-    char data[] = "32768";
-    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_parsing_short_will_error_over_narrowing_min() {
-    char data[] = "-32769";
-    short out = 0;
-    value_binder<short> binder{};
-    TS_ASSERT_THROWS(parse(bind(out, binder), data, allow_all),
-                     json::json_numeric_width_exception);
-  }
-
-  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_parse_hexadecimal_number() {
-    char data[] = "0xF";
-    int out = 0;
-    value_binder<int> binder{};
-    parse(bind(out, binder), data, allow_all);
-    TS_ASSERT_EQUALS(out, 15);
-  }
-  
-  void test_parse_hexadecimal_number_out_of_bounds() {
-    char data[] = "0x100000000";
-    int out = 0;
-    value_binder<int> binder{};
-    TS_ASSERT_THROWS(parse(bind(out, binder), data, allow_all),
-                     json::json_numeric_exception);
-  }
-
-  void test_parse_octal_number() {
-    char data[] = "010";
-    int out = 0;
-    value_binder<int> binder{};
-    parse(bind(out, binder), data, allow_all);
-    TS_ASSERT_EQUALS(out, 8);
-  }
-  
-  void test_parse_octal_number_out_of_bounds() {
-    char data[] = "040000000000";
-    int out = 0;
-    value_binder<int> binder{};
-    TS_ASSERT_THROWS(parse(bind(out, binder), data, allow_all),
-                     json::json_numeric_exception);
-  }
-
-  void test_output_from_integer_type() {
-    std::string const 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 const 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 const expected = "-2147483648";
-    std::stringstream ss;
-    int const in = -2147483648;
-    value_binder<int> binder{};
-    write(bind(in, binder), ss);
-    TS_ASSERT_EQUALS(ss.str(), expected);
-  }
-};

+ 171 - 0
test/json_binder_value_int_test.cxx

@@ -0,0 +1,171 @@
+//
+//  json_binder_value_int.t.h
+//  json
+//
+//  Created by Sam Jaffe on 2/24/17.
+//
+
+#include <gmock/gmock.h>
+#include "json/json_binder.hpp"
+
+using namespace json::binder;
+using namespace json::parser;
+using namespace ::testing;
+
+TEST(JsonBinderIntTest, ParsesSuccessfully) {
+  char data[] = "100";
+  int out = 0;
+  value_binder<int> binder{};
+  EXPECT_NO_THROW(parse(bind(out, binder), data, allow_all));
+  EXPECT_THAT(out, 100);
+}
+
+TEST(JsonBinderIntTest, ThrowsWhenDomainMismatch) {
+  char data[] = "-1";
+  unsigned int out = 0;
+  value_binder<unsigned int> binder{};
+  EXPECT_THROW(parse(bind(out, binder), data, allow_all),
+               json::json_numeric_exception);
+}
+
+TEST(JsonBinderIntTest, ThrowsOnEOB) {
+  char data[] = "";
+  int out = 0;
+  value_binder<int> binder{};
+  EXPECT_THROW(parse(bind(out, binder), data, allow_all),
+               json::unterminated_json_exception);
+}
+
+TEST(JsonBinderIntTest, ThrowsOnNaN) {
+  char data[] = "one";
+  int out = 0;
+  value_binder<int> binder{};
+  EXPECT_THROW(parse(bind(out, binder), data, allow_all),
+               json::json_numeric_exception);
+}
+
+TEST(JsonBinderIntTest, ThrowsOnOOB) {
+  char data[] = "1000000000000";
+  int out = 0;
+  value_binder<int> binder{};
+  EXPECT_THROW(parse(bind(out, binder), data, allow_all),
+               json::json_numeric_exception);
+  EXPECT_THAT(out, 0);
+}
+
+TEST(JsonBinderIntTest, ThrowsWhenNarrowing) {
+  char data[] = "2147483648";
+  int out = 0;
+  value_binder<int> binder{};
+  EXPECT_THROW(parse(bind(out, binder), data, allow_all),
+               json::json_numeric_width_exception);
+  EXPECT_THAT(out, 0);
+}
+
+TEST(JsonBinderIntTest, ThrowsWhenNarrowingNegative) {
+  char data[] = "-2147483649";
+  int out = 0;
+  value_binder<int> binder{};
+  EXPECT_THROW(parse(bind(out, binder), data, allow_all),
+               json::json_numeric_width_exception);
+}
+
+TEST(JsonBinderShortTest, ThrowsWhenNarrowing) {
+  char data[] = "32768";
+  short out = 0;
+  value_binder<short> binder{};
+  EXPECT_THROW(parse(bind(out, binder), data, allow_all),
+               json::json_numeric_width_exception);
+  EXPECT_THAT(out, 0);
+}
+
+TEST(JsonBinderShortTest, ThrowsWhenNarrowingNegative) {
+  char data[] = "-32769";
+  short out = 0;
+  value_binder<short> binder{};
+  EXPECT_THROW(parse(bind(out, binder), data, allow_all),
+               json::json_numeric_width_exception);
+}
+
+TEST(JsonBinderIntTest, CanParseMaxInt) {
+  char data[] = "2147483647";
+  int out = 0;
+  value_binder<int> binder{};
+  EXPECT_NO_THROW(parse(bind(out, binder), data, allow_all));
+}
+
+TEST(JsonBinderIntTest, CanParseMinInt) {
+  char data[] = "-2147483648";
+  int out = 0;
+  value_binder<int> binder{};
+  EXPECT_NO_THROW(parse(bind(out, binder), data, allow_all));
+}
+
+TEST(JsonBinderIntTest, ThrowsWhenGivenFloatingPoint) {
+  char data[] = "2.0";
+  int out = 0;
+  value_binder<int> binder{};
+  EXPECT_THROW(parse(bind(out, binder), data, allow_all),
+               json::json_numeric_exception);
+  EXPECT_THAT(out, 0);
+}
+
+TEST(JsonBinderIntTest, CanParseHex) {
+  char data[] = "0xF";
+  int out = 0;
+  value_binder<int> binder{};
+  parse(bind(out, binder), data, allow_all);
+  EXPECT_THAT(out, 15);
+}
+
+TEST(JsonBinderIntTest, ThrowsWhenNarrowingHex) {
+  char data[] = "0x100000000";
+  int out = 0;
+  value_binder<int> binder{};
+  EXPECT_THROW(parse(bind(out, binder), data, allow_all),
+               json::json_numeric_exception);
+}
+
+TEST(JsonBinderIntTest, CanParseOctal) {
+  char data[] = "010";
+  int out = 0;
+  value_binder<int> binder{};
+  parse(bind(out, binder), data, allow_all);
+  EXPECT_THAT(out, 8);
+}
+
+TEST(JsonBinderIntTest, ThrowsWhenNarrowingOctal) {
+  char data[] = "040000000000";
+  int out = 0;
+  value_binder<int> binder{};
+  EXPECT_THROW(parse(bind(out, binder), data, allow_all),
+               json::json_numeric_exception);
+}
+
+// TODO Parametrize this
+TEST(JsonBinderIntTest, WritesSuccessfully) {
+  std::string const expected = "100";
+  std::stringstream ss;
+  int const in = 100;
+  value_binder<int> binder{};
+  write(bind(in, binder), ss);
+  EXPECT_THAT(ss.str(), expected);
+}
+
+TEST(JsonBinderIntTest, CanWriteUnsigned) {
+  std::string const expected = "2147483648";
+  std::stringstream ss;
+  unsigned int const in = 2147483648;
+  value_binder<unsigned int> binder{};
+  write(bind(in, binder), ss);
+  EXPECT_THAT(ss.str(), expected);
+}
+
+TEST(JsonBinderIntTest, CanWriteMinInt) {
+  std::string const expected = "-2147483648";
+  std::stringstream ss;
+  int const in = -2147483648;
+  value_binder<int> binder{};
+  write(bind(in, binder), ss);
+  EXPECT_THAT(ss.str(), expected);
+}

+ 0 - 69
test/json_binder_value_string.t.h

@@ -1,69 +0,0 @@
-//
-//  json_binder_value_string.t.h
-//  json
-//
-//  Created by Sam Jaffe on 2/25/17.
-//
-#pragma once
-
-#include <cxxtest/TestSuite.h>
-
-#include "json/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_write_string() {
-    std::string const 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 const 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);
-  }
-};

+ 66 - 0
test/json_binder_value_string_test.cxx

@@ -0,0 +1,66 @@
+//
+//  json_binder_value_string.t.h
+//  json
+//
+//  Created by Sam Jaffe on 2/25/17.
+//
+
+#include <gmock/gmock.h>
+
+#include "json/json_binder.hpp"
+
+using namespace json::binder;
+using namespace json::parser;
+using namespace ::testing;
+
+TEST(JsonBinderStringTest, ParsesSuccessfully) {
+  char data[] = "\"This is a string\"";
+  std::string out = "";
+  value_binder<std::string> binder{};
+  parse(json::binder::bind(out, binder), data, allow_all);
+  EXPECT_THAT(out, "This is a string");
+}
+
+TEST(JsonBinderStringTest, CanParseInternalQuotes) {
+  char data[] = "\"This is a \\\"string\\\"\"";
+  std::string out = "";
+  value_binder<std::string> binder{};
+  parse(json::binder::bind(out, binder), data, allow_all);
+  EXPECT_THAT(out, "This is a \"string\"");
+}
+
+TEST(JsonBinderStringTest, CannotParseRawString) {
+  char data[] = "This is a string";
+  std::string out = "";
+  value_binder<std::string> binder{};
+  EXPECT_THROW(parse(json::binder::bind(out, binder), data, allow_all),
+                   json::malformed_json_exception);
+  EXPECT_THAT(out, "");
+}
+
+TEST(JsonBinderStringTest, ThrowsOnNoEndQuote) {
+  char data[] = "\"This is a string";
+  std::string out = "";
+  value_binder<std::string> binder{};
+  EXPECT_THROW(parse(json::binder::bind(out, binder), data, allow_all),
+                   json::unterminated_json_exception);
+  EXPECT_THAT(out, "");
+}
+
+TEST(JsonBinderStringTest, CanWrite) {
+  std::string const 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);
+  EXPECT_THAT(ss.str(), expected);
+}
+
+TEST(JsonBinderStringTest, EscapesQuotesWhenWriting) {
+  std::string const 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);
+  EXPECT_THAT(ss.str(), expected);
+}