Skip to content

Commit d6e18b9

Browse files
authored
Add Tests Against Sessions SDK and Internal API, Organize Errors (#10669)
1 parent 0142276 commit d6e18b9

27 files changed

+966
-104
lines changed

FirebaseSessions/Sources/FirebaseSessions.swift

Lines changed: 60 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -32,11 +32,11 @@ private enum GoogleDataTransportConfig {
3232
private let appID: String
3333

3434
/// Top-level Classes in the Sessions SDK
35-
private let coordinator: SessionCoordinator
35+
private let coordinator: SessionCoordinatorProtocol
3636
private let initiator: SessionInitiator
3737
private let sessionGenerator: SessionGenerator
38-
private let appInfo: ApplicationInfo
39-
private let settings: SessionsSettings
38+
private let appInfo: ApplicationInfoProtocol
39+
private let settings: SettingsProtocol
4040

4141
/// Subscribers
4242
/// `subscribers` are used to determine the Data Collection state of the Sessions SDK.
@@ -82,12 +82,50 @@ private enum GoogleDataTransportConfig {
8282
coordinator: coordinator,
8383
initiator: initiator,
8484
appInfo: appInfo,
85-
settings: settings)
85+
settings: settings) { result in
86+
switch result {
87+
case .success(()):
88+
Logger.logInfo("Successfully logged Session Start event")
89+
case let .failure(sessionsError):
90+
switch sessionsError {
91+
case let .SessionInstallationsError(error):
92+
Logger.logError(
93+
"Error getting Firebase Installation ID: \(error). Skipping this Session Event"
94+
)
95+
case let .DataTransportError(error):
96+
Logger
97+
.logError(
98+
"Error logging Session Start event to GoogleDataTransport: \(error)."
99+
)
100+
case .NoDependenciesError:
101+
Logger
102+
.logError(
103+
"Sessions SDK did not have any dependent SDKs register as dependencies. Events will not be sent."
104+
)
105+
case .SessionSamplingError:
106+
Logger
107+
.logDebug(
108+
"Sessions SDK has sampled this session"
109+
)
110+
case .DisabledViaSettingsError:
111+
Logger
112+
.logDebug(
113+
"Sessions SDK is disabled via Settings"
114+
)
115+
case .DataCollectionError:
116+
Logger
117+
.logDebug(
118+
"Data Collection is disabled for all subscribers. Skipping this Session Event"
119+
)
120+
}
121+
}
122+
}
86123
}
87124

