-
Notifications
You must be signed in to change notification settings - Fork 205
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
345d05e
commit c7f409a
Showing
9 changed files
with
430 additions
and
57 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
167 changes: 167 additions & 0 deletions
167
bugsnag-android-core/src/androidTest/java/com/bugsnag/android/DeviceIdStoreTest.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,167 @@ | ||
package com.bugsnag.android | ||
|
||
import android.content.Context | ||
import androidx.test.core.app.ApplicationProvider | ||
import junit.framework.TestCase.assertNull | ||
import org.junit.Assert.assertEquals | ||
import org.junit.Before | ||
import org.junit.Test | ||
import java.io.File | ||
import java.util.UUID | ||
import java.util.concurrent.CountDownLatch | ||
import java.util.concurrent.Executors | ||
import java.util.concurrent.atomic.AtomicReference | ||
|
||
internal class DeviceIdStoreTest { | ||
|
||
lateinit var file: File | ||
lateinit var storageDir: File | ||
|
||
private val uuidProvider = { | ||
UUID.fromString("ab0c1482-2ffe-11eb-adc1-0242ac120002") | ||
} | ||
private val diffUuidProvider = { | ||
UUID.fromString("d9901bff-2ffe-11eb-adc1-0242ac120002") | ||
} | ||
|
||
@Before | ||
fun setUp() { | ||
val ctx = ApplicationProvider.getApplicationContext<Context>() | ||
storageDir = ctx.cacheDir | ||
file = File(storageDir, "device.json") | ||
file.delete() | ||
} | ||
|
||
/** | ||
* A file should be created if it does not already exist | ||
*/ | ||
@Test | ||
fun nonExistentFile() { | ||
val nonExistentFile = File(storageDir, "foo") | ||
nonExistentFile.delete() | ||
val store = DeviceIdStore(nonExistentFile, NoopLogger) | ||
val deviceId = store.loadDeviceId( | ||
NoopLogger, | ||
uuidProvider | ||
) | ||
requireNotNull(deviceId) | ||
assertEquals("ab0c1482-2ffe-11eb-adc1-0242ac120002", deviceId) | ||
} | ||
|
||
/** | ||
* An empty file should be overwritten with a new device ID | ||
*/ | ||
@Test | ||
fun emptyFile() { | ||
val store = DeviceIdStore(file, NoopLogger) | ||
val deviceId = store.loadDeviceId(NoopLogger, uuidProvider) | ||
requireNotNull(deviceId) | ||
assertEquals("ab0c1482-2ffe-11eb-adc1-0242ac120002", deviceId) | ||
} | ||
|
||
/** | ||
* A file of the incorrect length should be overwritten with a new device ID | ||
*/ | ||
@Test | ||
fun incorrectFileLength() { | ||
val store = DeviceIdStore(file, NoopLogger) | ||
val deviceId = store.loadDeviceId(NoopLogger, uuidProvider) | ||
requireNotNull(deviceId) | ||
assertEquals("ab0c1482-2ffe-11eb-adc1-0242ac120002", deviceId) | ||
} | ||
|
||
/** | ||
* A file of the correct length with invalid contents should be overwritten with a new device ID | ||
*/ | ||
@Test | ||
fun invalidFileContents() { | ||
file.writeText("{\"hamster\": 2}") | ||
val store = DeviceIdStore(file, NoopLogger) | ||
val deviceId = store.loadDeviceId(NoopLogger, uuidProvider) | ||
requireNotNull(deviceId) | ||
assertEquals("ab0c1482-2ffe-11eb-adc1-0242ac120002", deviceId) | ||
} | ||
|
||
/** | ||
* A valid file should not be overwritten with a new device ID | ||
*/ | ||
@Test | ||
fun validFileContents() { | ||
file.writeText("{\"id\": \"24c51482-2ffe-11eb-adc1-0242ac120002\"}") | ||
val store = DeviceIdStore(file, NoopLogger) | ||
|
||
// device ID is loaded without writing file | ||
assertEquals( | ||
"24c51482-2ffe-11eb-adc1-0242ac120002", | ||
store.loadDeviceId(NoopLogger, uuidProvider) | ||
) | ||
// same device ID is retrieved as before | ||
assertEquals( | ||
"24c51482-2ffe-11eb-adc1-0242ac120002", | ||
store.loadDeviceId(NoopLogger, diffUuidProvider) | ||
) | ||
} | ||
|
||
/** | ||
* A non-writable file does not crash the app | ||
*/ | ||
@Test | ||
fun nonWritableFile() { | ||
val nonReadableFile = File(storageDir, "foo").apply { | ||
delete() | ||
createNewFile() | ||
setWritable(false) | ||
} | ||
val store = DeviceIdStore(nonReadableFile, NoopLogger) | ||
val deviceId = store.loadDeviceId(NoopLogger, uuidProvider) | ||
assertNull(deviceId) | ||
} | ||
|
||
/** | ||
* The device ID store should take out a file lock to prevent concurrent writes to the file. | ||
*/ | ||
@Test(timeout = 2000) | ||
fun fileLockUsed() { | ||
val store = DeviceIdStore(file, NoopLogger) | ||
val latch = CountDownLatch(1) | ||
|
||
// the main thread & executor thread race to persist a device ID first. | ||
// this asserts that the first value written to disk is used | ||
// and thus indirectly verifies that the file is appropriately synchronized. | ||
val firstPersistedValue = AtomicReference<String>() | ||
var bgThreadValue: String? = null | ||
|
||
// persist a new device ID on a background thread | ||
Executors.newSingleThreadExecutor().submit { | ||
bgThreadValue = store.loadDeviceId(NoopLogger, uuidProvider) | ||
firstPersistedValue.compareAndSet(null, bgThreadValue) | ||
latch.countDown() | ||
} | ||
|
||
// persist a new device ID on the main thread | ||
val deviceId = store.loadDeviceId(NoopLogger, diffUuidProvider) | ||
firstPersistedValue.compareAndSet(null, deviceId) | ||
latch.await() | ||
|
||
// validate that the device ID is consistent for each. | ||
// the device ID of whichever thread won the race first should be used. | ||
val expected = firstPersistedValue.get() | ||
assertEquals(expected, deviceId) | ||
assertEquals(expected, bgThreadValue) | ||
} | ||
|
||
@Test | ||
fun sharedPrefMigration() { | ||
val store = DeviceIdStore(file, NoopLogger) | ||
val context = ApplicationProvider.getApplicationContext<Context>() | ||
|
||
val prefs = | ||
context.getSharedPreferences("com.bugsnag.android", Context.MODE_PRIVATE) | ||
prefs.edit().putString("install.iud", "55670bff-9024-fc0a-b392-0242ac88ccd9").commit() | ||
|
||
val prefDeviceId = SharedPrefMigrator(context).loadDeviceId() | ||
val storeDeviceId = store.loadDeviceId(context) | ||
assertEquals("55670bff-9024-fc0a-b392-0242ac88ccd9", storeDeviceId) | ||
assertEquals(prefDeviceId, storeDeviceId) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.