Merge pull request #63 from Dexterp37/asyncAPI

Expose an internal sync API and make the public API async
This commit is contained in:
Alessio Placitelli 2019-05-15 14:48:16 +02:00 коммит произвёл GitHub
Родитель bd2abd819f 9c806ee175
Коммит 24760b4bbb
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
10 изменённых файлов: 167 добавлений и 42 удалений

Просмотреть файл

@ -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 {