88125
// Initializes the SDK and begines the process of listening for lifecycle events and logging events
89-
init(appID: String, sessionGenerator: SessionGenerator, coordinator: SessionCoordinator,
90-
initiator: SessionInitiator, appInfo: ApplicationInfo, settings: SessionsSettings) {
126+
init(appID: String, sessionGenerator: SessionGenerator, coordinator: SessionCoordinatorProtocol,
127+
initiator: SessionInitiator, appInfo: ApplicationInfoProtocol, settings: SettingsProtocol,
128+
loggedEventCallback: @escaping (Result<Void, FirebaseSessionsError>) -> Void) {
91129
self.appID = appID
92130

93131
self.sessionGenerator = sessionGenerator
@@ -115,14 +153,18 @@ private enum GoogleDataTransportConfig {
115153

116154
let event = SessionStartEvent(sessionInfo: sessionInfo, appInfo: self.appInfo)
117155

156+
// If there are no Dependencies, then the Sessions SDK can't acknowledge
157+
// any products data collection state, so the Sessions SDK won't send events.
158+
guard !self.subscriberPromises.isEmpty else {
159+
loggedEventCallback(.failure(.NoDependenciesError))
160+
return
161+
}
162+
118163
// Wait until all subscriber promises have been fulfilled before
119164
// doing any data collection.
120165
all(self.subscriberPromises.values).then(on: .global(qos: .background)) { _ in
121166
guard self.isAnyDataCollectionEnabled else {
122-
Logger
123-
.logDebug(
124-
"Data Collection is disabled for all subscribers. Skipping this Session Event"
125-
)
167+
loggedEventCallback(.failure(.DataCollectionError))
126168
return
127169
}
128170

@@ -136,15 +178,18 @@ private enum GoogleDataTransportConfig {
136178

137179
self.addEventDataCollectionState(event: event)
138180

139-
if !(self.settings.sessionsEnabled && sessionInfo.shouldDispatchEvents) {
140-
Logger
141-
.logDebug(
142-
"Session Event logging is disabled sessionsEnabled: \(self.settings.sessionsEnabled), shouldDispatchEvents: \(sessionInfo.shouldDispatchEvents)"
143-
)
181+
guard sessionInfo.shouldDispatchEvents else {
182+
loggedEventCallback(.failure(.SessionSamplingError))
183+
return
184+
}
185+
186+
guard self.settings.sessionsEnabled else {
187+
loggedEventCallback(.failure(.DisabledViaSettingsError))
144188
return
145189
}
146190

147191
self.coordinator.attemptLoggingSessionStart(event: event) { result in
192+
loggedEventCallback(result)
148193
}
149194
}
150195
}

FirebaseSessions/Sources/FirebaseSessionsError.swift

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,15 @@ enum FirebaseSessionsError: Error {
1919
/// Event sampling related error
2020
case SessionSamplingError
2121
/// Firebase Installation ID related error
22-
case SessionInstallationsError
23-
/// Settings related error
24-
case SettingsError(String)
22+
case SessionInstallationsError(Error)
23+
/// Error from the GoogleDataTransport SDK
24+
case DataTransportError(Error)
25+
/// Sessions SDK is disabled via settings error
26+
case DisabledViaSettingsError
27+
/// Sessions SDK is disabled because all Subscribers have their
28+
/// data collection disabled
29+
case DataCollectionError
30+
/// Sessions SDK didn't have any Subscribers depend
31+
/// on it via addDependency in SessionDependencies
32+
case NoDependenciesError
2533
}

FirebaseSessions/Sources/SessionCoordinator.swift

Lines changed: 10 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -14,11 +14,16 @@
1414

1515
import Foundation
1616

17+
protocol SessionCoordinatorProtocol {
18+
func attemptLoggingSessionStart(event: SessionStartEvent,
19+
callback: @escaping (Result<Void, FirebaseSessionsError>) -> Void)
20+
}
21+
1722
///
1823
/// SessionCoordinator is responsible for coordinating the systems in this SDK
1924
/// involved with sending a Session Start event.
2025
///
21-
class SessionCoordinator {
26+
class SessionCoordinator: SessionCoordinatorProtocol {
2227
let installations: InstallationsProtocol
2328
let fireLogger: EventGDTLoggerProtocol
2429

@@ -30,7 +35,8 @@ class SessionCoordinator {
3035

3136
// Begins the process of logging a SessionStartEvent to FireLog, while taking into account Data Collection, Sampling, and fetching Settings
3237
func attemptLoggingSessionStart(event: SessionStartEvent,
33-
callback: @escaping (Result<Void, Error>) -> Void) {
38+
callback: @escaping (Result<Void, FirebaseSessionsError>)
39+
-> Void) {
3440
/// Order of execution
3541
/// 1. Check if the session can be sent. If yes, move to 2. Else, drop the event.
3642
/// 2. Fetch the installations Id. If successful, move to 3. Else, drop sending the event.
@@ -42,22 +48,13 @@ class SessionCoordinator {
4248
self.fireLogger.logEvent(event: event) { logResult in
4349
switch logResult {
4450
case .success():
45-
Logger.logInfo("Successfully logged Session Start event to GoogleDataTransport")
4651
callback(.success(()))
4752
case let .failure(error):
48-
Logger
49-
.logError(
50-
"Error logging Session Start event to GoogleDataTransport: \(error)."
51-
)
52-
callback(.failure(error))
53+
callback(.failure(FirebaseSessionsError.DataTransportError(error)))
5354
}
5455
}
5556
case let .failure(error):
56-
Logger
57-
.logError(
58-
"Error getting Firebase Installation ID: \(error). Skip sending event."
59-
)
60-
callback(.failure(FirebaseSessionsError.SessionInstallationsError))
57+
callback(.failure(FirebaseSessionsError.SessionInstallationsError(error)))
6158
}
6259
}
6360
}

FirebaseSessions/Sources/SessionGenerator.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,9 +37,9 @@ struct SessionInfo {
3737
///
3838
class SessionGenerator {
3939
private var thisSession: SessionInfo?
40-
private var settings: SessionsSettings
40+
private var settings: SettingsProtocol
4141

42-
init(settings: SessionsSettings) {
42+
init(settings: SettingsProtocol) {
4343
self.settings = settings
4444
}
4545

FirebaseSessions/Sources/SessionInitiator.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,11 +31,11 @@ import Foundation
3131
///
3232
class SessionInitiator {
3333
let currentTime: () -> Date
34-
var settings: SessionsSettings
34+
var settings: SettingsProtocol
3535
var backgroundTime = Date.distantFuture
3636
var initiateSessionStart: () -> Void = {}
3737

38-
init(settings: SessionsSettings, currentTimeProvider: @escaping () -> Date = Date.init) {
38+
init(settings: SettingsProtocol, currentTimeProvider: @escaping () -> Date = Date.init) {
3939
currentTime = currentTimeProvider
4040
self.settings = settings
4141
}

FirebaseSessions/Sources/Settings/LocalOverrideSettings.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
import Foundation
1717

1818
/// Class that manages the local overrides configs related to the library.
19-
class LocalOverrideSettings: SettingsProvider, SettingsProtocol {
19+
class LocalOverrideSettings: SettingsProvider {
2020
static let PlistKey_sessions_enabled = "FirebaseSessionsEnabled"
2121
static let PlistKey_sessions_timeout = "FirebaseSessionsTimeout"
2222
static let PlistKey_sessions_samplingRate = "FirebaseSessionsSampingRate"

FirebaseSessions/Sources/Settings/RemoteSettings.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ extension ApplicationInfoProtocol {
2020
var synthesizedVersion: String { return "\(appDisplayVersion) (\(appBuildVersion))" }
2121
}
2222

23-
class RemoteSettings: SettingsProvider, SettingsProtocol {
23+
class RemoteSettings: SettingsProvider {
2424
private static let cacheDurationSecondsDefault: TimeInterval = 60 * 60
2525
private static let flagSessionsEnabled = "sessions_enabled"
2626
private static let flagSamplingRate = "sampling_rate"

FirebaseSessions/Sources/Settings/SDKDefaultSettings.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
import Foundation
1717

1818
/// Class that manages the local overrides configs related to the library.
19-
class SDKDefaultSettings: SettingsProvider, SettingsProtocol {
19+
class SDKDefaultSettings: SettingsProvider {
2020
var sessionsEnabled: Bool? {
2121
// Default is sessions enabled
2222
return true

FirebaseSessions/Sources/Settings/SessionsSettings.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
import Foundation
1717

1818
/// Class that manages the configs related to the settings library
19-
class SessionsSettings {
19+
class SessionsSettings: SettingsProtocol {
2020
private let appInfo: ApplicationInfoProtocol
2121
private let installations: InstallationsProtocol
2222
private let sdkDefaults: SDKDefaultSettings

FirebaseSessions/Sources/Settings/SettingsDownloadClient.swift

Lines changed: 19 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,18 @@ import Foundation
2222
#endif // SWIFT_PACKAGE
2323

2424
protocol SettingsDownloadClient {
25-
func fetch(completion: @escaping (Result<[String: Any], Error>) -> Void)
25+
func fetch(completion: @escaping (Result<[String: Any], SettingsDownloaderError>) -> Void)
26+
}
27+
28+
enum SettingsDownloaderError: Error {
29+
/// Error contructing the URL
30+
case URLError(String)
31+
/// Error from the URLSession task
32+
case URLSessionError(String)
33+
/// Error parsing the JSON response from Settings
34+
case JSONParseError(String)
35+
/// Error getting the Installation ID
36+
case InstallationIDError(String)
2637
}
2738

2839
class SettingsDownloader: SettingsDownloadClient {
@@ -34,9 +45,9 @@ class SettingsDownloader: SettingsDownloadClient {
3445
self.installations = installations
3546
}
3647

37-
func fetch(completion: @escaping (Result<[String: Any], Error>) -> Void) {
48+
func fetch(completion: @escaping (Result<[String: Any], SettingsDownloaderError>) -> Void) {
3849
guard let validURL = url else {
39-
completion(.failure(FirebaseSessionsError.SettingsError("Invalid URL")))
50+
completion(.failure(.URLError("Invalid URL")))
4051
return
4152
}
4253

@@ -49,17 +60,18 @@ class SettingsDownloader: SettingsDownloadClient {
4960
if let dict = try? JSONSerialization.jsonObject(with: data) as? [String: Any] {
5061
completion(.success(dict))
5162
} else {
52-
completion(.failure(FirebaseSessionsError
53-
.SettingsError("Failed to parse JSON to dictionary")))
63+
completion(.failure(
64+
.JSONParseError("Failed to parse JSON to dictionary")
65+
))
5466
}
5567
} else if let error = error {
56-
completion(.failure(FirebaseSessionsError.SettingsError(error.localizedDescription)))
68+
completion(.failure(.URLSessionError(error.localizedDescription)))
5769
}
5870
}
5971
// Start the task that sends the network request
6072
task.resume()
6173
case let .failure(error):
62-
completion(.failure(FirebaseSessionsError.SettingsError(error.localizedDescription)))
74+
completion(.failure(.InstallationIDError(error.localizedDescription)))
6375
}
6476
}
6577
}

0 commit comments

Comments
 (0)