From c27b5b30acc81cec29725530bdb3eb9d23cb815b Mon Sep 17 00:00:00 2001 From: Cody Garvin <emig647@gmail.com> Date: Mon, 22 Jun 2020 21:51:33 -0700 Subject: [PATCH 1/2] LIBMOBILE-77: Removed Quick and Nimble, updated unit tests --- Analytics.xcodeproj/project.pbxproj | 8 - AnalyticsTests/AnalyticsTests.swift | 373 ++++++++--------- AnalyticsTests/AnalyticsUtilTests.swift | 197 +++++---- AnalyticsTests/AutoScreenReportingTest.swift | 134 +++--- AnalyticsTests/ContextTest.swift | 116 +++--- AnalyticsTests/CryptoTest.swift | 88 ++-- AnalyticsTests/FileStorageTest.swift | 200 ++++----- AnalyticsTests/HTTPClientTest.swift | 410 +++++++++---------- AnalyticsTests/IntegrationsManagerTest.swift | 173 ++++---- AnalyticsTests/MiddlewareTests.swift | 233 +++++------ AnalyticsTests/StoreKitTrackerTests.swift | 46 +-- AnalyticsTests/TrackingTests.swift | 165 ++++---- AnalyticsTests/UserDefaultsStorageTest.swift | 153 ++++--- AnalyticsTests/Utils/TestUtils.swift | 29 +- Podfile | 2 - Podfile.lock | 12 +- 16 files changed, 1113 insertions(+), 1226 deletions(-) diff --git a/Analytics.xcodeproj/project.pbxproj b/Analytics.xcodeproj/project.pbxproj index e7631f89b..9f61cce48 100644 --- a/Analytics.xcodeproj/project.pbxproj +++ b/Analytics.xcodeproj/project.pbxproj @@ -589,16 +589,12 @@ ); inputPaths = ( "${PODS_ROOT}/Target Support Files/Pods-AnalyticsTests/Pods-AnalyticsTests-frameworks.sh", - "${BUILT_PRODUCTS_DIR}/Nimble-iOS/Nimble.framework", "${BUILT_PRODUCTS_DIR}/Nocilla-iOS/Nocilla.framework", - "${BUILT_PRODUCTS_DIR}/Quick-iOS/Quick.framework", "${BUILT_PRODUCTS_DIR}/SwiftTryCatch-iOS/SwiftTryCatch.framework", ); name = "[CP] Embed Pods Frameworks"; outputPaths = ( - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Nimble.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Nocilla.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Quick.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/SwiftTryCatch.framework", ); runOnlyForDeploymentPostprocessing = 0; @@ -657,16 +653,12 @@ ); inputPaths = ( "${PODS_ROOT}/Target Support Files/Pods-AnalyticsTestsTVOS/Pods-AnalyticsTestsTVOS-frameworks.sh", - "${BUILT_PRODUCTS_DIR}/Nimble-tvOS/Nimble.framework", "${BUILT_PRODUCTS_DIR}/Nocilla-tvOS/Nocilla.framework", - "${BUILT_PRODUCTS_DIR}/Quick-tvOS/Quick.framework", "${BUILT_PRODUCTS_DIR}/SwiftTryCatch-tvOS/SwiftTryCatch.framework", ); name = "[CP] Embed Pods Frameworks"; outputPaths = ( - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Nimble.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Nocilla.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Quick.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/SwiftTryCatch.framework", ); runOnlyForDeploymentPostprocessing = 0; diff --git a/AnalyticsTests/AnalyticsTests.swift b/AnalyticsTests/AnalyticsTests.swift index 53b026b6b..baa657089 100644 --- a/AnalyticsTests/AnalyticsTests.swift +++ b/AnalyticsTests/AnalyticsTests.swift @@ -7,227 +7,212 @@ // -import Quick -import Nimble import Analytics +import XCTest -class AnalyticsTests: QuickSpec { - override func spec() { +class AnalyticsTests: XCTestCase { + let config = AnalyticsConfiguration(writeKey: "QUI5ydwIGeFFTa1IvCBUhxL9PyW5B0jE") let cachedSettings = [ - "integrations": [ - "Segment.io": ["apiKey": "QUI5ydwIGeFFTa1IvCBUhxL9PyW5B0jE"] - ], - "plan": ["track": [:]], - ] as NSDictionary + "integrations": [ + "Segment.io": ["apiKey": "QUI5ydwIGeFFTa1IvCBUhxL9PyW5B0jE"] + ], + "plan": ["track": [:]], + ] as NSDictionary var analytics: Analytics! var testMiddleware: TestMiddleware! var testApplication: TestApplication! - - beforeEach { - testMiddleware = TestMiddleware() - config.sourceMiddleware = [testMiddleware] - testApplication = TestApplication() - config.application = testApplication - config.trackApplicationLifecycleEvents = true - - UserDefaults.standard.set("test SEGQueue should be removed", forKey: "SEGQueue") - expect(UserDefaults.standard.string(forKey: "SEGQueue")).toNot(beNil()) - - analytics = Analytics(configuration: config) - analytics.test_integrationsManager()?.test_setCachedSettings(settings: cachedSettings) + + override func setUp() { + super.setUp() + testMiddleware = TestMiddleware() + config.sourceMiddleware = [testMiddleware] + testApplication = TestApplication() + config.application = testApplication + config.trackApplicationLifecycleEvents = true + + UserDefaults.standard.set("test SEGQueue should be removed", forKey: "SEGQueue") + XCTAssertNotNil(UserDefaults.standard.string(forKey: "SEGQueue")) + + analytics = Analytics(configuration: config) + analytics.test_integrationsManager()?.test_setCachedSettings(settings: cachedSettings) } - - afterEach { - analytics.reset() + + override func tearDown() { + super.tearDown() + analytics.reset() } - - it("initialized correctly") { - expect(analytics.configuration.flushAt) == 20 - expect(analytics.configuration.flushInterval) == 30 - expect(analytics.configuration.maxQueueSize) == 1000 - expect(analytics.configuration.writeKey) == "QUI5ydwIGeFFTa1IvCBUhxL9PyW5B0jE" - expect(analytics.configuration.shouldUseLocationServices) == false - expect(analytics.configuration.enableAdvertisingTracking) == true - expect(analytics.configuration.shouldUseBluetooth) == false - expect(analytics.configuration.httpSessionDelegate).to(beNil()) - expect(analytics.getAnonymousId()).toNot(beNil()) + + func testInitializedCorrectly() { + XCTAssertEqual(analytics.configuration.flushAt, 20) + XCTAssertEqual(analytics.configuration.flushInterval, 30) + XCTAssertEqual(analytics.configuration.maxQueueSize, 1000) + XCTAssertEqual(analytics.configuration.writeKey, "QUI5ydwIGeFFTa1IvCBUhxL9PyW5B0jE") + XCTAssertEqual(analytics.configuration.shouldUseLocationServices, false) + XCTAssertEqual(analytics.configuration.enableAdvertisingTracking, true) + XCTAssertEqual(analytics.configuration.shouldUseBluetooth, false) + XCTAssertNil(analytics.configuration.httpSessionDelegate) + XCTAssertNotNil(analytics.getAnonymousId()) } - it("clears SEGQueue from UserDefaults after initialized") { - expect(UserDefaults.standard.string(forKey: "SEGQueue")).toEventually(beNil()) + func testClearsSEGQueueFromUserDefaults() { + expectUntil(2.0, expression: UserDefaults.standard.string(forKey: "SEGQueue") == nil) } /* TODO: Fix me when the Context object isn't so wild. - it("collects IDFA") { - testMiddleware.swallowEvent = true - analytics.configuration.enableAdvertisingTracking = true - analytics.configuration.adSupportBlock = { () -> String in - return "1234AdsNoMore!" - } - - analytics.track("test"); - - let event = testMiddleware.lastContext?.payload as? SEGTrackPayload - expect(event?.properties?["url"] as? String) == "myapp://auth?token=((redacted/my-auth))&other=stuff" - }*/ + func testCollectsIDFA() { + testMiddleware.swallowEvent = true + analytics.configuration.enableAdvertisingTracking = true + analytics.configuration.adSupportBlock = { () -> String in + return "1234AdsNoMore!" + } + + analytics.track("test"); + + let event = testMiddleware.lastContext?.payload as? TrackPayload + XCTAssertEqual(event?.properties?["url"] as? String, "myapp://auth?token=((redacted/my-auth))&other=stuff") + }*/ - it("persists anonymousId") { - let analytics2 = Analytics(configuration: config) - expect(analytics.getAnonymousId()) == analytics2.getAnonymousId() + func testPersistsAnonymousId() { + let analytics2 = Analytics(configuration: config) + XCTAssertEqual(analytics.getAnonymousId(), analytics2.getAnonymousId()) } - - it("persists userId") { - analytics.identify("testUserId1") - - let analytics2 = Analytics(configuration: config) - analytics2.test_integrationsManager()?.test_setCachedSettings(settings: cachedSettings) - - expect(analytics.test_integrationsManager()?.test_segmentIntegration()?.test_userId()) == "testUserId1" - expect(analytics2.test_integrationsManager()?.test_segmentIntegration()?.test_userId()) == "testUserId1" + + func testPersistsUserId() { + analytics.identify("testUserId1") + + let analytics2 = Analytics(configuration: config) + analytics2.test_integrationsManager()?.test_setCachedSettings(settings: cachedSettings) + + XCTAssertEqual(analytics.test_integrationsManager()?.test_segmentIntegration()?.test_userId(), "testUserId1") + XCTAssertEqual(analytics2.test_integrationsManager()?.test_segmentIntegration()?.test_userId(), "testUserId1") } - - /* This test is pretty flaky. - it("continues user activity") { - let activity = NSUserActivity(activityType: NSUserActivityTypeBrowsingWeb) - activity.webpageURL = URL(string: "http://www.segment.com") - analytics.continue(activity) - let referrer = analytics.test_integrationsManager()?.test_segmentIntegration()?.test_referrer() - expect(referrer?["url"] as? String).toEventually(equal("http://www.segment.com")) - } - */ - - it("clears user data") { - analytics.identify("testUserId1", traits: [ "Test trait key" : "Test trait value"]) - analytics.reset() - expect(analytics.test_integrationsManager()?.test_segmentIntegration()?.test_userId()).toEventually(beNil()) - expect(analytics.test_integrationsManager()?.test_segmentIntegration()?.test_traits()?.count).toEventually(equal(0)) + + func testClearsUserData() { + analytics.identify("testUserId1", traits: [ "Test trait key" : "Test trait value"]) + analytics.reset() + + expectUntil(2.0, expression: self.analytics.test_integrationsManager()?.test_segmentIntegration()?.test_userId() == nil) + + expectUntil(2.0, expression: self.analytics.test_integrationsManager()?.test_segmentIntegration()?.test_traits()?.count == 0) } - - it("fires Application Opened for UIApplicationDidFinishLaunching") { - testMiddleware.swallowEvent = true - NotificationCenter.default.post(name: .UIApplicationDidFinishLaunching, object: testApplication, userInfo: [ - UIApplicationLaunchOptionsKey.sourceApplication: "testApp", - UIApplicationLaunchOptionsKey.url: "test://test", - ]) - let event = testMiddleware.lastContext?.payload as? TrackPayload - expect(event?.event) == "Application Opened" - expect(event?.properties?["from_background"] as? Bool) == false - expect(event?.properties?["referring_application"] as? String) == "testApp" - expect(event?.properties?["url"] as? String) == "test://test" + + func testFiresApplicationOpenedForAppLaunchingEvent() { + testMiddleware.swallowEvent = true + NotificationCenter.default.post(name: .UIApplicationDidFinishLaunching, object: testApplication, userInfo: [ + UIApplicationLaunchOptionsKey.sourceApplication: "testApp", + UIApplicationLaunchOptionsKey.url: "test://test", + ]) + let event = testMiddleware.lastContext?.payload as? TrackPayload + XCTAssertEqual(event?.event, "Application Opened") + XCTAssertEqual(event?.properties?["from_background"] as? Bool, false) + XCTAssertEqual(event?.properties?["referring_application"] as? String, "testApp") + XCTAssertEqual(event?.properties?["url"] as? String, "test://test") } - - it("fires Application Opened during UIApplicationWillEnterForeground") { - testMiddleware.swallowEvent = true - NotificationCenter.default.post(name: .UIApplicationWillEnterForeground, object: testApplication) - let event = testMiddleware.lastContext?.payload as? TrackPayload - expect(event?.event) == "Application Opened" - expect(event?.properties?["from_background"] as? Bool) == true + + func testFiresApplicationEnterForeground() { + testMiddleware.swallowEvent = true + NotificationCenter.default.post(name: .UIApplicationWillEnterForeground, object: testApplication) + let event = testMiddleware.lastContext?.payload as? TrackPayload + XCTAssertEqual(event?.event, "Application Opened") + XCTAssertEqual(event?.properties?["from_background"] as? Bool, true) } - it("fires Application Backgrounded during UIApplicationDidEnterBackground") { - testMiddleware.swallowEvent = true - NotificationCenter.default.post(name: .UIApplicationDidEnterBackground, object: testApplication) - let event = testMiddleware.lastContext?.payload as? TrackPayload - expect(event?.event) == "Application Backgrounded" + func testFiresApplicationDuringEnterBackground() { + testMiddleware.swallowEvent = true + NotificationCenter.default.post(name: .UIApplicationDidEnterBackground, object: testApplication) + let event = testMiddleware.lastContext?.payload as? TrackPayload + XCTAssertEqual(event?.event, "Application Backgrounded") } - - it("flushes when UIApplicationDidEnterBackgroundNotification is fired") { - analytics.track("test") - NotificationCenter.default.post(name: .UIApplicationDidEnterBackground, object: testApplication) - expect(testApplication.backgroundTasks.count).toEventually(equal(1)) - expect(testApplication.backgroundTasks[0].isEnded).toEventually(beFalse()) + + func testFlushesWhenApplicationBackgroundIsFired() { + analytics.track("test") + NotificationCenter.default.post(name: .UIApplicationDidEnterBackground, object: testApplication) + + expectUntil(2.0, expression: self.testApplication.backgroundTasks.count == 1) + expectUntil(2.0, expression: self.testApplication.backgroundTasks[0].isEnded == false) } - it("respects maxQueueSize") { - let max = 72 - config.maxQueueSize = UInt(max) - - for i in 1...max * 2 { - analytics.track("test #\(i)") - } - - let integration = analytics.test_integrationsManager()?.test_segmentIntegration() - expect(integration).notTo(beNil()) - - analytics.flush() - waitUntil(timeout: 60) {done in - let queue = DispatchQueue(label: "test") - - queue.async { - while(integration?.test_queue()?.count != max) { + func testRespectsMaxQueueSize() { + let max = 72 + config.maxQueueSize = UInt(max) + + for i in 1...max * 2 { + analytics.track("test #\(i)") + } + + let integration = analytics.test_integrationsManager()?.test_segmentIntegration() + XCTAssertNotNil(integration) + + analytics.flush() + let currentTime = Date() + while(integration?.test_queue()?.count != max && currentTime < currentTime + 60) { sleep(1) - } - - done() } - } } - - it("protocol conformance should not interfere with UIApplication interface") { - // In Xcode8/iOS10, UIApplication.h typedefs UIBackgroundTaskIdentifier as NSUInteger, - // whereas Swift has UIBackgroundTaskIdentifier typealiaed to Int. - // This is likely due to a custom Swift mapping for UIApplication which got out of sync. - // If we extract the exact UIApplication method names in SEGApplicationProtocol, - // it will cause a type mismatch between the return value from beginBackgroundTask - // and the argument for endBackgroundTask. - // This would impact all code in a project that imports the Segment framework. - // Note that this doesn't appear to be an issue any longer in Xcode9b3. - let task = UIApplication.shared.beginBackgroundTask(expirationHandler: nil) - UIApplication.shared.endBackgroundTask(task) - } - - it("flushes using flushTimer") { - let integration = analytics.test_integrationsManager()?.test_segmentIntegration() - - analytics.track("test") - - expect(integration?.test_flushTimer()).toEventuallyNot(beNil()) - expect(integration?.test_batchRequest()).to(beNil()) - - integration?.test_flushTimer()?.fire() - - expect(integration?.test_batchRequest()).toEventuallyNot(beNil()) + + func testProtocolConformanceShouldNotInterfere() { + // In Xcode8/iOS10, UIApplication.h typedefs UIBackgroundTaskIdentifier as NSUInteger, + // whereas Swift has UIBackgroundTaskIdentifier typealiaed to Int. + // This is likely due to a custom Swift mapping for UIApplication which got out of sync. + // If we extract the exact UIApplication method names in SEGApplicationProtocol, + // it will cause a type mismatch between the return value from beginBackgroundTask + // and the argument for endBackgroundTask. + // This would impact all code in a project that imports the Segment framework. + // Note that this doesn't appear to be an issue any longer in Xcode9b3. + let task = UIApplication.shared.beginBackgroundTask(expirationHandler: nil) + UIApplication.shared.endBackgroundTask(task) } - - it("respects flushInterval") { - let timer = analytics - .test_integrationsManager()? - .test_segmentIntegration()? - .test_flushTimer() - - expect(timer).toNot(beNil()) - expect(timer?.timeInterval) == config.flushInterval - } - - it("redacts sensible URLs from deep links tracking") { - testMiddleware.swallowEvent = true - analytics.configuration.trackDeepLinks = true - analytics.open(URL(string: "fb123456789://authorize#access_token=hastoberedacted")!, options: [:]) - - - let event = testMiddleware.lastContext?.payload as? TrackPayload - expect(event?.event) == "Deep Link Opened" - expect(event?.properties?["url"] as? String) == "fb123456789://authorize#access_token=((redacted/fb-auth-token))" + + func testFlushesUsingFlushTimer() { + let integration = analytics.test_integrationsManager()?.test_segmentIntegration() + + analytics.track("test") + + expectUntil(2.0, expression: integration?.test_flushTimer() != nil) + XCTAssertNil(integration?.test_batchRequest()) + + integration?.test_flushTimer()?.fire() + expectUntil(2.0, expression: integration?.test_batchRequest() != nil) + } + + func testRespectsFlushIntervale() { + let timer = analytics + .test_integrationsManager()? + .test_segmentIntegration()? + .test_flushTimer() + + XCTAssertNotNil(timer) + XCTAssertEqual(timer?.timeInterval, config.flushInterval) + } + + func testRedactsSensibleURLsFromDeepLinksTracking() { + testMiddleware.swallowEvent = true + analytics.configuration.trackDeepLinks = true + analytics.open(URL(string: "fb123456789://authorize#access_token=hastoberedacted")!, options: [:]) + + + let event = testMiddleware.lastContext?.payload as? TrackPayload + XCTAssertEqual(event?.event, "Deep Link Opened") + XCTAssertEqual(event?.properties?["url"] as? String, "fb123456789://authorize#access_token=((redacted/fb-auth-token))") + } + + func testRedactsSensibleURLsFromDeepLinksWithFilters() { + testMiddleware.swallowEvent = true + analytics.configuration.payloadFilters["(myapp://auth\\?token=)([^&]+)"] = "$1((redacted/my-auth))" + analytics.configuration.trackDeepLinks = true + analytics.open(URL(string: "myapp://auth?token=hastoberedacted&other=stuff")!, options: [:]) + + + let event = testMiddleware.lastContext?.payload as? TrackPayload + XCTAssertEqual(event?.event, "Deep Link Opened") + XCTAssertEqual(event?.properties?["url"] as? String, "myapp://auth?token=((redacted/my-auth))&other=stuff") + } + + func testDefaultsSEGQueueToEmptyArray() { + let integration = analytics.test_integrationsManager()?.test_segmentIntegration() + XCTAssertNotNil(integration) + integration?.test_fileStorage()?.resetAll() + XCTAssert(integration?.test_queue()?.isEmpty ?? false) } - - it("redacts sensible URLs from deep links tracking using custom filters") { - testMiddleware.swallowEvent = true - analytics.configuration.payloadFilters["(myapp://auth\\?token=)([^&]+)"] = "$1((redacted/my-auth))" - analytics.configuration.trackDeepLinks = true - analytics.open(URL(string: "myapp://auth?token=hastoberedacted&other=stuff")!, options: [:]) - - - let event = testMiddleware.lastContext?.payload as? TrackPayload - expect(event?.event) == "Deep Link Opened" - expect(event?.properties?["url"] as? String) == "myapp://auth?token=((redacted/my-auth))&other=stuff" - } - - it("defaults SEGQueue to an empty array when missing from file storage") { - let integration = analytics.test_integrationsManager()?.test_segmentIntegration() - expect(integration).notTo(beNil()) - integration?.test_fileStorage()?.resetAll() - expect(integration?.test_queue()).to(beEmpty()) - } - } } diff --git a/AnalyticsTests/AnalyticsUtilTests.swift b/AnalyticsTests/AnalyticsUtilTests.swift index 53998922f..1bc075d45 100644 --- a/AnalyticsTests/AnalyticsUtilTests.swift +++ b/AnalyticsTests/AnalyticsUtilTests.swift @@ -7,134 +7,127 @@ // -import Quick -import Nimble import Analytics +import XCTest -class AnalyticsUtilTests: QuickSpec { - override func spec() { - - it("format NSDate objects to RFC 3339 complaint string") { - let date = Date(timeIntervalSince1970: 0) - let formattedString = iso8601FormattedString(date) - expect(formattedString) == "1970-01-01T00:00:00.000Z" - - var components = DateComponents() - components.year = 1992 - components.month = 8 - components.day = 6 - components.hour = 7 - components.minute = 32 - components.second = 4 - components.nanosecond = 335000000 - let calendar = NSCalendar(calendarIdentifier: .gregorian)! - calendar.timeZone = TimeZone(secondsFromGMT: -4 * 60 * 60)! - let date2 = calendar.date(from: components)! - let formattedString2 = iso8601FormattedString(date2) - expect(formattedString2) == "1992-08-06T11:32:04.335Z" +class AnalyticsUtilTests: XCTestCase { + + let filters = [ + "(foo)": "$1-bar" + ] + + func equals(a: Any, b: Any) -> Bool { + let aData = try! JSONSerialization.data(withJSONObject: a, options: .prettyPrinted) as NSData + let bData = try! JSONSerialization.data(withJSONObject: b, options: .prettyPrinted) + + return aData.isEqual(to: bData) } - - it("format NSDate objects to RFC 3339 complaint string w/ nanoseconds") { - let date = Date(timeIntervalSince1970: 0) - let formattedString = iso8601NanoFormattedString(date) - expect(formattedString) == "1970-01-01T00:00:00.000000000Z" - - var components = DateComponents() - components.year = 1992 - components.month = 8 - components.day = 6 - components.hour = 7 - components.minute = 32 - components.second = 4 - components.nanosecond = 335000008 - let calendar = NSCalendar(calendarIdentifier: .gregorian)! - calendar.timeZone = TimeZone(secondsFromGMT: -4 * 60 * 60)! - let date2 = calendar.date(from: components)! - let formattedString2 = iso8601NanoFormattedString(date2) - expect(formattedString2) == "1992-08-06T11:32:04.335000008Z" + + func testFormatNSDateObjects() { + let date = Date(timeIntervalSince1970: 0) + let formattedString = iso8601FormattedString(date) + XCTAssertEqual(formattedString, "1970-01-01T00:00:00.000Z") + + var components = DateComponents() + components.year = 1992 + components.month = 8 + components.day = 6 + components.hour = 7 + components.minute = 32 + components.second = 4 + components.nanosecond = 335000000 + let calendar = NSCalendar(calendarIdentifier: .gregorian)! + calendar.timeZone = TimeZone(secondsFromGMT: -4 * 60 * 60)! + let date2 = calendar.date(from: components)! + let formattedString2 = iso8601FormattedString(date2) + XCTAssertEqual(formattedString2, "1992-08-06T11:32:04.335Z") } - - describe("trimQueue", { - it("does nothing when count < max") { + + func testFormatNSDateRFC3339() { + let date = Date(timeIntervalSince1970: 0) + let formattedString = iso8601NanoFormattedString(date) + XCTAssertEqual(formattedString, "1970-01-01T00:00:00.000000000Z") + + var components = DateComponents() + components.year = 1992 + components.month = 8 + components.day = 6 + components.hour = 7 + components.minute = 32 + components.second = 4 + components.nanosecond = 335000008 + let calendar = NSCalendar(calendarIdentifier: .gregorian)! + calendar.timeZone = TimeZone(secondsFromGMT: -4 * 60 * 60)! + let date2 = calendar.date(from: components)! + let formattedString2 = iso8601NanoFormattedString(date2) + XCTAssertEqual(formattedString2, "1992-08-06T11:32:04.335000008Z") + } + + func testTrimQueueDoesNothingCountLessThan() { let queue = NSMutableArray(array: []) for i in 1...4 { - queue.add(i) + queue.add(i) } trimQueue(queue, 5) - expect(queue) == [1, 2, 3, 4] - } - - it("trims when count > max") { + XCTAssertEqual(queue, [1, 2, 3, 4]) + } + + func testTrimQueueWhenCountGreaterThan() { let queue = NSMutableArray(array: []) for i in 1...10 { - queue.add(i) + queue.add(i) } trimQueue(queue, 5) - expect(queue) == [6, 7, 8, 9, 10] - } - - it("does not trim when count == max") { + XCTAssertEqual(queue, [6, 7, 8, 9, 10]) + } + + func testTrimQueueWhenCountEqual() { let queue = NSMutableArray(array: []) for i in 1...5 { - queue.add(i) + queue.add(i) } trimQueue(queue, 5) - expect(queue) == [1, 2, 3, 4, 5] - } - }) + XCTAssertEqual(queue, [1, 2, 3, 4, 5]) + } - describe("JSON traverse", { - let filters = [ - "(foo)": "$1-bar" - ] - - func equals(a: Any, b: Any) -> Bool { - let aData = try! JSONSerialization.data(withJSONObject: a, options: .prettyPrinted) as NSData - let bData = try! JSONSerialization.data(withJSONObject: b, options: .prettyPrinted) - - return aData.isEqual(to: bData) - } - - it("works with strings") { - expect(Utilities.traverseJSON("a b foo c", andReplaceWithFilters: filters) as? String) == "a b foo-bar c" - } - - it("works recursively") { - expect(Utilities.traverseJSON("a b foo foo c", andReplaceWithFilters: filters) as? String) == "a b foo-bar foo-bar c" - } - - it("works with nested dictionaries") { + func testJSONTraverseWithStrings() { + XCTAssertEqual(Utilities.traverseJSON("a b foo c", andReplaceWithFilters: filters) as? String, "a b foo-bar c") + } + + func testJSONTraverseRecursively() { + XCTAssertEqual(Utilities.traverseJSON("a b foo foo c", andReplaceWithFilters: filters) as? String, "a b foo-bar foo-bar c") + } + + func testJSONWorksNestedDictionaries() { let data = [ - "foo": [1, nil, "qfoob", ["baz": "foo"]], - "bar": "foo" - ] as [String: Any] + "foo": [1, nil, "qfoob", ["baz": "foo"]], + "bar": "foo" + ] as [String: Any] guard let input = Utilities.traverseJSON(data, andReplaceWithFilters: filters) as? [String: Any] else { - XCTFail("Failed to create actual result from traversed JSON replace") - return + XCTFail("Failed to create actual result from traversed JSON replace") + return } let output = [ - "foo": [1, nil, "qfoo-barb", ["baz": "foo-bar"]], - "bar": "foo-bar" - ] as [String: Any] + "foo": [1, nil, "qfoo-barb", ["baz": "foo-bar"]], + "bar": "foo-bar" + ] as [String: Any] - expect(NSDictionary(dictionary: output).isEqual(to: input)) == true - } - - it("works with nested arrays") { + XCTAssertEqual(NSDictionary(dictionary: output).isEqual(to: input), true) + } + + func testJSONWorksNestedArrays() { let data = [ - [1, nil, "qfoob", ["baz": "foo"]], - "foo" - ] as [Any] + [1, nil, "qfoob", ["baz": "foo"]], + "foo" + ] as [Any] let input = Utilities.traverseJSON(data, andReplaceWithFilters: filters) let output = [ - [1, nil, "qfoo-barb", ["baz": "foo-bar"]], - "foo-bar" - ] as [Any] + [1, nil, "qfoo-barb", ["baz": "foo-bar"]], + "foo-bar" + ] as [Any] - expect(equals(a: input!, b: output)) == true - } - }) - } + XCTAssertEqual(equals(a: input!, b: output), true) + } } diff --git a/AnalyticsTests/AutoScreenReportingTest.swift b/AnalyticsTests/AutoScreenReportingTest.swift index a574b9024..b58a7821d 100644 --- a/AnalyticsTests/AutoScreenReportingTest.swift +++ b/AnalyticsTests/AutoScreenReportingTest.swift @@ -4,133 +4,91 @@ // import Foundation -import Quick -import Nimble import SwiftTryCatch @testable import Analytics +import XCTest -class AutoScreenReportingTests: QuickSpec { - - override func spec() { - +class AutoScreenReportingTests: XCTestCase { + var window: UIWindow! var rootVC: UIViewController! - - beforeEach { - let config = AnalyticsConfiguration(writeKey: "foobar") - config.trackApplicationLifecycleEvents = true - config.recordScreenViews = true - - window = UIWindow() - rootVC = UIViewController() - window.addSubview(rootVC.view) + + override func setUp() { + super.setUp() + + let config = AnalyticsConfiguration(writeKey: "foobar") + config.trackApplicationLifecycleEvents = true + config.recordScreenViews = true + + window = UIWindow() + rootVC = UIViewController() + window.addSubview(rootVC.view) } - - - describe("given a single view controller") { - - it("seg_topViewController returns that view controller") { + + func testTopViewControllerReturnController() { let actualVC = UIViewController.seg_topViewController(rootVC) - expect(actualVC) === rootVC - } + XCTAssertEqual(actualVC, rootVC) } - - describe("given a presented view controller") { - - var expectedVC: UIViewController! - - beforeEach { + + func testTopViewControllerReturnsPresentedController() { + var expectedVC: UIViewController! expectedVC = UIViewController() rootVC.present(expectedVC, animated: false) - } - - it("seg_topViewController returns the presented view controller") { let actualVC = UIViewController.seg_topViewController(rootVC) - expect(actualVC) === expectedVC - } + XCTAssertEqual(actualVC, expectedVC) + } - - describe("given a pushed view controller") { - - var expectedVC: UIViewController! - - beforeEach { + + func testTopViewControllerReturnsPushedViewController() { + var expectedVC: UIViewController! expectedVC = UIViewController() let nc = UINavigationController() rootVC.present(nc, animated: false) nc.pushViewController(expectedVC, animated: false) - } - - it("seg_topViewController returns the pushed view controller") { let actualVC = UIViewController.seg_topViewController(rootVC) - expect(actualVC) === expectedVC - } + XCTAssertEqual(actualVC, expectedVC) } - - describe("given a child of a UITabBarController") { - - var expectedVC: UIViewController! - - beforeEach { + + func testTopViewControllerTeturnsCurrentSelectedController() { + var expectedVC: UIViewController! expectedVC = UIViewController() let tabBarController = UITabBarController() rootVC.present(tabBarController, animated: false) tabBarController.viewControllers = [UIViewController(), expectedVC, UIViewController()] tabBarController.selectedIndex = 1 - } - - it("seg_topViewController returns the currently selected view controller") { let actualVC = UIViewController.seg_topViewController(rootVC) - expect(actualVC) === expectedVC - } + XCTAssertEqual(actualVC, expectedVC) } - - describe("given a child of a custom container view controller conforming to SEGScreenReporting") { - - class CustomContainerViewController: UIViewController, SEGScreenReporting { - var selectedIndex: Int = 0 - var seg_mainViewController: UIViewController? { - return childViewControllers[selectedIndex] + + func testTopViewControllerReturnsCurrentSelectedViewController() { + class CustomContainerViewController: UIViewController, SEGScreenReporting { + var selectedIndex: Int = 0 + var seg_mainViewController: UIViewController? { + return childViewControllers[selectedIndex] + } } - } - - var expectedVC: UIViewController! - - beforeEach { + + var expectedVC: UIViewController! expectedVC = UIViewController() let containerVC = CustomContainerViewController() rootVC.present(containerVC, animated: false) [UIViewController(), expectedVC, UIViewController()].forEach { child in - containerVC.addChildViewController(child) + containerVC.addChildViewController(child) } containerVC.selectedIndex = 1 - } - - it("seg_topViewController returns the currently selected view controller") { let actualVC = UIViewController.seg_topViewController(rootVC) - expect(actualVC) === expectedVC - } + XCTAssertEqual(actualVC, expectedVC) } - - describe("given a child of a container view controller NOT conforming to SEGScreenReporting") { - - var expectedVC: UIViewController! - - beforeEach { + + func testTopViewControllerReturnsChildViewController() { + var expectedVC: UIViewController! expectedVC = UIViewController() let containerVC = UIViewController() rootVC.present(containerVC, animated: false) [expectedVC, UIViewController(), UIViewController()].forEach { child in - containerVC.addChildViewController(child) + containerVC.addChildViewController(child) } - } - - it("seg_topViewController returns the first child view controller") { let actualVC = UIViewController.seg_topViewController(rootVC) - expect(actualVC) === expectedVC - } + XCTAssertEqual(actualVC, expectedVC) } - } } - - diff --git a/AnalyticsTests/ContextTest.swift b/AnalyticsTests/ContextTest.swift index 972aa884f..950e2326a 100644 --- a/AnalyticsTests/ContextTest.swift +++ b/AnalyticsTests/ContextTest.swift @@ -6,82 +6,76 @@ // Copyright © 2016 Segment. All rights reserved. // -import Quick -import Nimble import SwiftTryCatch import Analytics +import XCTest -class ContextTests: QuickSpec { - override func spec() { +class ContextTests: XCTestCase { var analytics: Analytics! - beforeEach { - let config = AnalyticsConfiguration(writeKey: "foobar") - analytics = Analytics(configuration: config) + override func setUp() { + super.setUp() + let config = AnalyticsConfiguration(writeKey: "foobar") + analytics = Analytics(configuration: config) } - it("throws when used incorrectly") { - var context: Context? - var exception: NSException? - - SwiftTryCatch.tryRun({ - context = Context() - }, catchRun: { e in - exception = e - }, finallyRun: nil) - - expect(context).to(beNil()) - expect(exception).toNot(beNil()) + func testThrowsWhenUsedIncorrectly() { + var context: Context? + var exception: NSException? + + SwiftTryCatch.tryRun({ + context = Context() + }, catchRun: { e in + exception = e + }, finallyRun: nil) + + XCTAssertNil(context) + XCTAssertNotNil(exception) } - - it("initialized correctly") { - let context = Context(analytics: analytics) - expect(context._analytics) == analytics - expect(context.eventType) == EventType.undefined + func testInitializedCorrectly() { + let context = Context(analytics: analytics) + XCTAssertEqual(context._analytics, analytics) + XCTAssertEqual(context.eventType, EventType.undefined) } - it("accepts modifications") { - let context = Context(analytics: analytics) - - let newContext = context.modify { context in - context.userId = "sloth" - context.eventType = .track; - } - expect(newContext.userId) == "sloth" - expect(newContext.eventType) == EventType.track; - + func testAcceptsModifications() { + let context = Context(analytics: analytics) + + let newContext = context.modify { context in + context.userId = "sloth" + context.eventType = .track; + } + XCTAssertEqual(newContext.userId, "sloth") + XCTAssertEqual(newContext.eventType, EventType.track) } - it("modifies copy in debug mode to catch bugs") { - let context = Context(analytics: analytics).modify { context in - context.debug = true - } - expect(context.debug) == true - - let newContext = context.modify { context in - context.userId = "123" - } - expect(context) !== newContext - expect(newContext.userId) == "123" - expect(context.userId).to(beNil()) + func testModifiesCopyInDebugMode() { + let context = Context(analytics: analytics).modify { context in + context.debug = true + } + XCTAssertEqual(context.debug, true) + + let newContext = context.modify { context in + context.userId = "123" + } + XCTAssertNotEqual(context, newContext) + XCTAssertEqual(newContext.userId, "123") + XCTAssertNil(context.userId) } - it("modifies self in non-debug mode to optimize perf.") { - let context = Context(analytics: analytics).modify { context in - context.debug = false - } - expect(context.debug) == false - - let newContext = context.modify { context in - context.userId = "123" - } - expect(context) === newContext - expect(newContext.userId) == "123" - expect(context.userId) == "123" + func testModifiesSelfInNonDebug() { + let context = Context(analytics: analytics).modify { context in + context.debug = false + } + XCTAssertFalse(context.debug) + + let newContext = context.modify { context in + context.userId = "123" + } + XCTAssertEqual(context, newContext) + XCTAssertEqual(newContext.userId, "123") + XCTAssertEqual(context.userId, "123") } - - } - } diff --git a/AnalyticsTests/CryptoTest.swift b/AnalyticsTests/CryptoTest.swift index daa179283..053e4f05e 100644 --- a/AnalyticsTests/CryptoTest.swift +++ b/AnalyticsTests/CryptoTest.swift @@ -5,57 +5,57 @@ // Copyright © 2016 Segment. All rights reserved. // -import Quick -import Nimble import Analytics +import XCTest -class CryptoTest : QuickSpec { - override func spec() { - var crypto : AES256Crypto! - beforeEach { - crypto = AES256Crypto(password: "slothysloth") +class CryptoTest : XCTestCase { + + var crypto: AES256Crypto! + override func setUp() { + super.setUp() + crypto = AES256Crypto(password: "slothysloth") } - it("encrypts and decrypts data") { - let strIn = "segment" - let dataIn = strIn.data(using: String.Encoding.utf8)! - let encryptedData = crypto.encrypt(dataIn) - expect(encryptedData).toNot(beNil()) - - let dataOut = crypto.decrypt(encryptedData!) - expect(dataOut) == dataIn - - let strOut = String(data: dataOut!, encoding: String.Encoding.utf8) - expect(strOut) == "segment" + func testEncryptDecryptSuccess() { + let strIn = "segment" + let dataIn = strIn.data(using: String.Encoding.utf8)! + let encryptedData = crypto.encrypt(dataIn) + XCTAssert(encryptedData != nil, "Encrypted data should not be nil") + + let dataOut = crypto.decrypt(encryptedData!) + XCTAssert(dataOut == dataIn, "Data should be the same") + + let strOut = String(data: dataOut!, encoding: String.Encoding.utf8) + XCTAssertEqual(strOut, "segment", "Strings should be the same") } - it("fails for incorrect password") { - let strIn = "segment" - let dataIn = strIn.data(using: String.Encoding.utf8)! - let encryptedData = crypto.encrypt(dataIn) - expect(encryptedData).toNot(beNil()) - - let crypto2 = AES256Crypto(password: "wolf", salt: crypto.salt, iv: crypto.iv) - let dataOut = crypto2.decrypt(encryptedData!) - expect(dataOut) != dataIn - let strOut = String(data: dataOut!, encoding: String.Encoding.utf8) - // no built in way to check password correctness - // http://stackoverflow.com/questions/27712173/determine-if-key-is-incorrect-with-cccrypt-kccoptionpkcs7padding-objective-c - expect(strOut ?? "") != strIn + func testIncorrectPassword() { + let strIn = "segment" + let dataIn = strIn.data(using: String.Encoding.utf8)! + let encryptedData = crypto.encrypt(dataIn) + XCTAssert(encryptedData != nil, "Encrypted data should not be nil") + + let crypto2 = AES256Crypto(password: "wolf", salt: crypto.salt, iv: crypto.iv) + let dataOut = crypto2.decrypt(encryptedData!) + XCTAssertNotEqual(dataOut, dataIn, "In and out should not match") + + let strOut = String(data: dataOut!, encoding: String.Encoding.utf8) + // no built in way to check password correctness + // http://stackoverflow.com/questions/27712173/determine-if-key-is-incorrect-with-cccrypt-kccoptionpkcs7padding-objective-c + XCTAssertNotEqual(strOut ?? "", strIn, "String in and out should not match") } - it("fails for incorrect iv and sault") { - let strIn = "segment" - let dataIn = strIn.data(using: String.Encoding.utf8)! - let encryptedData = crypto.encrypt(dataIn) - expect(encryptedData).toNot(beNil()) - - let crypto2 = AES256Crypto(password: crypto.password) - let dataOut = crypto2.decrypt(encryptedData!) - expect(dataOut) != dataIn - - let strOut = String(data: dataOut!, encoding: String.Encoding.utf8) - expect(strOut ?? "") != strIn + func testFailureForIVAndSault() { + let strIn = "segment" + let dataIn = strIn.data(using: String.Encoding.utf8)! + let encryptedData = crypto.encrypt(dataIn) + XCTAssertNotNil(encryptedData, "Encrypted data should not be nil") + + let crypto2 = AES256Crypto(password: crypto.password) + let dataOut = crypto2.decrypt(encryptedData!) + XCTAssertNotEqual(dataOut, dataIn, "Out and In data should not match") + + let strOut = String(data: dataOut!, encoding: String.Encoding.utf8) + XCTAssertNotEqual(strOut ?? "", strIn, "Out and In strings should not match") } - } } diff --git a/AnalyticsTests/FileStorageTest.swift b/AnalyticsTests/FileStorageTest.swift index 993318ccc..563cbc38a 100644 --- a/AnalyticsTests/FileStorageTest.swift +++ b/AnalyticsTests/FileStorageTest.swift @@ -5,127 +5,129 @@ // Copyright © 2016 Segment. All rights reserved. // -import Quick -import Nimble import Analytics +import XCTest -class FileStorageTest : QuickSpec { - override func spec() { + +class FileStorageTest : XCTestCase { + var storage : FileStorage! - beforeEach { - let url = FileStorage.applicationSupportDirectoryURL() - expect(url).toNot(beNil()) - expect(url?.lastPathComponent) == "Application Support" - storage = FileStorage(folder: url!, crypto: nil) + + override func setUp() { + super.setUp() + let url = FileStorage.applicationSupportDirectoryURL() + XCTAssertNotNil(url, "URL Should not be nil") + XCTAssertEqual(url?.lastPathComponent, "Application Support") + storage = FileStorage(folder: url!, crypto: nil) } - it("Creates caches directory") { - let url = FileStorage.cachesDirectoryURL() - expect(url).toNot(beNil()) - expect(url?.lastPathComponent) == "Caches" + override func tearDown() { + super.tearDown() + storage.resetAll() } - it("creates folder if none exists") { - let tempDir = NSURL(fileURLWithPath: NSTemporaryDirectory()) - let url = tempDir.appendingPathComponent(NSUUID().uuidString) - - expect(try? url?.checkResourceIsReachable()).to(beNil()) - _ = FileStorage(folder: url!, crypto: nil) - - var isDir: ObjCBool = false - let exists = FileManager.default.fileExists(atPath: url!.path, isDirectory: &isDir) - - expect(exists) == true - expect(isDir.boolValue) == true + func testCreatesCachesDirectory() { + let url = FileStorage.cachesDirectoryURL() + XCTAssertNotNil(url, "URL should not be nil") + XCTAssertEqual(url?.lastPathComponent, "Caches", "Last part of url should be Caches") } - it("persists and loads data") { - let dataIn = "segment".data(using: String.Encoding.utf8)! - storage.setData(dataIn, forKey: "mydata") - - let dataOut = storage.data(forKey: "mydata") - expect(dataOut) == dataIn - - let strOut = String(data: dataOut!, encoding: String.Encoding.utf8) - expect(strOut) == "segment" + func testCreatesFolderIfNoneExists() { + let tempDir = NSURL(fileURLWithPath: NSTemporaryDirectory()) + let url = tempDir.appendingPathComponent(NSUUID().uuidString) + + XCTAssertNil(try? url?.checkResourceIsReachable() ?? true) + _ = FileStorage(folder: url!, crypto: nil) + + var isDir: ObjCBool = false + let exists = FileManager.default.fileExists(atPath: url!.path, isDirectory: &isDir) + + XCTAssertEqual(exists, true, "Exists should be true") + XCTAssertEqual(isDir.boolValue, true, "Should be a directory") } - it("persists and loads string") { - let str = "san francisco" - storage.setString(str, forKey: "city") - expect(storage.string(forKey: "city")) == str - - storage.removeKey("city") - expect(storage.string(forKey: "city")).to(beNil()) + func testPersistsAndLoadsData() { + let dataIn = "segment".data(using: String.Encoding.utf8)! + storage.setData(dataIn, forKey: "mydata") + + let dataOut = storage.data(forKey: "mydata") + XCTAssertEqual(dataOut, dataIn, "Out and In data should match") + + let strOut = String(data: dataOut!, encoding: String.Encoding.utf8) + XCTAssertEqual(strOut, "segment") } - it("persists and loads array") { - let array = [ - "san francisco", - "new york", - "tallinn", - ] - storage.setArray(array, forKey: "cities") - expect(storage.array(forKey: "cities") as? Array<String>) == array - - storage.removeKey("cities") - expect(storage.array(forKey: "cities")).to(beNil()) + func testPersistsAndLoadsString() { + let str = "san francisco" + storage.setString(str, forKey: "city") + XCTAssertEqual(storage.string(forKey: "city"), str) + + storage.removeKey("city") + XCTAssertNil(storage.string(forKey: "city")) } - it("persists and loads dictionary") { - let dict = [ - "san francisco": "tech", - "new york": "finance", - "paris": "fashion", - ] - storage.setDictionary(dict, forKey: "cityMap") - expect(storage.dictionary(forKey: "cityMap") as? Dictionary<String, String>) == dict - - storage.removeKey("cityMap") - expect(storage.dictionary(forKey: "cityMap")).to(beNil()) + func testPersistsAndLoadsArray() { + let array = [ + "san francisco", + "new york", + "tallinn", + ] + storage.setArray(array, forKey: "cities") + XCTAssertEqual(storage.array(forKey: "cities") as? Array<String>, array) + + storage.removeKey("cities") + XCTAssertNil(storage.array(forKey: "cities")) } - it("saves file to disk and removes from disk") { - let key = "input.txt" - let url = storage.url(forKey: key) - expect(try? url.checkResourceIsReachable()).to(beNil()) - storage.setString("sloth", forKey: key) - expect(try! url.checkResourceIsReachable()) == true - storage.removeKey(key) - expect(try? url.checkResourceIsReachable()).to(beNil()) + func testPersistsAndLoadsDictionary() { + let dict = [ + "san francisco": "tech", + "new york": "finance", + "paris": "fashion", + ] + storage.setDictionary(dict, forKey: "cityMap") + XCTAssertEqual(storage.dictionary(forKey: "cityMap") as? Dictionary<String, String>, dict) + + storage.removeKey("cityMap") + XCTAssertNil(storage.dictionary(forKey: "cityMap")) } - it("should be binary compatible with old SDKs") { - let key = "traits.plist" - let dictIn = [ - "san francisco": "tech", - "new york": "finance", - "paris": "fashion", - ] - - (dictIn as NSDictionary).write(to: storage.url(forKey: key), atomically: true) - let dictOut = storage.dictionary(forKey: key) - expect(dictOut as? [String: String]) == dictIn + func testSavesFileToDiskRemovesFromDisk() { + let key = "input.txt" + let url = storage.url(forKey: key) + XCTAssertNil(try? url.checkResourceIsReachable()) + storage.setString("sloth", forKey: key) + XCTAssertEqual(try! url.checkResourceIsReachable(), true) + storage.removeKey(key) + XCTAssertNil(try? url.checkResourceIsReachable()) } - it("should work with crypto") { - let url = FileStorage.applicationSupportDirectoryURL() - let crypto = AES256Crypto(password: "thetrees") - let s = FileStorage(folder: url!, crypto: crypto) - let dict = [ - "san francisco": "tech", - "new york": "finance", - "paris": "fashion", - ] - s.setDictionary(dict, forKey: "cityMap") - expect(s.dictionary(forKey: "cityMap") as? Dictionary<String, String>) == dict - - s.removeKey("cityMap") - expect(s.dictionary(forKey: "cityMap")).to(beNil()) + func testShouldBeBinaryCompatible() { + let key = "traits.plist" + let dictIn = [ + "san francisco": "tech", + "new york": "finance", + "paris": "fashion", + ] + + (dictIn as NSDictionary).write(to: storage.url(forKey: key), atomically: true) + let dictOut = storage.dictionary(forKey: key) + XCTAssertEqual(dictOut as? [String: String], dictIn) } - afterEach { - storage.resetAll() + func testShouldWorkWithCrypto() { + let url = FileStorage.applicationSupportDirectoryURL() + let crypto = AES256Crypto(password: "thetrees") + let s = FileStorage(folder: url!, crypto: crypto) + let dict = [ + "san francisco": "tech", + "new york": "finance", + "paris": "fashion", + ] + s.setDictionary(dict, forKey: "cityMap") + XCTAssertEqual(s.dictionary(forKey: "cityMap") as? Dictionary<String, String>, dict) + + s.removeKey("cityMap") + XCTAssertNil(s.dictionary(forKey: "cityMap")) } - } } diff --git a/AnalyticsTests/HTTPClientTest.swift b/AnalyticsTests/HTTPClientTest.swift index 9ae2ab64b..c377df191 100644 --- a/AnalyticsTests/HTTPClientTest.swift +++ b/AnalyticsTests/HTTPClientTest.swift @@ -6,260 +6,248 @@ // Copyright © 2016 Segment. All rights reserved. // -import Quick -import Nimble import Nocilla import Analytics +import XCTest -class HTTPClientTest: QuickSpec { - override func spec() { - +class HTTPClientTest: XCTestCase { + var client: HTTPClient! - - beforeEach { - LSNocilla.sharedInstance().start() - client = HTTPClient(requestFactory: nil) + let batch: [String: Any] = ["sentAt":"2016-07-19'T'19:25:06Z", "batch":[["type":"track", "event":"foo"]]] + let context: [String: Any] = [ + "os": [ + "name": "iPhone OS", + "version" : "8.1.3", + ], + "ip": "8.8.8.8", + ] + + override func setUp() { + super.setUp() + LSNocilla.sharedInstance().start() + client = HTTPClient(requestFactory: nil) } - afterEach { - LSNocilla.sharedInstance().clearStubs() - LSNocilla.sharedInstance().stop() + + override func tearDown() { + super.tearDown() + LSNocilla.sharedInstance().clearStubs() + LSNocilla.sharedInstance().stop() } - - describe("defaultRequestFactory") { - it("preserves url") { + + func testDefaultRequestFactor() { let factory = HTTPClient.defaultRequestFactory() let url = URL(string: "https://api.segment.io/v1/batch") let request = factory(url!) - expect(request.url) == url - } + XCTAssertEqual(request.url, url, "URLs should be the same") } - - describe("settingsForWriteKey") { - it("succeeds for 2xx response") { + + func testSettingsForWriteKeySucceeds2xx() { _ = stubRequest("GET", "https://cdn-settings.segment.com/v1/projects/foo/settings" as NSString) - .withHeader("User-Agent", "analytics-ios/" + Analytics.version())! - .withHeaders(["Accept-Encoding" : "gzip" ])! - .andReturn(200)! - .withHeaders(["Content-Type" : "application/json"])! - .withBody("{\"integrations\":{\"Segment.io\":{\"apiKey\":\"foo\"}},\"plan\":{\"track\":{}}}" as NSString) - - var done = false - let task = client.settings(forWriteKey: "foo", completionHandler: { success, settings in - expect(success) == true - expect(settings as NSDictionary?) == [ - "integrations": [ - "Segment.io": [ - "apiKey":"foo" - ] - ], - "plan":[ - "track": [:] - ] - ] as NSDictionary - done = true + .withHeader("User-Agent", "analytics-ios/" + Analytics.version())! + .withHeaders(["Accept-Encoding" : "gzip" ])! + .andReturn(200)! + .withHeaders(["Content-Type" : "application/json"])! + .withBody("{\"integrations\":{\"Segment.io\":{\"apiKey\":\"foo\"}},\"plan\":{\"track\":{}}}" as NSString) + + let doneExpectation = expectation(description: "Done with url session task") + + _ = client.settings(forWriteKey: "foo", completionHandler: { success, settings in + + XCTAssert(success, "Should be successful") + XCTAssertEqual(settings as NSDictionary?, [ + "integrations": [ + "Segment.io": [ + "apiKey":"foo" + ] + ], + "plan":[ + "track": [:] + ] + ] as NSDictionary) + doneExpectation.fulfill() }) - expect(task.state).toEventually(equal(URLSessionTask.State.completed)) - expect(done).toEventually(beTrue()) - } - - it("fails for non 2xx response") { + + wait(for: [doneExpectation], timeout: 1.0) + } + + func testSettingsWriteKey2xxResponse() { _ = stubRequest("GET", "https://cdn-settings.segment.com/v1/projects/foo/settings" as NSString) - .withHeader("User-Agent", "analytics-ios/" + Analytics.version())! - .withHeaders(["Accept-Encoding" : "gzip" ])! - .andReturn(400)! - .withHeaders(["Content-Type" : "application/json" ])! - .withBody("{\"integrations\":{\"Segment.io\":{\"apiKey\":\"foo\"}},\"plan\":{\"track\":{}}}" as NSString) - var done = false + .withHeader("User-Agent", "analytics-ios/" + Analytics.version())! + .withHeaders(["Accept-Encoding" : "gzip" ])! + .andReturn(400)! + .withHeaders(["Content-Type" : "application/json" ])! + .withBody("{\"integrations\":{\"Segment.io\":{\"apiKey\":\"foo\"}},\"plan\":{\"track\":{}}}" as NSString) + + let doneExpectation = expectation(description: "Done with url session task") + client.settings(forWriteKey: "foo", completionHandler: { success, settings in - expect(success) == false - expect(settings).to(beNil()) - done = true + XCTAssertFalse(success, "Success should be false") + XCTAssertNil(settings, "Failure should have nil settings") + + doneExpectation.fulfill() }) - expect(done).toEventually(beTrue()) - } - - it("fails for json error") { + + wait(for: [doneExpectation], timeout: 1.0) + + } + + func testSettingsWriteKey2xxJSONErrorResponse() { _ = stubRequest("GET", "https://cdn-settings.segment.com/v1/projects/foo/settings" as NSString) - .withHeader("User-Agent", "analytics-ios/" + Analytics.version())! - .withHeaders(["Accept-Encoding":"gzip"])! - .andReturn(200)! - .withHeaders(["Content-Type":"application/json"])! - .withBody("{\"integrations" as NSString) - - var done = false + .withHeader("User-Agent", "analytics-ios/" + Analytics.version())! + .withHeaders(["Accept-Encoding":"gzip"])! + .andReturn(200)! + .withHeaders(["Content-Type":"application/json"])! + .withBody("{\"integrations" as NSString) + + let doneExpectation = expectation(description: "Done with url session task") + client.settings(forWriteKey: "foo", completionHandler: { success, settings in - expect(success) == false - expect(settings).to(beNil()) - done = true + XCTAssertFalse(success, "Success should be false") + XCTAssertNil(settings, "Failure should have nil settings") + doneExpectation.fulfill() }) - expect(done).toEventually(beTrue()) - } + wait(for: [doneExpectation], timeout: 1.0) } - - describe("upload") { - it("does not ask to retry for json error") { + + func testUploadNoRetry() { let batch: [String: Any] = [ - // Dates cannot be serialized as is so the json serialzation will fail. - "sentAt": NSDate(), - "batch": [["type": "track", "event": "foo"]], + // Dates cannot be serialized as is so the json serialzation will fail. + "sentAt": NSDate(), + "batch": [["type": "track", "event": "foo"]], ] - var done = false + let doneExpectation = expectation(description: "Done with url session task") let task = client.upload(batch, forWriteKey: "bar") { retry in - expect(retry) == false - done = true + XCTAssertFalse(retry, "Retry should be false") + doneExpectation.fulfill() } - expect(task).to(beNil()) - expect(done).toEventually(beTrue()) - } - - let batch: [String: Any] = ["sentAt":"2016-07-19'T'19:25:06Z", "batch":[["type":"track", "event":"foo"]]] - - - it("does not ask to retry for 2xx response") { + XCTAssertNil(task, "Task should be nil") + wait(for: [doneExpectation], timeout: 1.0) + } + + func testUploadNoRetry2xx() { _ = stubRequest("POST", "https://api.segment.io/v1/batch" as NSString) - .withHeader("User-Agent", "analytics-ios/" + Analytics.version())! - .withJsonGzippedBody(batch as AnyObject) - .withWriteKey("bar") - .andReturn(200) - var done = false - let task = client.upload(batch, forWriteKey: "bar") { retry in - expect(retry) == false - done = true + .withHeader("User-Agent", "analytics-ios/" + Analytics.version())! + .withJsonGzippedBody(batch as AnyObject) + .withWriteKey("bar") + .andReturn(200) + let doneExpectation = expectation(description: "Done with url session task") + _ = client.upload(batch, forWriteKey: "bar") { retry in + XCTAssertFalse(retry, "Retry should be false") + doneExpectation.fulfill() } - expect(done).toEventually(beTrue()) - expect(task?.state).toEventually(equal(URLSessionTask.State.completed)) - } - - it("asks to retry for 3xx response") { + wait(for: [doneExpectation], timeout: 1.0) + } + + func testUploadRetry3xx() { _ = stubRequest("POST", "https://api.segment.io/v1/batch" as NSString) - .withHeader("User-Agent", "analytics-ios/" + Analytics.version())! - .withJsonGzippedBody(batch as AnyObject) - .withWriteKey("bar") - .andReturn(304) - var done = false - let task = client.upload(batch, forWriteKey: "bar") { retry in - expect(retry) == true - done = true + .withHeader("User-Agent", "analytics-ios/" + Analytics.version())! + .withJsonGzippedBody(batch as AnyObject) + .withWriteKey("bar") + .andReturn(304) + let doneExpectation = expectation(description: "Done with url session task") + _ = client.upload(batch, forWriteKey: "bar") { retry in + XCTAssert(retry, "Retry should be true") + doneExpectation.fulfill() } - expect(done).toEventually(beTrue()) - expect(task?.state).toEventually(equal(URLSessionTask.State.completed)) - } - - it("does not ask to retry for 4xx response") { + wait(for: [doneExpectation], timeout: 1.0) + } + + func testUploadNoRetry4xx() { _ = stubRequest("POST", "https://api.segment.io/v1/batch" as NSString) - .withHeader("User-Agent", "analytics-ios/" + Analytics.version())! - .withJsonGzippedBody(batch as AnyObject) - .withWriteKey("bar") - .andReturn(401) - var done = false - let task = client.upload(batch, forWriteKey: "bar") { retry in - expect(retry) == false - done = true + .withHeader("User-Agent", "analytics-ios/" + Analytics.version())! + .withJsonGzippedBody(batch as AnyObject) + .withWriteKey("bar") + .andReturn(401) + let doneExpectation = expectation(description: "Done with url session task") + _ = client.upload(batch, forWriteKey: "bar") { retry in + XCTAssertFalse(retry, "Retry should be false") + doneExpectation.fulfill() } - expect(done).toEventually(beTrue()) - expect(task?.state).toEventually(equal(URLSessionTask.State.completed)) - } - - it("asks to retry for 429 response") { + wait(for: [doneExpectation], timeout: 1.0) + } + + func testRetryFor429() { _ = stubRequest("POST", "https://api.segment.io/v1/batch" as NSString) - .withHeader("User-Agent", "analytics-ios/" + Analytics.version())! - .withJsonGzippedBody(batch as AnyObject) - .withWriteKey("bar") - .andReturn(429) - var done = false - let task = client.upload(batch, forWriteKey: "bar") { retry in - expect(retry) == true - done = true + .withHeader("User-Agent", "analytics-ios/" + Analytics.version())! + .withJsonGzippedBody(batch as AnyObject) + .withWriteKey("bar") + .andReturn(429) + let doneExpectation = expectation(description: "Done with url session task") + _ = client.upload(batch, forWriteKey: "bar") { retry in + XCTAssert(retry, "Retry should be true") + doneExpectation.fulfill() } - expect(done).toEventually(beTrue()) - expect(task?.state).toEventually(equal(URLSessionTask.State.completed)) - } - - it("asks to retry for 5xx response") { + wait(for: [doneExpectation], timeout: 1.0) + } + + func testRetryFor5xx() { _ = stubRequest("POST", "https://api.segment.io/v1/batch" as NSString) - .withHeader("User-Agent", "analytics-ios/" + Analytics.version())! - .withJsonGzippedBody(batch as AnyObject) - .withWriteKey("bar") - .andReturn(504) - var done = false - let task = client.upload(batch, forWriteKey: "bar") { retry in - expect(retry) == true - done = true + .withHeader("User-Agent", "analytics-ios/" + Analytics.version())! + .withJsonGzippedBody(batch as AnyObject) + .withWriteKey("bar") + .andReturn(504) + let doneExpectation = expectation(description: "Done with url session task") + _ = client.upload(batch, forWriteKey: "bar") { retry in + XCTAssert(retry, "Retry should be true") + doneExpectation.fulfill() } - expect(done).toEventually(beTrue()) - expect(task?.state).toEventually(equal(URLSessionTask.State.completed)) - } - - it("fails when batch size exceeds the max limit size") { + wait(for: [doneExpectation], timeout: 1.0) + } + + func testBatchSizeFailure() { let oversizedBatch: [String: Any] = ["sentAt":"2016-07-19'T'19:25:06Z", "batch": Array(repeating: ["type":"track", "event":"foo"], count: 16000)] - var done = false - let task = client.upload(oversizedBatch, forWriteKey: "bar") { retry in - expect(retry) == false - done = true + let doneExpectation = expectation(description: "Done with url session task") + _ = client.upload(oversizedBatch, forWriteKey: "bar") { retry in + XCTAssertFalse(retry, "Retry should be false") + doneExpectation.fulfill() } - expect(done).toEventually(beTrue()) - expect(task).toEventually(beNil()) - } + wait(for: [doneExpectation], timeout: 1.0) } - - describe("attribution") { - it("fails for json error") { + + func testAttributionFailsForJSONError() { let device = [ - // Dates cannot be serialized as is so the json serialzation will fail. - "sentAt": NSDate(), + // Dates cannot be serialized as is so the json serialzation will fail. + "sentAt": NSDate(), ] - var done = false - let task = client.attribution(withWriteKey: "bar", forDevice: device) { success, properties in - expect(success) == false - done = true + let doneExpectation = expectation(description: "Done with url session task") + _ = client.attribution(withWriteKey: "bar", forDevice: device) { success, properties in + XCTAssertFalse(success, "Retry should be false") + doneExpectation.fulfill() } - expect(task).to(beNil()) - expect(done).toEventually(beTrue()) - } - - let context: [String: Any] = [ - "os": [ - "name": "iPhone OS", - "version" : "8.1.3", - ], - "ip": "8.8.8.8", - ] - - it("succeeds for 2xx response") { + wait(for: [doneExpectation], timeout: 1.0) + } + + func testAttributionSucceedsFor2xx() { _ = stubRequest("POST", "https://mobile-service.segment.com/v1/attribution" as NSString) - .withHeader("User-Agent", "analytics-ios/" + Analytics.version())! - .withWriteKey("foo") - .andReturn(200)! - .withBody("{\"provider\": \"mock\"}" as NSString) - - var done = false - let task = client.attribution(withWriteKey: "foo", forDevice: context) { success, properties in - expect(success) == true - expect(properties as? [String: String]) == [ - "provider": "mock" - ] - done = true + .withHeader("User-Agent", "analytics-ios/" + Analytics.version())! + .withWriteKey("foo") + .andReturn(200)! + .withBody("{\"provider\": \"mock\"}" as NSString) + + let doneExpectation = expectation(description: "Done with url session task") + _ = client.attribution(withWriteKey: "foo", forDevice: context) { success, properties in + XCTAssert(success, "Success should be true") + XCTAssertEqual(properties as? [String: String], [ + "provider": "mock" + ]) + doneExpectation.fulfill() } - expect(task.state).toEventually(equal(URLSessionTask.State.completed)) - expect(done).toEventually(beTrue()) - } - - it("fails for non 2xx response") { + wait(for: [doneExpectation], timeout: 1.0) + } + + func testAttributionFailsForNon2xx() { _ = stubRequest("POST", "https://mobile-service.segment.com/v1/attribution" as NSString) - .withHeader("User-Agent", "analytics-ios/" + Analytics.version())! - .withWriteKey("foo") - .andReturn(404)! - .withBody("not found" as NSString) - var done = false - let task = client.attribution(withWriteKey: "foo", forDevice: context) { success, properties in - expect(success) == false - expect(properties).to(beNil()) - done = true + .withHeader("User-Agent", "analytics-ios/" + Analytics.version())! + .withWriteKey("foo") + .andReturn(404)! + .withBody("not found" as NSString) + let doneExpectation = expectation(description: "Done with url session task") + _ = client.attribution(withWriteKey: "foo", forDevice: context) { success, properties in + XCTAssertFalse(success, "Success should be false") + XCTAssertNil(properties, "Properties should be nil") + doneExpectation.fulfill() } - expect(task.state).toEventually(equal(URLSessionTask.State.completed)) - expect(done).toEventually(beTrue()) - } + wait(for: [doneExpectation], timeout: 1.0) } - } } diff --git a/AnalyticsTests/IntegrationsManagerTest.swift b/AnalyticsTests/IntegrationsManagerTest.swift index 4fa2700d2..25a43f7c7 100644 --- a/AnalyticsTests/IntegrationsManagerTest.swift +++ b/AnalyticsTests/IntegrationsManagerTest.swift @@ -1,106 +1,97 @@ import Analytics -import Quick -import Nimble import SwiftTryCatch - -class IntegrationsManagerTest: QuickSpec { - - override func spec() { - describe("IntegrationsManager") { - context("is track event enabled for integration in plan") { - - it("valid value types are used in integration enablement flags") { - var exception: NSException? = nil - SwiftTryCatch.tryRun({ +import XCTest +class IntegrationsManagerTest: XCTestCase { + + func testValidValueTypesInIntegrationEnablementFlags() { + var exception: NSException? = nil + SwiftTryCatch.tryRun({ IntegrationsManager.isIntegration("comScore", enabledInOptions: ["comScore": ["blah": 1]]) IntegrationsManager.isIntegration("comScore", enabledInOptions: ["comScore": true]) - }, catchRun: { e in + }, catchRun: { e in exception = e - }, finallyRun: nil) - - expect(exception).to(beNil()) - } + }, finallyRun: nil) - it("asserts when invalid value types are used integration enablement flags") { - var exception: NSException? = nil - SwiftTryCatch.tryRun({ + XCTAssertNil(exception) + } + + func testAssertsWhenInvalidValueTypesUsedIntegrationEnablement() { + var exception: NSException? = nil + SwiftTryCatch.tryRun({ IntegrationsManager.isIntegration("comScore", enabledInOptions: ["comScore": "blah"]) - }, catchRun: { e in + }, catchRun: { e in exception = e - }, finallyRun: nil) - - expect(exception).toNot(beNil()) - } + }, finallyRun: nil) - it("asserts when invalid value types are used integration enablement flags") { - var exception: NSException? = nil - SwiftTryCatch.tryRun({ + XCTAssertNotNil(exception) + } + + func testAssertsWhenInvalidValueTypesIntegrationEnableFlags() { + var exception: NSException? = nil + SwiftTryCatch.tryRun({ // we don't accept array's as values. IntegrationsManager.isIntegration("comScore", enabledInOptions: ["comScore": ["key", 1]]) - }, catchRun: { e in + }, catchRun: { e in exception = e - }, finallyRun: nil) - - expect(exception).toNot(beNil()) - } - - it("pulls valid integration data when supplied") { - let enabled = IntegrationsManager.isIntegration("comScore", enabledInOptions: ["comScore": true]) - expect(enabled).to(beTrue()) - } - - it("falls back correctly when values aren't explicitly specified") { - let enabled = IntegrationsManager.isIntegration("comScore", enabledInOptions: ["all": true]) - expect(enabled).to(beTrue()) - let allEnabled = IntegrationsManager.isIntegration("comScore", enabledInOptions: ["All": true]) - expect(allEnabled).to(beTrue()) - } - - it("returns true when there is no plan") { - let enabled = IntegrationsManager.isTrackEvent("hello world", enabledForIntegration: "Amplitude", inPlan:[:]) - expect(enabled).to(beTrue()) - } - - it("returns true when plan is empty") { - let enabled = IntegrationsManager.isTrackEvent("hello world", enabledForIntegration: "Mixpanel", inPlan:["track":[:]]) - expect(enabled).to(beTrue()) - } - - it("returns true when plan enables event") { - let enabled = IntegrationsManager.isTrackEvent("hello world", enabledForIntegration: "Mixpanel", inPlan:["track":["hello world":["enabled":true]]]) - expect(enabled).to(beTrue()) - } - - it("returns false when plan disables event") { - let enabled = IntegrationsManager.isTrackEvent("hello world", enabledForIntegration: "Amplitude", inPlan:["track":["hello world":["enabled":false]]]) - expect(enabled).to(beFalse()) - } - - it("returns true for Segment integration even when plan disables event") { - let enabled = IntegrationsManager.isTrackEvent("hello world", enabledForIntegration: "Segment.io", inPlan:["track":["hello world":["enabled":false]]]) - expect(enabled).to(beTrue()) - } - - it("returns true when plan enables event for integration") { - let enabled = IntegrationsManager.isTrackEvent("hello world", enabledForIntegration: "Mixpanel", inPlan:["track":["hello world":["enabled":true, "integrations":["Mixpanel":true]]]]) - expect(enabled).to(beTrue()) - } - - it("returns false when plan disables event for integration") { - let enabled = IntegrationsManager.isTrackEvent("hello world", enabledForIntegration: "Mixpanel", inPlan:["track":["hello world":["enabled":true, "integrations":["Mixpanel":false]]]]) - expect(enabled).to(beFalse()) - } - - it("returns false when plan disables new events by default") { - let enabled = IntegrationsManager.isTrackEvent("hello world", enabledForIntegration: "Mixpanel", inPlan:["track":["__default":["enabled":false]]]) - expect(enabled).to(beFalse()) - } + }, finallyRun: nil) - it("returns uses event plan rather over defaults") { - let enabled = IntegrationsManager.isTrackEvent("hello world", enabledForIntegration: "Mixpanel", inPlan:["track":["__default":["enabled":false],"hello world":["enabled":true]]]) - expect(enabled).to(beTrue()) - } - } + XCTAssertNotNil(exception) + } + + func testPullsValidIntegrationDataWhenSupplied() { + let enabled = IntegrationsManager.isIntegration("comScore", enabledInOptions: ["comScore": true]) + XCTAssert(enabled) + } + + func testFallsBackCorrectlyWhenNotSpecified() { + let enabled = IntegrationsManager.isIntegration("comScore", enabledInOptions: ["all": true]) + XCTAssert(enabled) + let allEnabled = IntegrationsManager.isIntegration("comScore", enabledInOptions: ["All": true]) + XCTAssert(allEnabled) + } + + func testReturnsTrueWhenThereisNoPlan() { + let enabled = IntegrationsManager.isTrackEvent("hello world", enabledForIntegration: "Amplitude", inPlan:[:]) + XCTAssert(enabled) + } + + func testReturnsTrueWhenPlanIsEmpty() { + let enabled = IntegrationsManager.isTrackEvent("hello world", enabledForIntegration: "Mixpanel", inPlan:["track":[:]]) + XCTAssert(enabled) + } + + func testReturnsTrueWhenPlanEnablesEvent() { + let enabled = IntegrationsManager.isTrackEvent("hello world", enabledForIntegration: "Mixpanel", inPlan:["track":["hello world":["enabled":true]]]) + XCTAssert(enabled) + } + + func testReturnsFalseWhenPlanDisablesEvent() { + let enabled = IntegrationsManager.isTrackEvent("hello world", enabledForIntegration: "Amplitude", inPlan:["track":["hello world":["enabled":false]]]) + XCTAssertFalse(enabled) + } + + func testReturnsTrueForSegmentIntegrationWhenDisablesEvent() { + let enabled = IntegrationsManager.isTrackEvent("hello world", enabledForIntegration: "Segment.io", inPlan:["track":["hello world":["enabled":false]]]) + XCTAssert(enabled) + } + + func testReturnsTrueWhenPlanEnablesEventForIntegration() { + let enabled = IntegrationsManager.isTrackEvent("hello world", enabledForIntegration: "Mixpanel", inPlan:["track":["hello world":["enabled":true, "integrations":["Mixpanel":true]]]]) + XCTAssert(enabled) + } + + func testReturnsFalseWhenPlanDisablesEventForIntegration() { + let enabled = IntegrationsManager.isTrackEvent("hello world", enabledForIntegration: "Mixpanel", inPlan:["track":["hello world":["enabled":true, "integrations":["Mixpanel":false]]]]) + XCTAssertFalse(enabled) + } + + func testReturnsFalseWhenPlanDisablesNewEvents() { + let enabled = IntegrationsManager.isTrackEvent("hello world", enabledForIntegration: "Mixpanel", inPlan:["track":["__default":["enabled":false]]]) + XCTAssertFalse(enabled) + } + + func testReturnsUsesEventPlanRatherOverDefaults() { + let enabled = IntegrationsManager.isTrackEvent("hello world", enabledForIntegration: "Mixpanel", inPlan:["track":["__default":["enabled":false],"hello world":["enabled":true]]]) + XCTAssert(enabled) } - } } diff --git a/AnalyticsTests/MiddlewareTests.swift b/AnalyticsTests/MiddlewareTests.swift index f02a85ebb..14f3ca977 100644 --- a/AnalyticsTests/MiddlewareTests.swift +++ b/AnalyticsTests/MiddlewareTests.swift @@ -7,154 +7,141 @@ // -import Quick -import Nimble import Analytics +import XCTest // Changing event names and adding custom attributes let customizeAllTrackCalls = BlockMiddleware { (context, next) in - if context.eventType == .track { - next(context.modify { ctx in - guard let track = ctx.payload as? TrackPayload else { - return - } - let newEvent = "[New] \(track.event)" - var newProps = track.properties ?? [:] - newProps["customAttribute"] = "Hello" - newProps["nullTest"] = NSNull() - ctx.payload = TrackPayload( - event: newEvent, - properties: newProps, - context: track.context, - integrations: track.integrations - ) - }) - } else { - next(context) - } + if context.eventType == .track { + next(context.modify { ctx in + guard let track = ctx.payload as? TrackPayload else { + return + } + let newEvent = "[New] \(track.event)" + var newProps = track.properties ?? [:] + newProps["customAttribute"] = "Hello" + newProps["nullTest"] = NSNull() + ctx.payload = TrackPayload( + event: newEvent, + properties: newProps, + context: track.context, + integrations: track.integrations + ) + }) + } else { + next(context) + } } // Simply swallows all calls and does not pass events downstream let eatAllCalls = BlockMiddleware { (context, next) in } -class SourceMiddlewareTests: QuickSpec { - override func spec() { - it("receives events") { - let config = AnalyticsConfiguration(writeKey: "TESTKEY") - let passthrough = PassthroughMiddleware() - config.sourceMiddleware = [ - passthrough, - ] - let analytics = Analytics(configuration: config) - analytics.identify("testUserId1") - expect(passthrough.lastContext?.eventType) == EventType.identify - let identify = passthrough.lastContext?.payload as? IdentifyPayload - expect(identify?.userId) == "testUserId1" +class SourceMiddlewareTests: XCTestCase { + + func testReceivesEvents() { + let config = AnalyticsConfiguration(writeKey: "TESTKEY") + let passthrough = PassthroughMiddleware() + config.sourceMiddleware = [ + passthrough, + ] + let analytics = Analytics(configuration: config) + analytics.identify("testUserId1") + XCTAssertEqual(passthrough.lastContext?.eventType, EventType.identify) + let identify = passthrough.lastContext?.payload as? IdentifyPayload + XCTAssertEqual(identify?.userId, "testUserId1") } - it("modifies and passes event to next") { - let config = AnalyticsConfiguration(writeKey: "TESTKEY") - let passthrough = PassthroughMiddleware() - config.sourceMiddleware = [ - customizeAllTrackCalls, - passthrough, - ] - let analytics = Analytics(configuration: config) - analytics.track("Purchase Success") - expect(passthrough.lastContext?.eventType) == EventType.track - let track = passthrough.lastContext?.payload as? TrackPayload - expect(track?.event) == "[New] Purchase Success" - expect(track?.properties?["customAttribute"] as? String) == "Hello" - let isNull = (track?.properties?["nullTest"] is NSNull) - expect(isNull) == true + func testModifiesAndPassesEventToNext() { + let config = AnalyticsConfiguration(writeKey: "TESTKEY") + let passthrough = PassthroughMiddleware() + config.sourceMiddleware = [ + customizeAllTrackCalls, + passthrough, + ] + let analytics = Analytics(configuration: config) + analytics.track("Purchase Success") + XCTAssertEqual(passthrough.lastContext?.eventType, EventType.track) + let track = passthrough.lastContext?.payload as? TrackPayload + XCTAssertEqual(track?.event, "[New] Purchase Success") + XCTAssertEqual(track?.properties?["customAttribute"] as? String, "Hello") + let isNull = (track?.properties?["nullTest"] is NSNull) + XCTAssert(isNull) } - it("expects event to be swallowed if next is not called") { - let config = AnalyticsConfiguration(writeKey: "TESTKEY") - let passthrough = PassthroughMiddleware() - config.sourceMiddleware = [ - eatAllCalls, - passthrough, - ] - let analytics = Analytics(configuration: config) - analytics.track("Purchase Success") - expect(passthrough.lastContext).to(beNil()) + func testExpectsEventToBeSwallowed() { + let config = AnalyticsConfiguration(writeKey: "TESTKEY") + let passthrough = PassthroughMiddleware() + config.sourceMiddleware = [ + eatAllCalls, + passthrough, + ] + let analytics = Analytics(configuration: config) + analytics.track("Purchase Success") + XCTAssertNil(passthrough.lastContext) } - } } -class IntegrationMiddlewareTests: QuickSpec { - override func spec() { - it("receives events") { - let config = AnalyticsConfiguration(writeKey: "TESTKEY") - let passthrough = PassthroughMiddleware() - config.destinationMiddleware = [DestinationMiddleware(key: SegmentIntegrationFactory().key(), middleware: [passthrough])] - let analytics = Analytics(configuration: config) - analytics.identify("testUserId1") - - // pump the runloop until we have a last context. - // integration middleware is held up until initialization is completed. - waitUntil(timeout: 60) { done in - let queue = DispatchQueue(label: "test") - queue.async { - while(passthrough.lastContext == nil) { +class IntegrationMiddlewareTests: XCTestCase { + + func testReceivesEvents() { + let config = AnalyticsConfiguration(writeKey: "TESTKEY") + let passthrough = PassthroughMiddleware() + config.destinationMiddleware = [DestinationMiddleware(key: SegmentIntegrationFactory().key(), middleware: [passthrough])] + let analytics = Analytics(configuration: config) + analytics.identify("testUserId1") + + // pump the runloop until we have a last context. + // integration middleware is held up until initialization is completed. + let currentTime = Date() + while(passthrough.lastContext == nil && currentTime < currentTime + 60) { sleep(1); - } - done() } - } - - expect(passthrough.lastContext?.eventType) == EventType.identify - let identify = passthrough.lastContext?.payload as? IdentifyPayload - expect(identify?.userId) == "testUserId1" + + XCTAssertEqual(passthrough.lastContext?.eventType, EventType.identify) + let identify = passthrough.lastContext?.payload as? IdentifyPayload + XCTAssertEqual(identify?.userId, "testUserId1") } - it("modifies and passes event to next") { - let config = AnalyticsConfiguration(writeKey: "TESTKEY") - let passthrough = PassthroughMiddleware() - config.destinationMiddleware = [DestinationMiddleware(key: SegmentIntegrationFactory().key(), middleware: [customizeAllTrackCalls, passthrough])] - let analytics = Analytics(configuration: config) - analytics.track("Purchase Success") - - // pump the runloop until we have a last context. - // integration middleware is held up until initialization is completed. - waitUntil(timeout: 60) { done in - let queue = DispatchQueue(label: "test") - queue.async { - while(passthrough.lastContext == nil) { + func testModifiesAndPassesEventToNext() { + let config = AnalyticsConfiguration(writeKey: "TESTKEY") + let passthrough = PassthroughMiddleware() + config.destinationMiddleware = [DestinationMiddleware(key: SegmentIntegrationFactory().key(), middleware: [customizeAllTrackCalls, passthrough])] + let analytics = Analytics(configuration: config) + analytics.track("Purchase Success") + + // pump the runloop until we have a last context. + // integration middleware is held up until initialization is completed. + let currentTime = Date() + while(passthrough.lastContext == nil && currentTime < currentTime + 60) { sleep(1) - } - done() } - } - - expect(passthrough.lastContext?.eventType) == EventType.track - let track = passthrough.lastContext?.payload as? TrackPayload - expect(track?.event) == "[New] Purchase Success" - expect(track?.properties?["customAttribute"] as? String) == "Hello" - let isNull = (track?.properties?["nullTest"] is NSNull) - expect(isNull) == true + + XCTAssertEqual(passthrough.lastContext?.eventType, EventType.track) + let track = passthrough.lastContext?.payload as? TrackPayload + XCTAssertEqual(track?.event, "[New] Purchase Success") + XCTAssertEqual(track?.properties?["customAttribute"] as? String, "Hello") + let isNull = (track?.properties?["nullTest"] is NSNull) + XCTAssert(isNull, "Should be true") } - it("expects event to be swallowed if next is not called") { - // Since we're testing that an event is dropped, the previously used run loop pump won't work here. - var initialized = false - NotificationCenter.default.addObserver(forName: NSNotification.Name(rawValue: SEGAnalyticsIntegrationDidStart), object: nil, queue: nil) { (notification) in - initialized = true - } + func testExpectsEventToBeSwallowedIfOtherIsNotCalled() { + // Since we're testing that an event is dropped, the previously used run loop pump won't work here. + var initialized = false + NotificationCenter.default.addObserver(forName: NSNotification.Name(rawValue: SEGAnalyticsIntegrationDidStart), object: nil, queue: nil) { (notification) in + initialized = true + } - let config = AnalyticsConfiguration(writeKey: "TESTKEY") - let passthrough = PassthroughMiddleware() - config.destinationMiddleware = [DestinationMiddleware(key: SegmentIntegrationFactory().key(), middleware: [eatAllCalls, passthrough])] - let analytics = Analytics(configuration: config) - analytics.track("Purchase Success") - - while (!initialized) { - sleep(1) - } - - expect(passthrough.lastContext).to(beNil()) + let config = AnalyticsConfiguration(writeKey: "TESTKEY") + let passthrough = PassthroughMiddleware() + config.destinationMiddleware = [DestinationMiddleware(key: SegmentIntegrationFactory().key(), middleware: [eatAllCalls, passthrough])] + let analytics = Analytics(configuration: config) + analytics.track("Purchase Success") + + while (!initialized) { + sleep(1) + } + + XCTAssertNil(passthrough.lastContext) } - } } diff --git a/AnalyticsTests/StoreKitTrackerTests.swift b/AnalyticsTests/StoreKitTrackerTests.swift index cee21c01c..5be94b17b 100644 --- a/AnalyticsTests/StoreKitTrackerTests.swift +++ b/AnalyticsTests/StoreKitTrackerTests.swift @@ -6,9 +6,8 @@ // Copyright © 2016 Segment. All rights reserved. // -import Quick -import Nimble import Analytics +import XCTest class mockTransaction: SKPaymentTransaction { override var transactionIdentifier: String? { @@ -39,33 +38,30 @@ class mockProductResponse: SKProductsResponse { } } -class StoreKitTrackerTests: QuickSpec { - override func spec() { +class StoreKitTrackerTests: XCTestCase { var test: TestMiddleware! var tracker: StoreKitTracker! var analytics: Analytics! - - beforeEach { - let config = AnalyticsConfiguration(writeKey: "foobar") - test = TestMiddleware() - config.sourceMiddleware = [test] - analytics = Analytics(configuration: config) - tracker = StoreKitTracker.trackTransactions(for: analytics) + + override func setUp() { + super.setUp() + let config = AnalyticsConfiguration(writeKey: "foobar") + test = TestMiddleware() + config.sourceMiddleware = [test] + analytics = Analytics(configuration: config) + tracker = StoreKitTracker.trackTransactions(for: analytics) } - - it("SKPaymentQueue Observer") { - let transaction = mockTransaction() - expect(transaction.transactionIdentifier) == "tid" - tracker.paymentQueue(SKPaymentQueue(), updatedTransactions: [transaction]) - - tracker.productsRequest(SKProductsRequest(), didReceive: mockProductResponse()) - - let payload = test.lastContext?.payload as? TrackPayload - - expect(payload?.event) == "Order Completed" + + func testSKPaymentQueueObserver() { + let transaction = mockTransaction() + XCTAssertEqual(transaction.transactionIdentifier, "tid") + tracker.paymentQueue(SKPaymentQueue(), updatedTransactions: [transaction]) + + tracker.productsRequest(SKProductsRequest(), didReceive: mockProductResponse()) + + let payload = test.lastContext?.payload as? TrackPayload + + XCTAssertEqual(payload?.event, "Order Completed") } - - } - } diff --git a/AnalyticsTests/TrackingTests.swift b/AnalyticsTests/TrackingTests.swift index 571445efa..dfeb3ae8e 100644 --- a/AnalyticsTests/TrackingTests.swift +++ b/AnalyticsTests/TrackingTests.swift @@ -7,103 +7,102 @@ // -import Quick -import Nimble import Analytics +import XCTest -class TrackingTests: QuickSpec { - override func spec() { +class TrackingTests: XCTestCase { + var passthrough: PassthroughMiddleware! var analytics: Analytics! - - beforeEach { - let config = AnalyticsConfiguration(writeKey: "QUI5ydwIGeFFTa1IvCBUhxL9PyW5B0jE") - passthrough = PassthroughMiddleware() - config.sourceMiddleware = [ - passthrough, - ] - analytics = Analytics(configuration: config) + + override func setUp() { + super.setUp() + let config = AnalyticsConfiguration(writeKey: "QUI5ydwIGeFFTa1IvCBUhxL9PyW5B0jE") + passthrough = PassthroughMiddleware() + config.sourceMiddleware = [ + passthrough, + ] + analytics = Analytics(configuration: config) } - - afterEach { - analytics.reset() + + override func tearDown() { + super.tearDown() + analytics.reset() } - - it("handles identify:") { - analytics.identify("testUserId1", traits: [ - "firstName": "Peter" - ]) - expect(passthrough.lastContext?.eventType) == EventType.identify - let identify = passthrough.lastContext?.payload as? IdentifyPayload - expect(identify?.userId) == "testUserId1" - expect(identify?.anonymousId).toNot(beNil()) - expect(identify?.traits?["firstName"] as? String) == "Peter" + + func testHandlesIdentify() { + analytics.identify("testUserId1", traits: [ + "firstName": "Peter" + ]) + XCTAssertEqual(passthrough.lastContext?.eventType, EventType.identify) + let identify = passthrough.lastContext?.payload as? IdentifyPayload + XCTAssertEqual(identify?.userId, "testUserId1") + XCTAssertNotNil(identify?.anonymousId) + XCTAssertEqual(identify?.traits?["firstName"] as? String, "Peter") } - - it("handles identify with custom anonymousId:") { - analytics.identify("testUserId1", traits: [ - "firstName": "Peter" - ], options: [ - "anonymousId": "a_custom_anonymous_id" + + func testHandlesIdentifyWithCustomAnonymousId() { + analytics.identify("testUserId1", traits: [ + "firstName": "Peter" + ], options: [ + "anonymousId": "a_custom_anonymous_id" ]) - expect(passthrough.lastContext?.eventType) == EventType.identify - let identify = passthrough.lastContext?.payload as? IdentifyPayload - expect(identify?.userId) == "testUserId1" - expect(identify?.anonymousId) == "a_custom_anonymous_id" - expect(identify?.traits?["firstName"] as? String) == "Peter" + XCTAssertEqual(passthrough.lastContext?.eventType, EventType.identify) + let identify = passthrough.lastContext?.payload as? IdentifyPayload + XCTAssertEqual(identify?.userId, "testUserId1") + XCTAssertEqual(identify?.anonymousId, "a_custom_anonymous_id") + XCTAssertEqual(identify?.traits?["firstName"] as? String, "Peter") } - - it("handles track:") { - analytics.track("User Signup", properties: [ - "method": "SSO" - ], options: [ - "context": [ - "device": [ - "token": "1234" - ] - ] - ]) - expect(passthrough.lastContext?.eventType) == EventType.track - let payload = passthrough.lastContext?.payload as? TrackPayload - expect(payload?.event) == "User Signup" - expect(payload?.properties?["method"] as? String) == "SSO" + + func testHandlesTrack() { + analytics.track("User Signup", properties: [ + "method": "SSO" + ], options: [ + "context": [ + "device": [ + "token": "1234" + ] + ] + ]) + XCTAssertEqual(passthrough.lastContext?.eventType, EventType.track) + let payload = passthrough.lastContext?.payload as? TrackPayload + XCTAssertEqual(payload?.event, "User Signup") + XCTAssertEqual(payload?.properties?["method"] as? String, "SSO") } - - it("handles alias:") { - analytics.alias("persistentUserId") - expect(passthrough.lastContext?.eventType) == EventType.alias - let payload = passthrough.lastContext?.payload as? AliasPayload - expect(payload?.theNewId) == "persistentUserId" + + func testHandlesAlias() { + analytics.alias("persistentUserId") + XCTAssertEqual(passthrough.lastContext?.eventType, EventType.alias) + let payload = passthrough.lastContext?.payload as? AliasPayload + XCTAssertEqual(payload?.theNewId, "persistentUserId") } - - it("handles screen:") { - analytics.screen("Home", properties: [ - "referrer": "Google" - ]) - expect(passthrough.lastContext?.eventType) == EventType.screen - let screen = passthrough.lastContext?.payload as? ScreenPayload - expect(screen?.name) == "Home" - expect(screen?.properties?["referrer"] as? String) == "Google" + + func testHandlesScreen() { + analytics.screen("Home", properties: [ + "referrer": "Google" + ]) + XCTAssertEqual(passthrough.lastContext?.eventType, EventType.screen) + let screen = passthrough.lastContext?.payload as? ScreenPayload + XCTAssertEqual(screen?.name, "Home") + XCTAssertEqual(screen?.properties?["referrer"] as? String, "Google") } - - it("handles group:") { - analytics.group("acme-company", traits: [ - "employees": 2333 - ]) - expect(passthrough.lastContext?.eventType) == EventType.group - let payload = passthrough.lastContext?.payload as? GroupPayload - expect(payload?.groupId) == "acme-company" - expect(payload?.traits?["employees"] as? Int) == 2333 + + func testHandlesGroup() { + analytics.group("acme-company", traits: [ + "employees": 2333 + ]) + XCTAssertEqual(passthrough.lastContext?.eventType, EventType.group) + let payload = passthrough.lastContext?.payload as? GroupPayload + XCTAssertEqual(payload?.groupId, "acme-company") + XCTAssertEqual(payload?.traits?["employees"] as? Int, 2333) } - it("handles null values") { - analytics.track("null test", properties: [ - "nullTest": NSNull() + func testHandlesNullValues() { + analytics.track("null test", properties: [ + "nullTest": NSNull() ]) - let payload = passthrough.lastContext?.payload as? TrackPayload - let isNull = (payload?.properties?["nullTest"] is NSNull) - expect(isNull) == true + let payload = passthrough.lastContext?.payload as? TrackPayload + let isNull = (payload?.properties?["nullTest"] is NSNull) + XCTAssert(isNull) } - } - } diff --git a/AnalyticsTests/UserDefaultsStorageTest.swift b/AnalyticsTests/UserDefaultsStorageTest.swift index 727225157..5e84830d3 100644 --- a/AnalyticsTests/UserDefaultsStorageTest.swift +++ b/AnalyticsTests/UserDefaultsStorageTest.swift @@ -5,99 +5,96 @@ // Copyright © 2016 Segment. All rights reserved. // -import Quick -import Nimble -import Analytics +@testable import Analytics +import XCTest -class UserDefaultsStorageTest : QuickSpec { - override func spec() { +class UserDefaultsStorageTest : XCTestCase { + var storage : UserDefaultsStorage! - beforeEach { -// let crypto = SEGAES256Crypto(password: "thetrees") -// storage = SEGUserDefaultsStorage(defaults: NSUserDefaults.standardUserDefaults(), namespacePrefix: "segment", crypto: crypto) -// storage = SEGUserDefaultsStorage(defaults: NSUserDefaults.standardUserDefaults(), namespacePrefix: nil, crypto: crypto) - storage = UserDefaultsStorage(defaults: UserDefaults.standard, namespacePrefix: nil, crypto: nil) - } - it("persists and loads data") { - let dataIn = "segment".data(using: String.Encoding.utf8)! - storage.setData(dataIn, forKey: "mydata") - - let dataOut = storage.data(forKey: "mydata") - expect(dataOut) == dataIn - - let strOut = String(data: dataOut!, encoding: .utf8) - expect(strOut) == "segment" + override func setUp() { + super.setUp() + storage = UserDefaultsStorage(defaults: UserDefaults.standard, namespacePrefix: nil, crypto: nil) } - it("persists and loads string") { - let str = "san francisco" - storage.setString(str, forKey: "city") - expect(storage.string(forKey: "city")) == str - - storage.removeKey("city") - expect(storage.string(forKey: "city")).to(beNil()) + override func tearDown() { + super.tearDown() + storage.resetAll() } - it("persists and loads array") { - let array = [ - "san francisco", - "new york", - "tallinn", - ] - storage.setArray(array, forKey: "cities") - expect(storage.array(forKey: "cities") as? Array<String>) == array - - storage.removeKey("cities") - expect(storage.array(forKey: "cities")).to(beNil()) + func testPersistsAndLoadsData() { + let dataIn = "segment".data(using: String.Encoding.utf8)! + storage.setData(dataIn, forKey: "mydata") + + let dataOut = storage.data(forKey: "mydata") + XCTAssertEqual(dataOut, dataIn) + + let strOut = String(data: dataOut!, encoding: .utf8) + XCTAssertEqual(strOut, "segment") } - it("persists and loads dictionary") { - let dict = [ - "san francisco": "tech", - "new york": "finance", - "paris": "fashion", - ] - storage.setDictionary(dict, forKey: "cityMap") - expect(storage.dictionary(forKey: "cityMap") as? Dictionary<String, String>) == dict - - storage.removeKey("cityMap") - expect(storage.dictionary(forKey: "cityMap")).to(beNil()) + func testPersistsAndLoadsString() { + let str = "san francisco" + storage.setString(str, forKey: "city") + XCTAssertEqual(storage.string(forKey: "city"), str) + + storage.removeKey("city") + XCTAssertNil(storage.string(forKey: "city")) } - it("should work with crypto") { - let crypto = AES256Crypto(password: "thetrees") - let s = UserDefaultsStorage(defaults: UserDefaults.standard, namespacePrefix: nil, crypto: crypto) - let dict = [ - "san francisco": "tech", - "new york": "finance", - "paris": "fashion", - ] - s.setDictionary(dict, forKey: "cityMap") - expect(s.dictionary(forKey: "cityMap") as? Dictionary<String, String>) == dict - - s.removeKey("cityMap") - expect(s.dictionary(forKey: "cityMap")).to(beNil()) + func testPersistsAndLoadsArray() { + let array = [ + "san francisco", + "new york", + "tallinn", + ] + storage.setArray(array, forKey: "cities") + XCTAssertEqual(storage.array(forKey: "cities") as? Array<String>, array) + + storage.removeKey("cities") + XCTAssertNil(storage.array(forKey: "cities")) } + func testPersistsAndLoadsDictionary() { + let dict = [ + "san francisco": "tech", + "new york": "finance", + "paris": "fashion", + ] + storage.setDictionary(dict, forKey: "cityMap") + XCTAssertEqual(storage.dictionary(forKey: "cityMap") as? Dictionary<String, String>, dict) + + storage.removeKey("cityMap") + XCTAssertNil(storage.dictionary(forKey: "cityMap")) + } - it("should work with namespace") { - let crypto = AES256Crypto(password: "thetrees") - let s = UserDefaultsStorage(defaults: UserDefaults.standard, namespacePrefix: "segment", crypto: crypto) - let dict = [ - "san francisco": "tech", - "new york": "finance", - "paris": "fashion", - ] - s.setDictionary(dict, forKey: "cityMap") - expect(s.dictionary(forKey: "cityMap") as? Dictionary<String, String>) == dict - - s.removeKey("cityMap") - expect(s.dictionary(forKey: "cityMap")).to(beNil()) + func testShouldWorkWithCrypto() { + let crypto = AES256Crypto(password: "thetrees") + let s = UserDefaultsStorage(defaults: UserDefaults.standard, namespacePrefix: nil, crypto: crypto) + let dict = [ + "san francisco": "tech", + "new york": "finance", + "paris": "fashion", + ] + s.setDictionary(dict, forKey: "cityMap") + XCTAssertEqual(s.dictionary(forKey: "cityMap") as? Dictionary<String, String>, dict) + + s.removeKey("cityMap") + XCTAssertNil(s.dictionary(forKey: "cityMap")) } - afterEach { - storage.resetAll() + func testShouldWorkWithNamespace() { + let crypto = AES256Crypto(password: "thetrees") + let s = UserDefaultsStorage(defaults: UserDefaults.standard, namespacePrefix: "segment", crypto: crypto) + let dict = [ + "san francisco": "tech", + "new york": "finance", + "paris": "fashion", + ] + s.setDictionary(dict, forKey: "cityMap") + XCTAssertEqual(s.dictionary(forKey: "cityMap") as? Dictionary<String, String>, dict) + + s.removeKey("cityMap") + XCTAssertNil(s.dictionary(forKey: "cityMap")) } - } } diff --git a/AnalyticsTests/Utils/TestUtils.swift b/AnalyticsTests/Utils/TestUtils.swift index f0e9aa19f..97975751c 100644 --- a/AnalyticsTests/Utils/TestUtils.swift +++ b/AnalyticsTests/Utils/TestUtils.swift @@ -6,9 +6,9 @@ // Copyright © 2016 Segment. All rights reserved. // -@testable import Nimble import Nocilla import Analytics +import XCTest class PassthroughMiddleware: Middleware { var lastContext: Context? @@ -85,12 +85,9 @@ class JsonGzippedBody : LSMatcher, LSMatcheable { } func matchesJson(_ json: AnyObject) -> Bool { - let actualValue : () -> NSObject = { - return json as! NSObject - } - let failureMessage = FailureMessage() - let location = SourceLocation() - let matches = Nimble.equal(expectedJson).matches(actualValue, failureMessage: failureMessage, location: location) + let expectedDictionary = expectedJson as? [String: AnyHashable] + let jsonDictionary = json as? [String: AnyHashable] + let matches = expectedDictionary == jsonDictionary // print("matches=\(matches) expected \(expectedJson) actual \(json)") return matches } @@ -184,3 +181,21 @@ class TestApplication: NSObject, ApplicationProtocol { backgroundTasks[index].isEnded = true } } + +extension XCTestCase { + + func expectUntil(_ time: TimeInterval, expression: @escaping @autoclosure () throws -> Bool) { + + let expectation = self.expectation(description: "Expect Until") + DispatchQueue.global().async { + while (true) { + if try! expression() { + expectation.fulfill() + return + } + usleep(500) // try every half second + } + } + wait(for: [expectation], timeout: time) + } +} diff --git a/Podfile b/Podfile index 69cd2a684..576f68d20 100644 --- a/Podfile +++ b/Podfile @@ -1,6 +1,4 @@ def shared_testing_pods - pod 'Quick', '~> 1.2.0' - pod 'Nimble', '~> 7.3.4' pod 'Nocilla', '~> 0.11.0' pod 'SwiftTryCatch', :git => 'https://github.com/segmentio/SwiftTryCatch.git' end diff --git a/Podfile.lock b/Podfile.lock index d09d52a7a..74fdd16eb 100644 --- a/Podfile.lock +++ b/Podfile.lock @@ -1,20 +1,14 @@ PODS: - - Nimble (7.3.4) - Nocilla (0.11.0) - - Quick (1.2.0) - SwiftTryCatch (1.0.0) DEPENDENCIES: - - Nimble (~> 7.3.4) - Nocilla (~> 0.11.0) - - Quick (~> 1.2.0) - SwiftTryCatch (from `https://github.com/segmentio/SwiftTryCatch.git`) SPEC REPOS: trunk: - - Nimble - Nocilla - - Quick EXTERNAL SOURCES: SwiftTryCatch: @@ -26,11 +20,9 @@ CHECKOUT OPTIONS: :git: https://github.com/segmentio/SwiftTryCatch.git SPEC CHECKSUMS: - Nimble: 051e3d8912d40138fa5591c78594f95fb172af37 Nocilla: 7af7a386071150cc8aa5da4da97d060f049dd61c - Quick: 58d203b1c5e27fff7229c4c1ae445ad7069a7a08 SwiftTryCatch: 6ef1f543b5d287a4f5763c0de2ee9c8568985269 -PODFILE CHECKSUM: 80d573cfd1dc222ba558c64e15bef5eb9d22c782 +PODFILE CHECKSUM: 0317236deb6b78344dafed0871776d4342f80caa -COCOAPODS: 1.9.1 +COCOAPODS: 1.9.3 From bdc96554a0a7f5af50ae5500cf767fa97a56f005 Mon Sep 17 00:00:00 2001 From: Cody Garvin <emig647@gmail.com> Date: Wed, 24 Jun 2020 17:03:42 -0700 Subject: [PATCH 2/2] Issue 906: Updated package to supports all types of libraries --- Package.swift | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/Package.swift b/Package.swift index 289c556a9..1b8a6962e 100644 --- a/Package.swift +++ b/Package.swift @@ -5,11 +5,13 @@ import PackageDescription let package = Package( name: "Analytics", + platforms: [ + .iOS(.v12), .tvOS(.v12) + ], products: [ // Products define the executables and libraries produced by a package, and make them visible to other packages. .library( name: "Analytics", - type: .dynamic, targets: ["Analytics"]), ], dependencies: [ @@ -27,8 +29,9 @@ let package = Package( sources: ["Classes", "Internal"], publicHeadersPath: "Classes", cSettings: [ - .headerSearchPath("Internal"), - .headerSearchPath("Classes"), - ]), + .headerSearchPath("Internal"), + .headerSearchPath("Classes") + ] + ), ] )