Jelajahi Sumber

Merge pull request #235 from kishikawakatsumi/testhost

Use test host for unit tests
kishikawa katsumi 9 tahun lalu
induk
melakukan
7d71a32044

+ 1 - 1
Examples/Example-iOS/Example-iOS.xcodeproj/xcshareddata/xcschemes/Example-iOS.xcscheme

@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <Scheme
-   LastUpgradeVersion = "0720"
+   LastUpgradeVersion = "0730"
    version = "1.3">
    <BuildAction
       parallelizeBuildables = "YES"

+ 26 - 0
Lib/Configurations/TestHost.xcconfig

@@ -0,0 +1,26 @@
+SUPPORTED_PLATFORMS = macosx iphoneos iphonesimulator appletvos appletvsimulator;
+TARGETED_DEVICE_FAMILY = 1,2,3;
+
+COMBINE_HIDPI_IMAGES = YES
+COPY_PHASE_STRIP = NO;
+INFOPLIST_FILE = TestHost/Info.plist;
+PRODUCT_NAME = $(TARGET_NAME);
+CLANG_MODULES_AUTOLINK = NO;
+ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
+
+PRODUCT_BUNDLE_IDENTIFIER = com.kishikawakatsumi.TestHost;
+
+CODE_SIGNING_REQUIRED[sdk=macosx] = NO;
+CODE_SIGN_ENTITLEMENTS = TestHost/TestHost.entitlements;
+OTHER_CODE_SIGN_FLAGS[sdk=macosx*] = --deep;
+PROVISIONING_PROFILE[sdk=iphone*] = ;
+PROVISIONING_PROFILE[sdk=appletv*] = ;
+
+PRINCIPAL_CLASS[sdk=iphone*] = UIApplication;
+PRINCIPAL_CLASS[sdk=appletv*] = UIApplication;
+PRINCIPAL_CLASS[sdk=macosx*] = NSApplication;
+
+LD_RUNPATH_SEARCH_PATHS[sdk=macosx*] = $(inherited) @executable_path/../Frameworks @loader_path/../Frameworks;
+LD_RUNPATH_SEARCH_PATHS[sdk=iphone*] = $(inherited) @executable_path/Frameworks @loader_path/Frameworks;
+LD_RUNPATH_SEARCH_PATHS[sdk=watch*] = $(inherited) @executable_path/Frameworks @loader_path/Frameworks;
+LD_RUNPATH_SEARCH_PATHS[sdk=appletv*] = $(inherited) @executable_path/Frameworks @loader_path/Frameworks;

+ 4 - 0
Lib/Configurations/Tests.xcconfig

@@ -11,6 +11,10 @@ LD_RUNPATH_SEARCH_PATHS[sdk=iphone*] = $(inherited) @executable_path/Frameworks
 LD_RUNPATH_SEARCH_PATHS[sdk=watch*] = $(inherited) @executable_path/Frameworks @loader_path/Frameworks;
 LD_RUNPATH_SEARCH_PATHS[sdk=appletv*] = $(inherited) @executable_path/Frameworks @loader_path/Frameworks;
 
+TEST_HOST[sdk=iphone*] = $(BUILT_PRODUCTS_DIR)/TestHost.app/TestHost;
+TEST_HOST[sdk=appletv*] = $(BUILT_PRODUCTS_DIR)/TestHost.app/TestHost;
+TEST_HOST[sdk=macosx*] = $(BUILT_PRODUCTS_DIR)/TestHost.app/Contents/MacOS/TestHost;
+
 EXCLUDED_SOURCE_FILE_NAMES[sdk=macosx*] = SharedCredentialTests.swift;
 EXCLUDED_SOURCE_FILE_NAMES[sdk=watch*] = *;
 EXCLUDED_SOURCE_FILE_NAMES[sdk=appletv*] = SharedCredentialTests.swift;

+ 148 - 2
Lib/KeychainAccess.xcodeproj/project.pbxproj

@@ -13,6 +13,10 @@
 		142EDA851BCB505F00A32149 /* ErrorTypeTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 142EDA841BCB505F00A32149 /* ErrorTypeTests.swift */; };
 		142EDB041BCBB0DD00A32149 /* SharedCredentialTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 142EDB031BCBB0DD00A32149 /* SharedCredentialTests.swift */; };
 		148F9D4A1BCB4118006EDF48 /* EnumTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 148F9D491BCB4118006EDF48 /* EnumTests.swift */; };
+		14A630181D3293C700809B3F /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 14A630171D3293C700809B3F /* AppDelegate.swift */; };
+		14A6301F1D3293C700809B3F /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 14A6301E1D3293C700809B3F /* Assets.xcassets */; };
+		14C3A6781D32BF9C00349459 /* KeychainAccess.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 140F195C1A49D79400B0016A /* KeychainAccess.framework */; };
+		14C3A6791D32BF9C00349459 /* KeychainAccess.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 140F195C1A49D79400B0016A /* KeychainAccess.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
 /* End PBXBuildFile section */
 
 /* Begin PBXContainerItemProxy section */
@@ -23,8 +27,36 @@
 			remoteGlobalIDString = 140F195B1A49D79400B0016A;
 			remoteInfo = KeychainAccess;
 		};
+		14C3A67A1D32BF9C00349459 /* PBXContainerItemProxy */ = {
+			isa = PBXContainerItemProxy;
+			containerPortal = 140F19531A49D79400B0016A /* Project object */;
+			proxyType = 1;
+			remoteGlobalIDString = 140F195B1A49D79400B0016A;
+			remoteInfo = KeychainAccess;
+		};
+		14F0C1991D32A160007DCDDB /* PBXContainerItemProxy */ = {
+			isa = PBXContainerItemProxy;
+			containerPortal = 140F19531A49D79400B0016A /* Project object */;
+			proxyType = 1;
+			remoteGlobalIDString = 14A630141D3293C700809B3F;
+			remoteInfo = TestHost;
+		};
 /* End PBXContainerItemProxy section */
 
