зеркало из https://github.com/mozilla/glean.git
Merge pull request #63 from Dexterp37/asyncAPI
Expose an internal sync API and make the public API async
This commit is contained in:
Коммит
24760b4bbb
|
@ -7,7 +7,7 @@
|
|||
// https://github.com/mozilla/application-services/blob/84e077d1534dc287bbd472da658ce22eea5af032/build.gradle
|
||||
|
||||
buildscript {
|
||||
ext.kotlin_version = '1.3.30'
|
||||
ext.kotlin_version = '1.3.31'
|
||||
ext.android_components_version = '0.50.0'
|
||||
ext.jna_version = '5.2.0'
|
||||
|
||||
|
|
|
@ -122,6 +122,7 @@ dependencies {
|
|||
implementation "net.java.dev.jna:jna:$jna_version@aar"
|
||||
|
||||
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
|
||||
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.2.1"
|
||||
|
||||
implementation "androidx.annotation:annotation:1.0.2"
|
||||
|
||||
|
|
|
@ -0,0 +1,74 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
package mozilla.telemetry.glean
|
||||
|
||||
import androidx.annotation.VisibleForTesting
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Job
|
||||
import kotlinx.coroutines.ObsoleteCoroutinesApi
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.newSingleThreadContext
|
||||
import kotlinx.coroutines.runBlocking
|
||||
|
||||
@ObsoleteCoroutinesApi
|
||||
internal object Dispatchers {
|
||||
class WaitableCoroutineScope(val coroutineScope: CoroutineScope) {
|
||||
// When true, jobs will be run synchronously
|
||||
internal var testingMode = false
|
||||
|
||||
/**
|
||||
* Launch a block of work asyncronously.
|
||||
*
|
||||
* If [setTestingMode] has enabled testing mode, the work will run
|
||||
* synchronously.
|
||||
*
|
||||
* @return [Job], or null if run synchronously.
|
||||
*/
|
||||
fun launch(
|
||||
block: suspend CoroutineScope.() -> Unit
|
||||
): Job? {
|
||||
return if (testingMode) {
|
||||
runBlocking {
|
||||
block()
|
||||
}
|
||||
null
|
||||
} else {
|
||||
coroutineScope.launch(block = block)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function to ensure Glean is being used in testing mode and async
|
||||
* jobs are being run synchronously. This should be called from every method
|
||||
* in the testing API to make sure that the results of the main API can be
|
||||
* tested as expected.
|
||||
*/
|
||||
@VisibleForTesting(otherwise = VisibleForTesting.NONE)
|
||||
fun assertInTestingMode() {
|
||||
assert(
|
||||
testingMode
|
||||
) {
|
||||
"To use the testing API, Glean must be in testing mode by calling " +
|
||||
"Glean.enableTestingMode() (for example, in a @Before method)."
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Enable testing mode, which makes all of the Glean public API synchronous.
|
||||
*
|
||||
* @param enabled whether or not to enable the testing mode
|
||||
*/
|
||||
@VisibleForTesting(otherwise = VisibleForTesting.NONE)
|
||||
fun setTestingMode(enabled: Boolean) {
|
||||
testingMode = enabled
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A coroutine scope to make it easy to dispatch API calls off the main thread.
|
||||
* This needs to be a `var` so that our tests can override this.
|
||||
*/
|
||||
var API = WaitableCoroutineScope(CoroutineScope(newSingleThreadContext("GleanAPIPool")))
|
||||
}
|
|
@ -8,6 +8,7 @@ import android.util.Log
|
|||
import android.content.Context
|
||||
import android.content.pm.PackageManager
|
||||
import android.os.Build
|
||||
import androidx.annotation.VisibleForTesting
|
||||
import mozilla.telemetry.glean.utils.getLocaleTag
|
||||
import java.io.File
|
||||
import mozilla.telemetry.glean.rust.LibGleanFFI
|
||||
|
@ -63,23 +64,22 @@ open class GleanInternalAPI internal constructor () {
|
|||
*/
|
||||
private fun initializeCoreMetrics(applicationContext: Context) {
|
||||
// Set a few more metrics that will be sent as part of every ping.
|
||||
// TODO: we should make sure to store the data below before any ping
|
||||
// is generated and sent. In a-c's Glean, we rely on the StorageEngine(s)
|
||||
// access to do so. Once we make the metric type API async, this won't work
|
||||
// anymore.
|
||||
GleanBaseline.locale.set(getLocaleTag())
|
||||
GleanInternalMetrics.os.set("Android")
|
||||
// Please note that the following metrics must be set synchronously, so
|
||||
// that they are guaranteed to be available with the first ping that is
|
||||
// generated. We use an internal only API to do that.
|
||||
GleanBaseline.locale.setSync(getLocaleTag())
|
||||
GleanInternalMetrics.os.setSync("Android")
|
||||
// https://developer.android.com/reference/android/os/Build.VERSION
|
||||
GleanInternalMetrics.androidSdkVersion.set(Build.VERSION.SDK_INT.toString())
|
||||
GleanInternalMetrics.osVersion.set(Build.VERSION.RELEASE)
|
||||
GleanInternalMetrics.androidSdkVersion.setSync(Build.VERSION.SDK_INT.toString())
|
||||
GleanInternalMetrics.osVersion.setSync(Build.VERSION.RELEASE)
|
||||
// https://developer.android.com/reference/android/os/Build
|
||||
GleanInternalMetrics.deviceManufacturer.set(Build.MANUFACTURER)
|
||||
GleanInternalMetrics.deviceModel.set(Build.MODEL)
|
||||
GleanInternalMetrics.architecture.set(Build.SUPPORTED_ABIS[0])
|
||||
GleanInternalMetrics.deviceManufacturer.setSync(Build.MANUFACTURER)
|
||||
GleanInternalMetrics.deviceModel.setSync(Build.MODEL)
|
||||
GleanInternalMetrics.architecture.setSync(Build.SUPPORTED_ABIS[0])
|
||||
|
||||
/*
|
||||
configuration.channel?.let {
|
||||
StringsStorageEngine.record(GleanInternalMetrics.appChannel, it)
|
||||
GleanInternalMetrics.appChannel.setSync(it)
|
||||
}*/
|
||||
|
||||
try {
|
||||
|
@ -87,9 +87,9 @@ open class GleanInternalAPI internal constructor () {
|
|||
applicationContext.packageName, 0
|
||||
)
|
||||
@Suppress("DEPRECATION")
|
||||
GleanInternalMetrics.appBuild.set(packageInfo.versionCode.toString())
|
||||
GleanInternalMetrics.appBuild.setSync(packageInfo.versionCode.toString())
|
||||
|
||||
GleanInternalMetrics.appDisplayVersion.set(
|
||||
GleanInternalMetrics.appDisplayVersion.setSync(
|
||||
packageInfo.versionName?.let { it } ?: "Unknown"
|
||||
)
|
||||
} catch (e: PackageManager.NameNotFoundException) {
|
||||
|
@ -152,6 +152,18 @@ open class GleanInternalAPI internal constructor () {
|
|||
LibGleanFFI.INSTANCE.glean_send_ping(handle, pingName)
|
||||
}
|
||||
|
||||
/**
|
||||
* Should be called from all users of the Glean testing API.
|
||||
*
|
||||
* This makes all asynchronous work synchronous so we can test the results of the
|
||||
* API synchronously.
|
||||
*/
|
||||
@VisibleForTesting(otherwise = VisibleForTesting.NONE)
|
||||
fun enableTestingMode() {
|
||||
@Suppress("EXPERIMENTAL_API_USAGE")
|
||||
Dispatchers.API.setTestingMode(enabled = true)
|
||||
}
|
||||
|
||||
/**
|
||||
* Test-only method to destroy the owned glean-core handle.
|
||||
*/
|
||||
|
|
|
@ -9,7 +9,7 @@ import com.sun.jna.StringArray
|
|||
import mozilla.telemetry.glean.Glean
|
||||
import mozilla.telemetry.glean.rust.LibGleanFFI
|
||||
|
||||
// import mozilla.components.service.glean.Dispatchers
|
||||
import mozilla.telemetry.glean.Dispatchers
|
||||
// import mozilla.components.service.glean.storages.CountersStorageEngine
|
||||
// import mozilla.components.support.base.log.logger.Logger
|
||||
|
||||
|
@ -55,17 +55,15 @@ class CounterMetricType(
|
|||
fun add(amount: Int = 1) {
|
||||
/*if (!shouldRecord(logger)) {
|
||||
return
|
||||
}
|
||||
}*/
|
||||
|
||||
@Suppress("EXPERIMENTAL_API_USAGE")
|
||||
Dispatchers.API.launch {
|
||||
// Delegate storing the new counter value to the storage engine.
|
||||
CountersStorageEngine.record(
|
||||
this@CounterMetricType,
|
||||
amount = amount
|
||||
)
|
||||
}*/
|
||||
LibGleanFFI.INSTANCE.glean_counter_add(Glean.handle, this.handle, amount.toLong())
|
||||
LibGleanFFI.INSTANCE.glean_counter_add(
|
||||
Glean.handle,
|
||||
this@CounterMetricType.handle,
|
||||
amount.toLong())
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -9,7 +9,7 @@ import com.sun.jna.StringArray
|
|||
import mozilla.telemetry.glean.Glean
|
||||
import mozilla.telemetry.glean.rust.LibGleanFFI
|
||||
|
||||
// import mozilla.components.service.glean.Dispatchers
|
||||
import mozilla.telemetry.glean.Dispatchers
|
||||
// import mozilla.components.service.glean.storages.StringsStorageEngine
|
||||
// import mozilla.components.support.base.log.logger.Logger
|
||||
|
||||
|
@ -54,16 +54,18 @@ class StringMetricType(
|
|||
fun set(value: String) {
|
||||
/*if (!shouldRecord(logger)) {
|
||||
return
|
||||
}
|
||||
}*/
|
||||
|
||||
@Suppress("EXPERIMENTAL_API_USAGE")
|
||||
Dispatchers.API.launch {
|
||||
// Delegate storing the string to the storage engine.
|
||||
StringsStorageEngine.record(
|
||||
this@StringMetricType,
|
||||
value = value
|
||||
)
|
||||
}*/
|
||||
setSync(value)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Internal only, synchronous API for setting a string value.
|
||||
*/
|
||||
internal fun setSync(value: String) {
|
||||
LibGleanFFI.INSTANCE.glean_string_set(Glean.handle, this.handle, value)
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,39 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
package mozilla.telemetry.glean
|
||||
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import org.junit.Test
|
||||
|
||||
import org.junit.Assert.assertEquals
|
||||
import org.junit.Assert.assertNotSame
|
||||
import org.junit.Assert.assertSame
|
||||
|
||||
class DispatchersTest {
|
||||
|
||||
@Test
|
||||
fun `API scope runs off the main thread`() {
|
||||
val mainThread = Thread.currentThread()
|
||||
var threadCanary = false
|
||||
@Suppress("EXPERIMENTAL_API_USAGE")
|
||||
Dispatchers.API.setTestingMode(false)
|
||||
|
||||
runBlocking {
|
||||
@Suppress("EXPERIMENTAL_API_USAGE")
|
||||
Dispatchers.API.launch {
|
||||
assertNotSame(mainThread, Thread.currentThread())
|
||||
// Use the canary bool to make sure this is getting called before
|
||||
// the test completes.
|
||||
assertEquals(false, threadCanary)
|
||||
threadCanary = true
|
||||
}!!.join()
|
||||
}
|
||||
|
||||
@Suppress("EXPERIMENTAL_API_USAGE")
|
||||
Dispatchers.API.setTestingMode(true)
|
||||
assertEquals(true, threadCanary)
|
||||
assertSame(mainThread, Thread.currentThread())
|
||||
}
|
||||
}
|
|
@ -118,7 +118,7 @@ internal fun resetGlean(
|
|||
// config: Configuration = Configuration(),
|
||||
clearStores: Boolean = true
|
||||
) {
|
||||
// Glean.enableTestingMode()
|
||||
Glean.enableTestingMode()
|
||||
|
||||
// We're using the WorkManager in a bunch of places, and Glean will crash
|
||||
// in tests without this line. Let's simply put it here.
|
||||
|
|
|
@ -10,9 +10,8 @@
|
|||
|
||||
package mozilla.telemetry.glean.private
|
||||
|
||||
// import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
// import kotlinx.coroutines.ObsoleteCoroutinesApi
|
||||
// import mozilla.components.service.glean.resetGlean
|
||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
import kotlinx.coroutines.ObsoleteCoroutinesApi
|
||||
import mozilla.telemetry.glean.resetGlean
|
||||
import org.junit.Assert.assertEquals
|
||||
import org.junit.Assert.assertFalse
|
||||
|
@ -24,8 +23,8 @@ import org.junit.runner.RunWith
|
|||
import org.robolectric.RobolectricTestRunner
|
||||
import java.lang.NullPointerException
|
||||
|
||||
// @ObsoleteCoroutinesApi
|
||||
// @ExperimentalCoroutinesApi
|
||||
@ObsoleteCoroutinesApi
|
||||
@ExperimentalCoroutinesApi
|
||||
@RunWith(RobolectricTestRunner::class)
|
||||
class CounterMetricTypeTest {
|
||||
|
||||
|
|
|
@ -10,8 +10,8 @@
|
|||
|
||||
package mozilla.telemetry.glean.private
|
||||
|
||||
// import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
// import kotlinx.coroutines.ObsoleteCoroutinesApi
|
||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
import kotlinx.coroutines.ObsoleteCoroutinesApi
|
||||
import mozilla.telemetry.glean.resetGlean
|
||||
import org.junit.Assert.assertEquals
|
||||
import org.junit.Assert.assertFalse
|
||||
|
@ -23,8 +23,8 @@ import org.junit.runner.RunWith
|
|||
import org.robolectric.RobolectricTestRunner
|
||||
import java.lang.NullPointerException
|
||||
|
||||
// @ObsoleteCoroutinesApi
|
||||
// @ExperimentalCoroutinesApi
|
||||
@ObsoleteCoroutinesApi
|
||||
@ExperimentalCoroutinesApi
|
||||
@RunWith(RobolectricTestRunner::class)
|
||||
class StringMetricTypeTest {
|
||||
|
||||
|
|
Загрузка…
Ссылка в новой задаче