Browse Source

Perform some cleanup, add some test skeletons.

Sam Jaffe 4 years ago
parent
commit
f36d481b18

+ 3 - 2
include/program_args/arguments_impl.hpp

@@ -1,5 +1,6 @@
 #pragma once
 
+#include <iostream>
 #include <utility>
 
 #include "arguments.h"
@@ -14,7 +15,7 @@ Arguments<Impl>::Argument::operator T() const {
   if (!primed()) {
     return T();
   } else if (self->arguments.size() > index) {
-    return convert<T>(self->arguments.at(index));
+    return convert<T>(name, self->arguments.at(index));
   }
   throw IllegalPositionError("No argument provided", index);
 }
@@ -77,7 +78,7 @@ namespace program {
 template <typename Impl>
 template <typename T>
 Arguments<Impl>::Option::operator T() const {
-  return (*this) ? convert<T>(self->options.at(name)) : T();
+  return (*this) ? convert<T>(name, self->options.at(name)) : T();
 }
 
 template <typename Impl> Arguments<Impl>::Option::operator bool() const {

+ 43 - 7
include/program_args/utilities.h

@@ -4,6 +4,19 @@
 #include <type_traits>
 #include <vector>
 
+namespace program::traits {
+template <typename T, typename = void>
+struct is_repeatable : std::false_type {};
+
+template <typename F>
+struct is_repeatable<F, std::enable_if_t<!std::is_void_v<
+                            std::result_of_t<F(std::vector<std::string>)>>>>
+    : std::true_type {};
+
+template <typename T>
+constexpr bool const is_repeatable_v = is_repeatable<T>::value;
+}
+
 namespace program {
 inline std::string join(std::string const & tok,
                         std::vector<std::string> const & data) {
@@ -17,24 +30,47 @@ inline std::string join(std::string const & tok,
 
 template <typename T, typename = void> struct conversion_helper;
 
-template <typename T, typename D> T convert(D const & data) {
+/**
+ * \brief Conversion method for positional arguments. Positional arguments are
+ * always represented with a singular string.
+ * \param name The name of the argument being parsed, for logging purposes
+ * \param data A string containing the value to be processed.
+ * \return An object of the given type
+ */
+template <typename T>
+T convert(std::string const & name, std::string const & data) {
   return conversion_helper<T>{}(data);
 }
 
+/**
+ * \brief Conversion method for command-line options. Because some options are
+ * repeatable, we need to pass in a vector of objects that might be used.
+ * \param name The name of the option being parsed, for logging purposes
+ * \param data A vector of arguments assigned to this option.
+ * \invariant data.size() > 0
+ * \return An object of the given type
+ * \throws ArgumentStructureError if the argument has been repeated but is
+ * not a repeatable type.
+ */
+template <typename T>
+T convert(std::string const & name, std::vector<std::string> const & data) {
+  conversion_helper<T> helper;
+  if constexpr (!traits::is_repeatable_v<decltype(helper)>) {
+    return helper(data);
+  } else if (data.size() == 1) {
+    return helper(data.front());
+  }
+  throw ArgumentStructureError("Repeated option not allowed", name);
+}
+
 template <typename T>
 struct conversion_helper<
     T, std::enable_if_t<std::is_convertible_v<std::string, T>>> {
   T operator()(std::string const & str) const { return T(str); }
-  T operator()(std::vector<std::string> const & data) const {
-    return operator()(data.front());
-  }
 };
 
 template <> struct conversion_helper<int> {
   int operator()(std::string const & str) const { return std::stoi(str); }
-  int operator()(std::vector<std::string> const & data) const {
-    return operator()(data.front());
-  }
 };
 
 template <typename T>

+ 22 - 0
program_args-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>

+ 161 - 2
program_args.xcodeproj/project.pbxproj

@@ -6,7 +6,19 @@
 	objectVersion = 50;
 	objects = {
 
+/* Begin PBXBuildFile section */
+		CD8C5A8925D057900004A6D9 /* GoogleMock.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = CDE4F79B25CF316A009E4EC1 /* GoogleMock.framework */; };
+		CD8C5A8B25D057AA0004A6D9 /* options_test.cpp in Sources */ = {isa = PBXBuildFile; fileRef = CD8C5A8A25D057AA0004A6D9 /* options_test.cpp */; };
+/* End PBXBuildFile section */
+
 /* Begin PBXContainerItemProxy section */
+		CD8C5A7B25D0577E0004A6D9 /* PBXContainerItemProxy */ = {
+			isa = PBXContainerItemProxy;
+			containerPortal = CDE4F78225CF309E009E4EC1 /* Project object */;
+			proxyType = 1;
+			remoteGlobalIDString = CDE4F78925CF309E009E4EC1;
+			remoteInfo = program_args;
+		};
 		CDE4F79A25CF316A009E4EC1 /* PBXContainerItemProxy */ = {
 			isa = PBXContainerItemProxy;
 			containerPortal = CDE4F79325CF316A009E4EC1 /* GoogleMock.xcodeproj */;
@@ -38,12 +50,28 @@
 /* End PBXContainerItemProxy section */
 
 /* Begin PBXFileReference section */
+		CD8C5A7525D0577E0004A6D9 /* program_args-test.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "program_args-test.xctest"; sourceTree = BUILT_PRODUCTS_DIR; };
+		CD8C5A7925D0577E0004A6D9 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
+		CD8C5A8A25D057AA0004A6D9 /* options_test.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = options_test.cpp; sourceTree = "<group>"; };
+		CD8C5A9025D057C00004A6D9 /* arguments.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = arguments.h; sourceTree = "<group>"; };
+		CD8C5A9125D057C00004A6D9 /* utilities.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = utilities.h; sourceTree = "<group>"; };
+		CD8C5A9225D057C00004A6D9 /* arguments_impl.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = arguments_impl.hpp; sourceTree = "<group>"; };
+		CD8C5A9325D057C00004A6D9 /* exception.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = exception.h; sourceTree = "<group>"; };
+		CD8C5A9625D058470004A6D9 /* xcode_gtest_helper.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = xcode_gtest_helper.h; sourceTree = "<group>"; };
 		CDE4F78A25CF309E009E4EC1 /* libprogram_args.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libprogram_args.a; sourceTree = BUILT_PRODUCTS_DIR; };
 		CDE4F79325CF316A009E4EC1 /* GoogleMock.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = GoogleMock.xcodeproj; path = "../../../gmock-xcode-master/GoogleMock.xcodeproj"; sourceTree = "<group>"; };
 		CDE4F7A225CF317C009E4EC1 /* program_args */ = {isa = PBXFileReference; lastKnownFileType = folder; name = program_args; path = include/program_args; sourceTree = "<group>"; };
 /* End PBXFileReference section */
 
 /* Begin PBXFrameworksBuildPhase section */
+		CD8C5A7225D0577E0004A6D9 /* Frameworks */ = {
+			isa = PBXFrameworksBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+				CD8C5A8925D057900004A6D9 /* GoogleMock.framework in Frameworks */,
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+		};
 		CDE4F78825CF309E009E4EC1 /* Frameworks */ = {
 			isa = PBXFrameworksBuildPhase;
 			buildActionMask = 2147483647;
@@ -54,13 +82,50 @@
 /* End PBXFrameworksBuildPhase section */
 
 /* Begin PBXGroup section */
+		CD8C5A7625D0577E0004A6D9 /* program_args-test */ = {
+			isa = PBXGroup;
+			children = (
+				CD8C5A7925D0577E0004A6D9 /* Info.plist */,
+			);
+			path = "program_args-test";
+			sourceTree = "<group>";
+		};
+		CD8C5A8825D057900004A6D9 /* Frameworks */ = {
+			isa = PBXGroup;
+			children = (
+			);
+			name = Frameworks;
+			sourceTree = "<group>";
+		};
+		CD8C5A8E25D057C00004A6D9 /* include */ = {
+			isa = PBXGroup;
+			children = (
+				CD8C5A8F25D057C00004A6D9 /* program_args */,
+			);
+			path = include;
+			sourceTree = "<group>";
+		};
+		CD8C5A8F25D057C00004A6D9 /* program_args */ = {
+			isa = PBXGroup;
+			children = (
+				CD8C5A9025D057C00004A6D9 /* arguments.h */,
+				CD8C5A9125D057C00004A6D9 /* utilities.h */,
+				CD8C5A9225D057C00004A6D9 /* arguments_impl.hpp */,
+				CD8C5A9325D057C00004A6D9 /* exception.h */,
+			);
+			path = program_args;
+			sourceTree = "<group>";
+		};
 		CDE4F78125CF309E009E4EC1 = {
 			isa = PBXGroup;
 			children = (
 				CDE4F79325CF316A009E4EC1 /* GoogleMock.xcodeproj */,
+				CD8C5A8E25D057C00004A6D9 /* include */,
 				CDE4F7A225CF317C009E4EC1 /* program_args */,
 				CDE4F79125CF30BA009E4EC1 /* test */,
+				CD8C5A7625D0577E0004A6D9 /* program_args-test */,
 				CDE4F78B25CF309E009E4EC1 /* Products */,
+				CD8C5A8825D057900004A6D9 /* Frameworks */,
 			);
 			sourceTree = "<group>";
 		};
@@ -68,6 +133,7 @@
 			isa = PBXGroup;
 			children = (
 				CDE4F78A25CF309E009E4EC1 /* libprogram_args.a */,
+				CD8C5A7525D0577E0004A6D9 /* program_args-test.xctest */,
 			);
 			name = Products;
 			sourceTree = "<group>";
@@ -75,6 +141,8 @@
 		CDE4F79125CF30BA009E4EC1 /* test */ = {
 			isa = PBXGroup;
 			children = (
+				CD8C5A9625D058470004A6D9 /* xcode_gtest_helper.h */,
+				CD8C5A8A25D057AA0004A6D9 /* options_test.cpp */,
 			);
 			path = test;
 			sourceTree = "<group>";
@@ -103,6 +171,24 @@
 /* End PBXHeadersBuildPhase section */
 
 /* Begin PBXNativeTarget section */
+		CD8C5A7425D0577E0004A6D9 /* program_args-test */ = {
+			isa = PBXNativeTarget;
+			buildConfigurationList = CD8C5A8325D0577E0004A6D9 /* Build configuration list for PBXNativeTarget "program_args-test" */;
+			buildPhases = (
+				CD8C5A7125D0577E0004A6D9 /* Sources */,
+				CD8C5A7225D0577E0004A6D9 /* Frameworks */,
+				CD8C5A7325D0577E0004A6D9 /* Resources */,
+			);
+			buildRules = (
+			);
+			dependencies = (
+				CD8C5A7C25D0577E0004A6D9 /* PBXTargetDependency */,
+			);
+			name = "program_args-test";
+			productName = "program_args-test";
+			productReference = CD8C5A7525D0577E0004A6D9 /* program_args-test.xctest */;
+			productType = "com.apple.product-type.bundle.unit-test";
+		};
 		CDE4F78925CF309E009E4EC1 /* program_args */ = {
 			isa = PBXNativeTarget;
 			buildConfigurationList = CDE4F78E25CF309E009E4EC1 /* Build configuration list for PBXNativeTarget "program_args" */;
@@ -128,6 +214,9 @@
 			attributes = {
 				LastUpgradeCheck = 1240;
 				TargetAttributes = {
+					CD8C5A7425D0577E0004A6D9 = {
+						CreatedOnToolsVersion = 12.4;
+					};
 					CDE4F78925CF309E009E4EC1 = {
 						CreatedOnToolsVersion = 12.4;
 					};
@@ -153,6 +242,7 @@
 			projectRoot = "";
 			targets = (
 				CDE4F78925CF309E009E4EC1 /* program_args */,
+				CD8C5A7425D0577E0004A6D9 /* program_args-test */,
 			);
 		};
 /* End PBXProject section */
@@ -188,7 +278,25 @@
 		};
 /* End PBXReferenceProxy section */
 
+/* Begin PBXResourcesBuildPhase section */
+		CD8C5A7325D0577E0004A6D9 /* Resources */ = {
+			isa = PBXResourcesBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+		};
+/* End PBXResourcesBuildPhase section */
+
 /* Begin PBXSourcesBuildPhase section */
+		CD8C5A7125D0577E0004A6D9 /* Sources */ = {
+			isa = PBXSourcesBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+				CD8C5A8B25D057AA0004A6D9 /* options_test.cpp in Sources */,
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+		};
 		CDE4F78725CF309E009E4EC1 /* Sources */ = {
 			isa = PBXSourcesBuildPhase;
 			buildActionMask = 2147483647;
@@ -198,14 +306,54 @@
 		};
 /* End PBXSourcesBuildPhase section */
 
+/* Begin PBXTargetDependency section */
+		CD8C5A7C25D0577E0004A6D9 /* PBXTargetDependency */ = {
+			isa = PBXTargetDependency;
+			target = CDE4F78925CF309E009E4EC1 /* program_args */;
+			targetProxy = CD8C5A7B25D0577E0004A6D9 /* PBXContainerItemProxy */;
+		};
+/* End PBXTargetDependency section */
+
 /* Begin XCBuildConfiguration section */
+		CD8C5A7D25D0577E0004A6D9 /* Debug */ = {
+			isa = XCBuildConfiguration;
+			buildSettings = {
+				CODE_SIGN_STYLE = Automatic;
+				COMBINE_HIDPI_IMAGES = YES;
+				INFOPLIST_FILE = "program_args-test/Info.plist";
+				LD_RUNPATH_SEARCH_PATHS = (
+					"$(inherited)",
+					"@executable_path/../Frameworks",
+					"@loader_path/../Frameworks",
+				);
+				PRODUCT_BUNDLE_IDENTIFIER = "leumasjaffe.program-args-test";
+				PRODUCT_NAME = "$(TARGET_NAME)";
+			};
+			name = Debug;
+		};
+		CD8C5A7E25D0577E0004A6D9 /* Release */ = {
+			isa = XCBuildConfiguration;
+			buildSettings = {
+				CODE_SIGN_STYLE = Automatic;
+				COMBINE_HIDPI_IMAGES = YES;
+				INFOPLIST_FILE = "program_args-test/Info.plist";
+				LD_RUNPATH_SEARCH_PATHS = (
+					"$(inherited)",
+					"@executable_path/../Frameworks",
+					"@loader_path/../Frameworks",
+				);
+				PRODUCT_BUNDLE_IDENTIFIER = "leumasjaffe.program-args-test";
+				PRODUCT_NAME = "$(TARGET_NAME)";
+			};
+			name = Release;
+		};
 		CDE4F78C25CF309E009E4EC1 /* Debug */ = {
 			isa = XCBuildConfiguration;
 			buildSettings = {
 				ALWAYS_SEARCH_USER_PATHS = NO;
 				CLANG_ANALYZER_NONNULL = YES;
 				CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
-				CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
+				CLANG_CXX_LANGUAGE_STANDARD = "c++17";
 				CLANG_CXX_LIBRARY = "libc++";
 				CLANG_ENABLE_MODULES = YES;
 				CLANG_ENABLE_OBJC_ARC = YES;
@@ -255,6 +403,7 @@
 				MTL_FAST_MATH = YES;
 				ONLY_ACTIVE_ARCH = YES;
 				SDKROOT = macosx;
+				USER_HEADER_SEARCH_PATHS = include/;
 			};
 			name = Debug;
 		};
@@ -264,7 +413,7 @@
 				ALWAYS_SEARCH_USER_PATHS = NO;
 				CLANG_ANALYZER_NONNULL = YES;
 				CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
-				CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
+				CLANG_CXX_LANGUAGE_STANDARD = "c++17";
 				CLANG_CXX_LIBRARY = "libc++";
 				CLANG_ENABLE_MODULES = YES;
 				CLANG_ENABLE_OBJC_ARC = YES;
@@ -307,6 +456,7 @@
 				MTL_ENABLE_DEBUG_INFO = NO;
 				MTL_FAST_MATH = YES;
 				SDKROOT = macosx;
+				USER_HEADER_SEARCH_PATHS = include/;
 			};
 			name = Release;
 		};
@@ -333,6 +483,15 @@
 /* End XCBuildConfiguration section */
 
 /* Begin XCConfigurationList section */
+		CD8C5A8325D0577E0004A6D9 /* Build configuration list for PBXNativeTarget "program_args-test" */ = {
+			isa = XCConfigurationList;
+			buildConfigurations = (
+				CD8C5A7D25D0577E0004A6D9 /* Debug */,
+				CD8C5A7E25D0577E0004A6D9 /* Release */,
+			);
+			defaultConfigurationIsVisible = 0;
+			defaultConfigurationName = Release;
+		};
 		CDE4F78525CF309E009E4EC1 /* Build configuration list for PBXProject "program_args" */ = {
 			isa = XCConfigurationList;
 			buildConfigurations = (

+ 52 - 0
program_args.xcodeproj/xcshareddata/xcschemes/program_args-test.xcscheme

@@ -0,0 +1,52 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<Scheme
+   LastUpgradeVersion = "1240"
+   version = "1.3">
+   <BuildAction
+      parallelizeBuildables = "YES"
+      buildImplicitDependencies = "YES">
+   </BuildAction>
+   <TestAction
+      buildConfiguration = "Debug"
+      selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
+      selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
+      shouldUseLaunchSchemeArgsEnv = "YES">
+      <Testables>
+         <TestableReference
+            skipped = "NO">
+            <BuildableReference
+               BuildableIdentifier = "primary"
+               BlueprintIdentifier = "CD8C5A7425D0577E0004A6D9"
+               BuildableName = "program_args-test.xctest"
+               BlueprintName = "program_args-test"
+               ReferencedContainer = "container:program_args.xcodeproj">
+            </BuildableReference>
+         </TestableReference>
+      </Testables>
+   </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">
+   </LaunchAction>
+   <ProfileAction
+      buildConfiguration = "Release"
+      shouldUseLaunchSchemeArgsEnv = "YES"
+      savedToolIdentifier = ""
+      useCustomWorkingDirectory = "NO"
+      debugDocumentVersioning = "YES">
+   </ProfileAction>
+   <AnalyzeAction
+      buildConfiguration = "Debug">
+   </AnalyzeAction>
+   <ArchiveAction
+      buildConfiguration = "Release"
+      revealArchiveInOrganizer = "YES">
+   </ArchiveAction>
+</Scheme>

+ 78 - 0
program_args.xcodeproj/xcshareddata/xcschemes/program_args.xcscheme

@@ -0,0 +1,78 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<Scheme
+   LastUpgradeVersion = "1240"
+   version = "1.3">
+   <BuildAction
+      parallelizeBuildables = "YES"
+      buildImplicitDependencies = "YES">
+      <BuildActionEntries>
+         <BuildActionEntry
+            buildForTesting = "YES"
+            buildForRunning = "YES"
+            buildForProfiling = "YES"
+            buildForArchiving = "YES"
+            buildForAnalyzing = "YES">
+            <BuildableReference
+               BuildableIdentifier = "primary"
+               BlueprintIdentifier = "CDE4F78925CF309E009E4EC1"
+               BuildableName = "libprogram_args.a"
+               BlueprintName = "program_args"
+               ReferencedContainer = "container:program_args.xcodeproj">
+            </BuildableReference>
+         </BuildActionEntry>
+      </BuildActionEntries>
+   </BuildAction>
+   <TestAction
+      buildConfiguration = "Debug"
+      selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
+      selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
+      shouldUseLaunchSchemeArgsEnv = "YES"
+      codeCoverageEnabled = "YES">
+      <Testables>
+         <TestableReference
+            skipped = "NO">
+            <BuildableReference
+               BuildableIdentifier = "primary"
+               BlueprintIdentifier = "CD8C5A7425D0577E0004A6D9"
+               BuildableName = "program_args-test.xctest"
+               BlueprintName = "program_args-test"
+               ReferencedContainer = "container:program_args.xcodeproj">
+            </BuildableReference>
+         </TestableReference>
+      </Testables>
+   </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">
+   </LaunchAction>
+   <ProfileAction
+      buildConfiguration = "Release"
+      shouldUseLaunchSchemeArgsEnv = "YES"
+      savedToolIdentifier = ""
+      useCustomWorkingDirectory = "NO"
+      debugDocumentVersioning = "YES">
+      <MacroExpansion>
+         <BuildableReference
+            BuildableIdentifier = "primary"
+            BlueprintIdentifier = "CDE4F78925CF309E009E4EC1"
+            BuildableName = "libprogram_args.a"
+            BlueprintName = "program_args"
+            ReferencedContainer = "container:program_args.xcodeproj">
+         </BuildableReference>
+      </MacroExpansion>
+   </ProfileAction>
+   <AnalyzeAction
+      buildConfiguration = "Debug">
+   </AnalyzeAction>
+   <ArchiveAction
+      buildConfiguration = "Release"
+      revealArchiveInOrganizer = "YES">
+   </ArchiveAction>
+</Scheme>

+ 10 - 0
test/options_test.cpp

@@ -0,0 +1,10 @@
+//
+//  options_test.cpp
+//  program_args-test
+//
+//  Created by Sam Jaffe on 2/7/21.
+//
+
+#include "program_args/arguments.h"
+
+#include "xcode_gtest_helper.h"

+ 38 - 0
test/xcode_gtest_helper.h

@@ -0,0 +1,38 @@
+//
+//  xcode_gtest_helper.h
+//  tax-calculator-test
+//
+//  Created by Sam Jaffe on 11/25/20.
+//  Copyright © 2020 Sam Jaffe. All rights reserved.
+//
+
+#pragma once
+
+#if defined(__APPLE__)
+
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wquoted-include-in-framework-header"
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+#pragma clang diagnostic pop
+
+#if defined(TARGET_OS_OSX)
+// This is a hack to allow XCode to properly display failures when running
+// unit tests.
+#undef EXPECT_THAT
+#define EXPECT_THAT ASSERT_THAT
+#undef EXPECT_THROW
+#define EXPECT_THROW ASSERT_THROW
+#undef EXPECT_ANY_THROW
+#define EXPECT_ANY_THROW ASSERT_ANY_THROW
+#undef EXPECT_NO_THROW
+#define EXPECT_NO_THROW ASSERT_NO_THROW
+#undef EXPECT_TRUE
+#define EXPECT_TRUE ASSERT_TRUE
+#undef EXPECT_FALSE
+#define EXPECT_FALSE ASSERT_FALSE
+
+#endif
+#endif