SessionGeneratorTests.swift 7.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203
  1. //
  2. // Copyright 2022 Google LLC
  3. //
  4. // Licensed under the Apache License, Version 2.0 (the "License");
  5. // you may not use this file except in compliance with the License.
  6. // You may obtain a copy of the License at
  7. //
  8. // http://www.apache.org/licenses/LICENSE-2.0
  9. //
  10. // Unless required by applicable law or agreed to in writing, software
  11. // distributed under the License is distributed on an "AS IS" BASIS,
  12. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. // See the License for the specific language governing permissions and
  14. // limitations under the License.
  15. import XCTest
  16. @testable import FirebaseSessions
  17. class SessionGeneratorTests: XCTestCase {
  18. var generator: SessionGenerator!
  19. let validSettings: [String: Any] = [:]
  20. var cache: SettingsCacheClient!
  21. var appInfo: MockApplicationInfo!
  22. var downloader: MockSettingsDownloader!
  23. var remoteSettings: RemoteSettings!
  24. var localOverrideSettings: LocalOverrideSettings!
  25. var sdkDefaultSettings: SDKDefaultSettings!
  26. var sessionSettings: SessionsSettings!
  27. override func setUp() {
  28. // Clear all UserDefaults
  29. if let appDomain = Bundle.main.bundleIdentifier {
  30. UserDefaults.standard.removePersistentDomain(forName: appDomain)
  31. }
  32. appInfo = MockApplicationInfo()
  33. cache = SettingsCache()
  34. cache.removeCache() // just reinstantiating cache isn't enough because of persistence
  35. downloader = MockSettingsDownloader(successResponse: validSettings)
  36. remoteSettings = RemoteSettings(appInfo: appInfo, downloader: downloader, cache: cache)
  37. remoteSettings.updateSettings(currentTime: Date())
  38. localOverrideSettings = LocalOverrideSettings()
  39. sdkDefaultSettings = SDKDefaultSettings()
  40. sessionSettings = SessionsSettings(
  41. appInfo: appInfo,
  42. installations: MockInstallationsProtocol(),
  43. sdkDefaults: sdkDefaultSettings,
  44. localOverrides: localOverrideSettings,
  45. remoteSettings: remoteSettings
  46. )
  47. generator = SessionGenerator(collectEvents: Sessions
  48. .shouldCollectEvents(settings: sessionSettings))
  49. }
  50. func isValidSessionID(_ sessionID: String) -> Bool {
  51. if sessionID.count != 32 {
  52. assertionFailure("Session ID isn't 32 characters long")
  53. return false
  54. }
  55. if sessionID.contains("-") {
  56. assertionFailure("Session ID contains a dash")
  57. return false
  58. }
  59. if sessionID.lowercased().compare(sessionID) != ComparisonResult.orderedSame {
  60. assertionFailure("Session ID is not lowercase")
  61. return false
  62. }
  63. return true
  64. }
  65. // This test case isn't important behavior. When Crash and Perf integrate
  66. // with the Sessions SDK, we may want to move to a lazy solution where
  67. // sessionID can never be empty
  68. func test_sessionID_beforeGenerateReturnsNothing() throws {
  69. XCTAssertNil(generator.currentSession)
  70. }
  71. func test_generateNewSessionID_generatesValidID() throws {
  72. let sessionInfo = generator.generateNewSession()
  73. XCTAssert(isValidSessionID(sessionInfo.sessionId))
  74. XCTAssert(isValidSessionID(sessionInfo.firstSessionId))
  75. XCTAssertEqual(sessionInfo.firstSessionId, sessionInfo.sessionId)
  76. }
  77. /// Ensures that generating a Session ID multiple times results in the fist Session ID being set
  78. /// in the firstSessionId field
  79. func test_generateNewSessionID_rotatesPreviousID() throws {
  80. let firstSessionInfo = generator.generateNewSession()
  81. XCTAssert(isValidSessionID(firstSessionInfo.sessionId))
  82. XCTAssert(isValidSessionID(firstSessionInfo.firstSessionId))
  83. XCTAssertEqual(firstSessionInfo.firstSessionId, firstSessionInfo.sessionId)
  84. XCTAssertEqual(firstSessionInfo.sessionIndex, 0)
  85. let secondSessionInfo = generator.generateNewSession()
  86. XCTAssert(isValidSessionID(secondSessionInfo.sessionId))
  87. XCTAssert(isValidSessionID(secondSessionInfo.firstSessionId))
  88. // Ensure the new firstSessionId is equal to the first Session ID from earlier
  89. XCTAssertEqual(secondSessionInfo.firstSessionId, firstSessionInfo.sessionId)
  90. // Session Index should increase
  91. XCTAssertEqual(secondSessionInfo.sessionIndex, 1)
  92. // Do a third round just in case
  93. let thirdSessionInfo = generator.generateNewSession()
  94. XCTAssert(isValidSessionID(thirdSessionInfo.sessionId))
  95. XCTAssert(isValidSessionID(thirdSessionInfo.firstSessionId))
  96. // Ensure the new firstSessionId is equal to the first Session ID from earlier
  97. XCTAssertEqual(thirdSessionInfo.firstSessionId, firstSessionInfo.sessionId)
  98. // Session Index should increase
  99. XCTAssertEqual(thirdSessionInfo.sessionIndex, 2)
  100. }
  101. func test_sessionsNotSampled_AllEventsAllowed() throws {
  102. let someSettings: [String: Any] = [
  103. "cache_duration": 10,
  104. "app_quality": [
  105. "sampling_rate": 1.0,
  106. "session_timeout_seconds": 50,
  107. ],
  108. ]
  109. cache.removeCache()
  110. downloader = MockSettingsDownloader(successResponse: someSettings)
  111. remoteSettings = RemoteSettings(appInfo: appInfo, downloader: downloader, cache: cache)
  112. remoteSettings.updateSettings(currentTime: Date())
  113. sessionSettings = SessionsSettings(
  114. appInfo: appInfo,
  115. installations: MockInstallationsProtocol(),
  116. sdkDefaults: sdkDefaultSettings,
  117. localOverrides: localOverrideSettings,
  118. remoteSettings: remoteSettings
  119. )
  120. // Rebuild the SessionGenerator with the new settings
  121. generator = SessionGenerator(collectEvents: Sessions
  122. .shouldCollectEvents(settings: sessionSettings))
  123. let sessionInfo = generator.generateNewSession()
  124. XCTAssertTrue(sessionInfo.shouldDispatchEvents)
  125. }
  126. func test_sessionsSampled_NoEventsAllowed() throws {
  127. let someSettings: [String: Any] = [
  128. "cache_duration": 10,
  129. "app_quality": [
  130. "sampling_rate": 0.0,
  131. "session_timeout_seconds": 50,
  132. ],
  133. ]
  134. cache.removeCache()
  135. downloader = MockSettingsDownloader(successResponse: someSettings)
  136. remoteSettings = RemoteSettings(appInfo: appInfo, downloader: downloader, cache: cache)
  137. remoteSettings.updateSettings(currentTime: Date())
  138. sessionSettings = SessionsSettings(
  139. appInfo: appInfo,
  140. installations: MockInstallationsProtocol(),
  141. sdkDefaults: sdkDefaultSettings,
  142. localOverrides: localOverrideSettings,
  143. remoteSettings: remoteSettings
  144. )
  145. // Rebuild the SessionGenerator with the new settings
  146. generator = SessionGenerator(collectEvents: Sessions
  147. .shouldCollectEvents(settings: sessionSettings))
  148. let sessionInfo = generator.generateNewSession()
  149. XCTAssertFalse(sessionInfo.shouldDispatchEvents)
  150. }
  151. func test_sessionsSampling_persistsPerRun() throws {
  152. let mockSettings = MockSettingsProtocol()
  153. mockSettings.samplingRate = 0
  154. // Rebuild the SessionGenerator with the new settings
  155. generator = SessionGenerator(collectEvents: Sessions
  156. .shouldCollectEvents(settings: mockSettings))
  157. let sessionInfo = generator.generateNewSession()
  158. XCTAssertFalse(sessionInfo.shouldDispatchEvents)
  159. // Try again
  160. let sessionInfo2 = generator.generateNewSession()
  161. XCTAssertFalse(sessionInfo2.shouldDispatchEvents)
  162. // Start returning true from the calculator
  163. mockSettings.samplingRate = 1
  164. // Try again after the calculator starts returning true.
  165. // It should still be false in the sessionInfo because the
  166. // sampling rate is calculated per-run
  167. let sessionInfo3 = generator.generateNewSession()
  168. XCTAssertFalse(sessionInfo3.shouldDispatchEvents)
  169. }
  170. }