+/* Begin PBXCopyFilesBuildPhase section */
+		14C3A67C1D32BF9D00349459 /* Embed Frameworks */ = {
+			isa = PBXCopyFilesBuildPhase;
+			buildActionMask = 2147483647;
+			dstPath = "";
+			dstSubfolderSpec = 10;
+			files = (
+				14C3A6791D32BF9C00349459 /* KeychainAccess.framework in Embed Frameworks */,
+			);
+			name = "Embed Frameworks";
+			runOnlyForDeploymentPostprocessing = 0;
+		};
+/* End PBXCopyFilesBuildPhase section */
+
 /* Begin PBXFileReference section */
 		140F195C1A49D79400B0016A /* KeychainAccess.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = KeychainAccess.framework; sourceTree = BUILT_PRODUCTS_DIR; };
 		140F19601A49D79400B0016A /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
@@ -41,6 +73,12 @@
 		148E44E91BF9EDE4004FFEC1 /* Tests.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Tests.xcconfig; path = Configurations/Tests.xcconfig; sourceTree = "<group>"; };
 		148E44EB1BF9EEB3004FFEC1 /* KeychainAccess.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = KeychainAccess.xcconfig; path = Configurations/KeychainAccess.xcconfig; sourceTree = "<group>"; };
 		148F9D491BCB4118006EDF48 /* EnumTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = EnumTests.swift; sourceTree = "<group>"; };
+		14A630151D3293C700809B3F /* TestHost.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = TestHost.app; sourceTree = BUILT_PRODUCTS_DIR; };
+		14A630171D3293C700809B3F /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
+		14A6301E1D3293C700809B3F /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
+		14A630231D3293C700809B3F /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
+		14F0C1961D3295C4007DCDDB /* TestHost.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.xml; path = TestHost.entitlements; sourceTree = "<group>"; };
+		14F0C1981D329832007DCDDB /* TestHost.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = TestHost.xcconfig; path = Configurations/TestHost.xcconfig; sourceTree = "<group>"; };
 /* End PBXFileReference section */
 
 /* Begin PBXFrameworksBuildPhase section */
@@ -58,6 +96,14 @@
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};
+		14A630121D3293C700809B3F /* Frameworks */ = {
+			isa = PBXFrameworksBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+				14C3A6781D32BF9C00349459 /* KeychainAccess.framework in Frameworks */,
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+		};
 /* End PBXFrameworksBuildPhase section */
 
 /* Begin PBXGroup section */
@@ -67,6 +113,7 @@
 				140F195E1A49D79400B0016A /* KeychainAccess */,
 				140F196B1A49D79500B0016A /* KeychainAccessTests */,
 				148E44E41BF9ED6D004FFEC1 /* Cofigurations */,
+				14A630161D3293C700809B3F /* TestHost */,
 				140F195D1A49D79400B0016A /* Products */,
 			);
 			sourceTree = "<group>";
@@ -76,6 +123,7 @@
 			children = (
 				140F195C1A49D79400B0016A /* KeychainAccess.framework */,
 				140F19671A49D79500B0016A /* KeychainAccessTests.xctest */,
+				14A630151D3293C700809B3F /* TestHost.app */,
 			);
 			name = Products;
 			sourceTree = "<group>";
@@ -126,10 +174,22 @@
 				148E44E71BF9EDCB004FFEC1 /* Release.xcconfig */,
 				148E44EB1BF9EEB3004FFEC1 /* KeychainAccess.xcconfig */,
 				148E44E91BF9EDE4004FFEC1 /* Tests.xcconfig */,
+				14F0C1981D329832007DCDDB /* TestHost.xcconfig */,
 			);
 			name = Cofigurations;
 			sourceTree = "<group>";
 		};
+		14A630161D3293C700809B3F /* TestHost */ = {
+			isa = PBXGroup;
+			children = (
+				14A630171D3293C700809B3F /* AppDelegate.swift */,
+				14A6301E1D3293C700809B3F /* Assets.xcassets */,
+				14A630231D3293C700809B3F /* Info.plist */,
+				14F0C1961D3295C4007DCDDB /* TestHost.entitlements */,
+			);
+			path = TestHost;
+			sourceTree = "<group>";
+		};
 /* End PBXGroup section */
 
 /* Begin PBXHeadersBuildPhase section */
@@ -174,27 +234,58 @@
 			);
 			dependencies = (
 				140F196A1A49D79500B0016A /* PBXTargetDependency */,
+				14F0C19A1D32A160007DCDDB /* PBXTargetDependency */,
 			);
 			name = KeychainAccessTests;
 			productName = KeychainAccessTests;
 			productReference = 140F19671A49D79500B0016A /* KeychainAccessTests.xctest */;
 			productType = "com.apple.product-type.bundle.unit-test";
 		};
+		14A630141D3293C700809B3F /* TestHost */ = {
+			isa = PBXNativeTarget;
+			buildConfigurationList = 14A630241D3293C700809B3F /* Build configuration list for PBXNativeTarget "TestHost" */;
+			buildPhases = (
+				14A630111D3293C700809B3F /* Sources */,
+				14A630121D3293C700809B3F /* Frameworks */,
+				14A630131D3293C700809B3F /* Resources */,
+				14C3A67C1D32BF9D00349459 /* Embed Frameworks */,
+			);
+			buildRules = (
+			);
+			dependencies = (
+				14C3A67B1D32BF9C00349459 /* PBXTargetDependency */,
+			);
+			name = TestHost;
+			productName = TestHost;
+			productReference = 14A630151D3293C700809B3F /* TestHost.app */;
+			productType = "com.apple.product-type.application";
+		};
 /* End PBXNativeTarget section */
 
 /* Begin PBXProject section */
 		140F19531A49D79400B0016A /* Project object */ = {
 			isa = PBXProject;
 			attributes = {
-				LastSwiftUpdateCheck = 0710;
-				LastUpgradeCheck = 0720;
+				LastSwiftUpdateCheck = 0730;
+				LastUpgradeCheck = 0730;
 				ORGANIZATIONNAME = "kishikawa katsumi";
 				TargetAttributes = {
 					140F195B1A49D79400B0016A = {
 						CreatedOnToolsVersion = 6.1.1;
+						DevelopmentTeam = 27AEDK3C9F;
 					};
 					140F19661A49D79500B0016A = {
 						CreatedOnToolsVersion = 6.1.1;
+						TestTargetID = 14A62FFC1D32922C00809B3F;
+					};
+					14A630141D3293C700809B3F = {
+						CreatedOnToolsVersion = 7.3.1;
+						DevelopmentTeam = 27AEDK3C9F;
+						SystemCapabilities = {
+							com.apple.Keychain = {
+								enabled = 1;
+							};
+						};
 					};
 				};
 			};
@@ -204,6 +295,7 @@
 			hasScannedForEncodings = 0;
 			knownRegions = (
 				en,
+				Base,
 			);
 			mainGroup = 140F19521A49D79400B0016A;
 			productRefGroup = 140F195D1A49D79400B0016A /* Products */;
@@ -212,6 +304,7 @@
 			targets = (
 				140F195B1A49D79400B0016A /* KeychainAccess */,
 				140F19661A49D79500B0016A /* KeychainAccessTests */,
+				14A630141D3293C700809B3F /* TestHost */,
 			);
 		};
 /* End PBXProject section */
