浏览代码

Add test cases to reach 100% coverage, including demonstrating various error cases that I've had to debug.

Bugfix Coverage:
  02132d4: VariantMoveTest.*IsSelfReferentialObjectSafe
  caba555: VariantCopyTest.* and VariantMoveTest.*
Sam Jaffe 5 年之前
父节点
当前提交
3c19d66d36

variant.hpp → include/variant/variant.hpp


+ 21 - 0
test/gtest_variant_matchers.h

@@ -0,0 +1,21 @@
+//
+//  gtest_variant_matchers.h
+//  variant
+//
+//  Created by Sam Jaffe on 8/14/20.
+//  Copyright © 2020 Sam Jaffe. All rights reserved.
+//
+
+#pragma once
+
+#include <gmock/gmock.h>
+
+MATCHER(IsValid, "") {
+  return arg.valid();
+}
+
+MATCHER_P(IsStoringType, type_instance, "") {
+  return arg.template is<decltype(type_instance)>();
+}
+
+template <typename T> auto IsStoringType() { return IsStoringType(T()); };

+ 44 - 0
test/gtest_variant_printers.h

@@ -0,0 +1,44 @@
+//
+//  gtest_variant_printers.h
+//  variant
+//
+//  Created by Sam Jaffe on 8/14/20.
+//  Copyright © 2020 Sam Jaffe. All rights reserved.
+//
+
+#pragma once
+
+template <typename K, typename V>
+std::ostream & operator<<(std::ostream & os, std::map<K, V> const & value) {
+  os << "{";
+  if (value.size()) {
+    auto it = value.cbegin();
+    os << " " << it->first << ":" << it->second;
+    for (++it; it != value.cend(); ++it) {
+      os << ", " << it->first << ":" << it->second;
+    }
+  }
+  return os << " }";
+}
+
+template <std::size_t I, typename ...Ts>
+void PrintTo(variant<Ts...> const & value, std::ostream * os) {
+  if (value.index() == I) {
+    (*os) << value.template get<I>();
+  }
+}
+
+template <typename ...Ts, std::size_t ...Is>
+void PrintTo(variant<Ts...> const & value, std::ostream * os,
+             std::index_sequence<Is...>) {
+  [[maybe_unused]] auto l = { (PrintTo<Is>(value, os), 0)... };
+}
+
+template <typename ...Ts>
+void PrintTo(variant<Ts...> const & value, std::ostream * os) {
+  if (value.valid()) {
+    PrintTo(value, os, std::make_index_sequence<sizeof...(Ts)>());
+  } else {
+    (*os) << "<invalid>";
+  }
+}

+ 231 - 0
test/variant_test.cxx

