Skip to content

Commit

Permalink
add apphud autotest
Browse files Browse the repository at this point in the history
faf95b2d822a153d590b3b08fc021687a3792e1e
  • Loading branch information
KondratyonokNikita committed Sep 9, 2024
1 parent d399fbc commit 80fd70b
Show file tree
Hide file tree
Showing 57 changed files with 1,685 additions and 416 deletions.
40 changes: 29 additions & 11 deletions .mapping.json

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package io.appmetrica.analytics.impl.modules.client

import io.appmetrica.analytics.coreapi.internal.identifiers.SdkIdentifiers
import io.appmetrica.analytics.modulesapi.internal.client.ModuleServiceConfig

class ClientModuleServiceConfigModel<T>(
override val identifiers: SdkIdentifiers,
override val featuresConfig: T
) : ModuleServiceConfig<T>
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package io.appmetrica.analytics.impl.modules.client

import android.os.Bundle
import io.appmetrica.analytics.coreapi.internal.identifiers.SdkIdentifiers
import io.appmetrica.analytics.modulesapi.internal.client.ServiceConfigExtensionConfiguration

class ClientModuleServiceConfigModelFactory {

fun <T : Any> createClientModuleServiceConfigModel(
bundle: Bundle,
moduleIdentifier: String,
identifiers: SdkIdentifiers,
extensionConfiguration: ServiceConfigExtensionConfiguration<T>
): ClientModuleServiceConfigModel<T?>? {
return bundle.getBundle(moduleIdentifier)?.let { moduleConfig ->
ClientModuleServiceConfigModel(
identifiers = identifiers,
featuresConfig = extensionConfiguration.getBundleConverter().fromBundle(moduleConfig)
)
}
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package io.appmetrica.analytics.impl.modules.client

import android.os.Bundle
import io.appmetrica.analytics.coreapi.internal.identifiers.SdkIdentifiers
import io.appmetrica.analytics.impl.modules.client.context.CoreClientContext
import io.appmetrica.analytics.impl.selfreporting.AppMetricaSelfReportFacade
import io.appmetrica.analytics.logger.appmetrica.internal.DebugLogger
Expand All @@ -15,6 +16,7 @@ internal class ClientModulesController :

private val tag = "[ClientModulesController]"

private val clientModuleServiceConfigModelFactory = ClientModuleServiceConfigModelFactory()
private val modules = CopyOnWriteArrayList<ModuleClientEntryPoint<Any>>()
private var clientContext: CoreClientContext? = null

Expand Down Expand Up @@ -67,22 +69,35 @@ internal class ClientModulesController :
return clientContext?.moduleAdRevenueContext?.adRevenueProcessorsHolder
}

fun notifyModulesWithConfig(bundle: Bundle?) {
fun notifyModulesWithConfig(bundle: Bundle?, identifiers: SdkIdentifiers) {
if (bundle == null) {
return
}
modules.forEach { module ->
module.clientConfigExtension?.clientConfigListener?.let { listener ->
bundle?.getBundle(module.identifier)?.let { config ->
listener.onConfigReceived(config)
try {
module.serviceConfigExtensionConfiguration?.let { extension ->
val listener = extension.getServiceConfigUpdateListener()
clientModuleServiceConfigModelFactory.createClientModuleServiceConfigModel(
bundle = bundle,
moduleIdentifier = module.identifier,
identifiers = identifiers,
extensionConfiguration = extension
)?.let { config ->
DebugLogger.info(tag, "Notify module ${module.identifier} with config $config")
listener.onServiceConfigUpdated(config)
}
}
} catch (e: Throwable) {
DebugLogger.error(
"$tag [${module.identifier}]",
e,
"unhandled exception when notifying with config"
)
reportSelfErrorEvent(module.identifier, "notifyModulesWithConfig", e)
}
}
}

fun doModulesNeedConfig(): Boolean {
return modules.any { module ->
module.clientConfigExtension?.doesModuleNeedConfig() ?: false
}
}

private fun reportSelfErrorEvent(moduleIdentifier: String, tag: String, throwable: Throwable) {
AppMetricaSelfReportFacade.getReporter().reportEvent(
"client_module_errors",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import io.appmetrica.analytics.impl.modules.client.CompositeModuleAdRevenueProce
import io.appmetrica.analytics.modulesapi.internal.client.ClientStorageProvider

class ClientContextImpl(
context: Context
override val context: Context
) : CoreClientContext {

override val moduleAdRevenueContext: CoreModuleAdRevenueContext =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,17 @@ import io.appmetrica.analytics.impl.modules.PreferencesBasedModuleEntryPoint

class DefaultServiceComponentsInitializer : ServiceComponentsInitializer {

// order may be important
private val moduleEntryPoints = listOf(
"io.appmetrica.analytics.remotepermissions.internal.RemotePermissionsModuleEntryPoint",
"io.appmetrica.analytics.apphud.internal.ApphudServiceModuleEntryPoint",
)

override fun onCreate(context: Context) {
GlobalServiceLocator.getInstance().moduleEntryPointsRegister.register(
ConstantModuleEntryPointProvider(
"io.appmetrica.analytics.remotepermissions.internal.RemotePermissionsModuleEntryPoint"
),
*moduleEntryPoints.map { ConstantModuleEntryPointProvider(it) }.toTypedArray()
)
GlobalServiceLocator.getInstance().moduleEntryPointsRegister.register(
PreferencesBasedModuleEntryPoint(
context,
"io.appmetrica.analytics.modules.ads",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
import io.appmetrica.analytics.AdvIdentifiersResult;
import io.appmetrica.analytics.StartupParamsCallback;
import io.appmetrica.analytics.StartupParamsItem;
import io.appmetrica.analytics.coreapi.internal.identifiers.SdkIdentifiers;
import io.appmetrica.analytics.coreutils.internal.collection.CollectionUtils;
import io.appmetrica.analytics.impl.ClientIdentifiersHolder;
import io.appmetrica.analytics.impl.ClientServiceLocator;
Expand Down Expand Up @@ -68,6 +69,8 @@ public class StartupHelper implements StartupIdentifiersProvider, IServerTimeOff
private final Map<StartupParamsCallback, List<String>> mStartupParamsCallbacks = new WeakHashMap<>();

private Map<String, String> mClientClids;
@VisibleForTesting
boolean initialStartupSent = false;

public StartupHelper(@NonNull Context context,
final ReportsHandler reportsHandler,
Expand Down Expand Up @@ -178,13 +181,21 @@ private void updateAllParamsByReceiver(@NonNull Bundle resultData) {
DebugLogger.INSTANCE.info(TAG, "UpdateAllParamsByReceiver: %s", resultData);
ClientIdentifiersHolder clientIdentifiersHolder = new ClientIdentifiersHolder(resultData);
mStartupParams.updateAllParamsByReceiver(clientIdentifiersHolder);
notifyModulesWithConfig(clientIdentifiersHolder.getModulesConfig());
notifyModulesWithConfig(
clientIdentifiersHolder.getModulesConfig(),
new SdkIdentifiers(
clientIdentifiersHolder.getUuid().id,
clientIdentifiersHolder.getDeviceId().id,
clientIdentifiersHolder.getDeviceIdHash().id
)
);
notifyCallbacksIfValid();
}

public void sendStartupIfNeeded() {
synchronized (mStartupParamsLock) {
if (mStartupParams.shouldSendStartup()) {
if (!initialStartupSent || mStartupParams.shouldSendStartup()) {
initialStartupSent = true;
DebugLogger.INSTANCE.info(TAG, "Send startup event");
sendStartupEvent(mClientClids);
}
Expand Down Expand Up @@ -396,8 +407,8 @@ private void unregisterIdentifiersCallback(final StartupParamsCallback callback)
}
}

private void notifyModulesWithConfig(@Nullable Bundle bundle) {
ClientServiceLocator.getInstance().getModulesController().notifyModulesWithConfig(bundle);
private void notifyModulesWithConfig(@Nullable Bundle bundle, @NonNull SdkIdentifiers identifiers) {
ClientServiceLocator.getInstance().getModulesController().notifyModulesWithConfig(bundle, identifiers);
}

@VisibleForTesting
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -212,20 +212,18 @@ synchronized boolean shouldSendStartup(@NonNull List<String> identifiers) {
);
boolean advIdentifiersRequested = listContainsAdvIdentifiers(identifiers);
boolean outdated = StartupRequiredUtils.isOutdated(nextStartupTime);
boolean modulesNeedConfig = ClientServiceLocator.getInstance().getModulesController().doModulesNeedConfig();
boolean result = notAllIdentifiers || advIdentifiersRequested || outdated ||
mClientClidsChangedAfterLastIdentifiersUpdate || modulesNeedConfig;
mClientClidsChangedAfterLastIdentifiersUpdate;

DebugLogger.INSTANCE.info(
TAG,
"shouldSendStartup = %b: notAllIdentifiers = %b; advIdentifiersRequested = %b, outdated = %b; " +
"mClientClidsChanged = %b; modulesNeedConfig = %b, identifiers = %s",
"mClientClidsChanged = %b; identifiers = %s",
result,
notAllIdentifiers,
advIdentifiersRequested,
outdated,
mClientClidsChangedAfterLastIdentifiersUpdate,
modulesNeedConfig,
identifiers
);

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
package io.appmetrica.analytics.impl.modules.client

import android.os.Bundle
import io.appmetrica.analytics.assertions.ObjectPropertyAssertions
import io.appmetrica.analytics.coreapi.internal.identifiers.SdkIdentifiers
import io.appmetrica.analytics.modulesapi.internal.client.BundleToServiceConfigConverter
import io.appmetrica.analytics.modulesapi.internal.client.ServiceConfigExtensionConfiguration
import io.appmetrica.analytics.testutils.CommonTest
import org.assertj.core.api.Assertions.assertThat
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.kotlin.doReturn
import org.mockito.kotlin.mock
import org.robolectric.RobolectricTestRunner

@RunWith(RobolectricTestRunner::class)
class ClientModuleServiceConfigModelFactoryTest : CommonTest() {

private val moduleConfigBundle = Bundle()
private val moduleIdentifier = "some_identifier"
private val bundle = Bundle().also {
it.putBundle(moduleIdentifier, moduleConfigBundle)
}
private val identifiers: SdkIdentifiers = mock()
private val moduleConfig: TestModuleConfig = mock()
private val bundleParser: BundleToServiceConfigConverter<TestModuleConfig> = mock {
on { fromBundle(moduleConfigBundle) } doReturn moduleConfig
}
private val extension: ServiceConfigExtensionConfiguration<TestModuleConfig> = mock {
on { getBundleConverter() } doReturn bundleParser
}

private val factory: ClientModuleServiceConfigModelFactory by setUp { ClientModuleServiceConfigModelFactory() }

@Test
fun createClientModuleServiceConfigModel() {
val config = factory.createClientModuleServiceConfigModel(
bundle,
moduleIdentifier,
identifiers,
extension
)
ObjectPropertyAssertions(config)
.checkField("identifiers", identifiers)
.checkField("featuresConfig", moduleConfig)
.checkAll()
}

@Test
fun createClientModuleServiceConfigModelIfNoSuchModule() {
val config = factory.createClientModuleServiceConfigModel(
bundle,
"another_module_identifier",
identifiers,
extension
)

assertThat(config).isNull()
}
}

private class TestModuleConfig
Original file line number Diff line number Diff line change
@@ -1,14 +1,17 @@
package io.appmetrica.analytics.impl.modules.client

import android.os.Bundle
import io.appmetrica.analytics.coreapi.internal.identifiers.SdkIdentifiers
import io.appmetrica.analytics.impl.IReporterExtended
import io.appmetrica.analytics.impl.modules.client.context.CoreClientContext
import io.appmetrica.analytics.impl.modules.client.context.CoreModuleAdRevenueContext
import io.appmetrica.analytics.impl.selfreporting.AppMetricaSelfReportFacade
import io.appmetrica.analytics.modulesapi.internal.client.ClientConfigExtension
import io.appmetrica.analytics.modulesapi.internal.client.ClientConfigListener
import io.appmetrica.analytics.modulesapi.internal.client.BundleToServiceConfigConverter
import io.appmetrica.analytics.modulesapi.internal.client.ModuleClientEntryPoint
import io.appmetrica.analytics.modulesapi.internal.client.ServiceConfigExtensionConfiguration
import io.appmetrica.analytics.modulesapi.internal.client.ServiceConfigUpdateListener
import io.appmetrica.analytics.testutils.CommonTest
import io.appmetrica.analytics.testutils.constructionRule
import io.appmetrica.analytics.testutils.on
import io.appmetrica.analytics.testutils.staticRule
import org.assertj.core.api.Assertions.assertThat
Expand All @@ -29,23 +32,27 @@ import org.robolectric.RobolectricTestRunner
internal class ClientModulesControllerTest : CommonTest() {

private val firstModuleIdentifier = "firstModuleIdentifier"
private val firstClientConfigListener: ClientConfigListener = mock()
private val firstClientConfigExtension: ClientConfigExtension = mock {
on { clientConfigListener } doReturn firstClientConfigListener
private val firstBundleParser: BundleToServiceConfigConverter<Any> = mock()
private val firstServiceConfigUpdateListener: ServiceConfigUpdateListener<Any> = mock()
private val firstServiceConfigExtensionConfiguration: ServiceConfigExtensionConfiguration<Any> = mock {
on { getServiceConfigUpdateListener() } doReturn firstServiceConfigUpdateListener
on { getBundleConverter() } doReturn firstBundleParser
}
private val firstModule = mock<ModuleClientEntryPoint<Any>> {
on { identifier } doReturn firstModuleIdentifier
on { clientConfigExtension } doReturn firstClientConfigExtension
on { serviceConfigExtensionConfiguration } doReturn firstServiceConfigExtensionConfiguration
}

private val secondModuleIdentifier = "secondModuleIdentifier"
private val secondClientConfigListener: ClientConfigListener = mock()
private val secondClientConfigExtension: ClientConfigExtension = mock {
on { clientConfigListener } doReturn secondClientConfigListener
private val secondBundleParser: BundleToServiceConfigConverter<Any> = mock()
private val secondServiceConfigUpdateListener: ServiceConfigUpdateListener<Any> = mock()
private val secondServiceConfigExtensionConfiguration: ServiceConfigExtensionConfiguration<Any> = mock {
on { getServiceConfigUpdateListener() } doReturn secondServiceConfigUpdateListener
on { getBundleConverter() } doReturn secondBundleParser
}
private val secondModule = mock<ModuleClientEntryPoint<Any>> {
on { identifier } doReturn secondModuleIdentifier
on { clientConfigExtension } doReturn secondClientConfigExtension
on { serviceConfigExtensionConfiguration } doReturn secondServiceConfigExtensionConfiguration
}

private val initException = RuntimeException("initException")
Expand All @@ -62,6 +69,8 @@ internal class ClientModulesControllerTest : CommonTest() {
val selfReporterFacadeMockedStaticRule = staticRule<AppMetricaSelfReportFacade> {
on { AppMetricaSelfReportFacade.getReporter() } doReturn selfReporter
}
@get:Rule
val clientModuleServiceConfigModelFactoryRule = constructionRule<ClientModuleServiceConfigModelFactory>()

private val modulesController: ClientModulesController by setUp { ClientModulesController() }

Expand Down Expand Up @@ -130,27 +139,36 @@ internal class ClientModulesControllerTest : CommonTest() {

@Test
fun notifyModulesWithConfig() {
val secondBundle = Bundle()
val bundle = Bundle().also {
it.putBundle(secondModuleIdentifier, secondBundle)
}
val bundle = Bundle()
val identifiers: SdkIdentifiers = mock()
whenever(clientModuleServiceConfigModelFactory().createClientModuleServiceConfigModel(
bundle,
firstModuleIdentifier,
identifiers,
firstServiceConfigExtensionConfiguration
)).thenReturn(null)
val secondConfig: ClientModuleServiceConfigModel<Any?> = mock()
whenever(clientModuleServiceConfigModelFactory().createClientModuleServiceConfigModel(
bundle,
secondModuleIdentifier,
identifiers,
secondServiceConfigExtensionConfiguration
)).thenReturn(secondConfig)

modulesController.registerModule(firstModule)
modulesController.registerModule(secondModule)

modulesController.notifyModulesWithConfig(bundle)
modulesController.notifyModulesWithConfig(bundle, identifiers)

verify(firstModule.clientConfigExtension!!.clientConfigListener, never()).onConfigReceived(any())
verify(secondModule.clientConfigExtension!!.clientConfigListener).onConfigReceived(secondBundle)
verify(firstModule.serviceConfigExtensionConfiguration!!.getServiceConfigUpdateListener(), never())
.onServiceConfigUpdated(any())
verify(secondModule.serviceConfigExtensionConfiguration!!.getServiceConfigUpdateListener())
.onServiceConfigUpdated(secondConfig)
}

@Test
fun doModulesNeedConfig() {
whenever(firstModule.clientConfigExtension!!.doesModuleNeedConfig()).thenReturn(false)
whenever(secondModule.clientConfigExtension!!.doesModuleNeedConfig()).thenReturn(true)

modulesController.registerModule(firstModule)
modulesController.registerModule(secondModule)

assertThat(modulesController.doModulesNeedConfig()).isTrue()
private fun clientModuleServiceConfigModelFactory(): ClientModuleServiceConfigModelFactory {
assertThat(clientModuleServiceConfigModelFactoryRule.constructionMock.constructed()).hasSize(1)
assertThat(clientModuleServiceConfigModelFactoryRule.argumentInterceptor.flatArguments()).isEmpty()
return clientModuleServiceConfigModelFactoryRule.constructionMock.constructed().first()
}
}
Loading

0 comments on commit 80fd70b

Please sign in to comment.