Skip to content

Commit 9811e45

Browse files
authored
fix: (datastore): test cases, switch back to file based backing store for memory datastore. (#311)
1 parent 8b0f324 commit 9811e45

File tree

7 files changed

+150
-47
lines changed

7 files changed

+150
-47
lines changed

Sources/Customization/DefaultEventDispatcher.swift

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -22,13 +22,8 @@ public enum DataStoreType {
2222

2323
open class DefaultEventDispatcher: BackgroundingCallbacks, OPTEventDispatcher {
2424

25-
#if os(tvOS)
26-
static let sharedInstance =
27-
DefaultEventDispatcher(backingStore: .memory)
28-
#else
29-
static let sharedInstance =
25+
static let sharedInstance =
3026
DefaultEventDispatcher()
31-
#endif
3227

3328
// timer-interval for batching (0 = no batching, negative = use default)
3429
var timerInterval: TimeInterval

Sources/Implementation/Datastore/DataStoreFile.swift

Lines changed: 29 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -29,24 +29,46 @@ public class DataStoreFile<T>: OPTDataStore where T: Codable {
2929
self.async = async
3030
dataStoreName = storeName
3131
lock = DispatchQueue(label: storeName)
32-
if let url = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first {
32+
#if os(tvOS)
33+
let directory = FileManager.SearchPathDirectory.cachesDirectory
34+
#else
35+
let directory = FileManager.SearchPathDirectory.documentDirectory
36+
#endif
37+
if let url = FileManager.default.urls(for: directory, in: .userDomainMask).first {
3338
self.url = url.appendingPathComponent(storeName, isDirectory: false)
3439
} else {
3540
self.url = URL(fileURLWithPath: storeName)
3641
}
42+
3743
}
3844

45+
func isArray() -> Bool {
46+
let t = "\(type(of: T.self))"
47+
return t.hasPrefix("Array") || t.hasPrefix("Swift.Array") || t.hasPrefix("__C.NSArray") || t.hasPrefix("NSArray")
48+
}
3949
public func getItem(forKey: String) -> Any? {
4050
var returnItem: T?
4151

4252
lock.sync {
4353
do {
54+
55+
if !FileManager.default.fileExists(atPath: self.url.path) {
56+
return
57+
}
58+
4459
let contents = try Data(contentsOf: self.url)
60+
4561
if type(of: T.self) == type(of: Data.self) {
4662
returnItem = contents as? T
4763
} else {
48-
let item = try JSONDecoder().decode(T.self, from: contents)
49-
returnItem = item
64+
if isArray() {
65+
let item = try JSONDecoder().decode(T.self, from: contents)
66+
returnItem = item
67+
}
68+
else {
69+
let item = try JSONDecoder().decode([T].self, from: contents)
70+
returnItem = item.first
71+
}
5072
}
5173
} catch let e as NSError {
5274
if e.code != 260 {
@@ -78,9 +100,12 @@ public class DataStoreFile<T>: OPTDataStore where T: Codable {
78100
// don't bother to convert... otherwise, do
79101
if let value = value as? Data {
80102
data = value
81-
} else {
103+
} else if (value as? NSArray) != nil {
82104
data = try JSONEncoder().encode(value)
83105
}
106+
else {
107+
data = try JSONEncoder().encode([value])
108+
}
84109
if let data = data {
85110
try data.write(to: self.url, options: .atomic)
86111
}

Sources/Implementation/Datastore/DataStoreMemory.swift

Lines changed: 13 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -24,12 +24,18 @@ public class DataStoreMemory<T>: BackgroundingCallbacks, OPTDataStore where T: C
2424
let lock: DispatchQueue
2525
var data: T?
2626
var backupDataStore: OPTDataStore
27+
public enum BackingStore { case UserDefaults, File }
2728
lazy var logger: OPTLogger? = OPTLoggerFactory.getLogger()
2829

29-
init(storeName: String, backupStore: OPTDataStore = DataStoreUserDefaults()) {
30+
init(storeName: String, backupStore:BackingStore = .File) {
3031
dataStoreName = storeName
3132
lock = DispatchQueue(label: storeName)
32-
backupDataStore = backupStore
33+
switch backupStore {
34+
case .File:
35+
self.backupDataStore = DataStoreFile<T>(storeName: storeName, async: false)
36+
case .UserDefaults:
37+
self.backupDataStore = DataStoreUserDefaults()
38+
}
3339
load(forKey: dataStoreName)
3440
subscribe()
3541
}
@@ -48,13 +54,8 @@ public class DataStoreMemory<T>: BackgroundingCallbacks, OPTDataStore where T: C
4854

4955
public func load(forKey: String) {
5056
lock.sync {
51-
do {
52-
if let contents = backupDataStore.getItem(forKey: dataStoreName) as? Data {
53-
let item = try JSONDecoder().decode(T.self, from: contents)
54-
self.data = item
55-
}
56-
} catch let error {
57-
self.logger?.e(error.localizedDescription)
57+
if let contents = backupDataStore.getItem(forKey: dataStoreName) as? T {
58+
self.data = contents
5859
}
5960
}
6061
}
@@ -82,12 +83,12 @@ public class DataStoreMemory<T>: BackgroundingCallbacks, OPTDataStore where T: C
8283
}
8384

8485
@objc func applicationDidEnterBackground() {
85-
if let data = data {
86-
save(forKey: dataStoreName, value: data as Any)
86+
if let data = self.data {
87+
self.save(forKey: dataStoreName, value: data as Any)
8788
}
8889
}
8990

9091
@objc func applicationDidBecomeActive() {
91-
load(forKey: dataStoreName)
92+
self.load(forKey: dataStoreName)
9293
}
9394
}

Sources/Implementation/DefaultDatafileHandler.swift

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -260,15 +260,9 @@ class DefaultDatafileHandler: OPTDatafileHandler {
260260
if let cache = datafileCache[sdkKey] {
261261
return cache
262262
} else {
263-
#if os(tvOS)
264-
let store = DataStoreUserDefaults()
265-
datafileCache[sdkKey] = store
266-
return store
267-
#else
268263
let store = DataStoreFile<Data>(storeName: sdkKey)
269264
datafileCache[sdkKey] = store
270265
return store
271-
#endif
272266
}
273267
}
274268

Tests/OptimizelyTests-Common/DataStoreTests.swift

Lines changed: 91 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -57,38 +57,119 @@ class DataStoreTests: XCTestCase {
5757

5858
XCTAssert(v2 == value)
5959
}
60+
61+
func testUserDefaults2() {
62+
let ds = DataStoreUserDefaults()
63+
let data = "{}".data(using: .utf8)
64+
65+
ds.saveItem(forKey: "item", value: [data])
66+
67+
var item = ds.getItem(forKey: "item") as? [Data]
68+
69+
XCTAssertNotNil(item)
70+
71+
ds.removeItem(forKey: "item")
72+
73+
item = ds.getItem(forKey: "item") as? [Data]
74+
75+
XCTAssertNil(item)
76+
}
6077

6178
func testBackgroundSave() {
62-
let datastore = DataStoreMemory<String>(storeName: "testingBackgroundSave")
63-
64-
datastore.saveItem(forKey: "testString1", value: "value")
79+
let datastore = DataStoreMemory<[String]>(storeName: "testBackgroundSave")
80+
81+
let key = "testBackgroundSave"
82+
datastore.saveItem(forKey: key, value: ["value"])
83+
print("[DataStoreTest] \(String(describing: datastore.getItem(forKey: key)))")
84+
85+
datastore.applicationDidEnterBackground()
86+
datastore.saveItem(forKey: key, value:["v"])
87+
print("[DataStoreTest] \(String(describing: datastore.getItem(forKey: key)))")
88+
89+
datastore.applicationDidBecomeActive()
90+
91+
print("[DataStoreTest] \(String(describing: datastore.getItem(forKey: key)))")
92+
XCTAssertNotNil(datastore.data)
93+
94+
datastore.load(forKey: key)
95+
96+
print("[DataStoreTest] \(String(describing: datastore.getItem(forKey: key)))")
97+
XCTAssertEqual(datastore.data, ["value"])
98+
99+
datastore.removeItem(forKey: key)
100+
}
101+
102+
func testBackgroundSaveUserDefaults() {
103+
let datastore = DataStoreMemory<String>(storeName: "testBackgroundSaveUserDefaults",backupStore: DataStoreMemory.BackingStore.UserDefaults)
65104

105+
let key = "testBackgroundSaveUserDefaults"
106+
datastore.saveItem(forKey: key, value: "value")
107+
print("[DataStoreTest] \(String(describing: datastore.getItem(forKey: key)))")
108+
66109
datastore.applicationDidEnterBackground()
67-
110+
datastore.saveItem(forKey: key, value:"v")
111+
print("[DataStoreTest] \(String(describing: datastore.getItem(forKey: key)))")
112+
68113
datastore.applicationDidBecomeActive()
69114

115+
print("[DataStoreTest] \(String(describing: datastore.getItem(forKey: key)))")
70116
XCTAssertNotNil(datastore.data)
71117

72-
datastore.save(forKey: "testString1", value: 100)
73-
74-
datastore.load(forKey: "testingBackgroundSave")
118+
datastore.load(forKey: key)
75119

120+
print("[DataStoreTest] \(String(describing: datastore.getItem(forKey: key)))")
76121
XCTAssertEqual(datastore.data, "value")
122+
123+
datastore.removeItem(forKey: key)
77124
}
78125

79126
func testFileStore() {
80127
// simple file store test
81128

82-
let datastore = DataStoreFile<[String]>(storeName: "testingDataStoreFile")
129+
let datastore = DataStoreFile<[String]>(storeName: "testFileStore")
83130

84131
datastore.saveItem(forKey: "testString", value: ["value"])
85132

86133
let vj = datastore.getItem(forKey: "testString") as! [String]
87134

88135
XCTAssert(vj.first == "value")
89136

137+
datastore.removeItem(forKey: "testString")
138+
90139
}
91140

141+
func testFileStoreString() {
142+
let datastore = DataStoreFile<String>(storeName: "testFileStoreString")
143+
144+
let key = "testFileStoreString"
145+
datastore.saveItem(forKey: key, value: "value")
146+
print("[DataStoreTest] \(String(describing: datastore.getItem(forKey: key)))")
147+
148+
print("[DataStoreTest] \(String(describing: datastore.getItem(forKey: key)))")
149+
let item = datastore.getItem(forKey:key) as? String
150+
151+
XCTAssertEqual(item, "value")
152+
153+
datastore.removeItem(forKey: key)
154+
}
155+
156+
func testFileStoreInt() {
157+
let datastore = DataStoreFile<Int>(storeName: "testFileStoreInt")
158+
159+
let key = "testFileStoreInt"
160+
datastore.saveItem(forKey: key, value: 5)
161+
print("[DataStoreTest] \(String(describing: datastore.getItem(forKey: key)))")
162+
163+
print("[DataStoreTest] \(String(describing: datastore.getItem(forKey: key)))")
164+
let item = datastore.getItem(forKey:key) as? Int
165+
166+
XCTAssertEqual(item, 5)
167+
168+
datastore.removeItem(forKey: key)
169+
}
170+
171+
172+
92173
func testUserDefaults() {
93174
// simple user defaults test
94175

@@ -100,6 +181,8 @@ class DataStoreTests: XCTestCase {
100181

101182
XCTAssert(value == "value")
102183

184+
datastore.removeItem(forKey: "testString")
185+
103186
}
104187

105188
func testUserDefaultsTooBig() {

Tests/OptimizelyTests-Common/DatafileHandlerTests.swift

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -524,18 +524,20 @@ class DatafileHandlerTests: XCTestCase {
524524
let datafileData = datafileString.data(using: .utf8)!
525525

526526
#if os(tvOS)
527-
UserDefaults.standard.set(datafileData, forKey: testSDKKey)
528-
UserDefaults.standard.synchronize()
527+
var url = FileManager.default.urls(for: .cachesDirectory, in: .userDomainMask).first!
529528
#else
530529
var url = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!
530+
#endif
531531
url = url.appendingPathComponent(testSDKKey, isDirectory: false)
532532
try! datafileData.write(to: url, options: .atomic)
533-
#endif
534533

535534
// verify that a new datafileHandler can read an existing datafile cache
536535

537536
let datafileFromCache = DefaultDatafileHandler().loadSavedDatafile(sdkKey: testSDKKey)
538537
XCTAssert(datafileFromCache == datafileData, "failed to support old datafile cached data format")
538+
539+
let project = try! JSONDecoder().decode(Project.self, from: datafileFromCache!)
540+
XCTAssert(project.revision == "241")
539541
}
540542

541543
}

Tests/OptimizelyTests-Common/EventDispatcherTests.swift

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,13 @@ class EventDispatcherTests: XCTestCase {
2222

2323
override func setUp() {
2424
// Put setup code here. This method is called before the invocation of each test method in the class.
25-
if let url = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first {
25+
#if os(tvOS)
26+
let directory = FileManager.SearchPathDirectory.cachesDirectory
27+
#else
28+
let directory = FileManager.SearchPathDirectory.documentDirectory
29+
#endif
30+
31+
if let url = FileManager.default.urls(for: directory, in: .userDomainMask).first {
2632
if (!FileManager.default.fileExists(atPath: url.path)) {
2733
do {
2834
try FileManager.default.createDirectory(at: url, withIntermediateDirectories: false, attributes: nil)
@@ -293,20 +299,17 @@ class EventDispatcherTests: XCTestCase {
293299
let saveFormat = try! JSONEncoder().encode(events)
294300

295301
#if os(tvOS)
296-
let dispatcher = MockEventDispatcher(backingStore: .memory)
297-
let memoryStore: DataStoreMemory<[Data]> = dispatcher.dataStore.dataStore as! DataStoreMemory
298-
UserDefaults.standard.set(saveFormat, forKey: queueName)
299-
UserDefaults.standard.synchronize()
300-
memoryStore.load(forKey: queueName)
302+
var url = FileManager.default.urls(for: .cachesDirectory, in: .userDomainMask).first!
301303
#else
302-
let dispatcher = MockEventDispatcher()
303304
var url = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!
305+
#endif
304306
url = url.appendingPathComponent(queueName, isDirectory: false)
305307
try! saveFormat.write(to: url, options: .atomic)
306-
#endif
307308

308309
// verify that a new dataStore can read an existing queue items
309310

311+
let dispatcher = MockEventDispatcher()
312+
310313
XCTAssert(dispatcher.dataStore.count == 2)
311314
dispatcher.flushEvents()
312315
dispatcher.dispatcher.sync {}

0 commit comments

Comments
 (0)