@@ -0,0 +1,231 @@
+//
+//  variant_test.cpp
+//  variant-test
+//
+//  Created by Sam Jaffe on 8/14/20.
+//  Copyright © 2020 Sam Jaffe. All rights reserved.
+//
+
+#include "variant/variant.hpp"
+
+#include <map>
+#include <string>
+#include <utility>
+
+#include <gmock/gmock.h>
+
+#include "gtest_variant_printers.h"
+#include "gtest_variant_matchers.h"
+
+using test_variant = variant<int, bool, std::string, std::map<int, int>>;
+
+TEST(VariantTest, DefaultVariantIsTypeless) {
+  test_variant test;
+  EXPECT_THAT(test, testing::Not(IsValid()));
+  EXPECT_THAT(test.index(), 0xFFFFFFFF);
+}
+
+TEST(VariantTest, CanSetValue) {
+  test_variant test;
+  test.set<int>(0);
+  EXPECT_THAT(test, IsValid());
+  EXPECT_THAT(test, IsStoringType<int>());
+}
+
+TEST(VariantTest, CanConstructFromValue) {
+  EXPECT_NO_THROW(test_variant{int(0)});
+}
+
+TEST(VariantTest, CanGetValueByTypeId) {
+  test_variant test(int(0));
+  EXPECT_NO_THROW(test.get<int>());
+  EXPECT_THAT(test.get<int>(), 0);
+}
+
+TEST(VariantTest, CanGetValueByTypeIndex) {
+  test_variant test(int(0));
+  EXPECT_NO_THROW(test.get<0>());
+  EXPECT_THAT(test.get<0>(), 0);
+}
+
+TEST(VariantTest, CanInvokeStdGet) {
+  test_variant test(int(0));
+  EXPECT_NO_THROW(std::get<0>(test));
+  EXPECT_THAT(std::get<0>(test), 0);
+}
+
+TEST(VariantTest, WillThrowOnInvalidGetType) {
+  test_variant test(int(0));
+  EXPECT_THROW(test.get<bool>(), std::bad_cast);
+}
+
+TEST(VariantConstTest, CanGetValueByTypeId) {
+  test_variant const test(int(0));
+  EXPECT_NO_THROW(test.get<int>());
+  EXPECT_THAT(test.get<int>(), 0);
+}
+
+TEST(VariantConstTest, CanGetValueByTypeIndex) {
+  test_variant const test(int(0));
+  EXPECT_NO_THROW(test.get<0>());
+  EXPECT_THAT(test.get<0>(), 0);
+}
+
+TEST(VariantConstTest, CanInvokeStdGet) {
+  test_variant const test(int(0));
+  EXPECT_NO_THROW(std::get<0>(test));
+  EXPECT_THAT(std::get<0>(test), 0);
+}
+
+TEST(VariantConstTest, WillThrowOnInvalidGetType) {
+  test_variant const test(int(0));
+  EXPECT_THROW(test.get<bool>(), std::bad_cast);
+}
+
+TEST(VariantCopyTest, CtorIsInvalidSafe) {
+  test_variant const test;
+  test_variant copy(test);
+  EXPECT_THAT(test, testing::Not(IsValid()));
+  EXPECT_THAT(copy, testing::Not(IsValid()));
+}
+
+TEST(VariantCopyTest, CtorIsPODSafe) {
+  test_variant const test(int(0));
+  test_variant copy(test);
+  EXPECT_THAT(test, IsValid());
+  EXPECT_THAT(copy, IsValid());
+  EXPECT_THAT(copy, IsStoringType<int>());
+  EXPECT_THAT(copy.get<int>(), 0);
+}
+
+TEST(VariantCopyTest, CtorIsObjectSafe) {
+  test_variant const test(std::string("hello"));
+  test_variant copy(test);
+  EXPECT_THAT(test, IsValid());
+  EXPECT_THAT(copy, IsValid());
+  EXPECT_THAT(copy, IsStoringType<std::string>());
+  EXPECT_THAT(copy.get<std::string>(), "hello");
+}
+
+TEST(VariantCopyTest, CtorIsSelfReferentialObjectSafe) {
+  std::map<int, int> map{{1, 2}, {2, 3}};
+  test_variant const test(map);
+  test_variant copy(test);
+  EXPECT_THAT(test, IsValid());
+  EXPECT_THAT(copy, IsValid());
+  EXPECT_THAT(copy, IsStoringType<decltype(map)>());
+  EXPECT_THAT(copy.get<decltype(map)>(), map);
+}
+
+TEST(VariantCopyTest, AssignIsInvalidSafe) {
+  test_variant const test;
+  test_variant copy;
+  copy = test;
+  EXPECT_THAT(test, testing::Not(IsValid()));
+  EXPECT_THAT(copy, testing::Not(IsValid()));
+}
+
+TEST(VariantCopyTest, AssignIsPODSafe) {
+  test_variant const test(int(0));
+  test_variant copy;
+  copy = test;
+  EXPECT_THAT(test, IsValid());
+  EXPECT_THAT(copy, IsValid());
+  EXPECT_THAT(copy, IsStoringType<int>());
+  EXPECT_THAT(copy.get<int>(), 0);
+}
+
+TEST(VariantCopyTest, AssignIsObjectSafe) {
+  test_variant const test(std::string("hello"));
+  test_variant copy;
+  copy = test;
+  EXPECT_THAT(test, IsValid());
+  EXPECT_THAT(copy, IsValid());
+  EXPECT_THAT(copy, IsStoringType<std::string>());
+  EXPECT_THAT(copy.get<std::string>(), "hello");
+}
+
+TEST(VariantCopyTest, AssignIsSelfReferentialObjectSafe) {
+  std::map<int, int> map{{1, 2}, {2, 3}};
+  test_variant const test(map);
+  test_variant copy;
+  copy = test;
+  EXPECT_THAT(test, IsValid());
+  EXPECT_THAT(copy, IsValid());
+  EXPECT_THAT(copy, IsStoringType<decltype(map)>());
+  EXPECT_THAT(copy.get<decltype(map)>(), map);
+}
+
+TEST(VariantMoveTest, CtorIsInvalidSafe) {
+  test_variant test;
+  test_variant copy(std::move(test));
+  EXPECT_THAT(test, testing::Not(IsValid()));
+  EXPECT_THAT(copy, testing::Not(IsValid()));
+}
+
+TEST(VariantMoveTest, CtorIsPODSafe) {
+  test_variant test(int(0));
+  test_variant copy(std::move(test));
+  EXPECT_THAT(test, testing::Not(IsValid()));
+  EXPECT_THAT(copy, IsValid());
+  EXPECT_THAT(copy, IsStoringType<int>());
+  EXPECT_THAT(copy.get<int>(), 0);
+}
+
+TEST(VariantMoveTest, CtorIsObjectSafe) {
+  test_variant test(std::string("hello"));
+  test_variant copy(std::move(test));
+  EXPECT_THAT(test, testing::Not(IsValid()));
+  EXPECT_THAT(copy, IsValid());
+  EXPECT_THAT(copy, IsStoringType<std::string>());
+  EXPECT_THAT(copy.get<std::string>(), "hello");
+}
+
+TEST(VariantMoveTest, CtorIsSelfReferentialObjectSafe) {
+  std::map<int, int> map{{1, 2}, {2, 3}};
+  test_variant test(map);
+  test_variant copy(std::move(test));
+  EXPECT_THAT(test, testing::Not(IsValid()));
+  EXPECT_THAT(copy, IsValid());
+  EXPECT_THAT(copy, IsStoringType<decltype(map)>());
+  EXPECT_THAT(copy.get<decltype(map)>(), map);
+}
+
+TEST(VariantMoveTest, AssignIsInvalidSafe) {
+  test_variant test;
+  test_variant copy;
+  copy = std::move(test);
+  EXPECT_THAT(test, testing::Not(IsValid()));
+  EXPECT_THAT(copy, testing::Not(IsValid()));
+}
+
+TEST(VariantMoveTest, AssignIsPODSafe) {
+  test_variant test(int(0));
+  test_variant copy;
+  copy = std::move(test);
+  EXPECT_THAT(test, testing::Not(IsValid()));
+  EXPECT_THAT(copy, IsValid());
+  EXPECT_THAT(copy, IsStoringType<int>());
+  EXPECT_THAT(copy.get<int>(), 0);
+}
+
+TEST(VariantMoveTest, AssignIsObjectSafe) {
+  test_variant test(std::string("hello"));
+  test_variant copy;
+  copy = std::move(test);
+  EXPECT_THAT(test, testing::Not(IsValid()));
+  EXPECT_THAT(copy, IsValid());
+  EXPECT_THAT(copy, IsStoringType<std::string>());
+  EXPECT_THAT(copy.get<std::string>(), "hello");
+}
+
+TEST(VariantMoveTest, AssignIsSelfReferentialObjectSafe) {
+  std::map<int, int> map{{1, 2}, {2, 3}};
+  test_variant test(map);
+  test_variant copy;
+  copy = std::move(test);
+  EXPECT_THAT(test, testing::Not(IsValid()));
+  EXPECT_THAT(copy, IsValid());
+  EXPECT_THAT(copy, IsStoringType<decltype(map)>());
+  EXPECT_THAT(copy.get<decltype(map)>(), map);
+}

