Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions automotive/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,8 @@ dependencies {
testImplementation(libs.robolectric)
testImplementation(libs.kotlinx.coroutines.test)
testImplementation(libs.androidx.media3.test.utils)
testImplementation(libs.androidx.arch.core.testing)
testImplementation(libs.androidx.room.testing)
androidTestImplementation(libs.androidx.junit)
androidTestImplementation(libs.androidx.espresso.core)
}
155 changes: 155 additions & 0 deletions automotive/src/test/java/com/chamika/dashtune/FirebaseUtilsTest.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,155 @@
package com.chamika.dashtune

import com.google.firebase.crashlytics.FirebaseCrashlytics
import io.mockk.every
import io.mockk.just
import io.mockk.mockk
import io.mockk.mockkStatic
import io.mockk.runs
import io.mockk.verify
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.robolectric.RobolectricTestRunner

@RunWith(RobolectricTestRunner::class)
class FirebaseUtilsTest {

private lateinit var crashlytics: FirebaseCrashlytics

@Before
fun setUp() {
crashlytics = mockk(relaxed = true)
mockkStatic(FirebaseCrashlytics::class)
every { FirebaseCrashlytics.getInstance() } returns crashlytics
}

// --- safeRecordException tests ---

@Test
fun `safeRecordException records exception to Crashlytics when available`() {
val exception = RuntimeException("test error")
every { crashlytics.recordException(exception) } just runs

FirebaseUtils.safeRecordException(exception)

verify { crashlytics.recordException(exception) }
}

@Test
fun `safeRecordException swallows exception when Crashlytics is unavailable`() {
every { FirebaseCrashlytics.getInstance() } throws RuntimeException("GMS not available")

FirebaseUtils.safeRecordException(RuntimeException("app error"))
// No exception should propagate
}

@Test
fun `safeRecordException swallows exception when recordException itself throws`() {
val exception = RuntimeException("test error")
every { crashlytics.recordException(any()) } throws IllegalStateException("Crashlytics not initialised")

FirebaseUtils.safeRecordException(exception)
// No exception should propagate
}

// --- safeLog tests ---

@Test
fun `safeLog logs message to Crashlytics when available`() {
every { crashlytics.log(any()) } just runs

FirebaseUtils.safeLog("login successful")

verify { crashlytics.log("login successful") }
}

@Test
fun `safeLog swallows exception when Crashlytics is unavailable`() {
every { FirebaseCrashlytics.getInstance() } throws RuntimeException("GMS not available")

FirebaseUtils.safeLog("some message")
// No exception should propagate
}

@Test
fun `safeLog swallows exception when log itself throws`() {
every { crashlytics.log(any()) } throws IllegalStateException("Crashlytics not initialised")

FirebaseUtils.safeLog("some message")
// No exception should propagate
}

// --- safeSetCustomKey(String, String) tests ---

@Test
fun `safeSetCustomKey sets string key when Crashlytics is available`() {
every { crashlytics.setCustomKey(any<String>(), any<String>()) } just runs

FirebaseUtils.safeSetCustomKey("auth_method", "password")

verify { crashlytics.setCustomKey("auth_method", "password") }
}

@Test
fun `safeSetCustomKey string overload swallows exception when Crashlytics unavailable`() {
every { FirebaseCrashlytics.getInstance() } throws RuntimeException("GMS not available")

FirebaseUtils.safeSetCustomKey("key", "value")
// No exception should propagate
}

@Test
fun `safeSetCustomKey string overload swallows exception when setCustomKey throws`() {
every { crashlytics.setCustomKey(any<String>(), any<String>()) } throws RuntimeException("crash")

FirebaseUtils.safeSetCustomKey("key", "value")
// No exception should propagate
}

// --- safeSetCustomKey(String, Int) tests ---

@Test
fun `safeSetCustomKey sets int key when Crashlytics is available`() {
every { crashlytics.setCustomKey(any<String>(), any<Int>()) } just runs

FirebaseUtils.safeSetCustomKey("retry_count", 3)

verify { crashlytics.setCustomKey("retry_count", 3) }
}

@Test
fun `safeSetCustomKey int overload swallows exception when Crashlytics unavailable`() {
every { FirebaseCrashlytics.getInstance() } throws RuntimeException("GMS not available")

FirebaseUtils.safeSetCustomKey("key", 42)
// No exception should propagate
}

// --- safeSetCustomKey(String, Boolean) tests ---

@Test
fun `safeSetCustomKey sets boolean key when Crashlytics is available`() {
every { crashlytics.setCustomKey(any<String>(), any<Boolean>()) } just runs

FirebaseUtils.safeSetCustomKey("is_premium", true)

verify { crashlytics.setCustomKey("is_premium", true) }
}

@Test
fun `safeSetCustomKey boolean overload swallows exception when Crashlytics unavailable`() {
every { FirebaseCrashlytics.getInstance() } throws RuntimeException("GMS not available")

FirebaseUtils.safeSetCustomKey("key", false)
// No exception should propagate
}

@Test
fun `safeSetCustomKey boolean overload swallows exception when setCustomKey throws`() {
every { crashlytics.setCustomKey(any<String>(), any<Boolean>()) } throws RuntimeException("crash")

FirebaseUtils.safeSetCustomKey("key", true)
// No exception should propagate
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
package com.chamika.dashtune.auth

import android.accounts.Account
import android.accounts.AccountManager
import android.os.Bundle
import io.mockk.every
import io.mockk.mockk
import io.mockk.mockkStatic
import org.junit.Assert.assertEquals
import org.junit.Assert.assertFalse
import org.junit.Assert.assertNotNull
import org.junit.Assert.assertNull
import org.junit.Assert.assertTrue
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.robolectric.RobolectricTestRunner
import org.robolectric.RuntimeEnvironment

@RunWith(RobolectricTestRunner::class)
class AuthenticatorTest {

private val context get() = RuntimeEnvironment.getApplication()
private lateinit var accountManager: AccountManager
private lateinit var authenticator: Authenticator

@Before
fun setUp() {
accountManager = mockk(relaxed = true)
mockkStatic(AccountManager::class)
every { AccountManager.get(context) } returns accountManager
authenticator = Authenticator(context)
}

// --- constants ---

@Test
fun `ACCOUNT_TYPE is the app package name`() {
assertEquals("com.chamika.dashtune", Authenticator.ACCOUNT_TYPE)
}

@Test
fun `AUTHTOKEN_TYPE equals ACCOUNT_TYPE`() {
assertEquals(Authenticator.ACCOUNT_TYPE, Authenticator.AUTHTOKEN_TYPE)
}

// --- editProperties ---

@Test(expected = UnsupportedOperationException::class)
fun `editProperties throws UnsupportedOperationException`() {
authenticator.editProperties(null, Authenticator.ACCOUNT_TYPE)
}

// --- addAccount ---

@Test
fun `addAccount returns empty Bundle`() {
val result = authenticator.addAccount(null, Authenticator.ACCOUNT_TYPE, null, null, null)

assertNotNull(result)
assertTrue(result.isEmpty)
}

// --- confirmCredentials ---

@Test
fun `confirmCredentials returns null`() {
val account = Account("user", Authenticator.ACCOUNT_TYPE)
assertNull(authenticator.confirmCredentials(null, account, null))
}

// --- getAuthToken ---

@Test
fun `getAuthToken returns error bundle when authTokenType is invalid`() {
val account = Account("user", Authenticator.ACCOUNT_TYPE)
val result = authenticator.getAuthToken(null, account, "wrong.type", null)

assertEquals("invalid auth token type", result.getString(AccountManager.KEY_ERROR_MESSAGE))
}

@Test
fun `getAuthToken returns error bundle when account is null`() {
val result = authenticator.getAuthToken(null, null, Authenticator.AUTHTOKEN_TYPE, null)

assertEquals("account must not be null", result.getString(AccountManager.KEY_ERROR_MESSAGE))
}

@Test
fun `getAuthToken returns token bundle when password exists`() {
val account = Account("testuser", Authenticator.ACCOUNT_TYPE)
every { accountManager.getPassword(account) } returns "stored-token"

val result = authenticator.getAuthToken(null, account, Authenticator.AUTHTOKEN_TYPE, null)

assertEquals("testuser", result.getString(AccountManager.KEY_ACCOUNT_NAME))
assertEquals(Authenticator.ACCOUNT_TYPE, result.getString(AccountManager.KEY_ACCOUNT_TYPE))
assertEquals("stored-token", result.getString(AccountManager.KEY_AUTHTOKEN))
}

@Test
fun `getAuthToken returns intent bundle when no password stored`() {
val account = Account("testuser", Authenticator.ACCOUNT_TYPE)
every { accountManager.getPassword(account) } returns null

val result = authenticator.getAuthToken(null, account, Authenticator.AUTHTOKEN_TYPE, null)

assertNotNull(result.getParcelable(AccountManager.KEY_INTENT, android.content.Intent::class.java))
}

@Test
fun `getAuthToken with valid token does not include error message`() {
val account = Account("user", Authenticator.ACCOUNT_TYPE)
every { accountManager.getPassword(account) } returns "valid-token"

val result = authenticator.getAuthToken(null, account, Authenticator.AUTHTOKEN_TYPE, null)

assertNull(result.getString(AccountManager.KEY_ERROR_MESSAGE))
}

// --- getAuthTokenLabel ---

@Test
fun `getAuthTokenLabel returns null`() {
assertNull(authenticator.getAuthTokenLabel(Authenticator.AUTHTOKEN_TYPE))
}

// --- updateCredentials ---

@Test
fun `updateCredentials returns null`() {
val account = Account("user", Authenticator.ACCOUNT_TYPE)
assertNull(authenticator.updateCredentials(null, account, null, null))
}

// --- hasFeatures ---

@Test
fun `hasFeatures returns bundle with KEY_BOOLEAN_RESULT false`() {
val account = Account("user", Authenticator.ACCOUNT_TYPE)
val result = authenticator.hasFeatures(null, account, emptyArray())

assertFalse(result.getBoolean(AccountManager.KEY_BOOLEAN_RESULT))
}
}
Loading
Loading