Skip to content

Commit 6ee1f2c

Browse files
committed
add unit test cases for migration
lint fixes
1 parent 74bce0d commit 6ee1f2c

21 files changed

+248
-131
lines changed

.swiftlint.yml

+1
Original file line numberDiff line numberDiff line change
@@ -61,3 +61,4 @@ disabled_rules:
6161
- type_body_length
6262
- variable_name
6363
- trailing_whitespace
64+
- unused_optional_binding

MixpanelDemo/MixpanelDemo.xcodeproj/project.pbxproj

+28-19
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,11 @@
2828
8654F3002671636B00ACEED5 /* MixpanelPeopleTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8654F2FB2671636B00ACEED5 /* MixpanelPeopleTests.swift */; };
2929
8654F3012671636B00ACEED5 /* MixpanelOptOutTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8654F2FC2671636B00ACEED5 /* MixpanelOptOutTests.swift */; };
3030
8654F3052671C4B000ACEED5 /* MixpanelGroupTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8654F3032671C4B000ACEED5 /* MixpanelGroupTests.swift */; };
31+
8675E9A126DF356B0096858F /* mixpanel-testToken-events in Resources */ = {isa = PBXBuildFile; fileRef = 8675E99C26DF356A0096858F /* mixpanel-testToken-events */; };
32+
8675E9A226DF356B0096858F /* mixpanel-testToken-groups in Resources */ = {isa = PBXBuildFile; fileRef = 8675E99E26DF356A0096858F /* mixpanel-testToken-groups */; };
33+
8675E9A326DF356B0096858F /* mixpanel-testToken-people in Resources */ = {isa = PBXBuildFile; fileRef = 8675E99F26DF356A0096858F /* mixpanel-testToken-people */; };
34+
8675E9AB26E144FE0096858F /* mixpanel-testToken-optOutStatus in Resources */ = {isa = PBXBuildFile; fileRef = 8675E9AA26E144FE0096858F /* mixpanel-testToken-optOutStatus */; };
35+
8675E9B426E171AE0096858F /* mixpanel-testToken-properties in Resources */ = {isa = PBXBuildFile; fileRef = 8675E9B326E171AE0096858F /* mixpanel-testToken-properties */; };
3136
86F86E8F22440C5D00B69832 /* Interface.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 86F86E8D22440C5C00B69832 /* Interface.storyboard */; };
3237
86F86E9122440C5F00B69832 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 86F86E9022440C5F00B69832 /* Assets.xcassets */; };
3338
86F86E9822440C5F00B69832 /* MixpanelDemoWatch Extension.appex in Embed App Extensions */ = {isa = PBXBuildFile; fileRef = 86F86E9722440C5F00B69832 /* MixpanelDemoWatch Extension.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; };
@@ -275,6 +280,11 @@
275280
8654F2FB2671636B00ACEED5 /* MixpanelPeopleTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MixpanelPeopleTests.swift; sourceTree = "<group>"; };
276281
8654F2FC2671636B00ACEED5 /* MixpanelOptOutTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MixpanelOptOutTests.swift; sourceTree = "<group>"; };
277282
8654F3032671C4B000ACEED5 /* MixpanelGroupTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MixpanelGroupTests.swift; sourceTree = "<group>"; };
283+
8675E99C26DF356A0096858F /* mixpanel-testToken-events */ = {isa = PBXFileReference; explicitFileType = text.pbxproject; path = "mixpanel-testToken-events"; sourceTree = "<group>"; };
284+
8675E99E26DF356A0096858F /* mixpanel-testToken-groups */ = {isa = PBXFileReference; lastKnownFileType = file.bplist; path = "mixpanel-testToken-groups"; sourceTree = "<group>"; };
285+
8675E99F26DF356A0096858F /* mixpanel-testToken-people */ = {isa = PBXFileReference; lastKnownFileType = file.bplist; path = "mixpanel-testToken-people"; sourceTree = "<group>"; };
286+
8675E9AA26E144FE0096858F /* mixpanel-testToken-optOutStatus */ = {isa = PBXFileReference; lastKnownFileType = file.bplist; path = "mixpanel-testToken-optOutStatus"; sourceTree = "<group>"; };
287+
8675E9B326E171AE0096858F /* mixpanel-testToken-properties */ = {isa = PBXFileReference; lastKnownFileType = file.bplist; path = "mixpanel-testToken-properties"; sourceTree = "<group>"; };
278288
86F86E8B22440C5C00B69832 /* MixpanelDemoWatch.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = MixpanelDemoWatch.app; sourceTree = BUILT_PRODUCTS_DIR; };
279289
86F86E8E22440C5C00B69832 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Interface.storyboard; sourceTree = "<group>"; };
280290
86F86E9022440C5F00B69832 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
@@ -323,13 +333,6 @@
323333
/* End PBXFileReference section */
324334

325335
/* Begin PBXFrameworksBuildPhase section */
326-
0AAB97762ADB463FE09D6381 /* Frameworks */ = {
327-
isa = PBXFrameworksBuildPhase;
328-
buildActionMask = 2147483647;
329-
files = (
330-
);
331-
runOnlyForDeploymentPostprocessing = 0;
332-
};
333336
8654F2C1266ED84F00ACEED5 /* Frameworks */ = {
334337
isa = PBXFrameworksBuildPhase;
335338
buildActionMask = 2147483647;
@@ -470,6 +473,18 @@
470473
path = MixpanelDemoMacUITests;
471474
sourceTree = "<group>";
472475
};
476+
8675E9A626DF37B20096858F /* Resources */ = {
477+
isa = PBXGroup;
478+
children = (
479+
8675E99C26DF356A0096858F /* mixpanel-testToken-events */,
480+
8675E9B326E171AE0096858F /* mixpanel-testToken-properties */,
481+
8675E99E26DF356A0096858F /* mixpanel-testToken-groups */,
482+
8675E99F26DF356A0096858F /* mixpanel-testToken-people */,
483+
8675E9AA26E144FE0096858F /* mixpanel-testToken-optOutStatus */,
484+
);
485+
name = Resources;
486+
sourceTree = "<group>";
487+
};
473488
86F86E8C22440C5C00B69832 /* MixpanelDemoWatch */ = {
474489
isa = PBXGroup;
475490
children = (
@@ -524,6 +539,7 @@
524539
E12BD03A1D8A14D6008989C9 /* Supporting Files */ = {
525540
isa = PBXGroup;
526541
children = (
542+
8675E9A626DF37B20096858F /* Resources */,
527543
E12BD03C1D8A14F3008989C9 /* Assets.xcassets */,
528544
E15FF7ED1D0461130076CDE3 /* Info.plist */,
529545
);
@@ -825,11 +841,6 @@
825841
LastUpgradeCheck = 1200;
826842
ORGANIZATIONNAME = Mixpanel;
827843
TargetAttributes = {
828-
60DDD14623D39620004F7CBB = {
829-
CreatedOnToolsVersion = 11.2;
830-
DevelopmentTeam = E8FVX7QLET;
831-
ProvisioningStyle = Automatic;
832-
};
833844
8654F2C3266ED84F00ACEED5 = {
834845
CreatedOnToolsVersion = 12.5;
835846
};
@@ -960,13 +971,6 @@
960971
/* End PBXReferenceProxy section */
961972

962973
/* Begin PBXResourcesBuildPhase section */
963-
60DDD14523D39620004F7CBB /* Resources */ = {
964-
isa = PBXResourcesBuildPhase;
965-
buildActionMask = 2147483647;
966-
files = (
967-
);
968-
runOnlyForDeploymentPostprocessing = 0;
969-
};
970974
8654F2C2266ED84F00ACEED5 /* Resources */ = {
971975
isa = PBXResourcesBuildPhase;
972976
buildActionMask = 2147483647;
@@ -1045,6 +1049,11 @@
10451049
buildActionMask = 2147483647;
10461050
files = (
10471051
E12BD03D1D8A14F3008989C9 /* Assets.xcassets in Resources */,
1052+
8675E9A226DF356B0096858F /* mixpanel-testToken-groups in Resources */,
1053+
8675E9B426E171AE0096858F /* mixpanel-testToken-properties in Resources */,
1054+
8675E9A126DF356B0096858F /* mixpanel-testToken-events in Resources */,
1055+
8675E9A326DF356B0096858F /* mixpanel-testToken-people in Resources */,
1056+
8675E9AB26E144FE0096858F /* mixpanel-testToken-optOutStatus in Resources */,
10481057
);
10491058
runOnlyForDeploymentPostprocessing = 0;
10501059
};

MixpanelDemo/MixpanelDemoTests/MixpanelDemoTests.swift

+80
Original file line numberDiff line numberDiff line change
@@ -988,4 +988,84 @@ class MixpanelDemoTests: MixpanelBaseTests {
988988
removeDBfile(testToken)
989989
}
990990

991+
func testMigration() {
992+
let token = "testToken"
993+
// clean up
994+
removeDBfile(token)
995+
// copy the legacy archived file for the migration test
996+
let legacyFiles = ["mixpanel-testToken-events", "mixpanel-testToken-properties", "mixpanel-testToken-groups", "mixpanel-testToken-people", "mixpanel-testToken-optOutStatus"]
997+
prepareForMigrationFiles(legacyFiles)
998+
// initialize mixpanel will do the migration automatically if found legacy archive files.
999+
let testMixpanel = Mixpanel.initialize(token: token, flushInterval: 60)
1000+
let fileManager = FileManager.default
1001+
let libraryUrls = fileManager.urls(for: .libraryDirectory,
1002+
in: .userDomainMask)
1003+
XCTAssertFalse(fileManager.fileExists(atPath: (libraryUrls.first?.appendingPathComponent("mixpanel-testToken-events"))!.path), "after migration, the legacy archive files should be removed")
1004+
XCTAssertFalse(fileManager.fileExists(atPath: (libraryUrls.first?.appendingPathComponent("mixpanel-testToken-properties"))!.path), "after migration, the legacy archive files should be removed")
1005+
XCTAssertFalse(fileManager.fileExists(atPath: (libraryUrls.first?.appendingPathComponent("mixpanel-testToken-groups"))!.path), "after migration, the legacy archive files should be removed")
1006+
XCTAssertFalse(fileManager.fileExists(atPath: (libraryUrls.first?.appendingPathComponent("mixpanel-testToken-people"))!.path), "after migration, the legacy archive files should be removed")
1007+
1008+
let events = eventQueue(token: testMixpanel.apiToken)
1009+
XCTAssertEqual(events.count, 306)
1010+
1011+
XCTAssertEqual(events[0]["event"] as? String, "$identify")
1012+
XCTAssertEqual(events[1]["event"] as? String, "Logged in")
1013+
XCTAssertEqual(events[2]["event"] as? String, "$ae_first_open")
1014+
XCTAssertEqual(events[3]["event"] as? String, "Tracked event 1")
1015+
let properties = events.last?["properties"] as? InternalProperties
1016+
XCTAssertEqual(properties?["Cool Property"] as? [Int], [12345,301])
1017+
XCTAssertEqual(properties?["Super Property 2"] as? String, "p2")
1018+
1019+
let people = peopleQueue(token: testMixpanel.apiToken)
1020+
XCTAssertEqual(people.count, 6)
1021+
XCTAssertEqual(people[0]["$distinct_id"] as? String, "demo_user")
1022+
XCTAssertEqual(people[0]["$token"] as? String, "testToken")
1023+
let appendProperties = people[5]["$append"] as! InternalProperties
1024+
XCTAssertEqual(appendProperties["d"] as? String, "goodbye")
1025+
1026+
let group = groupQueue(token: testMixpanel.apiToken)
1027+
XCTAssertEqual(group.count, 2)
1028+
XCTAssertEqual(group[0]["$group_key"] as? String, "Cool Property")
1029+
let setProperties = group[0]["$set"] as! InternalProperties
1030+
XCTAssertEqual(setProperties["g"] as? String, "yo")
1031+
let setProperties2 = group[1]["$set"] as! InternalProperties
1032+
XCTAssertEqual(setProperties2["a"] as? Int, 1)
1033+
XCTAssertTrue(MixpanelPersistence.loadOptOutStatusFlag(apiToken: token)!)
1034+
XCTAssertTrue(MixpanelPersistence.loadAutomacticEventsEnabledFlag(apiToken: token))
1035+
1036+
//timedEvents
1037+
let testTimedEvents = MixpanelPersistence.loadTimedEvents(apiToken: token)
1038+
XCTAssertEqual(testTimedEvents.count, 3)
1039+
XCTAssertNotNil(testTimedEvents["Time Event A"])
1040+
XCTAssertNotNil(testTimedEvents["Time Event B"])
1041+
XCTAssertNotNil(testTimedEvents["Time Event C"])
1042+
let identity = MixpanelPersistence.loadIdentity(apiToken: token)
1043+
XCTAssertEqual(identity.distinctID, "demo_user")
1044+
XCTAssertEqual(identity.peopleDistinctID, "demo_user")
1045+
XCTAssertNotNil(identity.anonymousId)
1046+
XCTAssertEqual(identity.userId, "demo_user")
1047+
XCTAssertEqual(identity.alias, "New Alias")
1048+
XCTAssertEqual(identity.hadPersistedDistinctId, false)
1049+
1050+
let superProperties = MixpanelPersistence.loadSuperProperties(apiToken: token)
1051+
XCTAssertEqual(superProperties.count, 7)
1052+
XCTAssertEqual(superProperties["Super Property 1"] as? Int, 1)
1053+
XCTAssertEqual(superProperties["Super Property 7"] as? NSNull, NSNull())
1054+
removeDBfile("testToken")
1055+
}
1056+
1057+
func prepareForMigrationFiles(_ fileNames: [String]) {
1058+
for fileName in fileNames {
1059+
let fileManager = FileManager.default
1060+
let filepath = Bundle(for: type(of: self)).url(forResource: fileName, withExtension: nil)!
1061+
let libraryUrls = fileManager.urls(for: .libraryDirectory,
1062+
in: .userDomainMask)
1063+
let destURL = libraryUrls.first?.appendingPathComponent(fileName)
1064+
do {
1065+
try FileManager.default.copyItem(at: filepath, to: destURL!)
1066+
} catch let error {
1067+
print(error)
1068+
}
1069+
}
1070+
}
9911071
}
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.

Package.swift

+4-4
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,10 @@ let package = Package(
88
.iOS(.v9),
99
.tvOS(.v9),
1010
.macOS(.v10_10),
11-
.watchOS(.v3),
11+
.watchOS(.v3)
1212
],
1313
products: [
14-
.library(name: "Mixpanel", targets: ["Mixpanel"]),
14+
.library(name: "Mixpanel", targets: ["Mixpanel"])
1515
],
1616
targets: [
1717
.target(
@@ -21,8 +21,8 @@ let package = Package(
2121
"Info.plist"
2222
],
2323
swiftSettings: [
24-
.define("DECIDE", .when(platforms: [.iOS])),
24+
.define("DECIDE", .when(platforms: [.iOS]))
2525
]
26-
),
26+
)
2727
]
2828
)

Sources/AutomaticEvents.swift

+8-10
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
// Copyright © 2017 Mixpanel. All rights reserved.
77
//
88

9-
protocol AEDelegate {
9+
protocol AEDelegate: AnyObject {
1010
func track(event: String?, properties: Properties?)
1111
func setOnce(properties: Properties)
1212
func increment(property: String, by: Double)
@@ -21,26 +21,26 @@ class AutomaticEvents: NSObject, SKPaymentTransactionObserver, SKProductsRequest
2121

2222
var _minimumSessionDuration: UInt64 = 10000
2323
var minimumSessionDuration: UInt64 {
24-
set {
25-
_minimumSessionDuration = newValue
26-
}
2724
get {
2825
return _minimumSessionDuration
2926
}
27+
set {
28+
_minimumSessionDuration = newValue
29+
}
3030
}
3131
var _maximumSessionDuration: UInt64 = UINT64_MAX
3232
var maximumSessionDuration: UInt64 {
33-
set {
34-
_maximumSessionDuration = newValue
35-
}
3633
get {
3734
return _maximumSessionDuration
3835
}
36+
set {
37+
_maximumSessionDuration = newValue
38+
}
3939
}
4040

4141
var awaitingTransactions = [String: SKPaymentTransaction]()
4242
let defaults = UserDefaults(suiteName: "Mixpanel")
43-
var delegate: AEDelegate?
43+
weak var delegate: AEDelegate?
4444
var sessionLength: TimeInterval = 0
4545
var sessionStartTime: TimeInterval = Date().timeIntervalSince1970
4646
var hasAddedObserver = false
@@ -121,7 +121,6 @@ class AutomaticEvents: NSObject, SKPaymentTransactionObserver, SKProductsRequest
121121
case .purchased:
122122
productIdentifiers.insert(trans.payment.productIdentifier)
123123
awaitingTransactions[trans.payment.productIdentifier] = trans
124-
break
125124
case .failed: break
126125
case .restored: break
127126
default: break
@@ -172,4 +171,3 @@ class AutomaticEvents: NSObject, SKPaymentTransactionObserver, SKProductsRequest
172171
}
173172

174173
#endif
175-

Sources/Decide.swift

-1
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,6 @@ class Decide {
2222
let lock: ReadWriteLock
2323
var decideFetched = false
2424
let mixpanelPersistence: MixpanelPersistence
25-
2625

2726
required init(basePathIdentifier: String, lock: ReadWriteLock, mixpanelPersistence: MixpanelPersistence) {
2827
self.decideRequest = DecideRequest(basePathIdentifier: basePathIdentifier)

Sources/Flush.swift

+10-10
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88

99
import Foundation
1010

11-
protocol FlushDelegate {
11+
protocol FlushDelegate: AnyObject {
1212
func flush(completion: (() -> Void)?)
1313
func flushSuccess(type: FlushType, ids: [Int32])
1414

@@ -19,14 +19,19 @@ protocol FlushDelegate {
1919

2020
class Flush: AppLifecycle {
2121
var timer: Timer?
22-
var delegate: FlushDelegate?
22+
weak var delegate: FlushDelegate?
2323
var useIPAddressForGeoLocation = true
2424
var flushRequest: FlushRequest
2525
var flushOnBackground = true
2626
var _flushInterval = 0.0
2727
private let flushIntervalReadWriteLock: DispatchQueue
2828

2929
var flushInterval: Double {
30+
get {
31+
flushIntervalReadWriteLock.sync {
32+
return _flushInterval
33+
}
34+
}
3035
set {
3136
flushIntervalReadWriteLock.sync(flags: .barrier, execute: {
3237
_flushInterval = newValue
@@ -35,19 +40,13 @@ class Flush: AppLifecycle {
3540
delegate?.flush(completion: nil)
3641
startFlushTimer()
3742
}
38-
get {
39-
flushIntervalReadWriteLock.sync {
40-
return _flushInterval
41-
}
42-
}
4343
}
4444

4545
required init(basePathIdentifier: String) {
4646
self.flushRequest = FlushRequest(basePathIdentifier: basePathIdentifier)
4747
flushIntervalReadWriteLock = DispatchQueue(label: "com.mixpanel.flush_interval.lock", qos: .utility, attributes: .concurrent)
4848
}
4949

50-
5150
func flushQueue(type: FlushType, queue: Queue) {
5251
if flushRequest.requestNotAllowed() {
5352
return
@@ -119,7 +118,9 @@ class Flush: AppLifecycle {
119118
if success {
120119
// remove
121120
self.delegate?.flushSuccess(type: type, ids: ids)
122-
mutableQueue = self.removeProcessedBatch(batchSize: batchSize, queue: mutableQueue, type: type)
121+
mutableQueue = self.removeProcessedBatch(batchSize: batchSize,
122+
queue: mutableQueue,
123+
type: type)
123124
}
124125
shouldContinue = success
125126
semaphore.signal()
@@ -143,7 +144,6 @@ class Flush: AppLifecycle {
143144
return shadowQueue
144145
}
145146

146-
147147
// MARK: - Lifecycle
148148
func applicationDidBecomeActive() {
149149
startFlushTimer()

Sources/Group.swift

+8-2
Original file line numberDiff line numberDiff line change
@@ -17,11 +17,17 @@ open class Group {
1717
let lock: ReadWriteLock
1818
let groupKey: String
1919
let groupID: MixpanelType
20-
var delegate: FlushDelegate?
20+
weak var delegate: FlushDelegate?
2121
let metadata: SessionMetadata
2222
let mixpanelPersistence: MixpanelPersistence
2323

24-
init(apiToken: String, serialQueue: DispatchQueue, lock: ReadWriteLock, groupKey: String, groupID: MixpanelType, metadata: SessionMetadata, mixpanelPersistence: MixpanelPersistence) {
24+
init(apiToken: String,
25+
serialQueue: DispatchQueue,
26+
lock: ReadWriteLock,
27+
groupKey: String,
28+
groupID: MixpanelType,
29+
metadata: SessionMetadata,
30+
mixpanelPersistence: MixpanelPersistence) {
2531
self.apiToken = apiToken
2632
self.serialQueue = serialQueue
2733
self.lock = lock

Sources/JSONHandler.swift

+1-1
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ class JSONHandler {
3232
}
3333

3434
class func deserializeData(_ data: Data) -> MPObjectToParse? {
35-
var object: MPObjectToParse? = nil
35+
var object: MPObjectToParse?
3636
do {
3737
object = try JSONSerialization.jsonObject(with: data, options: [])
3838
} catch {

0 commit comments

Comments
 (0)