+ 22 - 0
variant-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>$(PRODUCT_BUNDLE_PACKAGE_TYPE)</string>
+	<key>CFBundleShortVersionString</key>
+	<string>1.0</string>
+	<key>CFBundleVersion</key>
+	<string>1</string>
+</dict>
+</plist>

+ 0 - 54
variant.cpp

@@ -1,54 +0,0 @@
-//
-//  vairant.cpp
-//  variant
-//
-//  Created by Sam Jaffe on 1/30/16.
-//  Copyright © 2016 Sam Jaffe. All rights reserved.
-//
-
-#include "variant.hpp"
-#include <iostream>
-
-struct test{
-  int * holder;
-  test() {
-    std::cout << "test()" << std::endl;
-    holder = new int();
-  }
-  
-  test(test&& old) : holder(nullptr) {
-    std::cout << "test(test&&)" << std::endl;
-    std::swap(holder,old.holder);
-  }
-  test(const test& old) {
-    std::cout << "test(const test&)" << std::endl;
-    holder = new int(*old.holder);
-  }
-  ~test()
-  {
-    std::cout << "~test()" << std::endl;
-    delete holder;
-  }
-};
-
-
-int main() {
-  using my_var = variant<std::string, test>;
-  
-  my_var d{};
-  
-  d.set<std::string>("First string");
-  std::cout << d.get<std::string>() << std::endl;
-  
-  d.set<test>();
-  *d.get<test>().holder = 42;
-  
-  my_var e(std::move(d));
-  std::cout << *e.get<test>().holder << std::endl;
-  
-  *e.get<test>().holder = 43;
-  
-  d = e;
-  
-  std::cout << *d.get<test>().holder << std::endl;
-}