@@ -231,6 +324,14 @@
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};
+		14A630131D3293C700809B3F /* Resources */ = {
+			isa = PBXResourcesBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+				14A6301F1D3293C700809B3F /* Assets.xcassets in Resources */,
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+		};
 /* End PBXResourcesBuildPhase section */
 
 /* Begin PBXSourcesBuildPhase section */
@@ -253,6 +354,14 @@
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};
+		14A630111D3293C700809B3F /* Sources */ = {
+			isa = PBXSourcesBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+				14A630181D3293C700809B3F /* AppDelegate.swift in Sources */,
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+		};
 /* End PBXSourcesBuildPhase section */
 
 /* Begin PBXTargetDependency section */
@@ -261,6 +370,16 @@
 			target = 140F195B1A49D79400B0016A /* KeychainAccess */;
 			targetProxy = 140F19691A49D79500B0016A /* PBXContainerItemProxy */;
 		};
+		14C3A67B1D32BF9C00349459 /* PBXTargetDependency */ = {
+			isa = PBXTargetDependency;
+			target = 140F195B1A49D79400B0016A /* KeychainAccess */;
+			targetProxy = 14C3A67A1D32BF9C00349459 /* PBXContainerItemProxy */;
+		};
+		14F0C19A1D32A160007DCDDB /* PBXTargetDependency */ = {
+			isa = PBXTargetDependency;
+			target = 14A630141D3293C700809B3F /* TestHost */;
+			targetProxy = 14F0C1991D32A160007DCDDB /* PBXContainerItemProxy */;
+		};
 /* End PBXTargetDependency section */
 
 /* Begin XCBuildConfiguration section */
@@ -306,6 +425,24 @@
 			};
 			name = Release;
 		};
+		14A630251D3293C700809B3F /* Debug */ = {
+			isa = XCBuildConfiguration;
+			baseConfigurationReference = 14F0C1981D329832007DCDDB /* TestHost.xcconfig */;
+			buildSettings = {
+				EMBEDDED_CONTENT_CONTAINS_SWIFT = YES;
+				LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks";
+			};
+			name = Debug;
+		};
+		14A630261D3293C700809B3F /* Release */ = {
+			isa = XCBuildConfiguration;
+			baseConfigurationReference = 14F0C1981D329832007DCDDB /* TestHost.xcconfig */;
+			buildSettings = {
+				EMBEDDED_CONTENT_CONTAINS_SWIFT = YES;
+				LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks";
+			};
+			name = Release;
+		};
 /* End XCBuildConfiguration section */
 
 /* Begin XCConfigurationList section */
@@ -336,6 +473,15 @@
 			defaultConfigurationIsVisible = 0;
 			defaultConfigurationName = Release;
 		};
+		14A630241D3293C700809B3F /* Build configuration list for PBXNativeTarget "TestHost" */ = {
+			isa = XCConfigurationList;
+			buildConfigurations = (
+				14A630251D3293C700809B3F /* Debug */,
+				14A630261D3293C700809B3F /* Release */,
+			);
+			defaultConfigurationIsVisible = 0;
+			defaultConfigurationName = Release;
+		};
 /* End XCConfigurationList section */
 	};
 	rootObject = 140F19531A49D79400B0016A /* Project object */;

+ 1 - 1
Lib/KeychainAccess.xcodeproj/xcshareddata/xcschemes/KeychainAccess.xcscheme

@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <Scheme
-   LastUpgradeVersion = "0720"
+   LastUpgradeVersion = "0730"
    version = "1.3">
    <BuildAction
       parallelizeBuildables = "YES"

+ 91 - 0
Lib/KeychainAccess.xcodeproj/xcshareddata/xcschemes/TestHost.xcscheme

@@ -0,0 +1,91 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<Scheme
+   LastUpgradeVersion = "0730"
+   version = "1.3">
+   <BuildAction
+      parallelizeBuildables = "YES"
+      buildImplicitDependencies = "YES">
+      <BuildActionEntries>
+         <BuildActionEntry
+            buildForTesting = "YES"
+            buildForRunning = "YES"
+            buildForProfiling = "YES"
+            buildForArchiving = "YES"
+            buildForAnalyzing = "YES">
+            <BuildableReference
+               BuildableIdentifier = "primary"
+               BlueprintIdentifier = "14A630141D3293C700809B3F"
+               BuildableName = "TestHost.app"
+               BlueprintName = "TestHost"
+               ReferencedContainer = "container:KeychainAccess.xcodeproj">
+            </BuildableReference>
+         </BuildActionEntry>
+      </BuildActionEntries>
+   </BuildAction>
+   <TestAction
+      buildConfiguration = "Debug"
+      selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
+      selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
+      shouldUseLaunchSchemeArgsEnv = "YES">
+      <Testables>
+      </Testables>
+      <MacroExpansion>
+         <BuildableReference
+            BuildableIdentifier = "primary"
+            BlueprintIdentifier = "14A630141D3293C700809B3F"
+            BuildableName = "TestHost.app"
+            BlueprintName = "TestHost"
+            ReferencedContainer = "container:KeychainAccess.xcodeproj">
+         </BuildableReference>
+      </MacroExpansion>
+      <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">
+      <BuildableProductRunnable
+         runnableDebuggingMode = "0">
+         <BuildableReference
+            BuildableIdentifier = "primary"
+            BlueprintIdentifier = "14A630141D3293C700809B3F"
+            BuildableName = "TestHost.app"
+            BlueprintName = "TestHost"
+            ReferencedContainer = "container:KeychainAccess.xcodeproj">
+         </BuildableReference>
+      </BuildableProductRunnable>
+      <AdditionalOptions>
+      </AdditionalOptions>
+   </LaunchAction>
+   <ProfileAction
+      buildConfiguration = "Release"
+      shouldUseLaunchSchemeArgsEnv = "YES"
+      savedToolIdentifier = ""
+      useCustomWorkingDirectory = "NO"
+      debugDocumentVersioning = "YES">
+      <BuildableProductRunnable
+         runnableDebuggingMode = "0">
+         <BuildableReference
+            BuildableIdentifier = "primary"
+            BlueprintIdentifier = "14A630141D3293C700809B3F"
+            BuildableName = "TestHost.app"
+            BlueprintName = "TestHost"
+            ReferencedContainer = "container:KeychainAccess.xcodeproj">
+         </BuildableReference>
+      </BuildableProductRunnable>
+   </ProfileAction>
+   <AnalyzeAction
+      buildConfiguration = "Debug">
+   </AnalyzeAction>
+   <ArchiveAction
+      buildConfiguration = "Release"
+      revealArchiveInOrganizer = "YES">
+   </ArchiveAction>
+</Scheme>

