Skip to content

Commit 5f44e38

Browse files
author
AR Abdul Azeez
committed
Injecting classes
1 parent f408b4a commit 5f44e38

3 files changed

Lines changed: 431 additions & 7 deletions

File tree

OneSignalSDK/onesignal/core/src/main/java/com/onesignal/internal/OtelLifecycleManager.kt

Lines changed: 22 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,9 @@ import com.onesignal.debug.internal.logging.otel.android.AndroidOtelLogger
1616
import com.onesignal.debug.internal.logging.otel.android.OtelPlatformProvider
1717
import com.onesignal.debug.internal.logging.otel.android.createAndroidOtelPlatformProvider
1818
import com.onesignal.otel.IOtelCrashHandler
19+
import com.onesignal.otel.IOtelLogger
1920
import com.onesignal.otel.IOtelOpenTelemetryRemote
21+
import com.onesignal.otel.IOtelPlatformProvider
2022
import com.onesignal.otel.OtelFactory
2123
import com.onesignal.otel.crash.IOtelAnrDetector
2224

@@ -31,18 +33,31 @@ import com.onesignal.otel.crash.IOtelAnrDetector
3133
*
3234
* Thread safety: methods are synchronized on [lock] so that concurrent
3335
* calls from initEssentials (main) and the config store callback (IO) are safe.
36+
*
37+
* All factory parameters default to the real implementations, so production
38+
* callers can use `OtelLifecycleManager(context)`. Tests can override any
39+
* factory to inject mocks or throwing stubs.
3440
*/
3541
@Suppress("TooManyFunctions")
3642
internal class OtelLifecycleManager(
3743
private val context: Context,
44+
private val crashHandlerFactory: (Context, IOtelLogger) -> IOtelCrashHandler =
45+
{ ctx, log -> OneSignalCrashHandlerFactory.createCrashHandler(ctx, log) },
46+
private val anrDetectorFactory: (IOtelPlatformProvider, IOtelLogger, Long, Long) -> IOtelAnrDetector =
47+
{ pp, log, threshold, interval -> createAnrDetector(pp, log, threshold, interval) },
48+
private val remoteTelemetryFactory: (IOtelPlatformProvider) -> IOtelOpenTelemetryRemote =
49+
{ pp -> OtelFactory.createRemoteTelemetry(pp) },
50+
private val platformProviderFactory: (Context) -> OtelPlatformProvider =
51+
{ ctx -> createAndroidOtelPlatformProvider(ctx) },
52+
private val loggerFactory: () -> IOtelLogger = { AndroidOtelLogger() },
3853
) : ISingletonModelStoreChangeHandler<ConfigModel> {
3954
private val lock = Any()
4055

4156
private val platformProvider: OtelPlatformProvider by lazy {
42-
createAndroidOtelPlatformProvider(context)
57+
platformProviderFactory(context)
4358
}
4459

45-
private val logger = AndroidOtelLogger()
60+
private val logger: IOtelLogger by lazy { loggerFactory() }
4661

4762
private var crashHandler: IOtelCrashHandler? = null
4863
private var anrDetector: IOtelAnrDetector? = null
@@ -192,19 +207,19 @@ internal class OtelLifecycleManager(
192207

193208
private fun startCrashHandler() {
194209
if (crashHandler != null) return
195-
val handler = OneSignalCrashHandlerFactory.createCrashHandler(context, logger)
210+
val handler = crashHandlerFactory(context, logger)
196211
handler.initialize()
197212
crashHandler = handler
198213
Logging.info("OneSignal: Crash handler initialized — logs at: ${platformProvider.crashStoragePath}")
199214
}
200215

201216
private fun startAnrDetector() {
202217
if (anrDetector != null) return
203-
val detector = createAnrDetector(
218+
val detector = anrDetectorFactory(
204219
platformProvider,
205220
logger,
206-
anrThresholdMs = AnrConstants.DEFAULT_ANR_THRESHOLD_MS,
207-
checkIntervalMs = AnrConstants.DEFAULT_CHECK_INTERVAL_MS,
221+
AnrConstants.DEFAULT_ANR_THRESHOLD_MS,
222+
AnrConstants.DEFAULT_CHECK_INTERVAL_MS,
208223
)
209224
detector.start()
210225
anrDetector = detector
@@ -214,7 +229,7 @@ internal class OtelLifecycleManager(
214229
@Suppress("TooGenericExceptionCaught")
215230
private fun startOtelLogging(logLevel: LogLevel) {
216231
remoteTelemetry?.shutdown()
217-
val telemetry = OtelFactory.createRemoteTelemetry(platformProvider)
232+
val telemetry = remoteTelemetryFactory(platformProvider)
218233
remoteTelemetry = telemetry
219234
val shouldSend: (LogLevel) -> Boolean = { level ->
220235
logLevel != LogLevel.NONE && level <= logLevel

OneSignalSDK/onesignal/core/src/test/java/com/onesignal/debug/internal/logging/otel/android/OtelPlatformProviderTest.kt

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -777,6 +777,104 @@ class OtelPlatformProviderTest : FunSpec({
777777
provider.osName shouldBe "Android"
778778
}
779779

780+
// ===== Fresh install / all-missing scenario =====
781+
782+
test("fresh install: all lazy properties return safe defaults without crashing") {
783+
val provider = createAndroidOtelPlatformProvider(appContext!!)
784+
785+
provider.appId shouldContain "e1100000-0000-4000-a000-"
786+
provider.onesignalId shouldBe null
787+
provider.pushSubscriptionId shouldBe null
788+
provider.isRemoteLoggingEnabled shouldBe false
789+
provider.remoteLogLevel shouldBe null
790+
provider.appIdForHeaders shouldNotBe null
791+
provider.sdkBase shouldBe "android"
792+
provider.osName shouldBe "Android"
793+
provider.crashStoragePath shouldContain "onesignal"
794+
}
795+
796+
test("lazy properties cache the initial value and ignore later SharedPreferences changes") {
797+
val provider = createAndroidOtelPlatformProvider(appContext!!)
798+
799+
provider.isRemoteLoggingEnabled shouldBe false
800+
provider.remoteLogLevel shouldBe null
801+
802+
val remoteLoggingParams = JSONObject().apply { put("logLevel", "ERROR") }
803+
val configModel = JSONObject().apply {
804+
put(ConfigModel::remoteLoggingParams.name, remoteLoggingParams)
805+
}
806+
val configArray = JSONArray().apply { put(configModel) }
807+
sharedPreferences!!.edit()
808+
.putString(PreferenceOneSignalKeys.MODEL_STORE_PREFIX + configNameSpace, configArray.toString())
809+
.commit()
810+
811+
provider.isRemoteLoggingEnabled shouldBe false
812+
provider.remoteLogLevel shouldBe null
813+
}
814+
815+
test("getIsInForeground callback throws — appState returns unknown") {
816+
val config = OtelPlatformProviderConfig(
817+
crashStoragePath = "/test/path",
818+
appPackageId = "com.test",
819+
appVersion = "1.0",
820+
context = appContext,
821+
getIsInForeground = { throw RuntimeException("callback boom") }
822+
)
823+
val provider = OtelPlatformProvider(config)
824+
provider.appState shouldBe "unknown"
825+
}
826+
827+
test("getIsInForeground returns null — falls back to ActivityManager") {
828+
val config = OtelPlatformProviderConfig(
829+
crashStoragePath = "/test/path",
830+
appPackageId = "com.test",
831+
appVersion = "1.0",
832+
context = appContext,
833+
getIsInForeground = { null }
834+
)
835+
val provider = OtelPlatformProvider(config)
836+
provider.appState shouldBeOneOf listOf("foreground", "background", "unknown")
837+
}
838+
839+
test("null context and null callback — all provider properties return safe defaults") {
840+
val config = OtelPlatformProviderConfig(
841+
crashStoragePath = "/test/path",
842+
appPackageId = "com.test",
843+
appVersion = "1.0",
844+
context = null,
845+
getIsInForeground = null
846+
)
847+
val provider = OtelPlatformProvider(config)
848+
849+
provider.appState shouldBe "unknown"
850+
provider.appPackageId shouldBe "com.test"
851+
provider.appVersion shouldBe "1.0"
852+
provider.crashStoragePath shouldBe "/test/path"
853+
provider.isRemoteLoggingEnabled shouldBe false
854+
provider.remoteLogLevel shouldBe null
855+
}
856+
857+
test("corrupted SharedPreferences JSON — isRemoteLoggingEnabled returns false") {
858+
sharedPreferences!!.edit()
859+
.putString(PreferenceOneSignalKeys.MODEL_STORE_PREFIX + configNameSpace, "not valid json {{{")
860+
.commit()
861+
862+
val provider = createAndroidOtelPlatformProvider(appContext!!)
863+
provider.isRemoteLoggingEnabled shouldBe false
864+
provider.remoteLogLevel shouldBe null
865+
}
866+
867+
test("corrupted SharedPreferences JSON — appId returns error UUID") {
868+
sharedPreferences!!.edit()
869+
.putString(PreferenceOneSignalKeys.MODEL_STORE_PREFIX + configNameSpace, "not valid json {{{")
870+
.commit()
871+
872+
val provider = createAndroidOtelPlatformProvider(appContext!!)
873+
provider.appId shouldContain "e1100000-0000-4000-a000-"
874+
}
875+
876+
// ===== Factory Function Tests =====
877+
780878
test("createAndroidOtelPlatformProvider handles null appVersion gracefully") {
781879
// Given
782880
val mockContext = mockk<Context>(relaxed = true)

0 commit comments

Comments
 (0)