+ 197 - 70
variant.xcodeproj/project.pbxproj

@@ -7,74 +7,138 @@
 	objects = {
 
 /* Begin PBXBuildFile section */
-		CDB2F7251C5D43010067C2EC /* variant.hpp in Headers */ = {isa = PBXBuildFile; fileRef = CDB2F7241C5D43000067C2EC /* variant.hpp */; };
-		CDB2F7281C5D45B60067C2EC /* variant.cpp in Sources */ = {isa = PBXBuildFile; fileRef = CDB2F7261C5D45B60067C2EC /* variant.cpp */; };
+		CD89E54024E700DA008167A8 /* GoogleMock.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = CD89E52A24E7008F008167A8 /* GoogleMock.framework */; };
+		CD89E54224E700E2008167A8 /* variant_test.cxx in Sources */ = {isa = PBXBuildFile; fileRef = CD89E54124E700E2008167A8 /* variant_test.cxx */; };
 /* End PBXBuildFile section */
 
+/* Begin PBXContainerItemProxy section */
+		CD89E52924E7008F008167A8 /* PBXContainerItemProxy */ = {
+			isa = PBXContainerItemProxy;
+			containerPortal = CD89E52224E7008F008167A8 /* GoogleMock.xcodeproj */;
+			proxyType = 2;
+			remoteGlobalIDString = 05818F861A685AEA0072A469;
+			remoteInfo = GoogleMock;
+		};
+		CD89E52B24E7008F008167A8 /* PBXContainerItemProxy */ = {
+			isa = PBXContainerItemProxy;
+			containerPortal = CD89E52224E7008F008167A8 /* GoogleMock.xcodeproj */;
+			proxyType = 2;
+			remoteGlobalIDString = 05E96ABD1A68600C00204102;
+			remoteInfo = gmock;
+		};
+		CD89E52D24E7008F008167A8 /* PBXContainerItemProxy */ = {
+			isa = PBXContainerItemProxy;
+			containerPortal = CD89E52224E7008F008167A8 /* GoogleMock.xcodeproj */;
+			proxyType = 2;
+			remoteGlobalIDString = 05E96B1F1A68634900204102;
+			remoteInfo = gtest;
+		};
+		CD89E52F24E7008F008167A8 /* PBXContainerItemProxy */ = {
+			isa = PBXContainerItemProxy;
+			containerPortal = CD89E52224E7008F008167A8 /* GoogleMock.xcodeproj */;
+			proxyType = 2;
+			remoteGlobalIDString = 05818F901A685AEA0072A469;
+			remoteInfo = GoogleMockTests;
+		};
+/* End PBXContainerItemProxy section */
+
 /* Begin PBXFileReference section */