+ 149 - 100
Lib/KeychainAccessTests/KeychainAccessTests.swift

@@ -33,7 +33,7 @@ class KeychainAccessTests: XCTestCase {
     override func setUp() {
         super.setUp()
 
-        do { try Keychain(service: "Twitter", accessGroup: "12ABCD3E4F.shared").removeAll() } catch {}
+        do { try Keychain(service: "Twitter", accessGroup: "27AEDK3C9F.shared").removeAll() } catch {}
         do { try Keychain(service: "Twitter").removeAll() } catch {}
         
         do { try Keychain(server: NSURL(string: "https://example.com")!, protocolType: .HTTPS).removeAll() } catch {}
@@ -88,11 +88,11 @@ class KeychainAccessTests: XCTestCase {
             XCTAssertNil(try! keychain.get("password"))
         }
     }
-    
+
     func testGenericPasswordSubscripting() {
         do {
             // Add Keychain items
-            let keychain = Keychain(service: "Twitter", accessGroup: "12ABCD3E4F.shared")
+            let keychain = Keychain(service: "Twitter", accessGroup: "27AEDK3C9F.shared")
             
             keychain["username"] = "kishikawa_katsumi"
             keychain["password"] = "password_1234"
@@ -106,7 +106,7 @@ class KeychainAccessTests: XCTestCase {
         
         do {
             // Update Keychain items
-            let keychain = Keychain(service: "Twitter", accessGroup: "12ABCD3E4F.shared")
+            let keychain = Keychain(service: "Twitter", accessGroup: "27AEDK3C9F.shared")
             
             keychain["username"] = "katsumi_kishikawa"
             keychain["password"] = "1234_password"
@@ -120,7 +120,7 @@ class KeychainAccessTests: XCTestCase {
         
         do {
             // Remove Keychain items
-            let keychain = Keychain(service: "Twitter", accessGroup: "12ABCD3E4F.shared")
+            let keychain = Keychain(service: "Twitter", accessGroup: "27AEDK3C9F.shared")
             
             keychain["username"] = nil
             keychain["password"] = nil
@@ -218,7 +218,8 @@ class KeychainAccessTests: XCTestCase {
     
     func testDefaultInitializer() {
         let keychain = Keychain()
-        XCTAssertEqual(keychain.service, "")
+        XCTAssertEqual(keychain.service, NSBundle.mainBundle().bundleIdentifier)
+        XCTAssertEqual(keychain.service, "com.kishikawakatsumi.TestHost")
         XCTAssertNil(keychain.accessGroup)
     }
     
@@ -229,15 +230,15 @@ class KeychainAccessTests: XCTestCase {
     }
     
     func testInitializerWithAccessGroup() {
-        let keychain = Keychain(accessGroup: "12ABCD3E4F.shared")
-        XCTAssertEqual(keychain.service, "")
-        XCTAssertEqual(keychain.accessGroup, "12ABCD3E4F.shared")
+        let keychain = Keychain(accessGroup: "27AEDK3C9F.shared")
+        XCTAssertEqual(keychain.service, "com.kishikawakatsumi.TestHost")
+        XCTAssertEqual(keychain.accessGroup, "27AEDK3C9F.shared")
     }
     
     func testInitializerWithServiceAndAccessGroup() {
-        let keychain = Keychain(service: "com.example.github-token", accessGroup: "12ABCD3E4F.shared")
+        let keychain = Keychain(service: "com.example.github-token", accessGroup: "27AEDK3C9F.shared")
         XCTAssertEqual(keychain.service, "com.example.github-token")
-        XCTAssertEqual(keychain.accessGroup, "12ABCD3E4F.shared")
+        XCTAssertEqual(keychain.accessGroup, "27AEDK3C9F.shared")
     }
     
     func testInitializerWithServer() {
@@ -458,67 +459,74 @@ class KeychainAccessTests: XCTestCase {
 
     #if os(iOS) || os(tvOS)
     func testSetAttributes() {
-        do {
-            var attributes = [String: AnyObject]()
-            attributes[String(kSecAttrDescription)] = "Description Test"
-            attributes[String(kSecAttrComment)] = "Comment Test"
-            attributes[String(kSecAttrCreator)] = "Creator Test"
-            attributes[String(kSecAttrType)] = "Type Test"
-            attributes[String(kSecAttrLabel)] = "Label Test"
-            attributes[String(kSecAttrIsInvisible)] = true
-            attributes[String(kSecAttrIsNegative)] = true
-
-            let keychain = Keychain(service: "Twitter")
-                .attributes(attributes)
-                .accessibility(.WhenPasscodeSetThisDeviceOnly, authenticationPolicy: .UserPresence)
-
-            XCTAssertNil(keychain["kishikawakatsumi"], "not stored password")
+        let expectation = expectationWithDescription("Touch ID authentication")
 
+        dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)) {
             do {
-                let attributes = try keychain.get("kishikawakatsumi") { $0 }
-                XCTAssertNil(attributes)
-            } catch {
-                XCTFail("error occurred")
-            }
+                var attributes = [String: AnyObject]()
+                attributes[String(kSecAttrDescription)] = "Description Test"
+                attributes[String(kSecAttrComment)] = "Comment Test"
+                attributes[String(kSecAttrCreator)] = "Creator Test"
+                attributes[String(kSecAttrType)] = "Type Test"
+                attributes[String(kSecAttrLabel)] = "Label Test"
+                attributes[String(kSecAttrIsInvisible)] = true
+                attributes[String(kSecAttrIsNegative)] = true
+
+                let keychain = Keychain(service: "Twitter")
+                    .attributes(attributes)
+                    .accessibility(.WhenPasscodeSetThisDeviceOnly, authenticationPolicy: .UserPresence)
+
+                XCTAssertNil(keychain["kishikawakatsumi"], "not stored password")
+
+                do {
+                    let attributes = try keychain.get("kishikawakatsumi") { $0 }
+                    XCTAssertNil(attributes)
+                } catch {
+                    XCTFail("error occurred")
+                }
 
-            keychain["kishikawakatsumi"] = "password1234"
-            XCTAssertEqual(keychain["kishikawakatsumi"], "password1234", "stored password")
+                keychain["kishikawakatsumi"] = "password1234"
+                XCTAssertEqual(keychain["kishikawakatsumi"], "password1234", "stored password")
 
-            do {
-                let attributes = try keychain.get("kishikawakatsumi") { $0 }
-                XCTAssertEqual(attributes?.`class`, ItemClass.GenericPassword.rawValue)
-                XCTAssertEqual(attributes?.data, "password1234".dataUsingEncoding(NSUTF8StringEncoding))
-                XCTAssertNil(attributes?.ref)
-                XCTAssertNotNil(attributes?.persistentRef)
-                XCTAssertEqual(attributes?.accessible, Accessibility.WhenPasscodeSetThisDeviceOnly.rawValue)
-                XCTAssertNotNil(attributes?.accessControl)
-                XCTAssertEqual(attributes?.accessGroup, "")
-                XCTAssertNotNil(attributes?.synchronizable)
-                XCTAssertNotNil(attributes?.creationDate)
-                XCTAssertNotNil(attributes?.modificationDate)
-                XCTAssertEqual(attributes?.attributeDescription, "Description Test")
-                XCTAssertEqual(attributes?.comment, "Comment Test")
-                XCTAssertEqual(attributes?.creator, "Creator Test")
-                XCTAssertEqual(attributes?.type, "Type Test")
-                XCTAssertEqual(attributes?.label, "Label Test")
-                XCTAssertEqual(attributes?.isInvisible, true)
-                XCTAssertEqual(attributes?.isNegative, true)
-                XCTAssertEqual(attributes?.account, "kishikawakatsumi")
-                XCTAssertEqual(attributes?.service, "Twitter")
-                XCTAssertNil(attributes?.generic)
-                XCTAssertNil(attributes?.securityDomain)
-                XCTAssertNil(attributes?.server)
-                XCTAssertNil(attributes?.`protocol`)
-                XCTAssertNil(attributes?.authenticationType)
-                XCTAssertNil(attributes?.port)
-                XCTAssertNil(attributes?.path)
-
-                XCTAssertEqual(attributes![String(kSecClass)] as? String, ItemClass.GenericPassword.rawValue)
-                XCTAssertEqual(attributes![String(kSecValueData)] as? NSData, "password1234".dataUsingEncoding(NSUTF8StringEncoding))
-            } catch {
-                XCTFail("error occurred")
+                do {
+                    let attributes = try keychain.get("kishikawakatsumi") { $0 }
+                    XCTAssertEqual(attributes?.`class`, ItemClass.GenericPassword.rawValue)
+                    XCTAssertEqual(attributes?.data, "password1234".dataUsingEncoding(NSUTF8StringEncoding))
+                    XCTAssertNil(attributes?.ref)
+                    XCTAssertNotNil(attributes?.persistentRef)
+                    XCTAssertEqual(attributes?.accessible, Accessibility.WhenPasscodeSetThisDeviceOnly.rawValue)
+                    XCTAssertNotNil(attributes?.accessControl)
+                    XCTAssertEqual(attributes?.accessGroup, "27AEDK3C9F.com.kishikawakatsumi.TestHost")
+                    XCTAssertNotNil(attributes?.synchronizable)
+                    XCTAssertNotNil(attributes?.creationDate)
+                    XCTAssertNotNil(attributes?.modificationDate)
+                    XCTAssertEqual(attributes?.attributeDescription, "Description Test")
+                    XCTAssertEqual(attributes?.comment, "Comment Test")
+                    XCTAssertEqual(attributes?.creator, "Creator Test")
+                    XCTAssertEqual(attributes?.type, "Type Test")
+                    XCTAssertEqual(attributes?.label, "Label Test")
+                    XCTAssertEqual(attributes?.isInvisible, true)
+                    XCTAssertEqual(attributes?.isNegative, true)
+                    XCTAssertEqual(attributes?.account, "kishikawakatsumi")
+                    XCTAssertEqual(attributes?.service, "Twitter")
+                    XCTAssertNil(attributes?.generic)
+                    XCTAssertNil(attributes?.securityDomain)
+                    XCTAssertNil(attributes?.server)
+                    XCTAssertNil(attributes?.`protocol`)
+                    XCTAssertNil(attributes?.authenticationType)
+                    XCTAssertNil(attributes?.port)
+                    XCTAssertNil(attributes?.path)
+
+                    XCTAssertEqual(attributes![String(kSecClass)] as? String, ItemClass.GenericPassword.rawValue)
+                    XCTAssertEqual(attributes![String(kSecValueData)] as? NSData, "password1234".dataUsingEncoding(NSUTF8StringEncoding))
+
+                    expectation.fulfill()
+                } catch {
+                    XCTFail("error occurred")
+                }
             }
         }
+        waitForExpectationsWithTimeout(10.0, handler: nil)
 
         do {
             var attributes = [String: AnyObject]()
@@ -558,7 +566,7 @@ class KeychainAccessTests: XCTestCase {
                 } else {
                     XCTAssertNotNil(attributes?.accessControl)
                 }
-                XCTAssertEqual(attributes?.accessGroup, "")
+                XCTAssertEqual(attributes?.accessGroup, "27AEDK3C9F.com.kishikawakatsumi.TestHost")
                 XCTAssertNotNil(attributes?.synchronizable)
                 XCTAssertNotNil(attributes?.creationDate)
                 XCTAssertNotNil(attributes?.modificationDate)
@@ -600,7 +608,7 @@ class KeychainAccessTests: XCTestCase {
                 } else {
                     XCTAssertNotNil(attributes?.accessControl)
                 }
-                XCTAssertEqual(attributes?.accessGroup, "")
+                XCTAssertEqual(attributes?.accessGroup, "27AEDK3C9F.com.kishikawakatsumi.TestHost")
                 XCTAssertNotNil(attributes?.synchronizable)
                 XCTAssertNotNil(attributes?.creationDate)
                 XCTAssertNotNil(attributes?.modificationDate)
@@ -643,7 +651,7 @@ class KeychainAccessTests: XCTestCase {
                 } else {
                     XCTAssertNotNil(attributes?.accessControl)
                 }
-                XCTAssertEqual(attributes?.accessGroup, "")
+                XCTAssertEqual(attributes?.accessGroup, "27AEDK3C9F.com.kishikawakatsumi.TestHost")
                 XCTAssertNotNil(attributes?.synchronizable)
                 XCTAssertNotNil(attributes?.creationDate)
                 XCTAssertNotNil(attributes?.modificationDate)
@@ -747,11 +755,11 @@ class KeychainAccessTests: XCTestCase {
     }
     
     // MARK:
-    
-    #if os(iOS)
+
+    #if !os(OSX) // Disable on CI
     func testErrorHandling() {
         do {
-            let keychain = Keychain(service: "Twitter", accessGroup: "12ABCD3E4F.shared")
+            let keychain = Keychain(service: "Twitter", accessGroup: "27AEDK3C9F.shared")
             try keychain.removeAll()
             XCTAssertTrue(true, "no error occurred")
         } catch {
@@ -894,7 +902,7 @@ class KeychainAccessTests: XCTestCase {
         
         do { try Keychain().set(username_1, key: "username") } catch {}
         XCTAssertEqual(try! Keychain().get("username"), username_1, "stored username")
-        XCTAssertEqual(try! Keychain(service: service_1).get("username"), username_1, "stored username")
+        XCTAssertNil(try! Keychain(service: service_1).get("password"), "not stored password")
         XCTAssertNil(try! Keychain(service: service_2).get("username"), "not stored username")
         XCTAssertNil(try! Keychain(service: service_3).get("username"), "not stored username")
         
@@ -918,7 +926,7 @@ class KeychainAccessTests: XCTestCase {
 
         do { try Keychain().set(password_1, key: "password") } catch {}
         XCTAssertEqual(try! Keychain().get("password"), password_1, "stored password")
-        XCTAssertEqual(try! Keychain(service: service_1).get("password"), password_1, "stored password")
+        XCTAssertNil(try! Keychain(service: service_1).get("password"), "not stored password")
         XCTAssertNil(try! Keychain(service: service_2).get("password"), "not stored password")
         XCTAssertNil(try! Keychain(service: service_3).get("password"), "not stored password")
         
@@ -942,7 +950,7 @@ class KeychainAccessTests: XCTestCase {
         
         do { try Keychain().remove("username") } catch {}
         XCTAssertNil(try! Keychain().get("username"), "removed username")
-        XCTAssertNil(try! Keychain(service: service_1).get("username"), "removed username")
+        XCTAssertEqual(try! Keychain(service: service_1).get("username"), username_1, "left username")
         XCTAssertEqual(try! Keychain(service: service_2).get("username"), username_2, "left username")
         XCTAssertEqual(try! Keychain(service: service_3).get("username"), username_3, "left username")
         
@@ -966,7 +974,7 @@ class KeychainAccessTests: XCTestCase {
         
         do { try Keychain().remove("password") } catch {}
         XCTAssertNil(try! Keychain().get("password"), "removed password")
-        XCTAssertNil(try! Keychain(service: service_1).get("password"), "removed password")
+        XCTAssertEqual(try! Keychain(service: service_1).get("password"), password_1, "left password")
         XCTAssertEqual(try! Keychain(service: service_2).get("password"), password_2, "left password")
         XCTAssertEqual(try! Keychain(service: service_3).get("password"), password_3, "left password")
         
@@ -1013,7 +1021,7 @@ class KeychainAccessTests: XCTestCase {
 
     // MARK:
 
-#if os(iOS)
+    #if !os(OSX) // Disable on CI
     func testAllKeys() {
         do {
             let keychain = Keychain()
@@ -1029,34 +1037,48 @@ class KeychainAccessTests: XCTestCase {
             XCTAssertEqual(allItems.count, 3)
 
             let sortedItems = allItems.sort { (item1, item2) -> Bool in
-                let value1 = item1["value"] as! String
-                let value2 = item2["value"] as! String
-                return value1.compare(value2) == NSComparisonResult.OrderedAscending || value1.compare(value2) == NSComparisonResult.OrderedSame
+                let key1 = item1["key"] as! String
+                let key2 = item2["key"] as! String
+                return key1.compare(key2) == .OrderedAscending || key1.compare(key2) == .OrderedSame
             }
 
-            XCTAssertEqual(sortedItems[0]["accessGroup"] as? String, "")
+            #if !os(OSX)
+            XCTAssertEqual(sortedItems[0]["accessGroup"] as? String, "27AEDK3C9F.com.kishikawakatsumi.TestHost")
             XCTAssertEqual(sortedItems[0]["synchronizable"] as? String, "false")
-            XCTAssertEqual(sortedItems[0]["service"] as? String, "")
+            XCTAssertEqual(sortedItems[0]["service"] as? String, "com.kishikawakatsumi.TestHost")
             XCTAssertEqual(sortedItems[0]["value"] as? String, "value1")
             XCTAssertEqual(sortedItems[0]["key"] as? String, "key1")
             XCTAssertEqual(sortedItems[0]["class"] as? String, "GenericPassword")
             XCTAssertEqual(sortedItems[0]["accessibility"] as? String, "AfterFirstUnlock")
 
-            XCTAssertEqual(sortedItems[1]["accessGroup"] as? String, "")
+            XCTAssertEqual(sortedItems[1]["accessGroup"] as? String, "27AEDK3C9F.com.kishikawakatsumi.TestHost")
             XCTAssertEqual(sortedItems[1]["synchronizable"] as? String, "false")
-            XCTAssertEqual(sortedItems[1]["service"] as? String, "")
+            XCTAssertEqual(sortedItems[1]["service"] as? String, "com.kishikawakatsumi.TestHost")
             XCTAssertEqual(sortedItems[1]["value"] as? String, "value2")
             XCTAssertEqual(sortedItems[1]["key"] as? String, "key2")
             XCTAssertEqual(sortedItems[1]["class"] as? String, "GenericPassword")
             XCTAssertEqual(sortedItems[1]["accessibility"] as? String, "AfterFirstUnlock")
 
-            XCTAssertEqual(sortedItems[2]["accessGroup"] as? String, "")
+            XCTAssertEqual(sortedItems[2]["accessGroup"] as? String, "27AEDK3C9F.com.kishikawakatsumi.TestHost")
             XCTAssertEqual(sortedItems[2]["synchronizable"] as? String, "false")
-            XCTAssertEqual(sortedItems[2]["service"] as? String, "")
+            XCTAssertEqual(sortedItems[2]["service"] as? String, "com.kishikawakatsumi.TestHost")
             XCTAssertEqual(sortedItems[2]["value"] as? String, "value3")
             XCTAssertEqual(sortedItems[2]["key"] as? String, "key3")
             XCTAssertEqual(sortedItems[2]["class"] as? String, "GenericPassword")
             XCTAssertEqual(sortedItems[2]["accessibility"] as? String, "AfterFirstUnlock")
+            #else
+            XCTAssertEqual(sortedItems[0]["service"] as? String, "com.kishikawakatsumi.TestHost")
+            XCTAssertEqual(sortedItems[0]["key"] as? String, "key1")
+            XCTAssertEqual(sortedItems[0]["class"] as? String, "GenericPassword")
+
+            XCTAssertEqual(sortedItems[1]["service"] as? String, "com.kishikawakatsumi.TestHost")
+            XCTAssertEqual(sortedItems[1]["key"] as? String, "key2")
+            XCTAssertEqual(sortedItems[1]["class"] as? String, "GenericPassword")
+
+            XCTAssertEqual(sortedItems[2]["service"] as? String, "com.kishikawakatsumi.TestHost")
+            XCTAssertEqual(sortedItems[2]["key"] as? String, "key3")
+            XCTAssertEqual(sortedItems[2]["class"] as? String, "GenericPassword")
+            #endif
         }
         do {
             let keychain = Keychain(service: "service1")
@@ -1078,12 +1100,13 @@ class KeychainAccessTests: XCTestCase {
             XCTAssertEqual(allItems.count, 2)
 
             let sortedItems = allItems.sort { (item1, item2) -> Bool in
-                let value1 = item1["value"] as! String
-                let value2 = item2["value"] as! String
-                return value1.compare(value2) == NSComparisonResult.OrderedAscending || value1.compare(value2) == NSComparisonResult.OrderedSame
+                let key1 = item1["key"] as! String
+                let key2 = item2["key"] as! String
+                return key1.compare(key2) == .OrderedAscending || key1.compare(key2) == .OrderedSame
             }
 
-            XCTAssertEqual(sortedItems[0]["accessGroup"] as? String, "")
+            #if !os(OSX)
+            XCTAssertEqual(sortedItems[0]["accessGroup"] as? String, "27AEDK3C9F.com.kishikawakatsumi.TestHost")
             XCTAssertEqual(sortedItems[0]["synchronizable"] as? String, "true")
             XCTAssertEqual(sortedItems[0]["service"] as? String, "service1")
             XCTAssertEqual(sortedItems[0]["value"] as? String, "service1_value1")
@@ -1091,13 +1114,22 @@ class KeychainAccessTests: XCTestCase {
             XCTAssertEqual(sortedItems[0]["class"] as? String, "GenericPassword")
             XCTAssertEqual(sortedItems[0]["accessibility"] as? String, "WhenUnlockedThisDeviceOnly")
 
-            XCTAssertEqual(sortedItems[1]["accessGroup"] as? String, "")
+            XCTAssertEqual(sortedItems[1]["accessGroup"] as? String, "27AEDK3C9F.com.kishikawakatsumi.TestHost")
             XCTAssertEqual(sortedItems[1]["synchronizable"] as? String, "false")
             XCTAssertEqual(sortedItems[1]["service"] as? String, "service1")
             XCTAssertEqual(sortedItems[1]["value"] as? String, "service1_value2")
             XCTAssertEqual(sortedItems[1]["key"] as? String, "service1_key2")
             XCTAssertEqual(sortedItems[1]["class"] as? String, "GenericPassword")
             XCTAssertEqual(sortedItems[1]["accessibility"] as? String, "AfterFirstUnlockThisDeviceOnly")
+            #else
+            XCTAssertEqual(sortedItems[0]["service"] as? String, "service1")
+            XCTAssertEqual(sortedItems[0]["key"] as? String, "service1_key1")
+            XCTAssertEqual(sortedItems[0]["class"] as? String, "GenericPassword")
+
+            XCTAssertEqual(sortedItems[1]["service"] as? String, "service1")
+            XCTAssertEqual(sortedItems[1]["key"] as? String, "service1_key2")
+            XCTAssertEqual(sortedItems[1]["class"] as? String, "GenericPassword")
+            #endif
         }
         do {
             let keychain = Keychain(server: "https://google.com", protocolType: .HTTPS)
@@ -1119,11 +1151,12 @@ class KeychainAccessTests: XCTestCase {
             XCTAssertEqual(allItems.count, 2)
 
             let sortedItems = allItems.sort { (item1, item2) -> Bool in
-                let value1 = item1["value"] as! String
-                let value2 = item2["value"] as! String
-                return value1.compare(value2) == NSComparisonResult.OrderedAscending || value1.compare(value2) == NSComparisonResult.OrderedSame
+                let key1 = item1["key"] as! String
+                let key2 = item2["key"] as! String
+                return key1.compare(key2) == .OrderedAscending || key1.compare(key2) == .OrderedSame
             }
 
+            #if !os(OSX)
             XCTAssertEqual(sortedItems[0]["synchronizable"] as? String, "false")
             XCTAssertEqual(sortedItems[0]["value"] as? String, "google.com_value1")
             XCTAssertEqual(sortedItems[0]["key"] as? String, "google.com_key1")
@@ -1141,19 +1174,34 @@ class KeychainAccessTests: XCTestCase {
             XCTAssertEqual(sortedItems[1]["authenticationType"] as? String, "Default")
             XCTAssertEqual(sortedItems[1]["protocol"] as? String, "HTTPS")
             XCTAssertEqual(sortedItems[1]["accessibility"] as? String, "Always")
+            #else
+            XCTAssertEqual(sortedItems[0]["key"] as? String, "google.com_key1")
+            XCTAssertEqual(sortedItems[0]["server"] as? String, "google.com")
+            XCTAssertEqual(sortedItems[0]["class"] as? String, "InternetPassword")
+            XCTAssertEqual(sortedItems[0]["authenticationType"] as? String, "Default")
+            XCTAssertEqual(sortedItems[0]["protocol"] as? String, "HTTPS")
+
+            XCTAssertEqual(sortedItems[1]["key"] as? String, "google.com_key2")
+            XCTAssertEqual(sortedItems[1]["server"] as? String, "google.com")
+            XCTAssertEqual(sortedItems[1]["class"] as? String, "InternetPassword")
+            XCTAssertEqual(sortedItems[1]["authenticationType"] as? String, "Default")
+            XCTAssertEqual(sortedItems[1]["protocol"] as? String, "HTTPS")
+            #endif
         }
+
+        #if !os(OSX)
         do {
             let allKeys = Keychain.allKeys(.GenericPassword)
             XCTAssertEqual(allKeys.count, 5)
 
             let sortedKeys = allKeys.sort { (key1, key2) -> Bool in
-                return key1.1.compare(key2.1) == NSComparisonResult.OrderedAscending || key1.1.compare(key2.1) == NSComparisonResult.OrderedSame
+                return key1.1.compare(key2.1) == .OrderedAscending || key1.1.compare(key2.1) == .OrderedSame
             }
-            XCTAssertEqual(sortedKeys[0].0, "")
+            XCTAssertEqual(sortedKeys[0].0, "com.kishikawakatsumi.TestHost")
             XCTAssertEqual(sortedKeys[0].1, "key1")
-            XCTAssertEqual(sortedKeys[1].0, "")
+            XCTAssertEqual(sortedKeys[1].0, "com.kishikawakatsumi.TestHost")
             XCTAssertEqual(sortedKeys[1].1, "key2")
-            XCTAssertEqual(sortedKeys[2].0, "")
+            XCTAssertEqual(sortedKeys[2].0, "com.kishikawakatsumi.TestHost")
             XCTAssertEqual(sortedKeys[2].1, "key3")
             XCTAssertEqual(sortedKeys[3].0, "service1")
             XCTAssertEqual(sortedKeys[3].1, "service1_key1")
@@ -1165,14 +1213,16 @@ class KeychainAccessTests: XCTestCase {
             XCTAssertEqual(allKeys.count, 2)
 
             let sortedKeys = allKeys.sort { (key1, key2) -> Bool in
-                return key1.1.compare(key2.1) == NSComparisonResult.OrderedAscending || key1.1.compare(key2.1) == NSComparisonResult.OrderedSame
+                return key1.1.compare(key2.1) == .OrderedAscending || key1.1.compare(key2.1) == .OrderedSame
             }
             XCTAssertEqual(sortedKeys[0].0, "google.com")
             XCTAssertEqual(sortedKeys[0].1, "google.com_key1")
             XCTAssertEqual(sortedKeys[1].0, "google.com")
             XCTAssertEqual(sortedKeys[1].1, "google.com_key2")
         }
+        #endif
     }
+    #endif
 
     func testDescription() {
         do {
@@ -1182,7 +1232,6 @@ class KeychainAccessTests: XCTestCase {
             XCTAssertEqual(keychain.debugDescription, "[]")
         }
     }
-#endif
 
     // MARK:
 

+ 49 - 0
Lib/TestHost/AppDelegate.swift

@@ -0,0 +1,49 @@
+//
+//  AppDelegate.swift
+//  TestHost
+//
+//  Created by kishikawa katsumi on 7/10/16.
+//  Copyright © 2016 kishikawa katsumi. All rights reserved.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+#if os(OSX)
+import Cocoa
+
+@NSApplicationMain
+class AppDelegate: NSObject, NSApplicationDelegate {
+
+    @IBOutlet weak var window: NSWindow!
+
+    func applicationDidFinishLaunching(aNotification: NSNotification) {}
+    
+}
+#else
+import UIKit
+
+@UIApplicationMain
+class AppDelegate: UIResponder, UIApplicationDelegate {
+
+    var window: UIWindow?
+
+    func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
+        return true
+    }
+}
+#endif

+ 73 - 0
Lib/TestHost/Assets.xcassets/AppIcon.appiconset/Contents.json

@@ -0,0 +1,73 @@
+{
+  "images" : [
+    {
+      "idiom" : "iphone",
+      "size" : "29x29",
+      "scale" : "2x"
+    },
+    {
+      "idiom" : "iphone",
+      "size" : "29x29",
+      "scale" : "3x"
+    },
+    {
+      "idiom" : "iphone",
+      "size" : "40x40",
+      "scale" : "2x"
+    },
+    {
+      "idiom" : "iphone",
+      "size" : "40x40",
+      "scale" : "3x"
+    },
+    {
+      "idiom" : "iphone",
+      "size" : "60x60",
+      "scale" : "2x"
+    },
+    {
+      "idiom" : "iphone",
+      "size" : "60x60",
+      "scale" : "3x"
+    },
+    {
+      "idiom" : "ipad",
+      "size" : "29x29",
+      "scale" : "1x"
+    },
+    {
+      "idiom" : "ipad",
+      "size" : "29x29",
+      "scale" : "2x"
+    },
+    {
+      "idiom" : "ipad",
+      "size" : "40x40",
+      "scale" : "1x"
+    },
+    {
+      "idiom" : "ipad",
+      "size" : "40x40",
+      "scale" : "2x"
+    },
+    {
+      "idiom" : "ipad",
+      "size" : "76x76",
+      "scale" : "1x"
+    },
+    {
+      "idiom" : "ipad",
+      "size" : "76x76",
+      "scale" : "2x"
+    },
+    {
+      "idiom" : "ipad",
+      "size" : "83.5x83.5",
+      "scale" : "2x"
+    }
+  ],
+  "info" : {
+    "version" : 1,
+    "author" : "xcode"
+  }
+}

+ 47 - 0
Lib/TestHost/Info.plist

@@ -0,0 +1,47 @@
+<?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>en</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>APPL</string>
+	<key>CFBundleShortVersionString</key>
+	<string>1.0</string>
+	<key>CFBundleSignature</key>
+	<string>????</string>
+	<key>CFBundleVersion</key>
+	<string>1</string>
+	<key>LSRequiresIPhoneOS</key>
+	<true/>
+	<key>UIRequiredDeviceCapabilities</key>
+	<array>
+		<string>armv7</string>
+	</array>
+	<key>UISupportedInterfaceOrientations</key>
+	<array>
+		<string>UIInterfaceOrientationPortrait</string>
+		<string>UIInterfaceOrientationLandscapeLeft</string>
+		<string>UIInterfaceOrientationLandscapeRight</string>
+	</array>
+	<key>UISupportedInterfaceOrientations~ipad</key>
+	<array>
+		<string>UIInterfaceOrientationPortrait</string>
+		<string>UIInterfaceOrientationPortraitUpsideDown</string>
+		<string>UIInterfaceOrientationLandscapeLeft</string>
+		<string>UIInterfaceOrientationLandscapeRight</string>
+	</array>
+	<key>UIRequiresFullScreen</key>
+	<true/>
+	<key>NSPrincipalClass</key>
+	<string>$(PRINCIPAL_CLASS)</string>
+</dict>
+</plist>

+ 11 - 0
Lib/TestHost/TestHost.entitlements

@@ -0,0 +1,11 @@
+<?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>keychain-access-groups</key>
+	<array>
+		<string>$(AppIdentifierPrefix)com.kishikawakatsumi.TestHost</string>
+		<string>$(AppIdentifierPrefix)shared</string>
+	</array>
+</dict>
+</plist>