-		CDB2F7161C5D42EB0067C2EC /* libvariant.dylib */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.dylib"; includeInIndex = 0; path = libvariant.dylib; sourceTree = BUILT_PRODUCTS_DIR; };
-		CDB2F7241C5D43000067C2EC /* variant.hpp */ = {isa = PBXFileReference; indentWidth = 2; lastKnownFileType = sourcecode.cpp.h; path = variant.hpp; sourceTree = "<group>"; tabWidth = 2; };
-		CDB2F7261C5D45B60067C2EC /* variant.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = variant.cpp; sourceTree = "<group>"; };
+		CD89E52224E7008F008167A8 /* GoogleMock.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = GoogleMock.xcodeproj; path = "../../../gmock-xcode-master/GoogleMock.xcodeproj"; sourceTree = "<group>"; };
+		CD89E53124E700A6008167A8 /* variant */ = {isa = PBXFileReference; lastKnownFileType = folder; name = variant; path = include/variant; sourceTree = "<group>"; };
+		CD89E53724E700BE008167A8 /* variant-test.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "variant-test.xctest"; sourceTree = BUILT_PRODUCTS_DIR; };
+		CD89E53B24E700BE008167A8 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
+		CD89E54124E700E2008167A8 /* variant_test.cxx */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = variant_test.cxx; sourceTree = "<group>"; };
+		CD89E54324E70324008167A8 /* gtest_variant_printers.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = gtest_variant_printers.h; sourceTree = "<group>"; };
+		CD89E54424E7034C008167A8 /* gtest_variant_matchers.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = gtest_variant_matchers.h; sourceTree = "<group>"; };
 /* End PBXFileReference section */
 
 /* Begin PBXFrameworksBuildPhase section */
-		CDB2F7131C5D42EB0067C2EC /* Frameworks */ = {
+		CD89E53424E700BE008167A8 /* Frameworks */ = {
 			isa = PBXFrameworksBuildPhase;
 			buildActionMask = 2147483647;
 			files = (
+				CD89E54024E700DA008167A8 /* GoogleMock.framework in Frameworks */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};
 /* End PBXFrameworksBuildPhase section */
 
 /* Begin PBXGroup section */
+		CD89E52324E7008F008167A8 /* Products */ = {
+			isa = PBXGroup;
+			children = (
+				CD89E52A24E7008F008167A8 /* GoogleMock.framework */,
+				CD89E52C24E7008F008167A8 /* gmock.framework */,
+				CD89E52E24E7008F008167A8 /* gtest.framework */,
+				CD89E53024E7008F008167A8 /* GoogleMockTests.xctest */,
+			);
+			name = Products;
+			sourceTree = "<group>";
+		};
+		CD89E53224E700B2008167A8 /* test */ = {
+			isa = PBXGroup;
+			children = (
+				CD89E54124E700E2008167A8 /* variant_test.cxx */,
+				CD89E54324E70324008167A8 /* gtest_variant_printers.h */,
+				CD89E54424E7034C008167A8 /* gtest_variant_matchers.h */,
+			);
+			path = test;
+			sourceTree = "<group>";
+		};
+		CD89E53824E700BE008167A8 /* variant-test */ = {
+			isa = PBXGroup;
+			children = (
+				CD89E53B24E700BE008167A8 /* Info.plist */,
+			);
+			path = "variant-test";
+			sourceTree = "<group>";
+		};
+		CD89E53F24E700DA008167A8 /* Frameworks */ = {
+			isa = PBXGroup;
+			children = (
+			);
+			name = Frameworks;
+			sourceTree = "<group>";
+		};
 		CDB2F70D1C5D42EB0067C2EC = {
 			isa = PBXGroup;
 			children = (
-				CDB2F7241C5D43000067C2EC /* variant.hpp */,
-				CDB2F7261C5D45B60067C2EC /* variant.cpp */,
+				CD89E52224E7008F008167A8 /* GoogleMock.xcodeproj */,
+				CD89E53124E700A6008167A8 /* variant */,
+				CD89E53224E700B2008167A8 /* test */,
+				CD89E53824E700BE008167A8 /* variant-test */,
 				CDB2F7171C5D42EB0067C2EC /* Products */,
+				CD89E53F24E700DA008167A8 /* Frameworks */,
 			);
 			sourceTree = "<group>";
 		};
 		CDB2F7171C5D42EB0067C2EC /* Products */ = {
 			isa = PBXGroup;
 			children = (
-				CDB2F7161C5D42EB0067C2EC /* libvariant.dylib */,
+				CD89E53724E700BE008167A8 /* variant-test.xctest */,
 			);
 			name = Products;
 			sourceTree = "<group>";
 		};
 /* End PBXGroup section */
 
-/* Begin PBXHeadersBuildPhase section */
-		CDB2F7141C5D42EB0067C2EC /* Headers */ = {
-			isa = PBXHeadersBuildPhase;
-			buildActionMask = 2147483647;
-			files = (
-				CDB2F7251C5D43010067C2EC /* variant.hpp in Headers */,
-			);
-			runOnlyForDeploymentPostprocessing = 0;
-		};
-/* End PBXHeadersBuildPhase section */
-
 /* Begin PBXNativeTarget section */
-		CDB2F7151C5D42EB0067C2EC /* variant */ = {
+		CD89E53624E700BE008167A8 /* variant-test */ = {
 			isa = PBXNativeTarget;
-			buildConfigurationList = CDB2F7211C5D42EB0067C2EC /* Build configuration list for PBXNativeTarget "variant" */;
+			buildConfigurationList = CD89E53C24E700BE008167A8 /* Build configuration list for PBXNativeTarget "variant-test" */;
 			buildPhases = (
-				CDB2F7121C5D42EB0067C2EC /* Sources */,
-				CDB2F7131C5D42EB0067C2EC /* Frameworks */,
-				CDB2F7141C5D42EB0067C2EC /* Headers */,
+				CD89E53324E700BE008167A8 /* Sources */,
+				CD89E53424E700BE008167A8 /* Frameworks */,
+				CD89E53524E700BE008167A8 /* Resources */,
 			);
 			buildRules = (
 			);
 			dependencies = (
 			);
-			name = variant;
-			productName = variant;
-			productReference = CDB2F7161C5D42EB0067C2EC /* libvariant.dylib */;
-			productType = "com.apple.product-type.library.dynamic";
+			name = "variant-test";
+			productName = "variant-test";
+			productReference = CD89E53724E700BE008167A8 /* variant-test.xctest */;
+			productType = "com.apple.product-type.bundle.unit-test";
 		};
 /* End PBXNativeTarget section */
 
@@ -85,8 +149,9 @@
 				LastUpgradeCheck = 1030;
 				ORGANIZATIONNAME = "Sam Jaffe";
 				TargetAttributes = {
-					CDB2F7151C5D42EB0067C2EC = {
-						CreatedOnToolsVersion = 7.2;
+					CD89E53624E700BE008167A8 = {
+						CreatedOnToolsVersion = 11.3.1;
+						ProvisioningStyle = Automatic;
 					};
 				};
 			};
@@ -101,25 +166,115 @@
 			mainGroup = CDB2F70D1C5D42EB0067C2EC;
 			productRefGroup = CDB2F7171C5D42EB0067C2EC /* Products */;
 			projectDirPath = "";
+			projectReferences = (
+				{
+					ProductGroup = CD89E52324E7008F008167A8 /* Products */;
+					ProjectRef = CD89E52224E7008F008167A8 /* GoogleMock.xcodeproj */;
+				},
+			);
 			projectRoot = "";
 			targets = (
-				CDB2F7151C5D42EB0067C2EC /* variant */,
+				CD89E53624E700BE008167A8 /* variant-test */,
 			);
 		};
 /* End PBXProject section */
 
+/* Begin PBXReferenceProxy section */
+		CD89E52A24E7008F008167A8 /* GoogleMock.framework */ = {
+			isa = PBXReferenceProxy;
+			fileType = wrapper.framework;
+			path = GoogleMock.framework;
+			remoteRef = CD89E52924E7008F008167A8 /* PBXContainerItemProxy */;
+			sourceTree = BUILT_PRODUCTS_DIR;
+		};
+		CD89E52C24E7008F008167A8 /* gmock.framework */ = {
+			isa = PBXReferenceProxy;
+			fileType = wrapper.framework;
+			path = gmock.framework;
+			remoteRef = CD89E52B24E7008F008167A8 /* PBXContainerItemProxy */;
+			sourceTree = BUILT_PRODUCTS_DIR;
+		};
+		CD89E52E24E7008F008167A8 /* gtest.framework */ = {
+			isa = PBXReferenceProxy;
+			fileType = wrapper.framework;
+			path = gtest.framework;
+			remoteRef = CD89E52D24E7008F008167A8 /* PBXContainerItemProxy */;
+			sourceTree = BUILT_PRODUCTS_DIR;
+		};
+		CD89E53024E7008F008167A8 /* GoogleMockTests.xctest */ = {
+			isa = PBXReferenceProxy;
+			fileType = wrapper.cfbundle;
+			path = GoogleMockTests.xctest;
+			remoteRef = CD89E52F24E7008F008167A8 /* PBXContainerItemProxy */;
+			sourceTree = BUILT_PRODUCTS_DIR;
+		};
+/* End PBXReferenceProxy section */
+
+/* Begin PBXResourcesBuildPhase section */
+		CD89E53524E700BE008167A8 /* Resources */ = {
+			isa = PBXResourcesBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+		};
+/* End PBXResourcesBuildPhase section */
+
 /* Begin PBXSourcesBuildPhase section */
-		CDB2F7121C5D42EB0067C2EC /* Sources */ = {
+		CD89E53324E700BE008167A8 /* Sources */ = {
 			isa = PBXSourcesBuildPhase;
 			buildActionMask = 2147483647;
 			files = (
-				CDB2F7281C5D45B60067C2EC /* variant.cpp in Sources */,
+				CD89E54224E700E2008167A8 /* variant_test.cxx in Sources */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};
 /* End PBXSourcesBuildPhase section */
 
 /* Begin XCBuildConfiguration section */
+		CD89E53D24E700BE008167A8 /* 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 = "variant-test/Info.plist";
+				LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks";
+				MACOSX_DEPLOYMENT_TARGET = 10.14;
+				MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
+				MTL_FAST_MATH = YES;
+				PRODUCT_BUNDLE_IDENTIFIER = "leumasjaffe.variant-test";
+				PRODUCT_NAME = "$(TARGET_NAME)";
+			};
+			name = Debug;
+		};
+		CD89E53E24E700BE008167A8 /* 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 = "variant-test/Info.plist";
+				LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks";
+				MACOSX_DEPLOYMENT_TARGET = 10.14;
+				MTL_FAST_MATH = YES;
+				PRODUCT_BUNDLE_IDENTIFIER = "leumasjaffe.variant-test";
+				PRODUCT_NAME = "$(TARGET_NAME)";
+			};
+			name = Release;
+		};
 		CDB2F71F1C5D42EB0067C2EC /* Debug */ = {
 			isa = XCBuildConfiguration;
 			buildSettings = {
@@ -171,6 +326,7 @@
 				MTL_ENABLE_DEBUG_INFO = YES;
 				ONLY_ACTIVE_ARCH = YES;
 				SDKROOT = macosx;
+				USER_HEADER_SEARCH_PATHS = include/;
 			};
 			name = Debug;
 		};
@@ -218,56 +374,27 @@
 				MACOSX_DEPLOYMENT_TARGET = 10.10;
 				MTL_ENABLE_DEBUG_INFO = NO;
 				SDKROOT = macosx;
-			};
-			name = Release;
-		};
-		CDB2F7221C5D42EB0067C2EC /* 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;
-				GCC_TREAT_WARNINGS_AS_ERRORS = YES;
-				GCC_WARN_PEDANTIC = YES;
-				PRODUCT_NAME = "$(TARGET_NAME)";
-			};
-			name = Debug;
-		};
-		CDB2F7231C5D42EB0067C2EC /* 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;
-				GCC_TREAT_WARNINGS_AS_ERRORS = YES;
-				GCC_WARN_PEDANTIC = YES;
-				PRODUCT_NAME = "$(TARGET_NAME)";
+				USER_HEADER_SEARCH_PATHS = include/;
 			};
 			name = Release;
 		};
 /* End XCBuildConfiguration section */
 
 /* Begin XCConfigurationList section */
-		CDB2F7111C5D42EB0067C2EC /* Build configuration list for PBXProject "variant" */ = {
+		CD89E53C24E700BE008167A8 /* Build configuration list for PBXNativeTarget "variant-test" */ = {
 			isa = XCConfigurationList;
 			buildConfigurations = (
-				CDB2F71F1C5D42EB0067C2EC /* Debug */,
-				CDB2F7201C5D42EB0067C2EC /* Release */,
+				CD89E53D24E700BE008167A8 /* Debug */,
+				CD89E53E24E700BE008167A8 /* Release */,
 			);
 			defaultConfigurationIsVisible = 0;
 			defaultConfigurationName = Release;
 		};
-		CDB2F7211C5D42EB0067C2EC /* Build configuration list for PBXNativeTarget "variant" */ = {
+		CDB2F7111C5D42EB0067C2EC /* Build configuration list for PBXProject "variant" */ = {
 			isa = XCConfigurationList;
 			buildConfigurations = (
-				CDB2F7221C5D42EB0067C2EC /* Debug */,
-				CDB2F7231C5D42EB0067C2EC /* Release */,
+				CDB2F71F1C5D42EB0067C2EC /* Debug */,
+				CDB2F7201C5D42EB0067C2EC /* Release */,
 			);
 			defaultConfigurationIsVisible = 0;
 			defaultConfigurationName = Release;

+ 5 - 0
variant.xcodeproj/xcuserdata/samjaffe.xcuserdatad/xcschemes/xcschememanagement.plist

@@ -4,6 +4,11 @@
 <dict>
 	<key>SchemeUserState</key>
 	<dict>
+		<key>variant-test.xcscheme_^#shared#^_</key>
+		<dict>
+			<key>orderHint</key>
+			<integer>62</integer>
+		</dict>
 		<key>variant.xcscheme</key>
 		<dict>
 			<key>orderHint</key>