From ecd5ef9b03dec0bff671c20c1589f866a8401e93 Mon Sep 17 00:00:00 2001 From: AnatolyPristensky Date: Mon, 21 Mar 2022 12:08:37 +0100 Subject: [PATCH 01/72] Start version 4.4.4 --- CHANGELOG.md | 4 ++++ versions.gradle | 4 ++-- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a7c12160b..0f73d8b0e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,9 @@ # App Center SDK for Android Change Log +## Version 4.4.4 (Under active development) + + ___ + ## Version 4.4.3 ### App Center Crashes diff --git a/versions.gradle b/versions.gradle index 344c2db86..fbaebf3a2 100644 --- a/versions.gradle +++ b/versions.gradle @@ -6,8 +6,8 @@ // Version constants ext { - versionCode = 67 - versionName = '4.4.3' + versionCode = 68 + versionName = '4.4.4' minSdkVersion = 21 compileSdkVersion = 31 targetSdkVersion = 31 From 1086031b3388bffbf2ef4f9a7fe9d8733f773adc Mon Sep 17 00:00:00 2001 From: Ivan Matkov Date: Mon, 18 Apr 2022 12:22:56 +0200 Subject: [PATCH 02/72] Update dependencies --- build.gradle | 9 +-------- sdk/build.gradle | 18 ++++++++++-------- 2 files changed, 11 insertions(+), 16 deletions(-) diff --git a/build.gradle b/build.gradle index c1815fbbe..1636969e8 100644 --- a/build.gradle +++ b/build.gradle @@ -18,7 +18,7 @@ buildscript { } dependencies { - classpath 'com.android.tools.build:gradle:4.0.1' + classpath 'com.android.tools.build:gradle:4.0.2' classpath 'com.github.dcendents:android-maven-gradle-plugin:2.0' classpath 'org.kt3k.gradle.plugin:coveralls-gradle-plugin:2.8.2' } @@ -28,13 +28,6 @@ allprojects { repositories { google() mavenCentral() - jcenter { - content { - - // https://youtrack.jetbrains.com/issue/IDEA-261387 - includeModule("org.jetbrains.trove4j", "trove4j") - } - } } tasks.withType(JavaCompile) { diff --git a/sdk/build.gradle b/sdk/build.gradle index d4ea5bd68..0db7d630b 100644 --- a/sdk/build.gradle +++ b/sdk/build.gradle @@ -11,10 +11,11 @@ allprojects { apply plugin: 'jacoco' jacoco { - toolVersion '0.7.9' + toolVersion '0.8.8' } tasks.withType(Test) { jacoco.includeNoLocationClasses = true + jacoco.excludes = ['jdk.internal.*'] } //noinspection GroovyAssignabilityCheck @@ -61,7 +62,7 @@ subprojects { testOptions { unitTests { all { - jvmArgs '-noverify' + jvmArgs '-noverify', '-Djdk.attach.allowAttachSelf=true' } returnDefaultValues = true } @@ -86,17 +87,18 @@ subprojects { } } - testImplementation 'org.powermock:powermock-api-mockito:1.6.5' - testImplementation 'org.powermock:powermock-module-junit4:1.6.5' - testImplementation 'org.powermock:powermock-module-junit4-rule-agent:1.6.5' + testImplementation 'org.powermock:powermock-api-mockito2:2.0.9' + testImplementation 'org.powermock:powermock-module-junit4:2.0.9' + testImplementation 'org.powermock:powermock-module-junit4-rule-agent:2.0.9' + testImplementation 'org.powermock:powermock-classloading-xstream:2.0.9' testImplementation "androidx.annotation:annotation:${ext.annotationVersion}" testImplementation project(':test') androidTestImplementation 'com.crittercism.dexmaker:dexmaker-dx:1.4' androidTestImplementation 'com.crittercism.dexmaker:dexmaker-mockito:1.4' - androidTestImplementation 'androidx.test:core:1.2.0' - androidTestImplementation 'androidx.test:rules:1.2.0' - androidTestImplementation 'androidx.test:runner:1.3.0' + androidTestImplementation 'androidx.test:core:1.4.0' + androidTestImplementation 'androidx.test:rules:1.4.0' + androidTestImplementation 'androidx.test:runner:1.4.0' androidTestImplementation ("androidx.annotation:annotation:${ext.annotationVersion}") { force = true } From 7b56dd917bef9e917a10bf277d264a08a09c7bc2 Mon Sep 17 00:00:00 2001 From: Ivan Matkov Date: Mon, 18 Apr 2022 13:23:16 +0200 Subject: [PATCH 03/72] Update gradle --- build.gradle | 3 +- gradle.properties | 3 + gradle/wrapper/gradle-wrapper.properties | 2 +- sdk/build.gradle | 86 ++---------------------- test/build.gradle | 6 +- 5 files changed, 14 insertions(+), 86 deletions(-) diff --git a/build.gradle b/build.gradle index 1636969e8..4f8cbbaff 100644 --- a/build.gradle +++ b/build.gradle @@ -18,8 +18,7 @@ buildscript { } dependencies { - classpath 'com.android.tools.build:gradle:4.0.2' - classpath 'com.github.dcendents:android-maven-gradle-plugin:2.0' + classpath 'com.android.tools.build:gradle:7.1.3' classpath 'org.kt3k.gradle.plugin:coveralls-gradle-plugin:2.8.2' } } diff --git a/gradle.properties b/gradle.properties index 5bac8ac50..b6513c826 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1 +1,4 @@ android.useAndroidX=true + +# Software Components will not be created automatically for Maven publishing from Android Gradle Plugin 8.0. +android.disableAutomaticComponentCreation=true diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 12b705be3..49c73ad4f 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -7,4 +7,4 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-6.1.1-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-7.2-all.zip diff --git a/sdk/build.gradle b/sdk/build.gradle index 0db7d630b..df3d66b51 100644 --- a/sdk/build.gradle +++ b/sdk/build.gradle @@ -26,7 +26,6 @@ allprojects { subprojects { apply plugin: 'com.android.library' - apply plugin: 'com.github.dcendents.android-maven' apply plugin: 'maven-publish' apply plugin: 'signing' @@ -46,6 +45,7 @@ subprojects { buildConfigField 'int', "MIN_SDK_VERSION", "${ext.minSdkVersion}" buildConfigField 'int', "TARGET_SDK_VERSION", "${ext.targetSdkVersion}" + buildConfigField 'String', 'VERSION_NAME', "\"${ext.versionName}\"" } buildTypes { @@ -107,8 +107,8 @@ subprojects { task coverageReport(type: JacocoReport, dependsOn: ['createDebugCoverageReport', 'testDebugUnitTest']) { reports { - xml.enabled = true - html.enabled = true + xml.required = true + html.required = true } def fileFilter = ['**/R.class', '**/R$*.class', '**/BuildConfig.*', '**/Manifest*.*', '**/*Test*.*', 'android/**/*.*'] @@ -129,13 +129,6 @@ subprojects { } } - task sourcesJar(type: Jar) { - afterEvaluate { - from android.sourceSets.main.java.srcDirs - classifier = 'sources' - } - } - task javadoc(type: Javadoc) { afterEvaluate { source = android.sourceSets.main.java.srcDirs @@ -145,6 +138,7 @@ subprojects { classpath += project.files(android.getBootClasspath().join(File.pathSeparator)) android.libraryVariants.all { variant -> if (variant.name == 'release') { + // FIXME: API 'variant.getJavaCompile()' is obsolete and has been replaced with 'variant.getJavaCompileProvider()'. owner.classpath += variant.javaCompile.classpath } } @@ -152,80 +146,14 @@ subprojects { } } - task javadocJar(type: Jar, dependsOn: javadoc) { - classifier = 'javadoc' - //noinspection GroovyAccessibility - from javadoc.destinationDir - } - - // Set artifacts for publish. - artifacts { - archives javadocJar - archives sourcesJar - } - - afterEvaluate { project -> - uploadArchives { - repositories { - mavenDeployer { - beforeDeployment { MavenDeployment deployment -> signing.signPom(deployment) } - - repository(url: ext.mavenRepoUrl) { - authentication(userName: ext.mavenUser, password: ext.mavenKey) - } - - pom.project { - - // Set base information about assemble. - packaging = 'aar' - name = project.name - description = project.description - url = ext.siteUrl - - // Set identifiers of assemble. - groupId = ext.groupId - artifactId = project.name - - // Set license information. - licenses { - license { - name = ext.licenseName - url = ext.licenseSite - } - } - - // Set information about developers. - developers { - developer { - id = ext.developerId - name = ext.developerName - email = ext.developerEmail - } - } - - // Set information about connection with developers. - scm { - connection = ext.gitUrl - developerConnection = ext.gitUrl - url = ext.siteUrl - } - } - } - } - - signing { - required { gradle.taskGraph.hasTask("uploadArchives") } - sign configurations.archives - } - } - } + // FIXME: Add publishing back } // :sdk:coverageReport which combines all coverageReports generated by sub projects task coverageReport(type: JacocoReport, dependsOn: subprojects.coverageReport) { reports { - xml.enabled = true - html.enabled = true + xml.required = true + html.required = true } def coverageReports = subprojects.coverageReport diff --git a/test/build.gradle b/test/build.gradle index cbfd2194f..678d13dc6 100644 --- a/test/build.gradle +++ b/test/build.gradle @@ -6,13 +6,11 @@ apply plugin: 'com.android.library' android { - - //noinspection GroovyMissingReturnStatement - lintOptions { + lint { disable 'InvalidPackage' } } dependencies { - implementation 'junit:junit:4.12' + implementation 'junit:junit:4.13.2' } From b4f65cb038c58425652b261ba785db6f460e5aef Mon Sep 17 00:00:00 2001 From: Ivan Matkov Date: Mon, 18 Apr 2022 15:00:30 +0200 Subject: [PATCH 04/72] Fix core tests --- .../AbstractAppCenterServiceTest.java | 39 +++-- .../appcenter/AbstractAppCenterTest.java | 8 +- .../appcenter/AppCenterLibraryTest.java | 18 +-- .../appcenter/AppCenterStorageTest.java | 2 +- .../microsoft/appcenter/AppCenterTest.java | 94 +++++------- .../DependencyConfigurationTest.java | 6 +- .../com/microsoft/appcenter/FlagsTest.java | 18 +-- .../channel/AbstractDefaultChannelTest.java | 6 +- .../channel/ChannelLogDecorateTest.java | 6 +- .../DefaultChannelAlternateIngestionTest.java | 29 ++-- .../DefaultChannelOtherOperationsTest.java | 22 +-- .../DefaultChannelPauseResumeTest.java | 56 +++---- .../DefaultChannelRaceConditionTest.java | 37 ++--- .../appcenter/channel/DefaultChannelTest.java | 130 ++++++++-------- .../OneCollectorChannelListenerTest.java | 21 +-- .../appcenter/http/DefaultHttpClientTest.java | 141 +++++++----------- .../HttpClientNetworkStateHandlerTest.java | 8 +- .../appcenter/http/HttpClientRetryerTest.java | 52 +++---- .../http/Tls1_2SocketFactoryTest.java | 24 +-- .../ingestion/AppCenterIngestionTest.java | 24 +-- .../ingestion/OneCollectorIngestionTest.java | 22 +-- .../ingestion/models/AbstractLogTest.java | 2 +- .../models/one/CommonSchemaDataUtilsTest.java | 4 +- .../persistence/DatabasePersistenceTest.java | 73 ++++----- .../appcenter/utils/AppCenterLogTest.java | 31 ++-- .../ApplicationLifecycleListenerTest.java | 4 +- .../appcenter/utils/AsyncTaskUtilsTest.java | 20 +-- .../appcenter/utils/DeviceInfoHelperTest.java | 9 +- .../appcenter/utils/HashUtilsTest.java | 2 +- .../InstrumentationRegistryHelperTest.java | 2 +- .../NetworkStateHelperTestFromLollipop.java | 12 +- .../appcenter/utils/ShutdownHelperTest.java | 5 +- .../utils/async/AppCenterFutureTest.java | 4 +- .../utils/context/UserIdContextTest.java | 4 +- .../CryptoDefaultKeyGeneratorMockTest.java | 2 +- .../appcenter/utils/crypto/CryptoTest.java | 43 +++--- .../utils/storage/DatabaseManagerTest.java | 31 ++-- .../utils/storage/FileManagerTest.java | 18 +-- 38 files changed, 490 insertions(+), 539 deletions(-) diff --git a/sdk/appcenter/src/test/java/com/microsoft/appcenter/AbstractAppCenterServiceTest.java b/sdk/appcenter/src/test/java/com/microsoft/appcenter/AbstractAppCenterServiceTest.java index 1d45746d2..53f3095aa 100644 --- a/sdk/appcenter/src/test/java/com/microsoft/appcenter/AbstractAppCenterServiceTest.java +++ b/sdk/appcenter/src/test/java/com/microsoft/appcenter/AbstractAppCenterServiceTest.java @@ -15,6 +15,7 @@ import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; +import org.mockito.Mock; import org.mockito.invocation.InvocationOnMock; import org.mockito.stubbing.Answer; import org.powermock.api.mockito.PowerMockito; @@ -26,12 +27,12 @@ import static org.junit.Assert.assertNull; import static org.junit.Assert.assertSame; import static org.junit.Assert.assertTrue; -import static org.mockito.Matchers.any; -import static org.mockito.Matchers.anyBoolean; -import static org.mockito.Matchers.anyInt; -import static org.mockito.Matchers.anyLong; -import static org.mockito.Matchers.eq; -import static org.mockito.Matchers.isNull; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyBoolean; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.anyLong; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.ArgumentMatchers.isNull; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.times; @@ -52,6 +53,9 @@ public class AbstractAppCenterServiceTest { private AbstractAppCenterService mService; + @Mock + private Channel.GroupListener mChannelListener; + @Before public void setUp() { mService = new AbstractAppCenterService() { @@ -70,6 +74,11 @@ public String getServiceName() { protected String getLoggerTag() { return "TestLog"; } + + @Override + protected Channel.GroupListener getChannelListener() { + return mChannelListener; + } }; mockStatic(AppCenter.class); @@ -146,25 +155,25 @@ public Void answer(InvocationOnMock invocation) { /* Change state to true will have no effect. */ mService.setInstanceEnabledAsync(true); - verifyStatic(never()); + verifyStatic(SharedPreferencesManager.class, never()); SharedPreferencesManager.putBoolean(eq(mService.getEnabledPreferenceKey()), anyBoolean()); /* Disable. */ mService.setInstanceEnabledAsync(false); assertFalse(mService.isInstanceEnabledAsync().get()); - verifyStatic(); + verifyStatic(SharedPreferencesManager.class); SharedPreferencesManager.putBoolean(mService.getEnabledPreferenceKey(), false); /* Disable again will have no effect. */ mService.setInstanceEnabledAsync(false); assertFalse(mService.isInstanceEnabledAsync().get()); - verifyStatic(); + verifyStatic(SharedPreferencesManager.class); SharedPreferencesManager.putBoolean(mService.getEnabledPreferenceKey(), false); /* Enable back. */ mService.setInstanceEnabledAsync(true); assertTrue(mService.isInstanceEnabledAsync().get()); - verifyStatic(); + verifyStatic(SharedPreferencesManager.class); SharedPreferencesManager.putBoolean(mService.getEnabledPreferenceKey(), true); /* Enable again has no effect. */ @@ -172,9 +181,9 @@ public Void answer(InvocationOnMock invocation) { assertTrue(mService.isInstanceEnabledAsync().get()); /* Verify only 2 actual changes in storage: false to true, then true to false. */ - verifyStatic(); + verifyStatic(SharedPreferencesManager.class); SharedPreferencesManager.putBoolean(mService.getEnabledPreferenceKey(), false); - verifyStatic(); + verifyStatic(SharedPreferencesManager.class); SharedPreferencesManager.putBoolean(mService.getEnabledPreferenceKey(), true); } @@ -201,7 +210,7 @@ public Void answer(InvocationOnMock invocation) { assertFalse(mService.isInstanceEnabledAsync().get()); mService.setInstanceEnabledAsync(false); assertFalse(mService.isInstanceEnabledAsync().get()); - verifyStatic(never()); + verifyStatic(SharedPreferencesManager.class, never()); SharedPreferencesManager.putBoolean(eq(mService.getEnabledPreferenceKey()), anyBoolean()); } @@ -215,7 +224,7 @@ public void onChannelReadyEnabledThenDisable() { Channel channel = mock(Channel.class); mService.onStarted(mock(Context.class), channel, "", null, true); verify(channel).removeGroup(mService.getGroupName()); - verify(channel).addGroup(mService.getGroupName(), mService.getTriggerCount(), mService.getTriggerInterval(), mService.getTriggerMaxParallelRequests(), null, mService.getChannelListener()); + verify(channel).addGroup(mService.getGroupName(), mService.getTriggerCount(), mService.getTriggerInterval(), mService.getTriggerMaxParallelRequests(), null, mChannelListener); verifyNoMoreInteractions(channel); assertSame(channel, mService.mChannel); @@ -237,7 +246,7 @@ public void onChannelReadyDisabledThenEnable() { verifyNoMoreInteractions(channel); assertSame(channel, mService.mChannel); mService.setInstanceEnabled(true); - verify(channel, times(2)).addGroup(mService.getGroupName(), mService.getTriggerCount(), mService.getTriggerInterval(), mService.getTriggerMaxParallelRequests(), null, mService.getChannelListener()); + verify(channel, times(2)).addGroup(mService.getGroupName(), mService.getTriggerCount(), mService.getTriggerInterval(), mService.getTriggerMaxParallelRequests(), null, mChannelListener); verifyNoMoreInteractions(channel); } diff --git a/sdk/appcenter/src/test/java/com/microsoft/appcenter/AbstractAppCenterTest.java b/sdk/appcenter/src/test/java/com/microsoft/appcenter/AbstractAppCenterTest.java index db72ae34d..67783d828 100644 --- a/sdk/appcenter/src/test/java/com/microsoft/appcenter/AbstractAppCenterTest.java +++ b/sdk/appcenter/src/test/java/com/microsoft/appcenter/AbstractAppCenterTest.java @@ -45,10 +45,10 @@ import static com.microsoft.appcenter.AppCenter.KEY_VALUE_DELIMITER; import static com.microsoft.appcenter.AppCenter.TRANSMISSION_TARGET_TOKEN_KEY; -import static org.mockito.Matchers.any; -import static org.mockito.Matchers.anyBoolean; -import static org.mockito.Matchers.anyString; -import static org.mockito.Matchers.eq; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyBoolean; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.when; diff --git a/sdk/appcenter/src/test/java/com/microsoft/appcenter/AppCenterLibraryTest.java b/sdk/appcenter/src/test/java/com/microsoft/appcenter/AppCenterLibraryTest.java index 8e7149f06..e1806074b 100644 --- a/sdk/appcenter/src/test/java/com/microsoft/appcenter/AppCenterLibraryTest.java +++ b/sdk/appcenter/src/test/java/com/microsoft/appcenter/AppCenterLibraryTest.java @@ -23,12 +23,12 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; -import static org.mockito.Matchers.any; -import static org.mockito.Matchers.anyBoolean; -import static org.mockito.Matchers.anyInt; -import static org.mockito.Matchers.anyString; -import static org.mockito.Matchers.eq; -import static org.mockito.Matchers.isNull; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyBoolean; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.ArgumentMatchers.isNull; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; @@ -41,7 +41,7 @@ public class AppCenterLibraryTest extends AbstractAppCenterTest { public void nullApplicationTest() { AppCenter.startFromLibrary(null, DummyService.class); verify(DummyService.getInstance(), never()).onStarted(any(Context.class), any(Channel.class), anyString(), anyString(), anyBoolean()); - verifyStatic(); + verifyStatic(AppCenterLog.class); AppCenterLog.error(eq(LOG_TAG), anyString()); } @@ -263,7 +263,7 @@ public void startFromAppThenFromLibrary() { AppCenter.startFromLibrary(mApplication, AnotherDummyService.class, DummyService.class); /* We get no warnings as app started those. */ - verifyStatic(never()); + verifyStatic(AppCenterLog.class, never()); AppCenterLog.warn(anyString(), anyString()); /* Check nothing changes as everything was already initialized by app start. */ @@ -356,7 +356,7 @@ public void startFromLibraryDoesNotStartFromApp() { verify(DummyService.getInstance()).onConfigurationUpdated(DUMMY_APP_SECRET, DUMMY_TRANSMISSION_TARGET_TOKEN); /* And not call onStarted again (verify 1 total call). */ - verify(DummyService.getInstance()).onStarted(any(Context.class), any(Channel.class), anyString(), anyString(), anyBoolean()); + verify(DummyService.getInstance()).onStarted(any(Context.class), any(Channel.class), isNull(String.class), isNull(String.class), anyBoolean()); } @Test diff --git a/sdk/appcenter/src/test/java/com/microsoft/appcenter/AppCenterStorageTest.java b/sdk/appcenter/src/test/java/com/microsoft/appcenter/AppCenterStorageTest.java index fa316f5a8..c4b9bf278 100644 --- a/sdk/appcenter/src/test/java/com/microsoft/appcenter/AppCenterStorageTest.java +++ b/sdk/appcenter/src/test/java/com/microsoft/appcenter/AppCenterStorageTest.java @@ -11,7 +11,7 @@ import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; -import static org.mockito.Matchers.anyLong; +import static org.mockito.ArgumentMatchers.anyLong; import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; import static org.powermock.api.mockito.PowerMockito.when; diff --git a/sdk/appcenter/src/test/java/com/microsoft/appcenter/AppCenterTest.java b/sdk/appcenter/src/test/java/com/microsoft/appcenter/AppCenterTest.java index 287f3c953..9cb84ccf0 100644 --- a/sdk/appcenter/src/test/java/com/microsoft/appcenter/AppCenterTest.java +++ b/sdk/appcenter/src/test/java/com/microsoft/appcenter/AppCenterTest.java @@ -48,16 +48,16 @@ import static org.junit.Assert.assertNull; import static org.junit.Assert.assertSame; import static org.junit.Assert.assertTrue; -import static org.mockito.Matchers.any; -import static org.mockito.Matchers.anyBoolean; -import static org.mockito.Matchers.anyInt; -import static org.mockito.Matchers.anyLong; -import static org.mockito.Matchers.anyString; -import static org.mockito.Matchers.argThat; -import static org.mockito.Matchers.eq; -import static org.mockito.Matchers.isA; -import static org.mockito.Matchers.isNull; -import static org.mockito.Matchers.notNull; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyBoolean; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.anyLong; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.ArgumentMatchers.argThat; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.ArgumentMatchers.isA; +import static org.mockito.ArgumentMatchers.isNull; +import static org.mockito.ArgumentMatchers.notNull; import static org.mockito.Mockito.doNothing; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; @@ -491,7 +491,7 @@ public void enableTest() { } dummyService.setInstanceEnabledAsync(true); assertFalse(dummyService.isInstanceEnabledAsync().get()); - verifyStatic(); + verifyStatic(AppCenterLog.class); AppCenterLog.error(eq(LOG_TAG), anyString()); assertFalse(AppCenter.isEnabled().get()); verify(mChannel, times(2)).setEnabled(false); @@ -542,7 +542,7 @@ public void enableBeforeConfiguredTest() { assertFalse(AppCenter.isEnabled().get()); AppCenter.setEnabled(true); assertFalse(AppCenter.isEnabled().get()); - verifyStatic(times(3)); + verifyStatic(AppCenterLog.class, times(3)); AppCenterLog.error(eq(LOG_TAG), anyString()); } @@ -615,7 +615,7 @@ public void disablePersistedAndDisable() { @Test public void invalidServiceTest() { AppCenter.start(mApplication, DUMMY_APP_SECRET, InvalidService.class); - verifyStatic(); + verifyStatic(AppCenterLog.class); AppCenterLog.error(eq(LOG_TAG), anyString(), any(NoSuchMethodException.class)); } @@ -625,7 +625,7 @@ public void nullApplicationTest() { /* First verify start from application. */ AppCenter.start(null, DUMMY_APP_SECRET, DummyService.class); verify(DummyService.getInstance(), never()).onStarted(any(Context.class), any(Channel.class), anyString(), anyString(), anyBoolean()); - verifyStatic(); + verifyStatic(AppCenterLog.class); AppCenterLog.error(eq(LOG_TAG), anyString()); } @@ -695,7 +695,7 @@ private void checkStartedWithoutAppSecret() { AppCenter.start(mApplication, "", DummyService.class, AnotherDummyService.class); /* Verify start not called again (1 total call). */ - verify(DummyService.getInstance()).onStarted(any(Context.class), any(Channel.class), anyString(), anyString(), anyBoolean()); + verify(DummyService.getInstance()).onStarted(any(Context.class), any(Channel.class), any(), any(), anyBoolean()); /* And not updated either. */ verify(DummyService.getInstance(), never()).onConfigurationUpdated(anyString(), anyString()); @@ -811,13 +811,13 @@ public void checkStartHandlerWhenDesableRunnableIsNull() throws Exception { // Call runnable with disabledRunnable is null. Runnable mockRunnable = mock(Runnable.class); AppCenter.getInstance().getAppCenterHandler().post(mockRunnable, null); - verifyStatic(); + verifyStatic(AppCenterLog.class); AppCenterLog.error(eq(LOG_TAG), eq("App Center SDK is disabled.")); // Call App Center start with null application and verify than anything happening. AppCenter.getInstance().resetApplication(); AppCenter.getInstance().getAppCenterHandler().post(mockRunnable, null); - verifyStatic(); + verifyStatic(AppCenterLog.class); AppCenterLog.error(eq(LOG_TAG), eq("App Center hasn't been configured. You need to call AppCenter.start with appSecret or AppCenter.configure first.")); } @@ -838,7 +838,7 @@ public void setWrapperSdkTest() { AppCenter.setWrapperSdk(wrapperSdk); /* Check propagation. */ - verifyStatic(); + verifyStatic(DeviceInfoHelper.class); DeviceInfoHelper.setWrapperSdk(wrapperSdk); /* Since the channel was not created when setting wrapper, no need to refresh channel after start. */ @@ -859,7 +859,7 @@ public void setCountryCode() { AppCenter.setCountryCode(expectedCountryCode); /* Check that method was called. */ - verifyStatic(); + verifyStatic(DeviceInfoHelper.class); DeviceInfoHelper.setCountryCode(eq(expectedCountryCode)); } @@ -867,24 +867,24 @@ public void setCountryCode() { public void setDefaultLogLevelRelease() { mApplicationInfo.flags = 0; AppCenter.start(mApplication, DUMMY_APP_SECRET, DummyService.class); - verifyStatic(never()); + verifyStatic(AppCenterLog.class, never()); AppCenterLog.setLogLevel(anyInt()); } @Test public void setDefaultLogLevelDebug() { AppCenter.start(mApplication, DUMMY_APP_SECRET, DummyService.class); - verifyStatic(); + verifyStatic(AppCenterLog.class); AppCenterLog.setLogLevel(android.util.Log.WARN); } @Test public void doNotSetDefaultLogLevel() { AppCenter.setLogLevel(android.util.Log.VERBOSE); - verifyStatic(); + verifyStatic(AppCenterLog.class); AppCenterLog.setLogLevel(android.util.Log.VERBOSE); AppCenter.start(mApplication, DUMMY_APP_SECRET, DummyService.class); - verifyStatic(never()); + verifyStatic(AppCenterLog.class, never()); AppCenterLog.setLogLevel(android.util.Log.WARN); } @@ -972,7 +972,7 @@ public void uncaughtExceptionHandler() { UncaughtExceptionHandler handler = AppCenter.getInstance().getUncaughtExceptionHandler(); assertNotNull(handler); assertEquals(defaultUncaughtExceptionHandler, handler.getDefaultUncaughtExceptionHandler()); - verifyStatic(); + verifyStatic(Thread.class); Thread.setDefaultUncaughtExceptionHandler(eq(handler)); /* Crash an verify. */ @@ -984,7 +984,7 @@ public void uncaughtExceptionHandler() { /* But we don't do it if App Center is disabled. */ AppCenter.setEnabled(false); - verifyStatic(); + verifyStatic(Thread.class); Thread.setDefaultUncaughtExceptionHandler(eq(defaultUncaughtExceptionHandler)); handler.uncaughtException(thread, exception); verify(mChannel, times(1)).shutdown(); @@ -994,7 +994,7 @@ public void uncaughtExceptionHandler() { AppCenter.setEnabled(true); assertNull(handler.getDefaultUncaughtExceptionHandler()); handler.uncaughtException(thread, exception); - verifyStatic(); + verifyStatic(ShutdownHelper.class); ShutdownHelper.shutdown(10); } @@ -1022,7 +1022,7 @@ public Void answer(InvocationOnMock invocation) { UncaughtExceptionHandler handler = AppCenter.getInstance().getUncaughtExceptionHandler(); assertNotNull(handler); assertEquals(defaultUncaughtExceptionHandler, handler.getDefaultUncaughtExceptionHandler()); - verifyStatic(); + verifyStatic(Thread.class); Thread.setDefaultUncaughtExceptionHandler(eq(handler)); /* Simulate crash to shutdown channel. */ @@ -1037,7 +1037,7 @@ public Void answer(InvocationOnMock invocation) { verify(defaultUncaughtExceptionHandler).uncaughtException(eq(thread), eq(exception)); /* Verify we log an error on timeout. */ - verifyStatic(); + verifyStatic(AppCenterLog.class); AppCenterLog.error(eq(LOG_TAG), anyString()); } @@ -1056,7 +1056,7 @@ public void uncaughtExceptionHandlerInterrupted() throws Exception { UncaughtExceptionHandler handler = AppCenter.getInstance().getUncaughtExceptionHandler(); assertNotNull(handler); assertEquals(defaultUncaughtExceptionHandler, handler.getDefaultUncaughtExceptionHandler()); - verifyStatic(); + verifyStatic(Thread.class); Thread.setDefaultUncaughtExceptionHandler(eq(handler)); /* Simulate crash to shutdown channel. */ @@ -1071,26 +1071,14 @@ public void uncaughtExceptionHandlerInterrupted() throws Exception { verify(defaultUncaughtExceptionHandler).uncaughtException(eq(thread), eq(exception)); /* Verify we log a warning on interruption. */ - verifyStatic(); - AppCenterLog.warn(eq(LOG_TAG), anyString(), argThat(new ArgumentMatcher() { - - @Override - public boolean matches(Object argument) { - return argument instanceof InterruptedException; - } - })); + verifyStatic(AppCenterLog.class); + AppCenterLog.warn(eq(LOG_TAG), anyString(), isA(InterruptedException.class)); } @Test public void addOneCollectorListenerOnStart() { AppCenter.start(mApplication, DUMMY_TARGET_TOKEN_STRING, DummyService.class); - verify(mChannel).addListener(argThat(new ArgumentMatcher() { - - @Override - public boolean matches(Object argument) { - return argument instanceof OneCollectorChannelListener; - } - })); + verify(mChannel).addListener(isA(OneCollectorChannelListener.class)); } @Test @@ -1166,7 +1154,7 @@ public void setNetworkRequestValue() { AppCenter.configure(mApplication); /* Check that this value wasn't saved to SharedPreferences. */ - verifyStatic(never()); + verifyStatic(SharedPreferencesManager.class, never()); SharedPreferencesManager.putBoolean(eq(PrefStorageConstants.ALLOWED_NETWORK_REQUEST), anyBoolean()); /* Set channel. */ @@ -1191,7 +1179,7 @@ public void setSameNetworkRequestsAllowedValue() { /* Configure App Center. */ AppCenter.configure(mApplication); AppCenter.getInstance().setChannel(null); - verifyStatic(never()); + verifyStatic(SharedPreferencesManager.class, never()); SharedPreferencesManager.putBoolean(eq(PrefStorageConstants.ALLOWED_NETWORK_REQUEST), anyBoolean()); /* Verify that default value is true. */ @@ -1201,18 +1189,18 @@ public void setSameNetworkRequestsAllowedValue() { AppCenter.setNetworkRequestsAllowed(true); /* Verify that value wasn't saved. */ - verifyStatic(never()); + verifyStatic(SharedPreferencesManager.class, never()); SharedPreferencesManager.putBoolean(eq(PrefStorageConstants.ALLOWED_NETWORK_REQUEST), anyBoolean()); /* Disallow network requests. */ AppCenter.setNetworkRequestsAllowed(false); assertFalse(AppCenter.isNetworkRequestsAllowed()); - verifyStatic(); + verifyStatic(SharedPreferencesManager.class); SharedPreferencesManager.putBoolean(eq(PrefStorageConstants.ALLOWED_NETWORK_REQUEST), anyBoolean()); /* Disallow network again requests. */ AppCenter.setNetworkRequestsAllowed(false); - verifyStatic(); + verifyStatic(SharedPreferencesManager.class); SharedPreferencesManager.putBoolean(eq(PrefStorageConstants.ALLOWED_NETWORK_REQUEST), anyBoolean()); } @@ -1229,24 +1217,24 @@ public void setNetworkRequestsAllowedValueWhenAppCenterIsNotConfigured() { Assert.assertFalse(AppCenter.isNetworkRequestsAllowed()); /* Check that this value wasn't saved to SharedPreferences. */ - verifyStatic(never()); + verifyStatic(SharedPreferencesManager.class, never()); SharedPreferencesManager.putBoolean(eq(PrefStorageConstants.ALLOWED_NETWORK_REQUEST), eq(false)); /* Verify that network requests value was saved to local variable. */ Assert.assertFalse(AppCenter.isNetworkRequestsAllowed()); - verifyStatic(never()); + verifyStatic(SharedPreferencesManager.class, never()); SharedPreferencesManager.getBoolean(eq(PrefStorageConstants.ALLOWED_NETWORK_REQUEST), eq(true)); /* Configure App Center. */ AppCenter.configure(mApplication); /* Verify that network requests value was saved to SharedPreferences. */ - verifyStatic(); + verifyStatic(SharedPreferencesManager.class); SharedPreferencesManager.putBoolean(eq(PrefStorageConstants.ALLOWED_NETWORK_REQUEST), eq(false)); /* Verify that network requests value was saved to SharedPreferences. */ Assert.assertFalse(AppCenter.isNetworkRequestsAllowed()); - verifyStatic(); + verifyStatic(SharedPreferencesManager.class); SharedPreferencesManager.getBoolean(eq(PrefStorageConstants.ALLOWED_NETWORK_REQUEST), eq(false)); } } diff --git a/sdk/appcenter/src/test/java/com/microsoft/appcenter/DependencyConfigurationTest.java b/sdk/appcenter/src/test/java/com/microsoft/appcenter/DependencyConfigurationTest.java index e7a1eceba..9ec3ac7de 100644 --- a/sdk/appcenter/src/test/java/com/microsoft/appcenter/DependencyConfigurationTest.java +++ b/sdk/appcenter/src/test/java/com/microsoft/appcenter/DependencyConfigurationTest.java @@ -16,9 +16,9 @@ import org.junit.After; import org.junit.Test; -import static org.mockito.Matchers.any; -import static org.mockito.Matchers.eq; -import static org.mockito.Matchers.isA; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.ArgumentMatchers.isA; import static org.mockito.Mockito.mock; import static org.powermock.api.mockito.PowerMockito.verifyNew; diff --git a/sdk/appcenter/src/test/java/com/microsoft/appcenter/FlagsTest.java b/sdk/appcenter/src/test/java/com/microsoft/appcenter/FlagsTest.java index 0e45561da..13ca48eed 100644 --- a/sdk/appcenter/src/test/java/com/microsoft/appcenter/FlagsTest.java +++ b/sdk/appcenter/src/test/java/com/microsoft/appcenter/FlagsTest.java @@ -14,7 +14,7 @@ import org.powermock.modules.junit4.PowerMockRunner; import static org.junit.Assert.assertEquals; -import static org.mockito.Matchers.anyString; +import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.Mockito.never; import static org.powermock.api.mockito.PowerMockito.mockStatic; import static org.powermock.api.mockito.PowerMockito.verifyStatic; @@ -32,7 +32,7 @@ public void setUp() { public void persistenceNone() { assertEquals(Flags.NORMAL, Flags.getPersistenceFlag(0, false)); assertEquals(Flags.NORMAL, Flags.getPersistenceFlag(0, true)); - verifyStatic(never()); + verifyStatic(AppCenterLog.class, never()); AppCenterLog.warn(anyString(), anyString()); } @@ -40,7 +40,7 @@ public void persistenceNone() { public void persistenceNormal() { assertEquals(Flags.NORMAL, Flags.getPersistenceFlag(Flags.NORMAL, false)); assertEquals(Flags.NORMAL, Flags.getPersistenceFlag(Flags.NORMAL, true)); - verifyStatic(never()); + verifyStatic(AppCenterLog.class, never()); AppCenterLog.warn(anyString(), anyString()); } @@ -48,7 +48,7 @@ public void persistenceNormal() { public void persistenceCritical() { assertEquals(Flags.CRITICAL, Flags.getPersistenceFlag(Flags.CRITICAL, false)); assertEquals(Flags.CRITICAL, Flags.getPersistenceFlag(Flags.CRITICAL, true)); - verifyStatic(never()); + verifyStatic(AppCenterLog.class, never()); AppCenterLog.warn(anyString(), anyString()); } @@ -57,7 +57,7 @@ public void persistenceCritical() { public void persistenceBackwardCompatible() { assertEquals(Flags.NORMAL, Flags.getPersistenceFlag(Flags.PERSISTENCE_NORMAL, true)); assertEquals(Flags.CRITICAL, Flags.getPersistenceFlag(Flags.PERSISTENCE_CRITICAL, true)); - verifyStatic(never()); + verifyStatic(AppCenterLog.class, never()); AppCenterLog.warn(anyString(), anyString()); } @@ -65,7 +65,7 @@ public void persistenceBackwardCompatible() { public void persistenceDefaults() { assertEquals(Flags.NORMAL, Flags.getPersistenceFlag(Flags.DEFAULTS, false)); assertEquals(Flags.NORMAL, Flags.getPersistenceFlag(Flags.DEFAULTS, true)); - verifyStatic(never()); + verifyStatic(AppCenterLog.class, never()); AppCenterLog.warn(anyString(), anyString()); } @@ -73,7 +73,7 @@ public void persistenceDefaults() { public void persistenceCriticalPlusOtherFlag() { assertEquals(Flags.CRITICAL, Flags.getPersistenceFlag(Flags.CRITICAL | 0x0100, false)); assertEquals(Flags.CRITICAL, Flags.getPersistenceFlag(Flags.CRITICAL | 0x0200, true)); - verifyStatic(never()); + verifyStatic(AppCenterLog.class, never()); AppCenterLog.warn(anyString(), anyString()); } @@ -82,12 +82,12 @@ public void persistenceInvalidFlag() { /* Fallback without warning. */ assertEquals(Flags.NORMAL, Flags.getPersistenceFlag(0x09, false)); - verifyStatic(never()); + verifyStatic(AppCenterLog.class, never()); AppCenterLog.warn(anyString(), anyString()); /* Fallback with warning. */ assertEquals(Flags.NORMAL, Flags.getPersistenceFlag(0x09, true)); - verifyStatic(); + verifyStatic(AppCenterLog.class); AppCenterLog.warn(anyString(), anyString()); } } diff --git a/sdk/appcenter/src/test/java/com/microsoft/appcenter/channel/AbstractDefaultChannelTest.java b/sdk/appcenter/src/test/java/com/microsoft/appcenter/channel/AbstractDefaultChannelTest.java index 8d955c229..57a750d0c 100644 --- a/sdk/appcenter/src/test/java/com/microsoft/appcenter/channel/AbstractDefaultChannelTest.java +++ b/sdk/appcenter/src/test/java/com/microsoft/appcenter/channel/AbstractDefaultChannelTest.java @@ -30,7 +30,7 @@ import java.util.ArrayList; import java.util.UUID; -import static org.mockito.Matchers.any; +import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; import static org.powermock.api.mockito.PowerMockito.doAnswer; @@ -54,8 +54,6 @@ public class AbstractDefaultChannelTest { static final int MAX_PARALLEL_BATCHES = 3; - static final String MOCK_TOKEN = UUID.randomUUID().toString(); - @Rule public PowerMockRule mPowerMockRule = new PowerMockRule(); @@ -75,7 +73,7 @@ public String answer(InvocationOnMock invocation) { Object[] args = invocation.getArguments(); int length = size >= 0 ? size : (int) args[2]; if (args[3] instanceof ArrayList) { - ArrayList logs = (ArrayList) args[3]; + ArrayList logs = (ArrayList) args[3]; for (int i = 0; i < length; i++) { logs.add(mock(Log.class)); } diff --git a/sdk/appcenter/src/test/java/com/microsoft/appcenter/channel/ChannelLogDecorateTest.java b/sdk/appcenter/src/test/java/com/microsoft/appcenter/channel/ChannelLogDecorateTest.java index b52a36076..f8af8ecf2 100644 --- a/sdk/appcenter/src/test/java/com/microsoft/appcenter/channel/ChannelLogDecorateTest.java +++ b/sdk/appcenter/src/test/java/com/microsoft/appcenter/channel/ChannelLogDecorateTest.java @@ -24,7 +24,7 @@ import java.util.UUID; import static com.microsoft.appcenter.Flags.DEFAULTS; -import static org.mockito.Matchers.any; +import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; @@ -57,7 +57,7 @@ public void checkLogAttributes() throws Exception { } /* Check cache was used, meaning only 1 call to generate a device. */ - verifyStatic(); + verifyStatic(DeviceInfoHelper.class); DeviceInfoHelper.getDeviceInfo(any(Context.class)); /* Test a log that is already decorated. */ @@ -82,7 +82,7 @@ public void checkLogAttributes() throws Exception { } /* Check only 1 device has been generated after cache invalidate. */ - verifyStatic(times(2)); + verifyStatic(DeviceInfoHelper.class, times(2)); DeviceInfoHelper.getDeviceInfo(any(Context.class)); } } diff --git a/sdk/appcenter/src/test/java/com/microsoft/appcenter/channel/DefaultChannelAlternateIngestionTest.java b/sdk/appcenter/src/test/java/com/microsoft/appcenter/channel/DefaultChannelAlternateIngestionTest.java index 4443cab6a..98f80e28f 100644 --- a/sdk/appcenter/src/test/java/com/microsoft/appcenter/channel/DefaultChannelAlternateIngestionTest.java +++ b/sdk/appcenter/src/test/java/com/microsoft/appcenter/channel/DefaultChannelAlternateIngestionTest.java @@ -20,11 +20,14 @@ import java.util.UUID; import static org.junit.Assert.assertEquals; -import static org.mockito.Matchers.any; -import static org.mockito.Matchers.anyInt; -import static org.mockito.Matchers.anyListOf; -import static org.mockito.Matchers.anyString; -import static org.mockito.Matchers.eq; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyCollection; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.anyList; +import static org.mockito.ArgumentMatchers.anyListOf; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.ArgumentMatchers.isNull; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.times; @@ -79,7 +82,8 @@ public void useAlternateIngestion() throws IOException { when(defaultIngestion.isEnabled()).thenReturn(true); Ingestion alternateIngestion = mock(Ingestion.class); when(alternateIngestion.isEnabled()).thenReturn(true); - when(mockPersistence.getLogs(any(String.class), anyListOf(String.class), anyInt(), anyListOf(Log.class))).then(getGetLogsAnswer(1)); + when(mockPersistence.getLogs(anyString(), anyCollection(), anyInt(), anyList())) + .thenAnswer(getGetLogsAnswer(1)); DefaultChannel channel = new DefaultChannel(mock(Context.class), UUID.randomUUID().toString(), mockPersistence, defaultIngestion, mAppCenterHandler); channel.addGroup(TEST_GROUP, 1, BATCH_TIME_INTERVAL, MAX_PARALLEL_BATCHES, alternateIngestion, null); @@ -118,7 +122,8 @@ public void startWithoutAppSecret() throws Persistence.PersistenceException { /* Simulate we have 1 pending log in storage. */ when(mockPersistence.countLogs(anyString())).thenReturn(1); - when(mockPersistence.getLogs(any(String.class), anyListOf(String.class), anyInt(), anyListOf(Log.class))).then(getGetLogsAnswer(1)); + when(mockPersistence.getLogs(anyString(), anyCollection(), anyInt(), anyList())) + .thenAnswer(getGetLogsAnswer(1)); /* Create channel and groups. */ DefaultChannel channel = new DefaultChannel(mock(Context.class), null, mockPersistence, defaultIngestion, mAppCenterHandler); @@ -129,7 +134,7 @@ public void startWithoutAppSecret() throws Persistence.PersistenceException { verify(defaultIngestion, never()).sendAsync(anyString(), any(UUID.class), any(LogContainer.class), any(ServiceCallback.class)); /* One collector previous log sent. */ - verify(alternateIngestion).sendAsync(anyString(), any(UUID.class), any(LogContainer.class), any(ServiceCallback.class)); + verify(alternateIngestion).sendAsync(isNull(), any(UUID.class), any(LogContainer.class), any(ServiceCallback.class)); /* Enqueuing 1 new event for app center. */ channel.enqueue(mock(Log.class), appCenterGroup, Flags.DEFAULTS); @@ -144,7 +149,7 @@ public void startWithoutAppSecret() throws Persistence.PersistenceException { channel.enqueue(mock(Log.class), oneCollectorGroup, Flags.DEFAULTS); /* Verify that we have called sendAsync on the alternate ingestion a second time. */ - verify(alternateIngestion, times(2)).sendAsync(anyString(), any(UUID.class), any(LogContainer.class), any(ServiceCallback.class)); + verify(alternateIngestion, times(2)).sendAsync(isNull(), any(UUID.class), any(LogContainer.class), any(ServiceCallback.class)); verify(defaultIngestion, never()).sendAsync(anyString(), any(UUID.class), any(LogContainer.class), any(ServiceCallback.class)); /* Verify that we can now send logs to app center after we have set app secret. */ @@ -164,7 +169,8 @@ public void sendPendingLogsAfterSettingAppSecret() { when(defaultIngestion.isEnabled()).thenReturn(true); Ingestion alternateIngestion = mock(Ingestion.class); when(alternateIngestion.isEnabled()).thenReturn(true); - when(mockPersistence.getLogs(any(String.class), anyListOf(String.class), anyInt(), anyListOf(Log.class))).then(getGetLogsAnswer(1)); + when(mockPersistence.getLogs(anyString(), anyCollection(), anyInt(), anyList())) + .thenAnswer(getGetLogsAnswer(1)); /* Simulate we have 1 pending log in storage. */ when(mockPersistence.countLogs(anyString())).thenReturn(1); @@ -193,7 +199,8 @@ public void pendingLogsDisableSetAppSecretThenEnable() { when(defaultIngestion.isEnabled()).thenReturn(true); Ingestion alternateIngestion = mock(Ingestion.class); when(alternateIngestion.isEnabled()).thenReturn(true); - when(mockPersistence.getLogs(any(String.class), anyListOf(String.class), anyInt(), anyListOf(Log.class))).then(getGetLogsAnswer(1)); + when(mockPersistence.getLogs(anyString(), anyCollection(), anyInt(), anyList())) + .thenAnswer(getGetLogsAnswer(1)); /* Simulate we have 1 pending log in storage for App Center. */ when(mockPersistence.countLogs(appCenterGroup)).thenReturn(1); diff --git a/sdk/appcenter/src/test/java/com/microsoft/appcenter/channel/DefaultChannelOtherOperationsTest.java b/sdk/appcenter/src/test/java/com/microsoft/appcenter/channel/DefaultChannelOtherOperationsTest.java index 684b3971f..0258bfe67 100644 --- a/sdk/appcenter/src/test/java/com/microsoft/appcenter/channel/DefaultChannelOtherOperationsTest.java +++ b/sdk/appcenter/src/test/java/com/microsoft/appcenter/channel/DefaultChannelOtherOperationsTest.java @@ -21,13 +21,15 @@ import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; -import static org.mockito.Matchers.any; -import static org.mockito.Matchers.anyInt; -import static org.mockito.Matchers.anyListOf; -import static org.mockito.Matchers.anyLong; -import static org.mockito.Matchers.anyString; -import static org.mockito.Matchers.eq; -import static org.mockito.Matchers.notNull; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyCollection; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.anyList; +import static org.mockito.ArgumentMatchers.anyListOf; +import static org.mockito.ArgumentMatchers.anyLong; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.ArgumentMatchers.notNull; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.reset; @@ -96,14 +98,14 @@ public void shutdown() { AppCenterIngestion mockIngestion = mock(AppCenterIngestion.class); when(mockIngestion.isEnabled()).thenReturn(true); Channel.GroupListener mockListener = mock(Channel.GroupListener.class); - when(mockPersistence.getLogs(any(String.class), anyListOf(String.class), anyInt(), Matchers.>any())) - .then(getGetLogsAnswer(1)); + when(mockPersistence.getLogs(anyString(), anyCollection(), anyInt(), anyList())) + .thenAnswer(getGetLogsAnswer(1)); DefaultChannel channel = new DefaultChannel(mock(Context.class), UUID.randomUUID().toString(), mockPersistence, mockIngestion, mAppCenterHandler); channel.addGroup(TEST_GROUP, 1, BATCH_TIME_INTERVAL, MAX_PARALLEL_BATCHES, null, mockListener); /* Enqueuing 1 event. */ channel.enqueue(mock(Log.class), TEST_GROUP, Flags.DEFAULTS); - verify(mockListener).onBeforeSending(notNull(Log.class)); + verify(mockListener).onBeforeSending(notNull()); channel.shutdown(); verify(mockListener, never()).onFailure(any(Log.class), any(Exception.class)); diff --git a/sdk/appcenter/src/test/java/com/microsoft/appcenter/channel/DefaultChannelPauseResumeTest.java b/sdk/appcenter/src/test/java/com/microsoft/appcenter/channel/DefaultChannelPauseResumeTest.java index 2ae45096c..a48f0e5a2 100644 --- a/sdk/appcenter/src/test/java/com/microsoft/appcenter/channel/DefaultChannelPauseResumeTest.java +++ b/sdk/appcenter/src/test/java/com/microsoft/appcenter/channel/DefaultChannelPauseResumeTest.java @@ -25,19 +25,20 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; -import static org.mockito.Matchers.any; -import static org.mockito.Matchers.anyInt; -import static org.mockito.Matchers.anyListOf; -import static org.mockito.Matchers.anyLong; -import static org.mockito.Matchers.anyString; -import static org.mockito.Matchers.eq; -import static org.mockito.Matchers.notNull; +import static org.mockito.ArgumentMatchers.anyCollection; +import static org.mockito.ArgumentMatchers.anyList; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.anyLong; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.ArgumentMatchers.notNull; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.reset; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.verifyZeroInteractions; +import static org.mockito.Mockito.verifyNoInteractions; import static org.mockito.Mockito.when; import static org.powermock.api.mockito.PowerMockito.spy; import static org.powermock.api.mockito.PowerMockito.verifyStatic; @@ -49,9 +50,10 @@ public void pauseResumeGroup() throws Persistence.PersistenceException { Persistence mockPersistence = mock(Persistence.class); AppCenterIngestion mockIngestion = mock(AppCenterIngestion.class); Channel.GroupListener mockListener = mock(Channel.GroupListener.class); - - when(mockPersistence.getLogs(any(String.class), anyListOf(String.class), anyInt(), anyListOf(Log.class))).then(getGetLogsAnswer(50)); - when(mockIngestion.sendAsync(anyString(), any(UUID.class), any(LogContainer.class), any(ServiceCallback.class))).then(getSendAsyncAnswer()); + when(mockPersistence.getLogs(anyString(), anyCollection(), anyInt(), anyList())) + .thenAnswer(getGetLogsAnswer(50)); + when(mockIngestion.sendAsync(anyString(), any(UUID.class), any(LogContainer.class), any(ServiceCallback.class))) + .thenAnswer(getSendAsyncAnswer()); when(mockIngestion.isEnabled()).thenReturn(true); DefaultChannel channel = new DefaultChannel(mock(Context.class), UUID.randomUUID().toString(), mockPersistence, mockIngestion, mAppCenterHandler); @@ -72,7 +74,7 @@ public void pauseResumeGroup() throws Persistence.PersistenceException { assertEquals(50, channel.getGroupState(TEST_GROUP).mPendingLogCount); verify(mockPersistence, times(50)).putLog(any(Log.class), eq(TEST_GROUP), eq(Flags.NORMAL)); verify(mockIngestion, never()).sendAsync(anyString(), any(UUID.class), any(LogContainer.class), any(ServiceCallback.class)); - verify(mockPersistence, never()).deleteLogs(any(String.class), any(String.class)); + verify(mockPersistence, never()).deleteLogs(anyString(), anyString()); verify(mockListener, never()).onBeforeSending(any(Log.class)); verify(mockListener, never()).onSuccess(any(Log.class)); @@ -86,7 +88,7 @@ public void pauseResumeGroup() throws Persistence.PersistenceException { /* Verify channel starts sending logs. */ verify(mAppCenterHandler, never()).postDelayed(any(Runnable.class), eq(BATCH_TIME_INTERVAL)); verify(mockIngestion).sendAsync(anyString(), any(UUID.class), any(LogContainer.class), any(ServiceCallback.class)); - verify(mockPersistence).deleteLogs(any(String.class), any(String.class)); + verify(mockPersistence).deleteLogs(anyString(), anyString()); verify(mockListener, times(50)).onBeforeSending(any(Log.class)); verify(mockListener, times(50)).onSuccess(any(Log.class)); @@ -148,7 +150,8 @@ public void pauseResumeTargetToken() throws Persistence.PersistenceException { channel.pauseGroup(TEST_GROUP, targetToken); /* Mock the database to return logs now. */ - when(persistence.getLogs(any(String.class), anyListOf(String.class), anyInt(), anyListOf(Log.class))).then(getGetLogsAnswer(1)); + when(persistence.getLogs(anyString(), anyCollection(), anyInt(), anyList())) + .thenAnswer(getGetLogsAnswer(1)); when(persistence.countLogs(TEST_GROUP)).thenReturn(1); /* Enqueue a log. */ @@ -186,8 +189,8 @@ public void pauseResumeTargetToken() throws Persistence.PersistenceException { reset(persistence); reset(ingestion); channel.resumeGroup(TEST_GROUP, targetToken); - verifyZeroInteractions(persistence); - verifyZeroInteractions(ingestion); + verifyNoInteractions(persistence); + verifyNoInteractions(ingestion); /* AppCenter ingestion never used. */ verify(appCenterIngestion, never()).sendAsync(anyString(), any(UUID.class), any(LogContainer.class), any(ServiceCallback.class)); @@ -215,7 +218,8 @@ public void pauseGroupPauseTargetResumeGroupResumeTarget() throws Persistence.Pe channel.pauseGroup(TEST_GROUP, targetToken); /* Mock the database to return logs now. */ - when(persistence.getLogs(any(String.class), anyListOf(String.class), anyInt(), anyListOf(Log.class))).then(getGetLogsAnswer(1)); + when(persistence.getLogs(anyString(), anyCollection(), anyInt(), anyList())) + .thenAnswer(getGetLogsAnswer(1)); when(persistence.countLogs(TEST_GROUP)).thenReturn(1); /* Enqueue a log. */ @@ -275,19 +279,20 @@ public void pauseWithCustomIntervalAndResumeBeforeIntervalDue() { DefaultChannel channel = new DefaultChannel(mock(Context.class), UUID.randomUUID().toString(), persistence, mockIngestion, mAppCenterHandler); int batchTimeInterval = 10000; channel.addGroup(TEST_GROUP, 10, batchTimeInterval, MAX_PARALLEL_BATCHES, mockIngestion, mock(Channel.GroupListener.class)); - verifyStatic(never()); + verifyStatic(SharedPreferencesManager.class, never()); SharedPreferencesManager.putLong(eq(START_TIMER_PREFIX + TEST_GROUP), anyLong()); verify(mockIngestion, never()).sendAsync(anyString(), any(UUID.class), any(LogContainer.class), any(ServiceCallback.class)); verify(mAppCenterHandler, never()).postDelayed(any(Runnable.class), anyLong()); /* When we enqueue a log while being paused. */ channel.pauseGroup(TEST_GROUP, null); - when(persistence.getLogs(any(String.class), anyListOf(String.class), anyInt(), anyListOf(Log.class))).then(getGetLogsAnswer(1)); + when(persistence.getLogs(anyString(), anyCollection(), anyInt(), anyList())) + .thenAnswer(getGetLogsAnswer(1)); when(persistence.countLogs(TEST_GROUP)).thenReturn(1); channel.enqueue(mock(Log.class), TEST_GROUP, Flags.DEFAULTS); /* Verify that timer does not start but that the current time is saved for future reference. */ - verifyStatic(); + verifyStatic(SharedPreferencesManager.class); SharedPreferencesManager.putLong(eq(START_TIMER_PREFIX + TEST_GROUP), eq(now)); verify(mockIngestion, never()).sendAsync(anyString(), any(UUID.class), any(LogContainer.class), any(ServiceCallback.class)); verify(mAppCenterHandler, never()).postDelayed(any(Runnable.class), anyLong()); @@ -300,7 +305,7 @@ public void pauseWithCustomIntervalAndResumeBeforeIntervalDue() { channel.resumeGroup(TEST_GROUP, null); /* Check timer is started with remaining time. */ - verify(mAppCenterHandler).postDelayed(notNull(Runnable.class), eq(expectedTimeToWait)); + verify(mAppCenterHandler).postDelayed(notNull(), eq(expectedTimeToWait)); verify(mockIngestion, never()).sendAsync(anyString(), any(UUID.class), any(LogContainer.class), any(ServiceCallback.class)); } @@ -319,19 +324,20 @@ public void pauseWithCustomIntervalAndResumeAfterIntervalDue() { DefaultChannel channel = new DefaultChannel(mock(Context.class), UUID.randomUUID().toString(), persistence, mockIngestion, mAppCenterHandler); int batchTimeInterval = 10000; channel.addGroup(TEST_GROUP, 10, batchTimeInterval, MAX_PARALLEL_BATCHES, mockIngestion, mock(Channel.GroupListener.class)); - verifyStatic(never()); + verifyStatic(SharedPreferencesManager.class, never()); SharedPreferencesManager.putLong(eq(START_TIMER_PREFIX + TEST_GROUP), anyLong()); verify(mockIngestion, never()).sendAsync(anyString(), any(UUID.class), any(LogContainer.class), any(ServiceCallback.class)); verify(mAppCenterHandler, never()).postDelayed(any(Runnable.class), anyLong()); /* When we enqueue a log while being paused. */ channel.pauseGroup(TEST_GROUP, null); - when(persistence.getLogs(any(String.class), anyListOf(String.class), anyInt(), anyListOf(Log.class))).then(getGetLogsAnswer(1)); + when(persistence.getLogs(anyString(), anyCollection(), anyInt(), anyList())) + .thenAnswer(getGetLogsAnswer(1)); when(persistence.countLogs(TEST_GROUP)).thenReturn(1); channel.enqueue(mock(Log.class), TEST_GROUP, Flags.DEFAULTS); /* Verify that timer does not start but that the current time is saved for future reference. */ - verifyStatic(); + verifyStatic(SharedPreferencesManager.class); SharedPreferencesManager.putLong(eq(START_TIMER_PREFIX + TEST_GROUP), eq(now)); verify(mockIngestion, never()).sendAsync(anyString(), any(UUID.class), any(LogContainer.class), any(ServiceCallback.class)); verify(mAppCenterHandler, never()).postDelayed(any(Runnable.class), anyLong()); @@ -342,7 +348,7 @@ public void pauseWithCustomIntervalAndResumeAfterIntervalDue() { channel.resumeGroup(TEST_GROUP, null); /* Check timer is not started and logs send immediately. */ - verify(mAppCenterHandler, never()).postDelayed(notNull(Runnable.class), anyLong()); + verify(mAppCenterHandler, never()).postDelayed(notNull(), anyLong()); verify(mockIngestion).sendAsync(anyString(), any(UUID.class), any(LogContainer.class), any(ServiceCallback.class)); } } diff --git a/sdk/appcenter/src/test/java/com/microsoft/appcenter/channel/DefaultChannelRaceConditionTest.java b/sdk/appcenter/src/test/java/com/microsoft/appcenter/channel/DefaultChannelRaceConditionTest.java index 2e389eec9..952e5b10d 100644 --- a/sdk/appcenter/src/test/java/com/microsoft/appcenter/channel/DefaultChannelRaceConditionTest.java +++ b/sdk/appcenter/src/test/java/com/microsoft/appcenter/channel/DefaultChannelRaceConditionTest.java @@ -26,11 +26,14 @@ import java.util.concurrent.Semaphore; import static com.microsoft.appcenter.channel.DefaultChannel.CLEAR_BATCH_SIZE; -import static org.mockito.Matchers.any; -import static org.mockito.Matchers.anyListOf; -import static org.mockito.Matchers.anyString; -import static org.mockito.Matchers.argThat; -import static org.mockito.Matchers.eq; +import static org.mockito.ArgumentMatchers.anyCollection; +import static org.mockito.ArgumentMatchers.anyList; +import static org.mockito.ArgumentMatchers.isA; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyListOf; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.ArgumentMatchers.argThat; +import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; @@ -46,8 +49,8 @@ public void disabledWhileHandlingIngestionSuccess() { final Semaphore afterCallSemaphore = new Semaphore(0); Persistence mockPersistence = mock(Persistence.class); when(mockPersistence.countLogs(anyString())).thenReturn(1); - when(mockPersistence.getLogs(anyString(), anyListOf(String.class), eq(1), anyListOf(Log.class))).then(getGetLogsAnswer(1)); - when(mockPersistence.getLogs(anyString(), anyListOf(String.class), eq(CLEAR_BATCH_SIZE), anyListOf(Log.class))).then(getGetLogsAnswer(0)); + when(mockPersistence.getLogs(anyString(), anyCollection(), eq(1), anyList())).then(getGetLogsAnswer(1)); + when(mockPersistence.getLogs(anyString(), anyCollection(), eq(CLEAR_BATCH_SIZE), anyList())).then(getGetLogsAnswer(0)); AppCenterIngestion mockIngestion = mock(AppCenterIngestion.class); when(mockIngestion.isEnabled()).thenReturn(true); when(mockIngestion.sendAsync(anyString(), any(UUID.class), any(LogContainer.class), any(ServiceCallback.class))).then(new Answer() { @@ -82,13 +85,7 @@ public void run() { /* Verify handling success was ignored. */ verify(listener, never()).onSuccess(any(Log.class)); - verify(listener).onFailure(any(Log.class), argThat(new ArgumentMatcher() { - - @Override - public boolean matches(Object argument) { - return argument instanceof CancellationException; - } - })); + verify(listener).onFailure(any(Log.class), isA(CancellationException.class)); verify(mockPersistence, never()).deleteLogs(anyString(), anyString()); } @@ -100,8 +97,8 @@ public void disabledWhileHandlingIngestionFailure() { final Semaphore afterCallSemaphore = new Semaphore(0); Persistence mockPersistence = mock(Persistence.class); when(mockPersistence.countLogs(anyString())).thenReturn(1); - when(mockPersistence.getLogs(anyString(), anyListOf(String.class), eq(1), anyListOf(Log.class))).then(getGetLogsAnswer(1)); - when(mockPersistence.getLogs(anyString(), anyListOf(String.class), eq(CLEAR_BATCH_SIZE), anyListOf(Log.class))).then(getGetLogsAnswer(0)); + when(mockPersistence.getLogs(anyString(), anyCollection(), eq(1), anyList())).then(getGetLogsAnswer(1)); + when(mockPersistence.getLogs(anyString(), anyCollection(), eq(CLEAR_BATCH_SIZE), anyList())).then(getGetLogsAnswer(0)); AppCenterIngestion mockIngestion = mock(AppCenterIngestion.class); when(mockIngestion.isEnabled()).thenReturn(true); final Exception mockException = new IOException(); @@ -137,12 +134,6 @@ public void run() { /* Verify handling error was ignored. */ verify(listener, never()).onFailure(any(Log.class), eq(mockException)); - verify(listener).onFailure(any(Log.class), argThat(new ArgumentMatcher() { - - @Override - public boolean matches(Object argument) { - return argument instanceof CancellationException; - } - })); + verify(listener).onFailure(any(Log.class), isA(CancellationException.class)); } } diff --git a/sdk/appcenter/src/test/java/com/microsoft/appcenter/channel/DefaultChannelTest.java b/sdk/appcenter/src/test/java/com/microsoft/appcenter/channel/DefaultChannelTest.java index 9475ded3e..3da2af3f7 100644 --- a/sdk/appcenter/src/test/java/com/microsoft/appcenter/channel/DefaultChannelTest.java +++ b/sdk/appcenter/src/test/java/com/microsoft/appcenter/channel/DefaultChannelTest.java @@ -5,6 +5,26 @@ package com.microsoft.appcenter.channel; +import static com.microsoft.appcenter.Flags.NORMAL; +import static com.microsoft.appcenter.channel.DefaultChannel.START_TIMER_PREFIX; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.mockito.ArgumentMatchers.anyCollection; +import static org.mockito.ArgumentMatchers.anyList; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.anyLong; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.doThrow; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; +import static org.powermock.api.mockito.PowerMockito.spy; +import static org.powermock.api.mockito.PowerMockito.verifyStatic; + import android.content.Context; import android.content.pm.PackageManager; @@ -24,7 +44,6 @@ import org.junit.Test; import org.mockito.ArgumentCaptor; -import org.mockito.Matchers; import org.mockito.invocation.InvocationOnMock; import org.mockito.stubbing.Answer; @@ -35,25 +54,6 @@ import java.util.List; import java.util.UUID; -import static com.microsoft.appcenter.Flags.NORMAL; -import static com.microsoft.appcenter.channel.DefaultChannel.START_TIMER_PREFIX; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.mockito.Matchers.any; -import static org.mockito.Matchers.anyInt; -import static org.mockito.Matchers.anyListOf; -import static org.mockito.Matchers.anyLong; -import static org.mockito.Matchers.anyString; -import static org.mockito.Matchers.eq; -import static org.mockito.Mockito.doThrow; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.never; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; -import static org.powermock.api.mockito.PowerMockito.spy; -import static org.powermock.api.mockito.PowerMockito.verifyStatic; - public class DefaultChannelTest extends AbstractDefaultChannelTest { private static final long CUSTOM_INTERVAL = 10000; @@ -81,7 +81,7 @@ public void analyticsSuccess() throws Persistence.PersistenceException { AppCenterIngestion mockIngestion = mock(AppCenterIngestion.class); when(mockIngestion.isEnabled()).thenReturn(true); Channel.GroupListener mockListener = mock(Channel.GroupListener.class); - when(mockPersistence.getLogs(any(String.class), anyListOf(String.class), anyInt(), anyListOf(Log.class))) + when(mockPersistence.getLogs(anyString(), anyCollection(), anyInt(), anyList())) .then(getGetLogsAnswer(50)).then(getGetLogsAnswer(1)).then(getGetLogsAnswer(2)); when(mockIngestion.sendAsync(anyString(), any(UUID.class), any(LogContainer.class), any(ServiceCallback.class))) .then(getSendAsyncAnswer()); @@ -110,7 +110,7 @@ public void analyticsSuccess() throws Persistence.PersistenceException { verify(mockIngestion).sendAsync(anyString(), any(UUID.class), any(LogContainer.class), any(ServiceCallback.class)); /* Verify that we have called deleteLogs on the Persistence. */ - verify(mockPersistence).deleteLogs(any(String.class), any(String.class)); + verify(mockPersistence).deleteLogs(anyString(), anyString()); /* Verify that we have called onBeforeSending in the listener. */ verify(mockListener, times(50)).onBeforeSending(any(Log.class)); @@ -130,7 +130,7 @@ public void analyticsSuccess() throws Persistence.PersistenceException { assertEquals(1, channel.getGroupState(TEST_GROUP).mPendingLogCount); verify(mockPersistence, times(51)).putLog(any(Log.class), eq(TEST_GROUP), eq(NORMAL)); verify(mockIngestion).sendAsync(anyString(), any(UUID.class), any(LogContainer.class), any(ServiceCallback.class)); - verify(mockPersistence).deleteLogs(any(String.class), any(String.class)); + verify(mockPersistence).deleteLogs(anyString(), anyString()); verify(mockListener, times(50)).onSuccess(any(Log.class)); /* Simulate the timer. */ @@ -138,7 +138,7 @@ public void analyticsSuccess() throws Persistence.PersistenceException { assertEquals(0, channel.getGroupState(TEST_GROUP).mPendingLogCount); verify(mockIngestion, times(2)).sendAsync(anyString(), any(UUID.class), any(LogContainer.class), any(ServiceCallback.class)); - verify(mockPersistence, times(2)).deleteLogs(any(String.class), any(String.class)); + verify(mockPersistence, times(2)).deleteLogs(anyString(), anyString()); verify(mockListener, times(51)).onSuccess(any(Log.class)); /* 2 more timed logs. */ @@ -147,7 +147,7 @@ public void analyticsSuccess() throws Persistence.PersistenceException { assertEquals(2, channel.getGroupState(TEST_GROUP).mPendingLogCount); verify(mockPersistence, times(53)).putLog(any(Log.class), eq(TEST_GROUP), eq(NORMAL)); verify(mockIngestion, times(2)).sendAsync(anyString(), any(UUID.class), any(LogContainer.class), any(ServiceCallback.class)); - verify(mockPersistence, times(2)).deleteLogs(any(String.class), any(String.class)); + verify(mockPersistence, times(2)).deleteLogs(anyString(), anyString()); verify(mockListener, times(51)).onSuccess(any(Log.class)); /* Simulate the timer. */ @@ -155,7 +155,7 @@ public void analyticsSuccess() throws Persistence.PersistenceException { assertEquals(0, channel.getGroupState(TEST_GROUP).mPendingLogCount); verify(mockIngestion, times(3)).sendAsync(anyString(), any(UUID.class), any(LogContainer.class), any(ServiceCallback.class)); - verify(mockPersistence, times(3)).deleteLogs(any(String.class), any(String.class)); + verify(mockPersistence, times(3)).deleteLogs(anyString(), anyString()); verify(mockListener, times(53)).onSuccess(any(Log.class)); /* Check total timers. */ @@ -172,7 +172,7 @@ public void lessLogsThanExpected() { Persistence mockPersistence = mock(Persistence.class); AppCenterIngestion mockIngestion = mock(AppCenterIngestion.class); Channel.GroupListener mockListener = mock(Channel.GroupListener.class); - when(mockPersistence.getLogs(any(String.class), anyListOf(String.class), anyInt(), Matchers.>any())) + when(mockPersistence.getLogs(anyString(), anyCollection(), anyInt(), anyList())) .then(getGetLogsAnswer(40)) .then(getGetLogsAnswer(0)); when(mockIngestion.sendAsync(anyString(), any(UUID.class), any(LogContainer.class), any(ServiceCallback.class))).then(getSendAsyncAnswer()); @@ -209,7 +209,7 @@ public void maxRequests() throws Persistence.PersistenceException { when(mockIngestion.isEnabled()).thenReturn(true); /* We make second request return less logs than expected to make sure counter is reset properly. */ - when(mockPersistence.getLogs(any(String.class), anyListOf(String.class), anyInt(), anyListOf(Log.class))) + when(mockPersistence.getLogs(anyString(), anyCollection(), anyInt(), anyList())) .then(getGetLogsAnswer()) .then(getGetLogsAnswer(49)) .then(getGetLogsAnswer()) @@ -245,11 +245,11 @@ public Object answer(InvocationOnMock invocation) { /* Verify all logs stored, N requests sent, not log deleted yet. */ verify(mockPersistence, times(200)).putLog(any(Log.class), eq(TEST_GROUP), eq(NORMAL)); verify(mockIngestion, times(3)).sendAsync(anyString(), any(UUID.class), any(LogContainer.class), any(ServiceCallback.class)); - verify(mockPersistence, never()).deleteLogs(any(String.class), any(String.class)); + verify(mockPersistence, never()).deleteLogs(anyString(), anyString()); /* Make 1 of the call succeed. Verify log deleted. */ callbacks.get(0).onCallSucceeded(new HttpResponse(200, "")); - verify(mockPersistence).deleteLogs(any(String.class), any(String.class)); + verify(mockPersistence).deleteLogs(anyString(), anyString()); /* The request N+1 is now unlocked. */ verify(mockIngestion, times(4)).sendAsync(anyString(), any(UUID.class), any(LogContainer.class), any(ServiceCallback.class)); @@ -258,7 +258,7 @@ public Object answer(InvocationOnMock invocation) { for (int i = 1; i < 4; i++) { callbacks.get(i).onCallSucceeded(new HttpResponse(200, "")); } - verify(mockPersistence, times(4)).deleteLogs(any(String.class), any(String.class)); + verify(mockPersistence, times(4)).deleteLogs(anyString(), anyString()); /* Wait for timer. */ delayedRunnable.getValue().run(); @@ -271,8 +271,8 @@ public Object answer(InvocationOnMock invocation) { public void maxRequestsInitial() throws Persistence.PersistenceException { Persistence mockPersistence = mock(Persistence.class); AppCenterIngestion mockIngestion = mock(AppCenterIngestion.class); - when(mockPersistence.countLogs(any(String.class))).thenReturn(100); - when(mockPersistence.getLogs(any(String.class), anyListOf(String.class), anyInt(), anyListOf(Log.class))).then(getGetLogsAnswer()); + when(mockPersistence.countLogs(anyString())).thenReturn(100); + when(mockPersistence.getLogs(anyString(), anyCollection(), anyInt(), anyList())).then(getGetLogsAnswer()); final List callbacks = new ArrayList<>(); when(mockIngestion.sendAsync(anyString(), any(UUID.class), any(LogContainer.class), any(ServiceCallback.class))).then(new Answer() { public Object answer(InvocationOnMock invocation) { @@ -297,11 +297,11 @@ public Object answer(InvocationOnMock invocation) { /* Verify all logs stored, N requests sent, not log deleted yet. */ verify(mockPersistence, times(100)).putLog(any(Log.class), eq(TEST_GROUP), eq(NORMAL)); verify(mockIngestion, times(3)).sendAsync(anyString(), any(UUID.class), any(LogContainer.class), any(ServiceCallback.class)); - verify(mockPersistence, never()).deleteLogs(any(String.class), any(String.class)); + verify(mockPersistence, never()).deleteLogs(anyString(), anyString()); /* Make 1 of the call succeed. Verify log deleted. */ callbacks.get(0).onCallSucceeded(new HttpResponse(200, "")); - verify(mockPersistence).deleteLogs(any(String.class), any(String.class)); + verify(mockPersistence).deleteLogs(anyString(), anyString()); /* The request N+1 is now unlocked. */ verify(mockIngestion, times(4)).sendAsync(anyString(), any(UUID.class), any(LogContainer.class), any(ServiceCallback.class)); @@ -309,7 +309,7 @@ public Object answer(InvocationOnMock invocation) { /* Unlock all requests and check logs deleted. */ for (int i = 1; i < 4; i++) callbacks.get(i).onCallSucceeded(new HttpResponse(200, "")); - verify(mockPersistence, times(4)).deleteLogs(any(String.class), any(String.class)); + verify(mockPersistence, times(4)).deleteLogs(anyString(), anyString()); /* The counter should be 0 now as we sent data. */ assertEquals(0, channel.getGroupState(TEST_GROUP).mPendingLogCount); @@ -324,7 +324,7 @@ public void analyticsRecoverable() throws Persistence.PersistenceException { Persistence mockPersistence = mock(Persistence.class); AppCenterIngestion mockIngestion = mock(AppCenterIngestion.class); Channel.GroupListener mockListener = mock(Channel.GroupListener.class); - when(mockPersistence.getLogs(any(String.class), anyListOf(String.class), anyInt(), anyListOf(Log.class))) + when(mockPersistence.getLogs(anyString(), anyCollection(), anyInt(), anyList())) .then(getGetLogsAnswer(50)) .then(getGetLogsAnswer(50)) .then(getGetLogsAnswer(20)); @@ -347,7 +347,7 @@ public void analyticsRecoverable() throws Persistence.PersistenceException { verify(mockIngestion).sendAsync(anyString(), any(UUID.class), any(LogContainer.class), any(ServiceCallback.class)); /* Verify that we have not called deleteLogs on the Persistence. */ - verify(mockPersistence, never()).deleteLogs(any(String.class), any(String.class)); + verify(mockPersistence, never()).deleteLogs(anyString(), anyString()); /* Verify that the Channel is disabled. */ assertFalse(channel.isEnabled()); @@ -382,7 +382,7 @@ public void analyticsRecoverable() throws Persistence.PersistenceException { verify(mockIngestion, times(3)).sendAsync(anyString(), any(UUID.class), any(LogContainer.class), any(ServiceCallback.class)); /* Verify that we have called deleteLogs on the Persistence (2 successful batches, the first call was a recoverable failure). */ - verify(mockPersistence, times(2)).deleteLogs(any(String.class), any(String.class)); + verify(mockPersistence, times(2)).deleteLogs(anyString(), anyString()); /* Verify that we have called onBeforeSending in the listener. getLogs will return 50, 50 and 20. */ verify(mockListener, times(120)).onBeforeSending(any(Log.class)); @@ -400,7 +400,7 @@ public void analyticsRecoverable() throws Persistence.PersistenceException { public void analyticsFatal() throws Exception { Persistence mockPersistence = mock(Persistence.class); AppCenterIngestion mockIngestion = mock(AppCenterIngestion.class); - when(mockPersistence.getLogs(any(String.class), anyListOf(String.class), anyInt(), anyListOf(Log.class))) + when(mockPersistence.getLogs(anyString(), anyCollection(), anyInt(), anyList())) .then(getGetLogsAnswer(50)) /* Second 50 logs will be used for clearing pending states. */ @@ -468,7 +468,7 @@ public void analyticsFatal() throws Exception { verify(mockIngestion, times(2)).sendAsync(anyString(), any(UUID.class), any(LogContainer.class), any(ServiceCallback.class)); /* Verify that we have called deleteLogs on the Persistence for the successful batch after re-enabling. */ - verify(mockPersistence).deleteLogs(any(String.class), any(String.class)); + verify(mockPersistence).deleteLogs(anyString(), anyString()); /* Verify 1 more timer call. */ verify(mAppCenterHandler, times(2)).postDelayed(any(Runnable.class), eq(BATCH_TIME_INTERVAL)); @@ -482,7 +482,7 @@ public void errorLogSuccess() throws Persistence.PersistenceException { Persistence mockPersistence = mock(Persistence.class); Ingestion mockIngestion = mock(Ingestion.class); Channel.GroupListener mockListener = mock(Channel.GroupListener.class); - when(mockPersistence.getLogs(any(String.class), anyListOf(String.class), anyInt(), anyListOf(Log.class))).then(getGetLogsAnswer()); + when(mockPersistence.getLogs(anyString(), anyCollection(), anyInt(), anyList())).then(getGetLogsAnswer()); when(mockIngestion.sendAsync(anyString(), any(UUID.class), any(LogContainer.class), any(ServiceCallback.class))).then(getSendAsyncAnswer()); when(mockIngestion.isEnabled()).thenReturn(true); DefaultChannel channel = new DefaultChannel(mock(Context.class), UUID.randomUUID().toString(), mockPersistence, mockIngestion, mAppCenterHandler); @@ -499,7 +499,7 @@ public void errorLogSuccess() throws Persistence.PersistenceException { verify(mockIngestion, times(2)).sendAsync(anyString(), any(UUID.class), any(LogContainer.class), any(ServiceCallback.class)); /* Verify that we have called deleteLogs on the Persistence. */ - verify(mockPersistence, times(2)).deleteLogs(any(String.class), any(String.class)); + verify(mockPersistence, times(2)).deleteLogs(anyString(), anyString()); /* Verify that we have called onBeforeSending in the listener. */ verify(mockListener, times(2)).onBeforeSending(any(Log.class)); @@ -521,7 +521,7 @@ public void errorLogRecoverable() throws Persistence.PersistenceException { Ingestion mockIngestion = mock(Ingestion.class); Channel.GroupListener mockListener = mock(Channel.GroupListener.class); - when(mockPersistence.getLogs(any(String.class), anyListOf(String.class), anyInt(), anyListOf(Log.class))).then(getGetLogsAnswer(1)); + when(mockPersistence.getLogs(anyString(), anyCollection(), anyInt(), anyList())).then(getGetLogsAnswer(1)); when(mockIngestion.sendAsync(anyString(), any(UUID.class), any(LogContainer.class), any(ServiceCallback.class))).then(getSendAsyncAnswer(new SocketException())).then(getSendAsyncAnswer()); when(mockIngestion.isEnabled()).thenReturn(true); @@ -540,7 +540,7 @@ public void errorLogRecoverable() throws Persistence.PersistenceException { verify(mockIngestion).sendAsync(anyString(), any(UUID.class), any(LogContainer.class), any(ServiceCallback.class)); /* Verify that we have not called deleteLogs on the Persistence. */ - verify(mockPersistence, never()).deleteLogs(any(String.class), any(String.class)); + verify(mockPersistence, never()).deleteLogs(anyString(), anyString()); /* Verify that we have called onBeforeSending in the listener. */ verify(mockListener).onBeforeSending(any(Log.class)); @@ -563,7 +563,7 @@ public void errorLogRecoverable() throws Persistence.PersistenceException { verify(mockIngestion, times(logNumber + 1)).sendAsync(anyString(), any(UUID.class), any(LogContainer.class), any(ServiceCallback.class)); /* Verify that we have called deleteLogs on the Persistence n times. */ - verify(mockPersistence, times(logNumber)).deleteLogs(any(String.class), any(String.class)); + verify(mockPersistence, times(logNumber)).deleteLogs(anyString(), anyString()); /* Verify timer. */ verify(mAppCenterHandler, never()).postDelayed(any(Runnable.class), eq(BATCH_TIME_INTERVAL)); @@ -586,7 +586,7 @@ public void suspendWithFailureCallback() { Persistence mockPersistence = mock(Persistence.class); Channel.GroupListener mockListener = mock(Channel.GroupListener.class); when(mockPersistence.countLogs(anyString())).thenReturn(30); - when(mockPersistence.getLogs(anyString(), anyListOf(String.class), anyInt(), anyListOf(Log.class))).thenAnswer(getGetLogsAnswer(10)); + when(mockPersistence.getLogs(anyString(), anyCollection(), anyInt(), anyList())).thenAnswer(getGetLogsAnswer(10)); when(mockIngestion.sendAsync(anyString(), any(UUID.class), any(LogContainer.class), any(ServiceCallback.class))) /* Simulate waiting for response for the first batch. */ .then(new Answer() { @@ -611,7 +611,7 @@ public Object answer(InvocationOnMock invocation) { /* 30 from countLogs and 10 new logs from getLogs. */ verify(mockListener, times(40)).onBeforeSending(any(Log.class)); - verify(mockListener, times(40)).onFailure(any(Log.class), any(SocketException.class)); + verify(mockListener, times(40)).onFailure(any(Log.class), any()); assertFalse(channel.isEnabled()); } @@ -620,7 +620,7 @@ public void suspendWithoutFailureCallback() { Ingestion mockIngestion = mock(Ingestion.class); Persistence mockPersistence = mock(Persistence.class); when(mockPersistence.countLogs(anyString())).thenReturn(3); - when(mockPersistence.getLogs(anyString(), anyListOf(String.class), anyInt(), anyListOf(Log.class))).thenAnswer(getGetLogsAnswer(1)); + when(mockPersistence.getLogs(anyString(), anyCollection(), anyInt(), anyList())).thenAnswer(getGetLogsAnswer(1)); when(mockIngestion.sendAsync(anyString(), any(UUID.class), any(LogContainer.class), any(ServiceCallback.class))) /* Simulate waiting for response for the first batch. */ .then(new Answer() { @@ -684,7 +684,7 @@ public void setEnabled() throws IOException { when(ingestion.isEnabled()).thenReturn(true); doThrow(new IOException()).when(ingestion).close(); Persistence persistence = mock(Persistence.class); - when(persistence.getLogs(anyString(), anyListOf(String.class), anyInt(), anyListOf(Log.class))).thenAnswer(getGetLogsAnswer(1)); + when(persistence.getLogs(anyString(), anyCollection(), anyInt(), anyList())).thenAnswer(getGetLogsAnswer(1)); DefaultChannel channel = new DefaultChannel(mock(Context.class), UUID.randomUUID().toString(), persistence, ingestion, mAppCenterHandler); Channel.Listener listener = spy(new AbstractChannelListener()); channel.addListener(listener); @@ -717,7 +717,7 @@ public void disableBeforeCheckingPendingLogs() { when(ingestion.isEnabled()).thenReturn(true); Persistence persistence = mock(Persistence.class); final DefaultChannel channel = new DefaultChannel(mock(Context.class), UUID.randomUUID().toString(), persistence, ingestion, mAppCenterHandler); - when(persistence.getLogs(anyString(), anyListOf(String.class), anyInt(), anyListOf(Log.class))).thenAnswer(getGetLogsAnswer(1)); + when(persistence.getLogs(anyString(), anyCollection(), anyInt(), anyList())).thenAnswer(getGetLogsAnswer(1)); when(ingestion.sendAsync(anyString(), any(UUID.class), any(LogContainer.class), any(ServiceCallback.class))).thenAnswer(new Answer() { @Override public Void answer(InvocationOnMock invocation) { @@ -744,7 +744,7 @@ public void initialLogs() throws IOException { doThrow(new IOException()).when(ingestion).close(); Persistence persistence = mock(Persistence.class); when(persistence.countLogs(anyString())).thenReturn(3); - when(persistence.getLogs(anyString(), anyListOf(String.class), anyInt(), anyListOf(Log.class))).thenAnswer(getGetLogsAnswer(3)); + when(persistence.getLogs(anyString(), anyCollection(), anyInt(), anyList())).thenAnswer(getGetLogsAnswer(3)); /* Create channel. */ DefaultChannel channel = new DefaultChannel(mock(Context.class), UUID.randomUUID().toString(), persistence, ingestion, mAppCenterHandler); @@ -766,7 +766,7 @@ public void initialLogsMoreThan1Batch() throws IOException { doThrow(new IOException()).when(ingestion).close(); Persistence persistence = mock(Persistence.class); when(persistence.countLogs(anyString())).thenReturn(103); - when(persistence.getLogs(anyString(), anyListOf(String.class), anyInt(), anyListOf(Log.class))).thenAnswer(getGetLogsAnswer(50)).thenAnswer(getGetLogsAnswer(50)).thenAnswer(getGetLogsAnswer(3)); + when(persistence.getLogs(anyString(), anyCollection(), anyInt(), anyList())).thenAnswer(getGetLogsAnswer(50)).thenAnswer(getGetLogsAnswer(50)).thenAnswer(getGetLogsAnswer(3)); DefaultChannel channel = new DefaultChannel(mock(Context.class), UUID.randomUUID().toString(), persistence, ingestion, mAppCenterHandler); channel.addGroup(TEST_GROUP, 50, BATCH_TIME_INTERVAL, MAX_PARALLEL_BATCHES, null, null); verify(ingestion, times(2)).sendAsync(anyString(), any(UUID.class), any(LogContainer.class), any(ServiceCallback.class)); @@ -786,7 +786,7 @@ public void initialLogsThenDisable() throws IOException { doThrow(new IOException()).when(ingestion).close(); Persistence persistence = mock(Persistence.class); when(persistence.countLogs(anyString())).thenReturn(3); - when(persistence.getLogs(anyString(), anyListOf(String.class), anyInt(), anyListOf(Log.class))).thenAnswer(getGetLogsAnswer(3)); + when(persistence.getLogs(anyString(), anyCollection(), anyInt(), anyList())).thenAnswer(getGetLogsAnswer(3)); DefaultChannel channel = new DefaultChannel(mock(Context.class), UUID.randomUUID().toString(), persistence, ingestion, mAppCenterHandler); channel.addGroup(TEST_GROUP, 50, BATCH_TIME_INTERVAL, MAX_PARALLEL_BATCHES, null, null); assertEquals(3, channel.getGroupState(TEST_GROUP).mPendingLogCount); @@ -846,7 +846,7 @@ public void invokeCallbacksAfterSuspendFatal() { when(mockIngestion.isEnabled()).thenReturn(true); Channel.GroupListener mockListener = mock(Channel.GroupListener.class); - when(mockPersistence.getLogs(eq(TEST_GROUP), anyListOf(String.class), anyInt(), anyListOf(Log.class))) + when(mockPersistence.getLogs(eq(TEST_GROUP), anyCollection(), anyInt(), anyList())) .then(getGetLogsAnswer(1)) /* Logs from here will be used TEST_GROUP to clear pending states. */ .then(getGetLogsAnswer(DefaultChannel.CLEAR_BATCH_SIZE)) @@ -878,7 +878,7 @@ public void invokeCallbacksAfterSuspendFatalNoListener() { Channel.GroupListener mockListener = mock(Channel.GroupListener.class); /* Simulate a lot of logs already in database. */ - when(mockPersistence.getLogs(eq(TEST_GROUP), anyListOf(String.class), anyInt(), anyListOf(Log.class))) + when(mockPersistence.getLogs(eq(TEST_GROUP), anyCollection(), anyInt(), anyList())) .then(getGetLogsAnswer(1)) .then(getGetLogsAnswer(1)) .then(getGetLogsAnswer(DefaultChannel.CLEAR_BATCH_SIZE)); @@ -909,7 +909,7 @@ public void invokeCallbacksAfterSuspendRecoverable() { when(mockIngestion.isEnabled()).thenReturn(true); Channel.GroupListener mockListener = mock(Channel.GroupListener.class); - when(mockPersistence.getLogs(eq(TEST_GROUP), anyListOf(String.class), anyInt(), anyListOf(Log.class))) + when(mockPersistence.getLogs(eq(TEST_GROUP), anyCollection(), anyInt(), anyList())) .then(getGetLogsAnswer(1)); when(mockIngestion.sendAsync(anyString(), any(UUID.class), any(LogContainer.class), any(ServiceCallback.class))).then(getSendAsyncAnswer(new HttpException(new HttpResponse(503)))); @@ -978,7 +978,7 @@ public void checkPendingLogsStoresStartTime() { channel.addGroup(TEST_GROUP, 10, CUSTOM_INTERVAL, MAX_PARALLEL_BATCHES, mockIngestion, mock(Channel.GroupListener.class)); /* Verify that timer starts and current time is saved into preferences. */ - verifyStatic(); + verifyStatic(SharedPreferencesManager.class); SharedPreferencesManager.putLong(eq(START_TIMER_PREFIX + TEST_GROUP), eq(now)); verify(mAppCenterHandler).postDelayed(any(Runnable.class), eq(CUSTOM_INTERVAL)); } @@ -999,7 +999,7 @@ public void checkPendingLogsDoesNotStartTimerWithoutLogs() { channel.addGroup(TEST_GROUP, 10, CUSTOM_INTERVAL, MAX_PARALLEL_BATCHES, mockIngestion, mock(Channel.GroupListener.class)); /* Verify that timer isn't started. */ - verifyStatic(never()); + verifyStatic(SharedPreferencesManager.class, never()); SharedPreferencesManager.putLong(eq(START_TIMER_PREFIX + TEST_GROUP), eq(now)); verify(mAppCenterHandler, never()).postDelayed(any(Runnable.class), eq(CUSTOM_INTERVAL)); } @@ -1024,7 +1024,7 @@ public void checkPendingLogsResumesStartTime() { channel.addGroup(TEST_GROUP, 10, CUSTOM_INTERVAL, MAX_PARALLEL_BATCHES, mockIngestion, mock(Channel.GroupListener.class)); /* Do not replace start timer value. */ - verifyStatic(never()); + verifyStatic(SharedPreferencesManager.class, never()); SharedPreferencesManager.putLong(eq(START_TIMER_PREFIX + TEST_GROUP), any(long.class)); /* Start timer for remaining time. */ @@ -1051,7 +1051,7 @@ public void checkPendingLogsReplacesInvalidStartTime() { channel.addGroup(TEST_GROUP, 10, CUSTOM_INTERVAL, MAX_PARALLEL_BATCHES, mockIngestion, mock(Channel.GroupListener.class)); /* Verify that start time is replaced. */ - verifyStatic(); + verifyStatic(SharedPreferencesManager.class); SharedPreferencesManager.putLong(eq(START_TIMER_PREFIX + TEST_GROUP), any(long.class)); /* Start timer for whole interval. */ @@ -1072,7 +1072,7 @@ public void checkPendingLogsTriggersIngestionIfTimerIsOver() { /* Create channel and group. */ Persistence mockPersistence = mock(Persistence.class); when(mockPersistence.countLogs(TEST_GROUP)).thenReturn(5); - when(mockPersistence.getLogs(any(String.class), anyListOf(String.class), anyInt(), anyListOf(Log.class))).then(getGetLogsAnswer(5)); + when(mockPersistence.getLogs(anyString(), anyCollection(), anyInt(), anyList())).then(getGetLogsAnswer(5)); AppCenterIngestion mockIngestion = mock(AppCenterIngestion.class); when(mockIngestion.isEnabled()).thenReturn(true); DefaultChannel channel = new DefaultChannel(mock(Context.class), UUID.randomUUID().toString(), mockPersistence, mockIngestion, mAppCenterHandler); @@ -1098,7 +1098,7 @@ public void checkPendingLogsSendsAllBatchesIfTimerIsOver() { /* Mock persistence. */ Persistence mockPersistence = mock(Persistence.class); - when(mockPersistence.getLogs(any(String.class), anyListOf(String.class), anyInt(), anyListOf(Log.class))) + when(mockPersistence.getLogs(anyString(), anyCollection(), anyInt(), anyList())) .then(getGetLogsAnswer()) .then(getGetLogsAnswer(50)) .then(getGetLogsAnswer(50)) @@ -1146,7 +1146,7 @@ public Object answer(InvocationOnMock invocation) { /* Successful finish one of sending the log. */ callbacks.get(0).onCallSucceeded(new HttpResponse(200, "")); - verify(mockPersistence).deleteLogs(any(String.class), any(String.class)); + verify(mockPersistence).deleteLogs(anyString(), anyString()); /* Check rest logs sending. */ verify(mockIngestion, times(4)).sendAsync(anyString(), any(UUID.class), any(LogContainer.class), any(ServiceCallback.class)); @@ -1162,7 +1162,7 @@ public void setNetworkRequest() { /* Create channel and group. */ Persistence mockPersistence = mock(Persistence.class); when(mockPersistence.countLogs(TEST_GROUP)).thenReturn(5); - when(mockPersistence.getLogs(anyString(), anyListOf(String.class), anyInt(), anyListOf(Log.class))).then(getGetLogsAnswer(1)); + when(mockPersistence.getLogs(anyString(), anyCollection(), anyInt(), anyList())).then(getGetLogsAnswer(1)); AppCenterIngestion mockIngestion = mock(AppCenterIngestion.class); DefaultChannel channel = new DefaultChannel(mock(Context.class), UUID.randomUUID().toString(), mockPersistence, mockIngestion, mAppCenterHandler); diff --git a/sdk/appcenter/src/test/java/com/microsoft/appcenter/channel/OneCollectorChannelListenerTest.java b/sdk/appcenter/src/test/java/com/microsoft/appcenter/channel/OneCollectorChannelListenerTest.java index bc9a49ffe..891050dd4 100644 --- a/sdk/appcenter/src/test/java/com/microsoft/appcenter/channel/OneCollectorChannelListenerTest.java +++ b/sdk/appcenter/src/test/java/com/microsoft/appcenter/channel/OneCollectorChannelListenerTest.java @@ -37,12 +37,13 @@ import static org.junit.Assert.assertNotEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; -import static org.mockito.Matchers.any; -import static org.mockito.Matchers.anyInt; -import static org.mockito.Matchers.anyString; -import static org.mockito.Matchers.argThat; -import static org.mockito.Matchers.eq; -import static org.mockito.Matchers.same; +import static org.mockito.ArgumentMatchers.isA; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.ArgumentMatchers.argThat; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.ArgumentMatchers.same; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.times; @@ -63,13 +64,7 @@ public void addCorrespondingGroup() { listener.onGroupAdded(TEST_GROUP, groupListener, batchTimeInterval); /* Verify one collector group added. */ - verify(channel).addGroup(eq(TEST_GROUP + ONE_COLLECTOR_GROUP_NAME_SUFFIX), eq(ONE_COLLECTOR_TRIGGER_COUNT), eq(batchTimeInterval), eq(ONE_COLLECTOR_TRIGGER_MAX_PARALLEL_REQUESTS), argThat(new ArgumentMatcher() { - - @Override - public boolean matches(Object argument) { - return argument instanceof OneCollectorIngestion; - } - }), same(groupListener)); + verify(channel).addGroup(eq(TEST_GROUP + ONE_COLLECTOR_GROUP_NAME_SUFFIX), eq(ONE_COLLECTOR_TRIGGER_COUNT), eq(batchTimeInterval), eq(ONE_COLLECTOR_TRIGGER_MAX_PARALLEL_REQUESTS), isA(OneCollectorIngestion.class), same(groupListener)); /* Mock one collector group added callback, should not loop indefinitely. */ listener.onGroupAdded(TEST_GROUP + ONE_COLLECTOR_GROUP_NAME_SUFFIX, groupListener, batchTimeInterval); diff --git a/sdk/appcenter/src/test/java/com/microsoft/appcenter/http/DefaultHttpClientTest.java b/sdk/appcenter/src/test/java/com/microsoft/appcenter/http/DefaultHttpClientTest.java index 820f1ca27..797c217b9 100644 --- a/sdk/appcenter/src/test/java/com/microsoft/appcenter/http/DefaultHttpClientTest.java +++ b/sdk/appcenter/src/test/java/com/microsoft/appcenter/http/DefaultHttpClientTest.java @@ -42,7 +42,6 @@ import java.util.List; import java.util.Map; import java.util.UUID; -import java.util.concurrent.Executor; import java.util.concurrent.RejectedExecutionException; import java.util.concurrent.Semaphore; import java.util.concurrent.atomic.AtomicReference; @@ -50,7 +49,6 @@ import java.util.zip.GZIPOutputStream; import javax.net.ssl.HttpsURLConnection; -import javax.net.ssl.SSLSocketFactory; import static com.microsoft.appcenter.http.DefaultHttpClient.METHOD_GET; import static com.microsoft.appcenter.http.DefaultHttpClient.METHOD_POST; @@ -58,19 +56,21 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.fail; -import static org.mockito.Matchers.anyInt; -import static org.mockito.Matchers.anyMapOf; -import static org.mockito.Matchers.anyString; -import static org.mockito.Matchers.argThat; -import static org.mockito.Matchers.eq; +import static org.mockito.ArgumentMatchers.contains; +import static org.mockito.ArgumentMatchers.isA; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.anyMapOf; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.ArgumentMatchers.argThat; +import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.any; import static org.mockito.Mockito.doThrow; import static org.mockito.Mockito.never; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoInteractions; import static org.mockito.Mockito.verifyNoMoreInteractions; -import static org.mockito.Mockito.verifyZeroInteractions; import static org.mockito.Mockito.when; import static org.powermock.api.mockito.PowerMockito.doAnswer; import static org.powermock.api.mockito.PowerMockito.mock; @@ -118,7 +118,7 @@ public Object answer(InvocationOnMock invocation) { (ServiceCallback) invocation.getArguments()[4], (DefaultHttpClientCallTask.Tracker) invocation.getArguments()[5], (boolean) invocation.getArguments()[6])); - when(call.executeOnExecutor(any(Executor.class))).then(new Answer() { + when(call.executeOnExecutor(any())).then(new Answer() { @Override public DefaultHttpClientCallTask answer(InvocationOnMock invocation) { @@ -181,13 +181,7 @@ private void testTls1_2Setting(int apiLevel, int tlsSetExpectedCalls) throws Exc DefaultHttpClient httpClient = new DefaultHttpClient(); TestUtils.setInternalState(Build.VERSION.class, "SDK_INT", apiLevel); httpClient.callAsync(urlString, METHOD_POST, new HashMap(), null, mock(ServiceCallback.class)); - verify(urlConnection, times(tlsSetExpectedCalls)).setSSLSocketFactory(argThat(new ArgumentMatcher() { - - @Override - public boolean matches(Object argument) { - return argument instanceof TLS1_2SocketFactory; - } - })); + verify(urlConnection, times(tlsSetExpectedCalls)).setSSLSocketFactory(isA(TLS1_2SocketFactory.class)); } @Test @@ -244,13 +238,13 @@ public void post200() throws Exception { assertEquals("{a:1,b:2}", sentPayload); /* Verify socket tagged to avoid strict mode error. */ - verifyStatic(); + verifyStatic(TrafficStats.class); TrafficStats.setThreadStatsTag(anyInt()); - verifyStatic(); + verifyStatic(TrafficStats.class); TrafficStats.clearThreadStatsTag(); /* We enabled verbose and it's json, check pretty print. */ - verifyStatic(); + verifyStatic(AppCenterLog.class); AppCenterLog.verbose(AppCenterLog.LOG_TAG, prettyString); } @@ -461,14 +455,8 @@ private void testPayloadLogging(final String payload, String mimeType) throws Ex httpClient.close(); /* Test binary placeholder used in logging code instead of real payload. */ - verifyStatic(); - AppCenterLog.verbose(anyString(), argThat(new ArgumentMatcher() { - - @Override - public boolean matches(Object argument) { - return argument.toString().contains(payload); - } - })); + verifyStatic(AppCenterLog.class); + AppCenterLog.verbose(anyString(), contains(payload)); } @Test @@ -521,14 +509,8 @@ public void hideSecretsInResponse() throws Exception { httpClient.close(); /* Test binary placeholder used in logging code instead of real payload. */ - verifyStatic(); - AppCenterLog.verbose(anyString(), argThat(new ArgumentMatcher() { - - @Override - public boolean matches(Object argument) { - return argument.toString().contains(expectedResponseString); - } - })); + verifyStatic(AppCenterLog.class); + AppCenterLog.verbose(anyString(), contains(expectedResponseString)); } @Test @@ -575,12 +557,11 @@ public void get200image() throws Exception { httpClient.close(); /* Test binary placeholder used in logging code instead of real payload. */ - verifyStatic(); + verifyStatic(AppCenterLog.class); AppCenterLog.verbose(anyString(), argThat(new ArgumentMatcher() { @Override - public boolean matches(Object argument) { - String logMessage = argument.toString(); + public boolean matches(String logMessage) { return logMessage.contains("") && !logMessage.contains("fake binary"); } })); @@ -646,9 +627,9 @@ public void error503() throws Exception { verify(urlConnection).disconnect(); /* Verify socket tagged to avoid strict mode error. */ - verifyStatic(); + verifyStatic(TrafficStats.class); TrafficStats.setThreadStatsTag(anyInt()); - verifyStatic(); + verifyStatic(TrafficStats.class); TrafficStats.clearThreadStatsTag(); } @@ -687,7 +668,7 @@ public Object answer(InvocationOnMock invocation) { (DefaultHttpClientCallTask.Tracker) invocation.getArguments()[5], (boolean) invocation.getArguments()[6])); callTask.set(call); - when(call.executeOnExecutor(any(Executor.class))).then(new Answer() { + when(call.executeOnExecutor(any())).then(new Answer() { @Override public DefaultHttpClientCallTask answer(InvocationOnMock invocation) { @@ -855,8 +836,8 @@ public void failedConnection() throws Exception { mockCall(); httpClient.callAsync(urlString, "", new HashMap(), callTemplate, serviceCallback); verify(serviceCallback).onCallFailed(exception); - verifyZeroInteractions(callTemplate); - verifyZeroInteractions(serviceCallback); + verifyNoInteractions(callTemplate); + verifyNoMoreInteractions(serviceCallback); } @Test @@ -878,11 +859,11 @@ public void failedToWritePayload() throws Exception { mockCall(); httpClient.callAsync(urlString, METHOD_POST, new HashMap(), callTemplate, serviceCallback); verify(serviceCallback).onCallFailed(exception); - verifyZeroInteractions(serviceCallback); + verifyNoMoreInteractions(serviceCallback); verify(out).close(); - verifyStatic(); + verifyStatic(TrafficStats.class); TrafficStats.setThreadStatsTag(anyInt()); - verifyStatic(); + verifyStatic(TrafficStats.class); TrafficStats.clearThreadStatsTag(); } @@ -907,11 +888,11 @@ public void failedToReadResponse() throws Exception { mockCall(); httpClient.callAsync(urlString, "", new HashMap(), callTemplate, serviceCallback); verify(serviceCallback).onCallFailed(exception); - verifyZeroInteractions(serviceCallback); + verifyNoMoreInteractions(serviceCallback); verify(inputStream).close(); - verifyStatic(); + verifyStatic(TrafficStats.class); TrafficStats.setThreadStatsTag(anyInt()); - verifyStatic(); + verifyStatic(TrafficStats.class); TrafficStats.clearThreadStatsTag(); } @@ -931,10 +912,10 @@ public void failedWithError() throws Exception { fail(); } catch (Error ignored) { } - verifyZeroInteractions(serviceCallback); - verifyStatic(); + verifyNoInteractions(serviceCallback); + verifyStatic(TrafficStats.class); TrafficStats.setThreadStatsTag(anyInt()); - verifyStatic(); + verifyStatic(TrafficStats.class); TrafficStats.clearThreadStatsTag(); } @@ -958,13 +939,13 @@ public void failedSerialization() throws Exception { verify(serviceCallback).onCallFailed(exception); verifyNoMoreInteractions(serviceCallback); verify(urlConnection).disconnect(); - verifyStatic(); + verifyStatic(TrafficStats.class); TrafficStats.setThreadStatsTag(anyInt()); - verifyStatic(); + verifyStatic(TrafficStats.class); TrafficStats.clearThreadStatsTag(); } - @Test + @Test(timeout = 5000) @PrepareForTest(HandlerUtils.class) public void rejectedAsyncTask() throws Exception { @@ -992,7 +973,7 @@ public void run() { DefaultHttpClientCallTask call = mock(DefaultHttpClientCallTask.class); whenNew(DefaultHttpClientCallTask.class).withAnyArguments().thenReturn(call); RejectedExecutionException exception = new RejectedExecutionException(); - when(call.executeOnExecutor(any(Executor.class))).thenThrow(exception); + when(call.executeOnExecutor(any())).thenThrow(exception); DefaultHttpClient httpClient = new DefaultHttpClient(); /* Test. */ @@ -1065,13 +1046,8 @@ public void sendGzipWithoutVerboseLogging() throws Exception { assertArrayEquals(compressedBytes, buffer.toByteArray()); /* Check no payload logging since log level not enabled. */ - verifyStatic(never()); - AppCenterLog.verbose(anyString(), argThat(new ArgumentMatcher() { - @Override - public boolean matches(Object argument) { - return argument.toString().contains(payload); - } - })); + verifyStatic(AppCenterLog.class, never()); + AppCenterLog.verbose(anyString(), contains(payload)); } @Test @@ -1127,13 +1103,8 @@ public void sendNoGzipWhenCompressionDisabled() throws Exception { assertEquals(payload, buffer.toString()); /* Check payload logged but not as JSON since different content type. */ - verifyStatic(); - AppCenterLog.verbose(anyString(), argThat(new ArgumentMatcher() { - @Override - public boolean matches(Object argument) { - return argument.toString().contains(payload); - } - })); + verifyStatic(AppCenterLog.class); + AppCenterLog.verbose(anyString(), contains(payload)); } @Test @@ -1189,13 +1160,8 @@ public void sendNoGzipWithPlainTextVerboseLogging() throws Exception { assertEquals(payload, buffer.toString()); /* Check payload logged but not as JSON since different content type. */ - verifyStatic(); - AppCenterLog.verbose(anyString(), argThat(new ArgumentMatcher() { - @Override - public boolean matches(Object argument) { - return argument.toString().contains(payload); - } - })); + verifyStatic(AppCenterLog.class); + AppCenterLog.verbose(anyString(), contains(payload)); } @Test @@ -1258,13 +1224,8 @@ public void sendGzipWithHugeTextAndVerboseLogging() throws Exception { assertArrayEquals(compressedBytes, buffer.toByteArray()); /* Check payload logged. */ - verifyStatic(); - AppCenterLog.verbose(anyString(), argThat(new ArgumentMatcher() { - @Override - public boolean matches(Object argument) { - return argument.toString().contains(payload); - } - })); + verifyStatic(AppCenterLog.class); + AppCenterLog.verbose(anyString(), contains(payload)); } @Test @@ -1279,8 +1240,8 @@ public void failedToConnectWithHttpUrl() throws Exception { mockCall(); httpClient.callAsync(urlString, "", new HashMap(), callTemplate, serviceCallback); verify(serviceCallback).onCallFailed(any(IOException.class)); - verifyZeroInteractions(callTemplate); - verifyZeroInteractions(serviceCallback); + verifyNoInteractions(callTemplate); + verifyNoMoreInteractions(serviceCallback); } @Test @@ -1295,8 +1256,8 @@ public void failedToConnectWithInvalidUrl() throws Exception { mockCall(); httpClient.callAsync(urlString, "", new HashMap(), callTemplate, serviceCallback); verify(serviceCallback).onCallFailed(any(IOException.class)); - verifyZeroInteractions(callTemplate); - verifyZeroInteractions(serviceCallback); + verifyNoInteractions(callTemplate); + verifyNoMoreInteractions(serviceCallback); } @Test @@ -1313,7 +1274,7 @@ public void failedToConnectWithHttpConnect() throws Exception { mockCall(); httpClient.callAsync(urlString, "", new HashMap(), callTemplate, serviceCallback); verify(serviceCallback).onCallFailed(any(IOException.class)); - verifyZeroInteractions(callTemplate); - verifyZeroInteractions(serviceCallback); + verifyNoInteractions(callTemplate); + verifyNoMoreInteractions(serviceCallback); } } diff --git a/sdk/appcenter/src/test/java/com/microsoft/appcenter/http/HttpClientNetworkStateHandlerTest.java b/sdk/appcenter/src/test/java/com/microsoft/appcenter/http/HttpClientNetworkStateHandlerTest.java index f9feaa85d..28fe8d847 100644 --- a/sdk/appcenter/src/test/java/com/microsoft/appcenter/http/HttpClientNetworkStateHandlerTest.java +++ b/sdk/appcenter/src/test/java/com/microsoft/appcenter/http/HttpClientNetworkStateHandlerTest.java @@ -25,8 +25,8 @@ import java.util.concurrent.CountDownLatch; import static com.microsoft.appcenter.http.DefaultHttpClient.METHOD_GET; -import static org.mockito.Matchers.any; -import static org.mockito.Matchers.eq; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.spy; @@ -234,7 +234,7 @@ public void cancelRunningCall() throws IOException { verify(httpClient).close(); } - @Test(timeout=3000) + @Test(timeout = 3000) public void changeNetworkConnectionDuringCallWithoutDeadlock() throws Exception { TestUtils.setInternalState(Build.VERSION.class, "SDK_INT", Build.VERSION_CODES.LOLLIPOP); @@ -283,7 +283,7 @@ public void run() { /* Simulate network lost event. */ ArgumentCaptor callback = ArgumentCaptor.forClass(ConnectivityManager.NetworkCallback.class); - verify(connectivityManager).registerNetworkCallback(any(NetworkRequest.class), callback.capture()); + verify(connectivityManager).registerNetworkCallback(any(), callback.capture()); callback.getValue().onAvailable(mock(Network.class)); /* Clear the state. */ diff --git a/sdk/appcenter/src/test/java/com/microsoft/appcenter/http/HttpClientRetryerTest.java b/sdk/appcenter/src/test/java/com/microsoft/appcenter/http/HttpClientRetryerTest.java index da3597821..22ac0ea9b 100644 --- a/sdk/appcenter/src/test/java/com/microsoft/appcenter/http/HttpClientRetryerTest.java +++ b/sdk/appcenter/src/test/java/com/microsoft/appcenter/http/HttpClientRetryerTest.java @@ -5,6 +5,18 @@ package com.microsoft.appcenter.http; +import static com.microsoft.appcenter.http.DefaultHttpClient.CONTENT_TYPE_KEY; +import static com.microsoft.appcenter.http.DefaultHttpClient.CONTENT_TYPE_VALUE; +import static com.microsoft.appcenter.http.DefaultHttpClient.X_MS_RETRY_AFTER_MS_HEADER; +import static org.mockito.ArgumentMatchers.anyLong; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.ArgumentMatchers.longThat; +import static org.mockito.Mockito.any; +import static org.mockito.Mockito.doAnswer; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoMoreInteractions; + import android.os.Handler; import org.junit.Test; @@ -17,20 +29,6 @@ import java.util.HashMap; import java.util.Map; -import static com.microsoft.appcenter.http.DefaultHttpClient.CONTENT_TYPE_KEY; -import static com.microsoft.appcenter.http.DefaultHttpClient.CONTENT_TYPE_VALUE; -import static com.microsoft.appcenter.http.DefaultHttpClient.X_MS_RETRY_AFTER_MS_HEADER; -import static org.mockito.Matchers.anyLong; -import static org.mockito.Matchers.anyMapOf; -import static org.mockito.Matchers.anyString; -import static org.mockito.Matchers.eq; -import static org.mockito.Matchers.longThat; -import static org.mockito.Mockito.any; -import static org.mockito.Mockito.doAnswer; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.verifyNoMoreInteractions; - @SuppressWarnings("unused") public class HttpClientRetryerTest { @@ -50,23 +48,15 @@ private static void verifyDelay(Handler handler, final int retryIndex) { verify(handler).postDelayed(any(Runnable.class), longThat(new ArgumentMatcher() { @Override - public boolean matches(Object argument) { - long interval = (Long) argument; + public boolean matches(Long interval) { long retryInterval = HttpClientRetryer.RETRY_INTERVALS[retryIndex]; return interval >= retryInterval / 2 && interval <= retryInterval; } })); } - private static void verifyDelayFromHeader(Handler handler, final long retryAfter) { - verify(handler).postDelayed(any(Runnable.class), longThat(new ArgumentMatcher() { - - @Override - public boolean matches(Object argument) { - long interval = (Long) argument; - return interval == retryAfter; - } - })); + private static void verifyDelayFromHeader(Handler handler, long retryAfter) { + verify(handler).postDelayed(any(Runnable.class), eq(retryAfter)); } @Test @@ -81,7 +71,7 @@ public ServiceCall answer(InvocationOnMock invocationOnMock) { ((ServiceCallback) invocationOnMock.getArguments()[4]).onCallSucceeded(new HttpResponse(200, "mockSuccessPayload")); return call; } - }).when(httpClient).callAsync(anyString(), anyString(), anyMapOf(String.class, String.class), any(HttpClient.CallTemplate.class), any(ServiceCallback.class)); + }).when(httpClient).callAsync(any(), any(), any(), any(), any(ServiceCallback.class)); HttpClientRetryer retryer = new HttpClientRetryer(httpClient); retryer.callAsync(null, null, null, null, callback); verify(callback).onCallSucceeded(eq(new HttpResponse(200, "mockSuccessPayload"))); @@ -107,7 +97,7 @@ public ServiceCall answer(InvocationOnMock invocationOnMock) { ((ServiceCallback) invocationOnMock.getArguments()[4]).onCallSucceeded(new HttpResponse(200, "mockSuccessPayload")); return mock(ServiceCall.class); } - }).when(httpClient).callAsync(anyString(), anyString(), anyMapOf(String.class, String.class), any(HttpClient.CallTemplate.class), any(ServiceCallback.class)); + }).when(httpClient).callAsync(any(), any(), any(), any(), any(ServiceCallback.class)); Handler handler = mock(Handler.class); HttpClient retryer = new HttpClientRetryer(httpClient, handler); simulateRetryAfterDelay(handler); @@ -137,7 +127,7 @@ public ServiceCall answer(InvocationOnMock invocationOnMock) { ((ServiceCallback) invocationOnMock.getArguments()[4]).onCallFailed(expectedException); return mock(ServiceCall.class); } - }).when(httpClient).callAsync(anyString(), anyString(), anyMapOf(String.class, String.class), any(HttpClient.CallTemplate.class), any(ServiceCallback.class)); + }).when(httpClient).callAsync(any(), any(), any(), any(), any(ServiceCallback.class)); Handler handler = mock(Handler.class); HttpClient retryer = new HttpClientRetryer(httpClient, handler); simulateRetryAfterDelay(handler); @@ -161,7 +151,7 @@ public ServiceCall answer(InvocationOnMock invocationOnMock) { ((ServiceCallback) invocationOnMock.getArguments()[4]).onCallFailed(new HttpException(new HttpResponse(408))); return call; } - }).when(httpClient).callAsync(anyString(), anyString(), anyMapOf(String.class, String.class), any(HttpClient.CallTemplate.class), any(ServiceCallback.class)); + }).when(httpClient).callAsync(any(), any(), any(), any(), any(ServiceCallback.class)); Handler handler = mock(Handler.class); HttpClient retryer = new HttpClientRetryer(httpClient, handler); simulateRetryAfterDelay(handler); @@ -201,7 +191,7 @@ public ServiceCall answer(InvocationOnMock invocationOnMock) { ((ServiceCallback) invocationOnMock.getArguments()[4]).onCallSucceeded(new HttpResponse(200, "mockSuccessPayload")); return mock(ServiceCall.class); } - }).when(httpClient).callAsync(anyString(), anyString(), anyMapOf(String.class, String.class), any(HttpClient.CallTemplate.class), any(ServiceCallback.class)); + }).when(httpClient).callAsync(any(), any(), any(), any(), any(ServiceCallback.class)); Handler handler = mock(Handler.class); HttpClient retryer = new HttpClientRetryer(httpClient, handler); simulateRetryAfterDelay(handler); @@ -228,7 +218,7 @@ public ServiceCall answer(InvocationOnMock invocationOnMock) { ((ServiceCallback) invocationOnMock.getArguments()[4]).onCallFailed(new HttpException(new HttpResponse(503))); return call; } - }).when(httpClient).callAsync(anyString(), anyString(), anyMapOf(String.class, String.class), any(HttpClient.CallTemplate.class), any(ServiceCallback.class)); + }).when(httpClient).callAsync(any(), any(), any(), any(), any(ServiceCallback.class)); Handler handler = mock(Handler.class); HttpClient retryer = new HttpClientRetryer(httpClient, handler); retryer.callAsync(null, null, null, null, callback).cancel(); diff --git a/sdk/appcenter/src/test/java/com/microsoft/appcenter/http/Tls1_2SocketFactoryTest.java b/sdk/appcenter/src/test/java/com/microsoft/appcenter/http/Tls1_2SocketFactoryTest.java index cbffad659..da12c9647 100644 --- a/sdk/appcenter/src/test/java/com/microsoft/appcenter/http/Tls1_2SocketFactoryTest.java +++ b/sdk/appcenter/src/test/java/com/microsoft/appcenter/http/Tls1_2SocketFactoryTest.java @@ -5,6 +5,7 @@ package com.microsoft.appcenter.http; +import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.powermock.core.classloader.annotations.PrepareForTest; @@ -26,10 +27,11 @@ import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertNotNull; -import static org.mockito.Matchers.any; -import static org.mockito.Matchers.anyBoolean; -import static org.mockito.Matchers.anyInt; -import static org.mockito.Matchers.anyString; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyBoolean; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.ArgumentMatchers.isNull; import static org.mockito.Mockito.doNothing; import static org.mockito.Mockito.never; import static org.mockito.Mockito.times; @@ -48,8 +50,12 @@ public class Tls1_2SocketFactoryTest { private static final String[] SUPPORTED_CIPHER_SUITES = {"mockCipher1", "mockCipher2"}; - private TLS1_2SocketFactory getFactory() throws IOException { + @Before + public void setUp() { mockStatic(HttpsURLConnection.class); + } + + private TLS1_2SocketFactory getFactory() throws IOException { SSLSocketFactory sslSocketFactory = mock(SSLSocketFactory.class); when(HttpsURLConnection.getDefaultSSLSocketFactory()).thenReturn(sslSocketFactory); when(sslSocketFactory.getDefaultCipherSuites()).thenReturn(DEFAULT_CIPHER_SUITES); @@ -73,7 +79,7 @@ private void checkProtocol(SSLSocket socket) { public void createFactory() throws Exception { SSLContext sslContext = mock(SSLContext.class); doNothing().doThrow(new KeyManagementException()) - .when(sslContext).init(any(KeyManager[].class), any(TrustManager[].class), any(SecureRandom.class)); + .when(sslContext).init(isNull(), isNull(), isNull()); SSLSocketFactory sslSocketFactory = mock(SSLSocketFactory.class); when(sslContext.getSocketFactory()) .thenReturn(sslSocketFactory); @@ -90,17 +96,17 @@ public void createFactory() throws Exception { /* Get factory from context. */ assertNotNull(new TLS1_2SocketFactory()); - verifyStatic(never()); + verifyStatic(HttpsURLConnection.class, never()); HttpsURLConnection.getDefaultSSLSocketFactory(); /* KeyManagementException */ assertNotNull(new TLS1_2SocketFactory()); - verifyStatic(times(1)); + verifyStatic(HttpsURLConnection.class, times(1)); HttpsURLConnection.getDefaultSSLSocketFactory(); /* NoSuchAlgorithmException */ assertNotNull(new TLS1_2SocketFactory()); - verifyStatic(times(2)); + verifyStatic(HttpsURLConnection.class, times(2)); HttpsURLConnection.getDefaultSSLSocketFactory(); } diff --git a/sdk/appcenter/src/test/java/com/microsoft/appcenter/ingestion/AppCenterIngestionTest.java b/sdk/appcenter/src/test/java/com/microsoft/appcenter/ingestion/AppCenterIngestionTest.java index 0b8b1725b..2ef36cd71 100644 --- a/sdk/appcenter/src/test/java/com/microsoft/appcenter/ingestion/AppCenterIngestionTest.java +++ b/sdk/appcenter/src/test/java/com/microsoft/appcenter/ingestion/AppCenterIngestionTest.java @@ -41,17 +41,18 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; -import static org.mockito.Matchers.anyMapOf; -import static org.mockito.Matchers.anyString; -import static org.mockito.Matchers.contains; -import static org.mockito.Matchers.eq; -import static org.mockito.Matchers.notNull; +import static org.mockito.ArgumentMatchers.anyMapOf; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.ArgumentMatchers.contains; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.ArgumentMatchers.notNull; import static org.mockito.Mockito.any; import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import static org.powermock.api.mockito.PowerMockito.mock; import static org.powermock.api.mockito.PowerMockito.mockStatic; +import static org.powermock.api.mockito.PowerMockito.spy; import static org.powermock.api.mockito.PowerMockito.verifyStatic; @SuppressWarnings("unused") @@ -70,6 +71,7 @@ public class AppCenterIngestionTest { @Before public void setUp() { + spy(AppCenterLog.class); mockStatic(SharedPreferencesManager.class); when(SharedPreferencesManager.getBoolean(ALLOWED_NETWORK_REQUEST, true)).thenReturn(true); } @@ -180,7 +182,6 @@ public void onBeforeCalling() throws Exception { URL url = new URL("http://mock/path/file"); String appSecret = UUID.randomUUID().toString(); String obfuscatedSecret = HttpUtils.hideSecret(appSecret); - String obfuscatedToken = "Bearer ***"; Map headers = new HashMap<>(); headers.put("Another-Header", "Another-Value"); HttpClient.CallTemplate callTemplate = getCallTemplate(appSecret); @@ -191,12 +192,12 @@ public void onBeforeCalling() throws Exception { callTemplate.onBeforeCalling(url, headers); /* Verify url log. */ - verifyStatic(); + verifyStatic(AppCenterLog.class); AppCenterLog.verbose(anyString(), contains(url.toString())); /* Verify header log. */ for (Map.Entry header : headers.entrySet()) { - verifyStatic(); + verifyStatic(AppCenterLog.class); AppCenterLog.verbose(anyString(), contains(header.getValue())); } @@ -205,9 +206,8 @@ public void onBeforeCalling() throws Exception { callTemplate.onBeforeCalling(url, headers); /* Verify app secret is in log. */ - verifyStatic(); + verifyStatic(AppCenterLog.class); AppCenterLog.verbose(anyString(), contains(obfuscatedSecret)); - AppCenterLog.verbose(anyString(), contains(obfuscatedToken)); } @Test @@ -225,12 +225,12 @@ public void onBeforeCallingWithAnotherLogLevel() { callTemplate.onBeforeCalling(mock(URL.class), mock(Map.class)); /* Verify. */ - verifyStatic(never()); + verifyStatic(AppCenterLog.class, never()); AppCenterLog.verbose(anyString(), anyString()); } @Test - public void sendLogsWhenIngestionDisable() throws JSONException, IOException { + public void sendLogsWhenIngestionDisable() throws JSONException { mockStatic(SharedPreferencesManager.class); when(SharedPreferencesManager.getBoolean(ALLOWED_NETWORK_REQUEST, true)).thenReturn(false); diff --git a/sdk/appcenter/src/test/java/com/microsoft/appcenter/ingestion/OneCollectorIngestionTest.java b/sdk/appcenter/src/test/java/com/microsoft/appcenter/ingestion/OneCollectorIngestionTest.java index 1a73af779..3aa98ebea 100644 --- a/sdk/appcenter/src/test/java/com/microsoft/appcenter/ingestion/OneCollectorIngestionTest.java +++ b/sdk/appcenter/src/test/java/com/microsoft/appcenter/ingestion/OneCollectorIngestionTest.java @@ -55,17 +55,18 @@ import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; -import static org.mockito.Matchers.anyMapOf; -import static org.mockito.Matchers.anyString; -import static org.mockito.Matchers.contains; -import static org.mockito.Matchers.eq; -import static org.mockito.Matchers.notNull; +import static org.mockito.ArgumentMatchers.anyMapOf; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.ArgumentMatchers.contains; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.ArgumentMatchers.notNull; import static org.mockito.Mockito.any; import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import static org.powermock.api.mockito.PowerMockito.mock; import static org.powermock.api.mockito.PowerMockito.mockStatic; +import static org.powermock.api.mockito.PowerMockito.spy; import static org.powermock.api.mockito.PowerMockito.verifyStatic; import static org.powermock.api.mockito.PowerMockito.whenNew; @@ -90,6 +91,7 @@ public class OneCollectorIngestionTest { @Before public void setUp() throws Exception { TicketCache.clear(); + spy(AppCenterLog.class); mockStatic(SharedPreferencesManager.class); when(SharedPreferencesManager.getBoolean(ALLOWED_NETWORK_REQUEST, true)).thenReturn(true); @@ -342,12 +344,12 @@ public void onBeforeCalling() throws Exception { callTemplate.onBeforeCalling(url, headers); /* Verify url log. */ - verifyStatic(); + verifyStatic(AppCenterLog.class); AppCenterLog.verbose(anyString(), contains(url.toString())); /* Verify header log. */ for (Map.Entry header : headers.entrySet()) { - verifyStatic(); + verifyStatic(AppCenterLog.class); AppCenterLog.verbose(anyString(), contains(header.getValue())); } @@ -356,13 +358,13 @@ public void onBeforeCalling() throws Exception { callTemplate.onBeforeCalling(url, headers); /* Verify api key is in log. */ - verifyStatic(); + verifyStatic(AppCenterLog.class); AppCenterLog.verbose(anyString(), contains(obfuscatedApiKeys)); /* Add ticket to header and check the same way as api key. */ headers.put(OneCollectorIngestion.TICKETS, tickets); callTemplate.onBeforeCalling(url, headers); - verifyStatic(); + verifyStatic(AppCenterLog.class); AppCenterLog.verbose(anyString(), contains(obfuscatedTickets)); } @@ -381,7 +383,7 @@ public void onBeforeCallingWithAnotherLogLevel() { callTemplate.onBeforeCalling(mock(URL.class), mock(Map.class)); /* Verify. */ - verifyStatic(never()); + verifyStatic(AppCenterLog.class, never()); AppCenterLog.verbose(anyString(), anyString()); } diff --git a/sdk/appcenter/src/test/java/com/microsoft/appcenter/ingestion/models/AbstractLogTest.java b/sdk/appcenter/src/test/java/com/microsoft/appcenter/ingestion/models/AbstractLogTest.java index 6257f636f..5691ccc36 100644 --- a/sdk/appcenter/src/test/java/com/microsoft/appcenter/ingestion/models/AbstractLogTest.java +++ b/sdk/appcenter/src/test/java/com/microsoft/appcenter/ingestion/models/AbstractLogTest.java @@ -19,7 +19,7 @@ import static com.microsoft.appcenter.test.TestUtils.checkNotEquals; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; -import static org.mockito.Matchers.anyString; +import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; diff --git a/sdk/appcenter/src/test/java/com/microsoft/appcenter/ingestion/models/one/CommonSchemaDataUtilsTest.java b/sdk/appcenter/src/test/java/com/microsoft/appcenter/ingestion/models/one/CommonSchemaDataUtilsTest.java index b59ce1433..1dcb58429 100644 --- a/sdk/appcenter/src/test/java/com/microsoft/appcenter/ingestion/models/one/CommonSchemaDataUtilsTest.java +++ b/sdk/appcenter/src/test/java/com/microsoft/appcenter/ingestion/models/one/CommonSchemaDataUtilsTest.java @@ -19,8 +19,8 @@ import java.util.List; import static org.junit.Assert.assertEquals; -import static org.mockito.Matchers.any; -import static org.mockito.Matchers.anyString; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyString; import static org.powermock.api.mockito.PowerMockito.mock; import static org.powermock.api.mockito.PowerMockito.when; import static org.powermock.api.mockito.PowerMockito.whenNew; diff --git a/sdk/appcenter/src/test/java/com/microsoft/appcenter/persistence/DatabasePersistenceTest.java b/sdk/appcenter/src/test/java/com/microsoft/appcenter/persistence/DatabasePersistenceTest.java index 7d37a35df..1ba2465e9 100644 --- a/sdk/appcenter/src/test/java/com/microsoft/appcenter/persistence/DatabasePersistenceTest.java +++ b/sdk/appcenter/src/test/java/com/microsoft/appcenter/persistence/DatabasePersistenceTest.java @@ -5,6 +5,23 @@ package com.microsoft.appcenter.persistence; +import static com.microsoft.appcenter.Flags.NORMAL; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyLong; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.ArgumentMatchers.isNotNull; +import static org.mockito.ArgumentMatchers.isNull; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; +import static org.powermock.api.mockito.PowerMockito.mockStatic; +import static org.powermock.api.mockito.PowerMockito.verifyStatic; +import static org.powermock.api.mockito.PowerMockito.whenNew; + import android.content.ContentValues; import android.content.Context; import android.database.Cursor; @@ -29,27 +46,8 @@ import java.util.ArrayList; import java.util.Collections; -import java.util.Date; import java.util.List; -import static com.microsoft.appcenter.Flags.NORMAL; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; -import static org.mockito.Matchers.any; -import static org.mockito.Matchers.anyInt; -import static org.mockito.Matchers.anyLong; -import static org.mockito.Matchers.anyString; -import static org.mockito.Matchers.eq; -import static org.mockito.Matchers.isNotNull; -import static org.mockito.Matchers.isNull; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; -import static org.powermock.api.mockito.PowerMockito.mockStatic; -import static org.powermock.api.mockito.PowerMockito.verifyStatic; -import static org.powermock.api.mockito.PowerMockito.whenNew; - @SuppressWarnings("unused") @PrepareForTest({AppCenterLog.class, DatabaseManager.class, DatabasePersistence.class}) public class DatabasePersistenceTest { @@ -66,7 +64,7 @@ public void countLogsWithGetCountException() throws Exception { whenNew(DatabaseManager.class).withAnyArguments().thenReturn(mockDatabaseManager); Cursor mockCursor = mock(Cursor.class); when(mockCursor.moveToNext()).thenThrow(new RuntimeException()); - when(mockDatabaseManager.getCursor(any(SQLiteQueryBuilder.class), any(String[].class), any(String[].class), anyString())).thenReturn(mockCursor); + when(mockDatabaseManager.getCursor(any(SQLiteQueryBuilder.class), any(), any(String[].class), anyString())).thenReturn(mockCursor); DatabasePersistence persistence = new DatabasePersistence(mock(Context.class), 1, DatabasePersistence.SCHEMA); /* Try to get logs count. */ @@ -80,7 +78,7 @@ public void countLogsWithGetCountException() throws Exception { } /* There is an error log. */ - verifyStatic(); + verifyStatic(AppCenterLog.class); AppCenterLog.error(eq(AppCenter.LOG_TAG), anyString(), any(RuntimeException.class)); } @@ -112,12 +110,12 @@ public void clearPendingLogState() throws Exception { for (int i = 0; i < groupCount; i++) { MockCursor mockCursor = new MockCursor(list.get(i)); mockCursor.mockBuildValues(mockDatabaseManager); - when(mockDatabaseManager.getCursor(any(SQLiteQueryBuilder.class), any(String[].class), eq(new String[]{String.valueOf(i)}), anyString())) + when(mockDatabaseManager.getCursor(any(SQLiteQueryBuilder.class), any(), eq(new String[]{String.valueOf(i)}), anyString())) .thenReturn(mockCursor); } LogSerializer mockLogSerializer = mock(LogSerializer.class); - when(mockLogSerializer.deserializeLog(anyString(), anyString())).thenReturn(mock(Log.class)); + when(mockLogSerializer.deserializeLog(anyString(), any())).thenReturn(mock(Log.class)); /* Instantiate Database Persistence. */ DatabasePersistence persistence = new DatabasePersistence(mock(Context.class)); @@ -146,7 +144,8 @@ public void getLogsWithGetCursorException() throws Exception { DatabaseManager databaseManager = mock(DatabaseManager.class); whenNew(DatabaseManager.class).withAnyArguments().thenReturn(databaseManager); when(databaseManager.nextValues(any(Cursor.class))).thenCallRealMethod(); - when(databaseManager.getCursor(any(SQLiteQueryBuilder.class), any(String[].class), any(String[].class), anyString())).thenThrow(new RuntimeException()); + when(databaseManager.getCursor(any(SQLiteQueryBuilder.class), any(), any(String[].class), anyString())) + .thenThrow(new RuntimeException()); DatabasePersistence persistence = new DatabasePersistence(mock(Context.class), 1, DatabasePersistence.SCHEMA); /* Try to get logs. */ @@ -155,7 +154,7 @@ public void getLogsWithGetCursorException() throws Exception { assertEquals(0, outLogs.size()); /* There is an error log. */ - verifyStatic(); + verifyStatic(AppCenterLog.class); AppCenterLog.error(eq(AppCenter.LOG_TAG), anyString(), any(RuntimeException.class)); } @@ -169,7 +168,7 @@ public void getLogsWithMoveNextException() throws Exception { when(databaseManager.nextValues(any(Cursor.class))).thenCallRealMethod(); Cursor mockCursor = mock(Cursor.class); when(mockCursor.moveToNext()).thenThrow(new RuntimeException()); - when(databaseManager.getCursor(any(SQLiteQueryBuilder.class), any(String[].class), any(String[].class), anyString())).thenReturn(mockCursor); + when(databaseManager.getCursor(any(SQLiteQueryBuilder.class), any(), any(String[].class), anyString())).thenReturn(mockCursor); DatabasePersistence persistence = new DatabasePersistence(mock(Context.class), 1, DatabasePersistence.SCHEMA); /* Try to get logs. */ @@ -178,7 +177,7 @@ public void getLogsWithMoveNextException() throws Exception { assertEquals(0, outLogs.size()); /* There is an error log. */ - verifyStatic(); + verifyStatic(AppCenterLog.class); AppCenterLog.error(eq(AppCenter.LOG_TAG), anyString(), any(RuntimeException.class)); } @@ -203,12 +202,12 @@ public void getLogsWithGetCorruptedIdsException() throws Exception { /* Mock log sequence retrieved from cursor. */ MockCursor mockCursor = new MockCursor(fieldValues); mockCursor.mockBuildValues(databaseManager); - when(databaseManager.getCursor(any(SQLiteQueryBuilder.class), isNull(String[].class), any(String[].class), anyString())).thenReturn(mockCursor); + when(databaseManager.getCursor(any(SQLiteQueryBuilder.class), isNull(), any(String[].class), anyString())).thenReturn(mockCursor); /* Mock second cursor with identifiers only. */ Cursor failingCursor = mock(Cursor.class); when(failingCursor.moveToNext()).thenThrow(new SQLiteDiskIOException()); - when(databaseManager.getCursor(any(SQLiteQueryBuilder.class), isNotNull(String[].class), any(String[].class), anyString())).thenReturn(failingCursor); + when(databaseManager.getCursor(any(SQLiteQueryBuilder.class), isNotNull(), any(String[].class), anyString())).thenReturn(failingCursor); /* Get logs and verify we get only non corrupted logs. */ DatabasePersistence persistence = new DatabasePersistence(mock(Context.class)); @@ -217,7 +216,7 @@ public void getLogsWithGetCorruptedIdsException() throws Exception { assertEquals(0, outLogs.size()); /* There is an error log. */ - verifyStatic(); + verifyStatic(AppCenterLog.class); AppCenterLog.error(eq(AppCenter.LOG_TAG), anyString(), any(RuntimeException.class)); } @@ -256,7 +255,8 @@ public void getLogsWithCorruption() throws Exception { /* Mock log sequence retrieved from cursor. */ MockCursor mockCursor = new MockCursor(fieldValues); mockCursor.mockBuildValues(databaseManager); - when(databaseManager.getCursor(any(SQLiteQueryBuilder.class), isNull(String[].class), any(String[].class), anyString())).thenReturn(mockCursor); + when(databaseManager.getCursor(any(SQLiteQueryBuilder.class), isNull(), any(String[].class), anyString())) + .thenReturn(mockCursor); /* Mock second cursor with identifiers only. */ List idValues = new ArrayList<>(logCount); @@ -267,11 +267,12 @@ public void getLogsWithCorruption() throws Exception { } MockCursor mockIdCursor = new MockCursor(idValues); mockIdCursor.mockBuildValues(databaseManager); - when(databaseManager.getCursor(any(SQLiteQueryBuilder.class), isNotNull(String[].class), any(String[].class), anyString())).thenReturn(mockIdCursor); + when(databaseManager.getCursor(any(SQLiteQueryBuilder.class), isNotNull(), any(String[].class), isNull())) + .thenReturn(mockIdCursor); /* Mock serializer and eventually the database. */ LogSerializer logSerializer = mock(LogSerializer.class); - when(logSerializer.deserializeLog(anyString(), anyString())).thenAnswer(new Answer() { + when(logSerializer.deserializeLog(anyString(), any())).thenAnswer(new Answer() { @Override public Log answer(InvocationOnMock invocation) { @@ -345,7 +346,7 @@ public void close() { } }; mockCursor.mockBuildValues(databaseManager); - when(databaseManager.getCursor(any(SQLiteQueryBuilder.class), isNull(String[].class), any(String[].class), anyString())).thenReturn(mockCursor); + when(databaseManager.getCursor(any(SQLiteQueryBuilder.class), isNull(), any(String[].class), anyString())).thenReturn(mockCursor); idValues = new ArrayList<>(4); /* Here the id cursor will also skip the new corrupted log which id would be 3. */ @@ -364,7 +365,7 @@ public void close() { } }; mockIdCursor.mockBuildValues(databaseManager); - when(databaseManager.getCursor(any(SQLiteQueryBuilder.class), isNotNull(String[].class), any(String[].class), anyString())).thenReturn(mockIdCursor); + when(databaseManager.getCursor(any(SQLiteQueryBuilder.class), isNotNull(), any(String[].class), anyString())).thenReturn(mockIdCursor); /* Verify next call is only the new valid log as others are marked pending. */ outLogs = new ArrayList<>(); @@ -382,7 +383,7 @@ public void checkSetStorageSizeForwarding() throws Exception { /* The real Android test for checking size is in DatabaseManagerAndroidTest. */ DatabaseManager databaseManager = mock(DatabaseManager.class); whenNew(DatabaseManager.class).withAnyArguments().thenReturn(databaseManager); - when(databaseManager.getCursor(any(SQLiteQueryBuilder.class), any(String[].class), any(String[].class), anyString())) + when(databaseManager.getCursor(any(SQLiteQueryBuilder.class), any(), any(String[].class), anyString())) .thenReturn(mock(Cursor.class)); when(databaseManager.setMaxSize(anyLong())).thenReturn(true).thenReturn(false); diff --git a/sdk/appcenter/src/test/java/com/microsoft/appcenter/utils/AppCenterLogTest.java b/sdk/appcenter/src/test/java/com/microsoft/appcenter/utils/AppCenterLogTest.java index c517dcfa1..fa1350af8 100644 --- a/sdk/appcenter/src/test/java/com/microsoft/appcenter/utils/AppCenterLogTest.java +++ b/sdk/appcenter/src/test/java/com/microsoft/appcenter/utils/AppCenterLogTest.java @@ -19,9 +19,9 @@ import org.powermock.modules.junit4.PowerMockRunner; import static org.junit.Assert.assertEquals; -import static org.mockito.Matchers.any; -import static org.mockito.Matchers.anyString; -import static org.mockito.Matchers.eq; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.times; @@ -54,45 +54,44 @@ private static void callLogs() { } private static void verifyAssert(VerificationMode verificationMode) { - verifyStatic(verificationMode); + verifyStatic(Log.class, verificationMode); Log.println(Log.ASSERT, "my-tag", "error with my-tag"); - verifyStatic(verificationMode); - //noinspection WrongConstant + verifyStatic(Log.class, verificationMode); Log.println(eq(Log.ASSERT), eq("my-tag"), eq("error with my-tag with exception\nmock stack trace")); } private static void verifyError(VerificationMode verificationMode) { - verifyStatic(verificationMode); + verifyStatic(Log.class, verificationMode); Log.e(eq("my-tag"), eq("error with my-tag")); - verifyStatic(verificationMode); + verifyStatic(Log.class, verificationMode); Log.e(eq("my-tag"), eq("error with my-tag with exception"), any(Exception.class)); } private static void verifyWarn(VerificationMode verificationMode) { - verifyStatic(verificationMode); + verifyStatic(Log.class, verificationMode); Log.w(eq("my-tag"), eq("warn with my-tag")); - verifyStatic(verificationMode); + verifyStatic(Log.class, verificationMode); Log.w(eq("my-tag"), eq("warn with my-tag with exception"), any(Exception.class)); } private static void verifyInfo(VerificationMode verificationMode) { - verifyStatic(verificationMode); + verifyStatic(Log.class, verificationMode); Log.i(eq("my-tag"), eq("info with my-tag")); - verifyStatic(verificationMode); + verifyStatic(Log.class, verificationMode); Log.i(eq("my-tag"), eq("info with my-tag with exception"), any(Exception.class)); } private static void verifyDebug(VerificationMode verificationMode) { - verifyStatic(verificationMode); + verifyStatic(Log.class, verificationMode); Log.d(eq("my-tag"), eq("debug with my-tag")); - verifyStatic(verificationMode); + verifyStatic(Log.class, verificationMode); Log.d(eq("my-tag"), eq("debug with my-tag with exception"), any(Exception.class)); } private static void verifyVerbose(VerificationMode verificationMode) { - verifyStatic(verificationMode); + verifyStatic(Log.class, verificationMode); Log.v(eq("my-tag"), eq("verbose with my-tag")); - verifyStatic(verificationMode); + verifyStatic(Log.class, verificationMode); Log.v(eq("my-tag"), eq("verbose with my-tag with exception"), any(Exception.class)); } diff --git a/sdk/appcenter/src/test/java/com/microsoft/appcenter/utils/ApplicationLifecycleListenerTest.java b/sdk/appcenter/src/test/java/com/microsoft/appcenter/utils/ApplicationLifecycleListenerTest.java index 28cb959c0..96123dd2f 100644 --- a/sdk/appcenter/src/test/java/com/microsoft/appcenter/utils/ApplicationLifecycleListenerTest.java +++ b/sdk/appcenter/src/test/java/com/microsoft/appcenter/utils/ApplicationLifecycleListenerTest.java @@ -16,8 +16,8 @@ import org.mockito.Mock; import org.mockito.runners.MockitoJUnitRunner; -import static org.mockito.Matchers.any; -import static org.mockito.Matchers.anyLong; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyLong; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.times; diff --git a/sdk/appcenter/src/test/java/com/microsoft/appcenter/utils/AsyncTaskUtilsTest.java b/sdk/appcenter/src/test/java/com/microsoft/appcenter/utils/AsyncTaskUtilsTest.java index 2e62bc33c..ac1143242 100644 --- a/sdk/appcenter/src/test/java/com/microsoft/appcenter/utils/AsyncTaskUtilsTest.java +++ b/sdk/appcenter/src/test/java/com/microsoft/appcenter/utils/AsyncTaskUtilsTest.java @@ -16,10 +16,10 @@ import java.util.concurrent.RejectedExecutionException; import static org.junit.Assert.assertSame; -import static org.mockito.Matchers.any; -import static org.mockito.Matchers.anyInt; -import static org.mockito.Matchers.anyString; -import static org.mockito.Matchers.eq; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; @@ -31,6 +31,7 @@ @PrepareForTest(AppCenterLog.class) public class AsyncTaskUtilsTest { + @SuppressWarnings("InstantiationOfUtilityClass") @Test public void init() { new AsyncTaskUtils(); @@ -41,9 +42,9 @@ public void execute() { @SuppressWarnings("unchecked") AsyncTask task = mock(AsyncTask.class); - when(task.executeOnExecutor(any(Executor.class), anyInt(), anyInt())).thenReturn(task); + when(task.executeOnExecutor(any(), anyInt(), anyInt())).thenReturn(task); assertSame(task, AsyncTaskUtils.execute("", task, 1, 2)); - verify(task).executeOnExecutor(any(Executor.class), eq(1), eq(2)); + verify(task).executeOnExecutor(any(), eq(1), eq(2)); } @Test @@ -53,11 +54,10 @@ public void executeFallback() { AsyncTask task = mock(AsyncTask.class); mockStatic(AppCenterLog.class); RejectedExecutionException exception = new RejectedExecutionException(); - when(task.executeOnExecutor(any(Executor.class), anyInt(), anyInt())).thenThrow(exception).thenReturn(task); + when(task.executeOnExecutor(any(), anyInt(), anyInt())).thenThrow(exception).thenReturn(task); assertSame(task, AsyncTaskUtils.execute("", task, 1, 2)); - verify(task, times(2)).executeOnExecutor(any(Executor.class), eq(1), eq(2)); - verifyStatic(); + verify(task, times(2)).executeOnExecutor(any(), eq(1), eq(2)); + verifyStatic(AppCenterLog.class); AppCenterLog.warn(eq(""), anyString(), eq(exception)); } - } diff --git a/sdk/appcenter/src/test/java/com/microsoft/appcenter/utils/DeviceInfoHelperTest.java b/sdk/appcenter/src/test/java/com/microsoft/appcenter/utils/DeviceInfoHelperTest.java index 2c2cc15e8..00e0ceb8f 100644 --- a/sdk/appcenter/src/test/java/com/microsoft/appcenter/utils/DeviceInfoHelperTest.java +++ b/sdk/appcenter/src/test/java/com/microsoft/appcenter/utils/DeviceInfoHelperTest.java @@ -37,7 +37,7 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNull; -import static org.mockito.Matchers.anyInt; +import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.Mockito.any; import static org.mockito.Mockito.anyString; import static org.mockito.Mockito.atLeastOnce; @@ -86,6 +86,7 @@ public class DeviceInfoHelperTest { public void setup() { DeviceInfoHelper.setCountryCode(null); when(mContext.getPackageManager()).thenReturn(mPackageManager); + when(mContext.getPackageName()).thenReturn("package-name"); } @Before @@ -251,7 +252,7 @@ public void getDeviceInfoMissingCarrierInfo() throws DeviceInfoHelper.DeviceInfo Device device = DeviceInfoHelper.getDeviceInfo(mContext); assertNull(device.getCarrierCountry()); assertNull(device.getCarrierName()); - verifyStatic(); + verifyStatic(AppCenterLog.class); AppCenterLog.error(eq(AppCenter.LOG_TAG), anyString(), any(Exception.class)); } @@ -288,7 +289,7 @@ public void getDeviceInfoMissingScreenSize() throws DeviceInfoHelper.DeviceInfoE /* Verify. */ Device device = DeviceInfoHelper.getDeviceInfo(mContext); assertNull(device.getScreenSize()); - verifyStatic(); + verifyStatic(AppCenterLog.class); AppCenterLog.error(eq(AppCenter.LOG_TAG), anyString(), any(Exception.class)); } @@ -355,7 +356,7 @@ public void verifyCountryCodeWithInvalidLength(String countryCode) throws Device DeviceInfoHelper.setCountryCode(countryCode); /* Verify that log was called.*/ - verifyStatic(); + verifyStatic(AppCenterLog.class); AppCenterLog.error(eq(AppCenter.LOG_TAG), eq("App Center accepts only the two-letter ISO country code.")); /* Verify that invalid value wasn't set. */ diff --git a/sdk/appcenter/src/test/java/com/microsoft/appcenter/utils/HashUtilsTest.java b/sdk/appcenter/src/test/java/com/microsoft/appcenter/utils/HashUtilsTest.java index d29c7fbc7..32f77d933 100644 --- a/sdk/appcenter/src/test/java/com/microsoft/appcenter/utils/HashUtilsTest.java +++ b/sdk/appcenter/src/test/java/com/microsoft/appcenter/utils/HashUtilsTest.java @@ -16,7 +16,7 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; -import static org.mockito.Matchers.anyString; +import static org.mockito.ArgumentMatchers.anyString; import static org.powermock.api.mockito.PowerMockito.doThrow; import static org.powermock.api.mockito.PowerMockito.mockStatic; diff --git a/sdk/appcenter/src/test/java/com/microsoft/appcenter/utils/InstrumentationRegistryHelperTest.java b/sdk/appcenter/src/test/java/com/microsoft/appcenter/utils/InstrumentationRegistryHelperTest.java index a610b60d3..7c4dc2015 100644 --- a/sdk/appcenter/src/test/java/com/microsoft/appcenter/utils/InstrumentationRegistryHelperTest.java +++ b/sdk/appcenter/src/test/java/com/microsoft/appcenter/utils/InstrumentationRegistryHelperTest.java @@ -15,7 +15,7 @@ import java.lang.reflect.Method; import static org.junit.Assert.assertFalse; -import static org.mockito.Matchers.any; +import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.when; import static org.powermock.api.mockito.PowerMockito.doThrow; import static org.powermock.api.mockito.PowerMockito.mockStatic; diff --git a/sdk/appcenter/src/test/java/com/microsoft/appcenter/utils/NetworkStateHelperTestFromLollipop.java b/sdk/appcenter/src/test/java/com/microsoft/appcenter/utils/NetworkStateHelperTestFromLollipop.java index 09d677021..4bab1a20a 100644 --- a/sdk/appcenter/src/test/java/com/microsoft/appcenter/utils/NetworkStateHelperTestFromLollipop.java +++ b/sdk/appcenter/src/test/java/com/microsoft/appcenter/utils/NetworkStateHelperTestFromLollipop.java @@ -24,8 +24,9 @@ import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; -import static org.mockito.Matchers.any; -import static org.mockito.Matchers.anyBoolean; +import static org.mockito.ArgumentMatchers.isNull; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.Mockito.doThrow; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; @@ -57,7 +58,7 @@ public void initialState() { public void permissionDenied() { doThrow(new SecurityException()) .when(mConnectivityManager) - .registerNetworkCallback(any(NetworkRequest.class), any(ConnectivityManager.NetworkCallback.class)); + .registerNetworkCallback(any(), any(ConnectivityManager.NetworkCallback.class)); assertTrue(new NetworkStateHelper(mContext).isNetworkConnected()); } @@ -66,7 +67,7 @@ public void permissionDenied() { public void listenNetwork() { NetworkStateHelper helper = new NetworkStateHelper(mContext); ArgumentCaptor callback = ArgumentCaptor.forClass(ConnectivityManager.NetworkCallback.class); - verify(mConnectivityManager).registerNetworkCallback(any(NetworkRequest.class), callback.capture()); + verify(mConnectivityManager).registerNetworkCallback(isNull(), callback.capture()); NetworkStateHelper.Listener listener = mock(NetworkStateHelper.Listener.class); helper.addListener(listener); @@ -146,7 +147,8 @@ public void listenNetwork() { network = mock(Network.class); callback.getValue().onAvailable(network); assertTrue(helper.isNetworkConnected()); - verify(mConnectivityManager, times(2)).registerNetworkCallback(any(NetworkRequest.class), any(ConnectivityManager.NetworkCallback.class)); + verify(mConnectivityManager, times(2)) + .registerNetworkCallback(any(), any(ConnectivityManager.NetworkCallback.class)); /* Check no extra listener calls. */ verifyNoMoreInteractions(listener); diff --git a/sdk/appcenter/src/test/java/com/microsoft/appcenter/utils/ShutdownHelperTest.java b/sdk/appcenter/src/test/java/com/microsoft/appcenter/utils/ShutdownHelperTest.java index bed0b2b0e..f7b14bbb4 100644 --- a/sdk/appcenter/src/test/java/com/microsoft/appcenter/utils/ShutdownHelperTest.java +++ b/sdk/appcenter/src/test/java/com/microsoft/appcenter/utils/ShutdownHelperTest.java @@ -34,15 +34,14 @@ public void setUp() { public void shutdown() { /* Dummy coverage */ + //noinspection InstantiationOfUtilityClass new ShutdownHelper(); /* Mock process id */ when(Process.myPid()).thenReturn(123); ShutdownHelper.shutdown(999); - verifyStatic(); + verifyStatic(Process.class); Process.killProcess(123); - verifyStatic(); - System.exit(999); } } \ No newline at end of file diff --git a/sdk/appcenter/src/test/java/com/microsoft/appcenter/utils/async/AppCenterFutureTest.java b/sdk/appcenter/src/test/java/com/microsoft/appcenter/utils/async/AppCenterFutureTest.java index e21813561..2b087a314 100644 --- a/sdk/appcenter/src/test/java/com/microsoft/appcenter/utils/async/AppCenterFutureTest.java +++ b/sdk/appcenter/src/test/java/com/microsoft/appcenter/utils/async/AppCenterFutureTest.java @@ -19,8 +19,8 @@ import java.util.concurrent.atomic.AtomicReference; import static org.junit.Assert.assertEquals; -import static org.mockito.Matchers.any; -import static org.mockito.Matchers.anyLong; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyLong; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; diff --git a/sdk/appcenter/src/test/java/com/microsoft/appcenter/utils/context/UserIdContextTest.java b/sdk/appcenter/src/test/java/com/microsoft/appcenter/utils/context/UserIdContextTest.java index 8825b1b9d..00caceb9b 100644 --- a/sdk/appcenter/src/test/java/com/microsoft/appcenter/utils/context/UserIdContextTest.java +++ b/sdk/appcenter/src/test/java/com/microsoft/appcenter/utils/context/UserIdContextTest.java @@ -21,8 +21,8 @@ import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; -import static org.mockito.Matchers.any; -import static org.mockito.Matchers.isNull; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.isNull; import static org.mockito.Mockito.never; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; diff --git a/sdk/appcenter/src/test/java/com/microsoft/appcenter/utils/crypto/CryptoDefaultKeyGeneratorMockTest.java b/sdk/appcenter/src/test/java/com/microsoft/appcenter/utils/crypto/CryptoDefaultKeyGeneratorMockTest.java index f780398c8..d9dcb5a00 100644 --- a/sdk/appcenter/src/test/java/com/microsoft/appcenter/utils/crypto/CryptoDefaultKeyGeneratorMockTest.java +++ b/sdk/appcenter/src/test/java/com/microsoft/appcenter/utils/crypto/CryptoDefaultKeyGeneratorMockTest.java @@ -15,7 +15,7 @@ import javax.crypto.KeyGenerator; -import static org.mockito.Matchers.anyString; +import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.Mockito.verify; import static org.powermock.api.mockito.PowerMockito.mock; import static org.powermock.api.mockito.PowerMockito.mockStatic; diff --git a/sdk/appcenter/src/test/java/com/microsoft/appcenter/utils/crypto/CryptoTest.java b/sdk/appcenter/src/test/java/com/microsoft/appcenter/utils/crypto/CryptoTest.java index c2cc35928..b4354e4e0 100644 --- a/sdk/appcenter/src/test/java/com/microsoft/appcenter/utils/crypto/CryptoTest.java +++ b/sdk/appcenter/src/test/java/com/microsoft/appcenter/utils/crypto/CryptoTest.java @@ -63,13 +63,14 @@ import static org.junit.Assert.assertNotEquals; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; -import static org.mockito.Matchers.any; -import static org.mockito.Matchers.anyInt; -import static org.mockito.Matchers.anyString; -import static org.mockito.Matchers.argThat; -import static org.mockito.Matchers.eq; -import static org.mockito.Matchers.isNull; -import static org.mockito.Matchers.notNull; +import static org.mockito.ArgumentMatchers.contains; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.ArgumentMatchers.argThat; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.ArgumentMatchers.isNull; +import static org.mockito.ArgumentMatchers.notNull; import static org.mockito.Mockito.atLeastOnce; import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.spy; @@ -149,13 +150,7 @@ public byte[] answer(InvocationOnMock invocation) throws UnsupportedEncodingExce when(mRsaBuilder.setKeySize(anyInt())).thenReturn(mRsaBuilder); when(KeyPairGenerator.getInstance(anyString(), anyString())).thenReturn(mock(KeyPairGenerator.class)); KeyStore.PrivateKeyEntry rsaKey = mock(KeyStore.PrivateKeyEntry.class); - when(mKeyStore.getEntry(argThat(new ArgumentMatcher() { - - @Override - public boolean matches(Object argument) { - return String.valueOf(argument).contains(CIPHER_RSA); - } - }), any(KeyStore.ProtectionParameter.class))).thenReturn(rsaKey); + when(mKeyStore.getEntry(contains(CIPHER_RSA), isNull())).thenReturn(rsaKey); when(rsaKey.getCertificate()).thenReturn(mRsaCert); when(mCipher.doFinal(any(byte[].class))).thenAnswer(new Answer() { @@ -176,13 +171,7 @@ public byte[] answer(InvocationOnMock invocation) { when(mAesAndEtmBuilder.setKeySize(anyInt())).thenReturn(mAesAndEtmBuilder); when(mAesAndEtmBuilder.setKeyValidityForOriginationEnd(any(Date.class))).thenReturn(mAesAndEtmBuilder); when(mAesAndEtmBuilder.build()).thenReturn(mock(KeyGenParameterSpec.class)); - when(mKeyStore.getEntry(argThat(new ArgumentMatcher() { - - @Override - public boolean matches(Object argument) { - return String.valueOf(argument).contains(CIPHER_AES); - } - }), any(KeyStore.ProtectionParameter.class))).thenReturn(aesAndEtmKey); + when(mKeyStore.getEntry(contains(CIPHER_AES), isNull())).thenReturn(aesAndEtmKey); when(mCryptoFactory.getKeyGenerator(anyString(), anyString())).thenReturn(mock(CryptoUtils.IKeyGenerator.class)); final byte[] mockInitVector = new byte[16]; when(mCipher.getBlockSize()).thenReturn(mockInitVector.length); @@ -208,9 +197,10 @@ public byte[] answer(InvocationOnMock invocation) { }); /* Mock ciphers. */ - when(mCryptoFactory.getCipher(anyString(), anyString())).thenReturn(mCipher); + when(mCryptoFactory.getCipher(anyString(), any())).thenReturn(mCipher); } + @SuppressWarnings("InstantiationOfUtilityClass") @Test public void initCryptoConstants() { new CryptoConstants(); @@ -228,6 +218,7 @@ public void nullData() { assertNull(nullDecryptedData.getNewEncryptedData()); } + @SuppressWarnings("SameParameterValue") private void verifyNoCrypto(int apiLevel) { CryptoUtils cryptoUtils = new CryptoUtils(mContext, mCryptoFactory, apiLevel); String encrypted = cryptoUtils.encrypt("anything"); @@ -243,7 +234,7 @@ private void verifyNoCrypto(int apiLevel) { public void keyStoreNotFound() throws Exception { when(KeyStore.getInstance(ANDROID_KEY_STORE)).thenThrow(new KeyStoreException()); verifyNoCrypto(Build.VERSION_CODES.LOLLIPOP); - verifyStatic(); + verifyStatic(KeyStore.class); KeyStore.getInstance(anyString()); } @@ -251,7 +242,7 @@ public void keyStoreNotFound() throws Exception { public void rsaFailsToLoadWhenPreferred() throws Exception { when(KeyPairGenerator.getInstance(anyString(), anyString())).thenThrow(new NoSuchAlgorithmException()); verifyNoCrypto(Build.VERSION_CODES.LOLLIPOP); - verifyStatic(); + verifyStatic(KeyStore.class); KeyStore.getInstance(anyString()); } @@ -463,7 +454,7 @@ private void verifyRsaPreferred(int apiLevel) throws Exception { /* Count how many times alias0 was used to test interactions after more easily... */ alias = ArgumentCaptor.forClass(String.class); - verify(mKeyStore, atLeastOnce()).getEntry(alias.capture(), any(KeyStore.ProtectionParameter.class)); + verify(mKeyStore, atLeastOnce()).getEntry(alias.capture(), isNull()); int alias0count = 0; for (String aliasValue : alias.getAllValues()) { if (aliasValue.equals(alias0)) { @@ -596,7 +587,7 @@ public void verifyEncryptAes() throws Exception { // Verify that data was encoded. String expectedText = CIPHER_AES + "/" + AES_KEY_SIZE + ALGORITHM_DATA_SEPARATOR + "IV" + sourceText; - assertEquals(encryptedText, expectedText); + assertEquals(expectedText, encryptedText); // Verify that data was decoded. when(mCipher.getBlockSize()).thenReturn(2); diff --git a/sdk/appcenter/src/test/java/com/microsoft/appcenter/utils/storage/DatabaseManagerTest.java b/sdk/appcenter/src/test/java/com/microsoft/appcenter/utils/storage/DatabaseManagerTest.java index 56cf5434d..3e592cd73 100644 --- a/sdk/appcenter/src/test/java/com/microsoft/appcenter/utils/storage/DatabaseManagerTest.java +++ b/sdk/appcenter/src/test/java/com/microsoft/appcenter/utils/storage/DatabaseManagerTest.java @@ -27,9 +27,10 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; -import static org.mockito.Matchers.any; -import static org.mockito.Matchers.anyString; -import static org.mockito.Matchers.eq; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.ArgumentMatchers.isNull; import static org.mockito.Mockito.doThrow; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.spy; @@ -61,7 +62,7 @@ public void setUp() { public void putFailed() { DatabaseManager databaseManagerMock = getDatabaseManagerMock(); databaseManagerMock.put(new ContentValues(), "priority"); - verifyStatic(); + verifyStatic(AppCenterLog.class); AppCenterLog.error(eq(AppCenter.LOG_TAG), anyString(), any(RuntimeException.class)); } @@ -69,7 +70,7 @@ public void putFailed() { public void deleteFailed() { DatabaseManager databaseManagerMock = getDatabaseManagerMock(); databaseManagerMock.delete(0); - verifyStatic(); + verifyStatic(AppCenterLog.class); AppCenterLog.error(eq(AppCenter.LOG_TAG), anyString(), any(RuntimeException.class)); } @@ -77,7 +78,7 @@ public void deleteFailed() { public void clearFailed() { DatabaseManager databaseManagerMock = getDatabaseManagerMock(); databaseManagerMock.clear(); - verifyStatic(); + verifyStatic(AppCenterLog.class); AppCenterLog.error(eq(AppCenter.LOG_TAG), anyString(), any(RuntimeException.class)); } @@ -88,7 +89,7 @@ public void closeFailed() { DatabaseManager databaseManagerMock = getDatabaseManagerMock(); databaseManagerMock.setSQLiteOpenHelper(helperMock); databaseManagerMock.close(); - verifyStatic(); + verifyStatic(AppCenterLog.class); AppCenterLog.error(eq(AppCenter.LOG_TAG), anyString(), any(RuntimeException.class)); } @@ -96,7 +97,7 @@ public void closeFailed() { public void rowCountFailed() { DatabaseManager databaseManagerMock = getDatabaseManagerMock(); assertEquals(-1, databaseManagerMock.getRowCount()); - verifyStatic(); + verifyStatic(AppCenterLog.class); AppCenterLog.error(eq(AppCenter.LOG_TAG), anyString(), any(RuntimeException.class)); } @@ -104,7 +105,7 @@ public void rowCountFailed() { public void setMaxSizeFailed() { DatabaseManager databaseManagerMock = getDatabaseManagerMock(); assertFalse(databaseManagerMock.setMaxSize(1024 * 1024)); - verifyStatic(); + verifyStatic(AppCenterLog.class); AppCenterLog.error(eq(AppCenter.LOG_TAG), anyString(), any(RuntimeException.class)); } @@ -112,7 +113,7 @@ public void setMaxSizeFailed() { public void getMaxSizeFailed() { DatabaseManager databaseManagerMock = getDatabaseManagerMock(); assertEquals(-1, databaseManagerMock.getMaxSize()); - verifyStatic(); + verifyStatic(AppCenterLog.class); AppCenterLog.error(eq(AppCenter.LOG_TAG), anyString(), any(RuntimeException.class)); } @@ -188,11 +189,12 @@ public void failsToDeleteLogDuringPutWhenFull() { Cursor cursor = mock(Cursor.class); SQLiteDiskIOException fatalException = new SQLiteDiskIOException(); when(cursor.moveToNext()).thenThrow(fatalException); - SQLiteQueryBuilder sqLiteQueryBuilder = mock(SQLiteQueryBuilder.class, new Returns(cursor)); + SQLiteQueryBuilder sqLiteQueryBuilder = mock(SQLiteQueryBuilder.class); + when(sqLiteQueryBuilder.query(any(), any(), any(), any(), any(), any(), any())).thenReturn(cursor); when(SQLiteUtils.newSQLiteQueryBuilder()).thenReturn(sqLiteQueryBuilder); /* Simulate that database is full and that deletes fail because of the cursor. */ - when(sqLiteDatabase.insertOrThrow(anyString(), anyString(), any(ContentValues.class))).thenThrow(new SQLiteFullException()); + when(sqLiteDatabase.insertOrThrow(anyString(), isNull(), any(ContentValues.class))).thenThrow(new SQLiteFullException()); /* Instantiate real instance for DatabaseManager. */ DatabaseManager databaseManager = new DatabaseManager(contextMock, "database", "table", 1, null, null, null); @@ -217,11 +219,12 @@ public void cursorFailsToCloseAfterPut() { SQLiteDiskIOException exception = new SQLiteDiskIOException(); doThrow(exception).when(cursor).close(); when(cursor.moveToNext()).thenReturn(true).thenReturn(false); - SQLiteQueryBuilder sqLiteQueryBuilder = mock(SQLiteQueryBuilder.class, new Returns(cursor)); + SQLiteQueryBuilder sqLiteQueryBuilder = mock(SQLiteQueryBuilder.class); + when(sqLiteQueryBuilder.query(any(), any(), any(), any(), any(), any(), any())).thenReturn(cursor); when(SQLiteUtils.newSQLiteQueryBuilder()).thenReturn(sqLiteQueryBuilder); /* Simulate that database is full only once (will work after purging 1 log). */ - when(sqLiteDatabase.insertOrThrow(anyString(), anyString(), any(ContentValues.class))).thenThrow(new SQLiteFullException()).thenReturn(1L); + when(sqLiteDatabase.insertOrThrow(anyString(), isNull(), any(ContentValues.class))).thenThrow(new SQLiteFullException()).thenReturn(1L); /* Instantiate real instance for DatabaseManager. */ DatabaseManager databaseManager = new DatabaseManager(contextMock, "database", "table", 1, null, null, null); diff --git a/sdk/appcenter/src/test/java/com/microsoft/appcenter/utils/storage/FileManagerTest.java b/sdk/appcenter/src/test/java/com/microsoft/appcenter/utils/storage/FileManagerTest.java index 2ff866fbe..7701683f6 100644 --- a/sdk/appcenter/src/test/java/com/microsoft/appcenter/utils/storage/FileManagerTest.java +++ b/sdk/appcenter/src/test/java/com/microsoft/appcenter/utils/storage/FileManagerTest.java @@ -32,8 +32,8 @@ import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; -import static org.mockito.Matchers.any; -import static org.mockito.Matchers.anyString; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.Mockito.doThrow; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; @@ -61,7 +61,7 @@ public void readFileNotFound() throws Exception { whenNew(FileReader.class).withAnyArguments().thenReturn(fileReader); assertNull(FileManager.read(new File(""))); verify(fileReader).close(); - verifyStatic(); + verifyStatic(AppCenterLog.class); AppCenterLog.error(anyString(), anyString(), any(IOException.class)); } @@ -75,7 +75,7 @@ public void readError() throws Exception { when(reader.readLine()).thenThrow(new EOFException()); assertNull(FileManager.read(new File(""))); verify(reader).close(); - verifyStatic(); + verifyStatic(AppCenterLog.class); AppCenterLog.error(anyString(), anyString(), any(IOException.class)); } @@ -86,7 +86,7 @@ public void readErrorAndCloseError() throws Exception { whenNew(FileReader.class).withAnyArguments().thenReturn(fileReader); assertNull(FileManager.read(new File(""))); verify(fileReader).close(); - verifyStatic(); + verifyStatic(AppCenterLog.class); AppCenterLog.error(anyString(), anyString(), any(IOException.class)); } @@ -144,7 +144,7 @@ public void readBytesError() throws Exception { doThrow(new IOException("mock")).when(dataInputStream).readFully(any(byte[].class)); assertNull(FileManager.readBytes(new File(""))); verify(fileInputStream).close(); - verifyStatic(); + verifyStatic(AppCenterLog.class); AppCenterLog.error(anyString(), anyString(), any(IOException.class)); } @@ -159,7 +159,7 @@ public void readBytesErrorAndCloseError() throws Exception { doThrow(new IOException("mock")).when(dataInputStream).readFully(any(byte[].class)); assertNull(FileManager.readBytes(new File(""))); verify(fileInputStream).close(); - verifyStatic(); + verifyStatic(AppCenterLog.class); AppCenterLog.error(anyString(), anyString(), any(IOException.class)); } @@ -176,7 +176,7 @@ public void deleteFilesWhenFileListIsNull() throws IOException { /* Verify. */ assertFalse(folder.exists()); - verifyStatic(); + verifyStatic(FileManager.class); FileManager.deleteDirectory(any(File.class)); } @@ -205,7 +205,7 @@ public void deleteFiles() throws IOException { assertFalse(folder.exists()); /* Verify. */ - verifyStatic(times(4)); + verifyStatic(FileManager.class, times(4)); FileManager.deleteDirectory(any(File.class)); } From 93f36f18bf6d6a413d1f99ed0fcceca8e432ac43 Mon Sep 17 00:00:00 2001 From: Ivan Matkov Date: Wed, 20 Apr 2022 15:50:11 +0200 Subject: [PATCH 05/72] Fix analytics tests --- .../analytics/AbstractAnalyticsTest.java | 10 +- .../appcenter/analytics/AnalyticsTest.java | 109 +++++------------- .../AnalyticsTransmissionTargetTest.java | 101 ++++++---------- .../analytics/AuthenticationProviderTest.java | 24 ++-- .../analytics/EventPropertiesTest.java | 32 ++--- .../appcenter/analytics/LogNameMatcher.java | 38 ++++++ .../analytics/PropertyConfiguratorTest.java | 16 +-- .../analytics/channel/SessionTrackerTest.java | 49 ++++---- .../models/json/EventLogFactoryTest.java | 14 +-- 9 files changed, 173 insertions(+), 220 deletions(-) create mode 100644 sdk/appcenter-analytics/src/test/java/com/microsoft/appcenter/analytics/LogNameMatcher.java diff --git a/sdk/appcenter-analytics/src/test/java/com/microsoft/appcenter/analytics/AbstractAnalyticsTest.java b/sdk/appcenter-analytics/src/test/java/com/microsoft/appcenter/analytics/AbstractAnalyticsTest.java index a321e6e56..db5c5cc91 100644 --- a/sdk/appcenter-analytics/src/test/java/com/microsoft/appcenter/analytics/AbstractAnalyticsTest.java +++ b/sdk/appcenter-analytics/src/test/java/com/microsoft/appcenter/analytics/AbstractAnalyticsTest.java @@ -24,10 +24,10 @@ import org.powermock.modules.junit4.PowerMockRunner; import static org.junit.Assert.assertFalse; -import static org.mockito.Matchers.any; -import static org.mockito.Matchers.anyBoolean; -import static org.mockito.Matchers.anyString; -import static org.mockito.Matchers.eq; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyBoolean; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.when; import static org.powermock.api.mockito.PowerMockito.doAnswer; import static org.powermock.api.mockito.PowerMockito.mockStatic; @@ -65,7 +65,7 @@ public Void answer(InvocationOnMock invocation) { return null; } }; - doAnswer(runNow).when(mAppCenterHandler).post(any(Runnable.class), any(Runnable.class)); + doAnswer(runNow).when(mAppCenterHandler).post(any(Runnable.class), any()); mockStatic(HandlerUtils.class); doAnswer(runNow).when(HandlerUtils.class); HandlerUtils.runOnUiThread(any(Runnable.class)); diff --git a/sdk/appcenter-analytics/src/test/java/com/microsoft/appcenter/analytics/AnalyticsTest.java b/sdk/appcenter-analytics/src/test/java/com/microsoft/appcenter/analytics/AnalyticsTest.java index 09e693a5d..5080bb06e 100644 --- a/sdk/appcenter-analytics/src/test/java/com/microsoft/appcenter/analytics/AnalyticsTest.java +++ b/sdk/appcenter-analytics/src/test/java/com/microsoft/appcenter/analytics/AnalyticsTest.java @@ -58,21 +58,22 @@ import static com.microsoft.appcenter.analytics.Analytics.ANALYTICS_GROUP; import static com.microsoft.appcenter.analytics.Analytics.MAXIMUM_TRANSMISSION_INTERVAL_IN_SECONDS; import static com.microsoft.appcenter.analytics.Analytics.MINIMUM_TRANSMISSION_INTERVAL_IN_SECONDS; +import static com.microsoft.appcenter.analytics.LogNameMatcher.logName; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; -import static org.mockito.Matchers.any; -import static org.mockito.Matchers.anyInt; -import static org.mockito.Matchers.anyLong; -import static org.mockito.Matchers.anyString; -import static org.mockito.Matchers.argThat; -import static org.mockito.Matchers.contains; -import static org.mockito.Matchers.eq; -import static org.mockito.Matchers.isA; -import static org.mockito.Matchers.isNull; -import static org.mockito.Matchers.notNull; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.anyLong; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.ArgumentMatchers.argThat; +import static org.mockito.ArgumentMatchers.contains; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.ArgumentMatchers.isA; +import static org.mockito.ArgumentMatchers.isNull; +import static org.mockito.ArgumentMatchers.notNull; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.times; @@ -125,7 +126,7 @@ public void notInit() { Analytics.trackPage("test", null); /* Verify we just get an error every time. */ - verifyStatic(times(9)); + verifyStatic(AppCenterLog.class, times(9)); AppCenterLog.error(eq(AppCenter.LOG_TAG), anyString()); } @@ -138,10 +139,10 @@ private void activityResumed(final String expectedName, android.app.Activity act Analytics analytics = Analytics.getInstance(); analytics.onActivityResumed(new Activity()); assertNull(analytics.getCurrentActivity()); - verifyStatic(); + verifyStatic(AppCenterLog.class); AppCenterLog.error(anyString(), anyString()); analytics.onActivityPaused(new Activity()); - verifyStatic(times(2)); + verifyStatic(AppCenterLog.class, times(2)); AppCenterLog.error(anyString(), anyString()); /* Start. */ @@ -152,17 +153,7 @@ private void activityResumed(final String expectedName, android.app.Activity act /* Test resume/pause. */ analytics.onActivityResumed(activity); analytics.onActivityPaused(activity); - verify(channel).enqueue(argThat(new ArgumentMatcher() { - - @Override - public boolean matches(Object item) { - if (item instanceof PageLog) { - PageLog pageLog = (PageLog) item; - return expectedName.equals(pageLog.getName()); - } - return false; - } - }), eq(analytics.getGroupName()), eq(DEFAULTS)); + verify(channel).enqueue(logName(PageLog.class, expectedName), eq(analytics.getGroupName()), eq(DEFAULTS)); } @Test @@ -190,34 +181,12 @@ public void disableAutomaticPageTracking() { analytics.onStarting(mAppCenterHandler); analytics.onStarted(mock(Context.class), channel, "", null, true); analytics.onActivityResumed(new MyActivity()); - verify(channel).enqueue(argThat(new ArgumentMatcher() { - - @Override - public boolean matches(Object argument) { - return argument instanceof StartSessionLog; - } - }), anyString(), eq(DEFAULTS)); - verify(channel, never()).enqueue(argThat(new ArgumentMatcher() { - - @Override - public boolean matches(Object argument) { - return argument instanceof PageLog; - } - }), anyString(), anyInt()); + verify(channel).enqueue(isA(StartSessionLog.class), anyString(), eq(DEFAULTS)); + verify(channel, never()).enqueue(isA(PageLog.class), anyString(), anyInt()); Analytics.setAutoPageTrackingEnabled(true); assertTrue(Analytics.isAutoPageTrackingEnabled()); analytics.onActivityResumed(new SomeScreen()); - verify(channel).enqueue(argThat(new ArgumentMatcher() { - - @Override - public boolean matches(Object item) { - if (item instanceof PageLog) { - PageLog pageLog = (PageLog) item; - return "SomeScreen".equals(pageLog.getName()); - } - return false; - } - }), eq(analytics.getGroupName()), eq(DEFAULTS)); + verify(channel).enqueue(logName(PageLog.class, "SomeScreen"), eq(analytics.getGroupName()), eq(DEFAULTS)); } @Test @@ -380,7 +349,7 @@ public void trackEventWithInvalidFlags() { Analytics.trackEvent("eventName1", (Map) null, 0x03); Analytics.trackEvent("eventName2", (EventProperties) null, 0x03); verify(channel, times(2)).enqueue(isA(EventLog.class), anyString(), eq(DEFAULTS)); - verifyStatic(times(2)); + verifyStatic(AppCenterLog.class, times(2)); AppCenterLog.warn(eq(AppCenter.LOG_TAG), anyString()); } @@ -500,7 +469,7 @@ public void setEnabled() throws InterruptedException { verify(channel).removeGroup(eq(ANALYTICS_CRITICAL_GROUP)); verify(channel, times(2)).removeGroup(eq(ANALYTICS_GROUP)); verify(channel).clear(analytics.getGroupName()); - verifyStatic(); + verifyStatic(SharedPreferencesManager.class); SharedPreferencesManager.remove("sessions"); /* Now try to use all methods. Should not work. */ @@ -628,28 +597,28 @@ public void enableManualSessionTrackerAfterAnalyticsStart() { /* Before start it does not work to change state, it's disabled. */ Analytics analytics = Analytics.getInstance(); - /* Prepare channel. */ + /* Prepare channel. */ Channel channel = mock(Channel.class); analytics.onStarting(mAppCenterHandler); analytics.onStarted(mock(Context.class), channel, "", null, true); analytics.onActivityResumed(mock(Activity.class)); /* Verify that start session log was sent. */ - verify(channel, times(2)).enqueue(any(StartSessionLog.class), eq(analytics.getGroupName()), anyInt()); + verify(channel).enqueue(isA(StartSessionLog.class), eq(analytics.getGroupName()), anyInt()); /* Set manual session tracker and start session. */ Analytics.enableManualSessionTracker(); Analytics.startSession(); /* Verify that start session log wasn't sent. */ - verify(channel, times(2)).enqueue(any(StartSessionLog.class), eq(analytics.getGroupName()), anyInt()); + verify(channel).enqueue(isA(StartSessionLog.class), eq(analytics.getGroupName()), anyInt()); /* Disable Analytics ans call start session. */ Analytics.setEnabled(false); Analytics.startSession(); /* Verify that start session log wasn't sent. */ - verify(channel, times(2)).enqueue(any(StartSessionLog.class), eq(analytics.getGroupName()), anyInt()); + verify(channel).enqueue(isA(StartSessionLog.class), eq(analytics.getGroupName()), anyInt()); } @Test @@ -711,20 +680,8 @@ public void startSessionAfterUserApproval() { /* Enable: start session sent retroactively. */ Analytics.setEnabled(true); - verify(channel).enqueue(argThat(new ArgumentMatcher() { - - @Override - public boolean matches(Object argument) { - return argument instanceof StartSessionLog; - } - }), eq(analytics.getGroupName()), eq(DEFAULTS)); - verify(channel).enqueue(argThat(new ArgumentMatcher() { - - @Override - public boolean matches(Object argument) { - return argument instanceof PageLog; - } - }), eq(analytics.getGroupName()), eq(DEFAULTS)); + verify(channel).enqueue(isA(StartSessionLog.class), eq(analytics.getGroupName()), eq(DEFAULTS)); + verify(channel).enqueue(isA(PageLog.class), eq(analytics.getGroupName()), eq(DEFAULTS)); /* Go background. */ analytics.onActivityPaused(new Activity()); @@ -879,17 +836,7 @@ public void appOnlyFeatures() { verify(channel).enqueue(isA(StartSessionLog.class), anyString(), eq(DEFAULTS)); /* Verify last page tracked as still in foreground. */ - verify(channel).enqueue(argThat(new ArgumentMatcher() { - - @Override - public boolean matches(Object item) { - if (item instanceof PageLog) { - PageLog pageLog = (PageLog) item; - return "My".equals(pageLog.getName()); - } - return false; - } - }), eq(analytics.getGroupName()), eq(DEFAULTS)); + verify(channel).enqueue(logName(PageLog.class, "My"), eq(analytics.getGroupName()), eq(DEFAULTS)); /* Check that was the only page sent. */ verify(channel).enqueue(isA(PageLog.class), eq(analytics.getGroupName()), eq(DEFAULTS)); @@ -916,7 +863,7 @@ public void createTransmissionTargetBeforeStart() { assertNull(Analytics.getTransmissionTarget("t1")); /* And prints an error. */ - verifyStatic(); + verifyStatic(AppCenterLog.class); AppCenterLog.error(anyString(), contains("AppCenter is not configured")); } diff --git a/sdk/appcenter-analytics/src/test/java/com/microsoft/appcenter/analytics/AnalyticsTransmissionTargetTest.java b/sdk/appcenter-analytics/src/test/java/com/microsoft/appcenter/analytics/AnalyticsTransmissionTargetTest.java index 7c543efe8..1bfff56a2 100644 --- a/sdk/appcenter-analytics/src/test/java/com/microsoft/appcenter/analytics/AnalyticsTransmissionTargetTest.java +++ b/sdk/appcenter-analytics/src/test/java/com/microsoft/appcenter/analytics/AnalyticsTransmissionTargetTest.java @@ -10,6 +10,7 @@ import com.microsoft.appcenter.AppCenter; import com.microsoft.appcenter.AppCenterHandler; import com.microsoft.appcenter.analytics.ingestion.models.EventLog; +import com.microsoft.appcenter.analytics.ingestion.models.PageLog; import com.microsoft.appcenter.analytics.ingestion.models.one.CommonSchemaEventLog; import com.microsoft.appcenter.channel.Channel; import com.microsoft.appcenter.ingestion.models.Log; @@ -39,19 +40,20 @@ import static com.microsoft.appcenter.Flags.NORMAL; import static com.microsoft.appcenter.analytics.Analytics.ANALYTICS_CRITICAL_GROUP; import static com.microsoft.appcenter.analytics.Analytics.ANALYTICS_GROUP; +import static com.microsoft.appcenter.analytics.LogNameMatcher.logName; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertSame; import static org.junit.Assert.assertTrue; -import static org.mockito.Matchers.any; -import static org.mockito.Matchers.anyInt; -import static org.mockito.Matchers.anyString; -import static org.mockito.Matchers.argThat; -import static org.mockito.Matchers.contains; -import static org.mockito.Matchers.eq; -import static org.mockito.Matchers.isA; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.ArgumentMatchers.argThat; +import static org.mockito.ArgumentMatchers.contains; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.ArgumentMatchers.isA; import static org.mockito.Mockito.doNothing; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; @@ -91,7 +93,7 @@ public void testGetTransmissionTargetWithNullToken() { assertNull(Analytics.getTransmissionTarget(null)); /* Verify log. */ - verifyStatic(); + verifyStatic(AppCenterLog.class); AppCenterLog.error(anyString(), contains("Transmission target token may not be null or empty.")); } @@ -101,7 +103,7 @@ public void testGetTransmissionTargetWithEmptyToken() { assertNull(Analytics.getTransmissionTarget("")); /* Verify log. */ - verifyStatic(); + verifyStatic(AppCenterLog.class); AppCenterLog.error(anyString(), contains("Transmission target token may not be null or empty.")); } @@ -135,9 +137,9 @@ private void testTrackEventWithTransmissionTarget(final String defaultToken, boo verify(mChannel).enqueue(argThat(new ArgumentMatcher() { @Override - public boolean matches(Object item) { - if (item instanceof EventLog) { - EventLog eventLog = (EventLog) item; + public boolean matches(Log log) { + if (log instanceof EventLog) { + EventLog eventLog = (EventLog) log; boolean nameAndPropertiesMatch = eventLog.getName().equals("name") && eventLog.getTypedProperties() == null; boolean tokenMatch; boolean tagMatch; @@ -163,9 +165,9 @@ public boolean matches(Object item) { verify(mChannel).enqueue(argThat(new ArgumentMatcher() { @Override - public boolean matches(Object item) { - if (item instanceof EventLog) { - EventLog eventLog = (EventLog) item; + public boolean matches(Log log) { + if (log instanceof EventLog) { + EventLog eventLog = (EventLog) log; boolean nameAndPropertiesMatch = eventLog.getName().equals("name") && eventLog.getTypedProperties() == null; boolean tokenMatch = eventLog.getTransmissionTargetTokens().size() == 1 && eventLog.getTransmissionTargetTokens().contains("token"); boolean tagMatch = target.equals(eventLog.getTag()); @@ -183,9 +185,9 @@ public boolean matches(Object item) { verify(mChannel).enqueue(argThat(new ArgumentMatcher() { @Override - public boolean matches(Object item) { - if (item instanceof EventLog) { - EventLog eventLog = (EventLog) item; + public boolean matches(Log log) { + if (log instanceof EventLog) { + EventLog eventLog = (EventLog) log; boolean nameMatches = eventLog.getName().equals("name"); List typedProperties = new ArrayList<>(); StringTypedProperty stringTypedProperty = new StringTypedProperty(); @@ -207,9 +209,9 @@ public boolean matches(Object item) { verify(mChannel).enqueue(argThat(new ArgumentMatcher() { @Override - public boolean matches(Object item) { - if (item instanceof EventLog) { - EventLog eventLog = (EventLog) item; + public boolean matches(Log log) { + if (log instanceof EventLog) { + EventLog eventLog = (EventLog) log; boolean nameAndPropertiesMatch = eventLog.getName().equals("name") && eventLog.getTypedProperties() == null; boolean tokenMatch = eventLog.getTransmissionTargetTokens().size() == 1 && eventLog.getTransmissionTargetTokens().contains("token3"); boolean tagMatch = childTarget.equals(eventLog.getTag()); @@ -231,33 +233,13 @@ public void setEnabled() { AnalyticsTransmissionTarget transmissionTarget = Analytics.getTransmissionTarget("test"); assertTrue(transmissionTarget.isEnabledAsync().get()); transmissionTarget.trackEvent("eventName1"); - verify(mChannel).enqueue(argThat(new ArgumentMatcher() { - - @Override - public boolean matches(Object item) { - if (item instanceof EventLog) { - EventLog eventLog = (EventLog) item; - return eventLog.getName().equals("eventName1"); - } - return false; - } - }), anyString(), eq(DEFAULTS)); + verify(mChannel).enqueue(logName(EventLog.class, "eventName1"), anyString(), eq(DEFAULTS)); /* Set enabled to false and assert that it cannot track event. */ transmissionTarget.setEnabledAsync(false).get(); assertFalse(transmissionTarget.isEnabledAsync().get()); transmissionTarget.trackEvent("eventName2"); - verify(mChannel, never()).enqueue(argThat(new ArgumentMatcher() { - - @Override - public boolean matches(Object item) { - if (item instanceof EventLog) { - EventLog eventLog = (EventLog) item; - return eventLog.getName().equals("eventName2"); - } - return false; - } - }), anyString(), anyInt()); + verify(mChannel, never()).enqueue(logName(EventLog.class, "eventName2"), anyString(), anyInt()); } @Test @@ -272,32 +254,12 @@ public void setEnabledOnParent() { assertFalse(parentTransmissionTarget.isEnabledAsync().get()); assertFalse(childTransmissionTarget.isEnabledAsync().get()); childTransmissionTarget.trackEvent("eventName1"); - verify(mChannel, never()).enqueue(argThat(new ArgumentMatcher() { - - @Override - public boolean matches(Object item) { - if (item instanceof EventLog) { - EventLog eventLog = (EventLog) item; - return eventLog.getName().equals("eventName1"); - } - return false; - } - }), anyString(), anyInt()); + verify(mChannel, never()).enqueue(logName(EventLog.class, "eventName1"), anyString(), anyInt()); /* Set enabled to true on parent. Verify that child can track event. */ parentTransmissionTarget.setEnabledAsync(true); childTransmissionTarget.trackEvent("eventName2"); - verify(mChannel).enqueue(argThat(new ArgumentMatcher() { - - @Override - public boolean matches(Object item) { - if (item instanceof EventLog) { - EventLog eventLog = (EventLog) item; - return eventLog.getName().equals("eventName2"); - } - return false; - } - }), anyString(), eq(DEFAULTS)); + verify(mChannel).enqueue(logName(EventLog.class, "eventName2"), anyString(), eq(DEFAULTS)); } @Test @@ -472,7 +434,7 @@ public Object answer(InvocationOnMock invocation) { Analytics analytics = Analytics.getInstance(); AppCenterHandler handler = mock(AppCenterHandler.class); ArgumentCaptor backgroundRunnable = ArgumentCaptor.forClass(Runnable.class); - doNothing().when(handler).post(backgroundRunnable.capture(), any(Runnable.class)); + doNothing().when(handler).post(backgroundRunnable.capture(), any()); analytics.onStarting(handler); analytics.onStarted(mock(Context.class), mChannel, null, "test", true); @@ -496,14 +458,17 @@ public Object answer(InvocationOnMock invocation) { /* Track an event. */ Analytics.trackEvent("test1"); Runnable trackEvent1Command = backgroundRunnable.getValue(); + assertNotNull(trackEvent1Command); /* Update authentication provider before the commands run and track a second event. */ AuthenticationProvider.TokenProvider tokenProvider2 = mock(AuthenticationProvider.TokenProvider.class); AuthenticationProvider authenticationProvider2 = spy(new AuthenticationProvider(AuthenticationProvider.Type.MSA_COMPACT, "key2", tokenProvider2)); AnalyticsTransmissionTarget.addAuthenticationProvider(authenticationProvider2); Runnable addAuthProvider2Command = backgroundRunnable.getValue(); + assertNotNull(addAuthProvider2Command); Analytics.trackEvent("test2"); Runnable trackEvent2Command = backgroundRunnable.getValue(); + assertNotNull(trackEvent2Command); /* Simulate background thread doing everything in a sequence. */ trackEvent1Command.run(); @@ -511,12 +476,14 @@ public Object answer(InvocationOnMock invocation) { trackEvent2Command.run(); /* Verify first log has first ticket. */ + assertTrue(sentLogs.size() > 0); assertEquals(Collections.singletonList(authenticationProvider1.getTicketKeyHash()), sentLogs.get(0).getExt().getProtocol().getTicketKeys()); /* And that we checked expiry. */ verify(authenticationProvider1).checkTokenExpiry(); /* Verify second log has the second ticket. */ + assertTrue(sentLogs.size() > 1); assertEquals(Collections.singletonList(authenticationProvider2.getTicketKeyHash()), sentLogs.get(1).getExt().getProtocol().getTicketKeys()); /* And that we checked expiry. */ @@ -636,7 +603,7 @@ public void trackEventWithInvalidFlags() { target.trackEvent("eventName1", (Map) null, 0x03); target.trackEvent("eventName2", (EventProperties) null, 0x03); verify(mChannel, times(2)).enqueue(isA(EventLog.class), anyString(), eq(DEFAULTS)); - verifyStatic(times(2)); + verifyStatic(AppCenterLog.class, times(2)); AppCenterLog.warn(eq(AppCenter.LOG_TAG), anyString()); } } diff --git a/sdk/appcenter-analytics/src/test/java/com/microsoft/appcenter/analytics/AuthenticationProviderTest.java b/sdk/appcenter-analytics/src/test/java/com/microsoft/appcenter/analytics/AuthenticationProviderTest.java index 124735dcd..f94f49c71 100644 --- a/sdk/appcenter-analytics/src/test/java/com/microsoft/appcenter/analytics/AuthenticationProviderTest.java +++ b/sdk/appcenter-analytics/src/test/java/com/microsoft/appcenter/analytics/AuthenticationProviderTest.java @@ -22,9 +22,9 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; -import static org.mockito.Matchers.any; -import static org.mockito.Matchers.anyString; -import static org.mockito.Matchers.eq; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.doNothing; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; @@ -71,13 +71,13 @@ public void acquireTokenAsyncInvalidToken() { /* When callback parameters are invalid, don't update cache. */ callback.getValue().onAuthenticationResult(null, new Date()); - verifyStatic(never()); + verifyStatic(TicketCache.class, never()); TicketCache.putTicket(anyString(), anyString()); /* Ignore calling callback more than once, even if parameters are valid the second time. */ long freshDate = System.currentTimeMillis() + 15 * 60 * 1000; callback.getValue().onAuthenticationResult("test", new Date(freshDate)); - verifyStatic(never()); + verifyStatic(TicketCache.class, never()); TicketCache.putTicket(eq(authenticationProvider.getTicketKeyHash()), eq("p:test")); } @@ -94,13 +94,13 @@ public void acquireTokenAsyncInvalidDate() { /* When callback parameters are invalid, don't update cache. */ callback.getValue().onAuthenticationResult("test", null); - verifyStatic(never()); + verifyStatic(TicketCache.class, never()); TicketCache.putTicket(anyString(), anyString()); /* Ignore calling callback more than once, even if parameters are valid the second time. */ long freshDate = System.currentTimeMillis() + 15 * 60 * 1000; callback.getValue().onAuthenticationResult("test", new Date(freshDate)); - verifyStatic(never()); + verifyStatic(TicketCache.class, never()); TicketCache.putTicket(eq(authenticationProvider.getTicketKeyHash()), eq("p:test")); } @@ -118,12 +118,12 @@ public void acquireTokenAsyncValid() { /* When callback parameters are valid update cache. */ long freshDate = System.currentTimeMillis() + 15 * 60 * 1000; callback.getValue().onAuthenticationResult("test", new Date(freshDate)); - verifyStatic(); + verifyStatic(TicketCache.class); TicketCache.putTicket(eq(authenticationProvider.getTicketKeyHash()), eq("p:test")); /* Duplicate calls are ignored. */ callback.getValue().onAuthenticationResult("test2", new Date(freshDate)); - verifyStatic(never()); + verifyStatic(TicketCache.class, never()); TicketCache.putTicket(eq(authenticationProvider.getTicketKeyHash()), eq("p:test2")); } @@ -143,7 +143,7 @@ public void refreshToken() { when(expiryDate.getTime()).thenReturn(System.currentTimeMillis() + 15 * 60 * 1000); verify(tokenProvider).acquireToken(anyString(), callback.capture()); callback.getValue().onAuthenticationResult("test", expiryDate); - verifyStatic(); + verifyStatic(TicketCache.class); TicketCache.putTicket(eq(authenticationProvider.getTicketKeyHash()), eq("d:test")); /* Then refresh does nothing. */ @@ -171,7 +171,7 @@ public void refreshToken() { callback.getValue().onAuthenticationResult("test", expiryDate); /* Verify cache updated. */ - verifyStatic(times(2)); + verifyStatic(TicketCache.class, times(2)); TicketCache.putTicket(eq(authenticationProvider.getTicketKeyHash()), eq("d:test")); /* Now that called back, we can refresh again. */ @@ -181,7 +181,7 @@ public void refreshToken() { verify(authenticationProvider).acquireTokenAsync(); verify(tokenProvider).acquireToken(anyString(), callback.capture()); callback.getValue().onAuthenticationResult("test", expiryDate); - verifyStatic(times(3)); + verifyStatic(TicketCache.class, times(3)); TicketCache.putTicket(eq(authenticationProvider.getTicketKeyHash()), eq("d:test")); } } diff --git a/sdk/appcenter-analytics/src/test/java/com/microsoft/appcenter/analytics/EventPropertiesTest.java b/sdk/appcenter-analytics/src/test/java/com/microsoft/appcenter/analytics/EventPropertiesTest.java index ac80c0d11..f488d1757 100644 --- a/sdk/appcenter-analytics/src/test/java/com/microsoft/appcenter/analytics/EventPropertiesTest.java +++ b/sdk/appcenter-analytics/src/test/java/com/microsoft/appcenter/analytics/EventPropertiesTest.java @@ -21,8 +21,8 @@ import java.util.Date; import static org.junit.Assert.assertEquals; -import static org.mockito.Matchers.anyString; -import static org.mockito.Matchers.eq; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.never; import static org.mockito.Mockito.times; import static org.powermock.api.mockito.PowerMockito.mockStatic; @@ -48,7 +48,7 @@ public void validKeys() { properties.set("t4", 0.1); properties.set("t5", false); assertEquals(5, properties.getProperties().size()); - verifyStatic(never()); + verifyStatic(AppCenterLog.class, never()); AppCenterLog.error(eq(Analytics.LOG_TAG), anyString()); } @@ -61,7 +61,7 @@ public void nullKeyValidation() { properties.set(null, 0.1); properties.set(null, false); assertEquals(0, properties.getProperties().size()); - verifyStatic(times(5)); + verifyStatic(AppCenterLog.class, times(5)); AppCenterLog.error(eq(Analytics.LOG_TAG), anyString()); } @@ -71,9 +71,9 @@ public void warningWhenOverridingKeys() { properties.set("t1", "test"); properties.set("t1", new Date(0)); assertEquals(1, properties.getProperties().size()); - verifyStatic(never()); + verifyStatic(AppCenterLog.class, never()); AppCenterLog.error(eq(Analytics.LOG_TAG), anyString()); - verifyStatic(); + verifyStatic(AppCenterLog.class); AppCenterLog.warn(eq(Analytics.LOG_TAG), anyString()); } @@ -86,7 +86,7 @@ public void setString() { /* Null value. */ properties.set(key, (String) null); assertEquals(0, properties.getProperties().size()); - verifyStatic(times(1)); + verifyStatic(AppCenterLog.class, times(1)); AppCenterLog.error(eq(Analytics.LOG_TAG), anyString()); /* Normal value. */ @@ -97,7 +97,7 @@ public void setString() { expected.setName(key); expected.setValue(normalValue); assertEquals(expected, properties.getProperties().get(key)); - verifyStatic(times(1)); + verifyStatic(AppCenterLog.class, times(1)); AppCenterLog.error(eq(Analytics.LOG_TAG), anyString()); } @@ -110,7 +110,7 @@ public void setDate() { /* Null value. */ properties.set(key, (Date) null); assertEquals(0, properties.getProperties().size()); - verifyStatic(times(1)); + verifyStatic(AppCenterLog.class, times(1)); AppCenterLog.error(eq(Analytics.LOG_TAG), anyString()); /* Normal value. */ @@ -121,7 +121,7 @@ public void setDate() { expected.setName(key); expected.setValue(normalValue); assertEquals(expected, properties.getProperties().get(key)); - verifyStatic(times(1)); + verifyStatic(AppCenterLog.class, times(1)); AppCenterLog.error(eq(Analytics.LOG_TAG), anyString()); } @@ -139,7 +139,7 @@ public void setLong() { expected.setName(key); expected.setValue(normalValue); assertEquals(expected, properties.getProperties().get(key)); - verifyStatic(never()); + verifyStatic(AppCenterLog.class, never()); AppCenterLog.error(eq(Analytics.LOG_TAG), anyString()); } @@ -154,7 +154,7 @@ public void setDouble() { properties = new EventProperties(); properties.set(key, nanValue); assertEquals(0, properties.getProperties().size()); - verifyStatic(times(1)); + verifyStatic(AppCenterLog.class, times(1)); AppCenterLog.error(eq(Analytics.LOG_TAG), anyString()); /* Positive infinity value. */ @@ -162,7 +162,7 @@ public void setDouble() { properties = new EventProperties(); properties.set(key, positiveInfinityValue); assertEquals(0, properties.getProperties().size()); - verifyStatic(times(2)); + verifyStatic(AppCenterLog.class, times(2)); AppCenterLog.error(eq(Analytics.LOG_TAG), anyString()); /* Negative infinity value. */ @@ -170,7 +170,7 @@ public void setDouble() { properties = new EventProperties(); properties.set(key, negativeInfinityValue); assertEquals(0, properties.getProperties().size()); - verifyStatic(times(3)); + verifyStatic(AppCenterLog.class, times(3)); AppCenterLog.error(eq(Analytics.LOG_TAG), anyString()); /* Normal value. */ @@ -180,7 +180,7 @@ public void setDouble() { DoubleTypedProperty expected = new DoubleTypedProperty(); expected.setName(key); expected.setValue(normalValue); - verifyStatic(times(3)); + verifyStatic(AppCenterLog.class, times(3)); AppCenterLog.error(eq(Analytics.LOG_TAG), anyString()); } @@ -196,7 +196,7 @@ public void setBoolean() { BooleanTypedProperty expected = new BooleanTypedProperty(); expected.setName(key); expected.setValue(false); - verifyStatic(never()); + verifyStatic(AppCenterLog.class, never()); AppCenterLog.error(eq(Analytics.LOG_TAG), anyString()); } } \ No newline at end of file diff --git a/sdk/appcenter-analytics/src/test/java/com/microsoft/appcenter/analytics/LogNameMatcher.java b/sdk/appcenter-analytics/src/test/java/com/microsoft/appcenter/analytics/LogNameMatcher.java new file mode 100644 index 000000000..a8ddd20b9 --- /dev/null +++ b/sdk/appcenter-analytics/src/test/java/com/microsoft/appcenter/analytics/LogNameMatcher.java @@ -0,0 +1,38 @@ +/* + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. + */ + +package com.microsoft.appcenter.analytics; + +import static org.mockito.ArgumentMatchers.argThat; + +import com.microsoft.appcenter.analytics.ingestion.models.LogWithNameAndProperties; +import com.microsoft.appcenter.ingestion.models.Log; + +import org.mockito.ArgumentMatcher; + +public class LogNameMatcher implements ArgumentMatcher { + + private final Class mClazz; + private final String mExpectedName; + + private LogNameMatcher(Class clazz, String expectedName) { + mClazz = clazz; + mExpectedName = expectedName; + } + + @SuppressWarnings("unchecked") + @Override + public boolean matches(Log log) { + if (log != null && mClazz.isAssignableFrom(log.getClass())) { + T pageLog = (T) log; + return mExpectedName.equals(pageLog.getName()); + } + return false; + } + + public static Log logName(Class clazz, String expectedName) { + return argThat(new LogNameMatcher(clazz, expectedName)); + } +} diff --git a/sdk/appcenter-analytics/src/test/java/com/microsoft/appcenter/analytics/PropertyConfiguratorTest.java b/sdk/appcenter-analytics/src/test/java/com/microsoft/appcenter/analytics/PropertyConfiguratorTest.java index 5bab84544..f44d1f59e 100644 --- a/sdk/appcenter-analytics/src/test/java/com/microsoft/appcenter/analytics/PropertyConfiguratorTest.java +++ b/sdk/appcenter-analytics/src/test/java/com/microsoft/appcenter/analytics/PropertyConfiguratorTest.java @@ -49,10 +49,10 @@ import static org.junit.Assert.assertNotSame; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; -import static org.mockito.Matchers.any; -import static org.mockito.Matchers.anyString; -import static org.mockito.Matchers.eq; -import static org.mockito.Matchers.notNull; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.ArgumentMatchers.notNull; import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; @@ -242,7 +242,7 @@ public void collectDeviceId() { /* Mock context. */ mockStatic(Secure.class); - when(Secure.getString(any(ContentResolver.class), anyString())).thenReturn("mockDeviceId"); + when(Secure.getString(any(), any())).thenReturn("mockDeviceId"); /* Get property configurator and collect device ID. */ PropertyConfigurator pc = Analytics.getTransmissionTarget("test").getPropertyConfigurator(); @@ -265,7 +265,7 @@ public void collectDeviceIdSavedWhenDisabled() { /* Mock context. */ mockStatic(Secure.class); - when(Secure.getString(any(ContentResolver.class), anyString())).thenReturn("mockDeviceId"); + when(Secure.getString(any(), any())).thenReturn("mockDeviceId"); /* Disable Analytics. */ Analytics.setEnabled(false).get(); @@ -722,7 +722,7 @@ public void overridingPartAIsNotRetroactive() { /* Mock deviceId. */ mockStatic(Secure.class); - when(Secure.getString(any(ContentResolver.class), anyString())).thenReturn("mockDeviceId"); + when(Secure.getString(any(), any())).thenReturn("mockDeviceId"); /* Start analytics and simulate background thread handler (we hold the thread command and run it in the test). */ Analytics analytics = Analytics.getInstance(); @@ -735,7 +735,7 @@ public Object answer(InvocationOnMock invocation) { backgroundCommands.add((Runnable) invocation.getArguments()[0]); return null; } - }).when(handler).post(notNull(Runnable.class), any(Runnable.class)); + }).when(handler).post(notNull(), any()); analytics.onStarting(handler); analytics.onStarted(mock(Context.class), mChannel, null, null, true); diff --git a/sdk/appcenter-analytics/src/test/java/com/microsoft/appcenter/analytics/channel/SessionTrackerTest.java b/sdk/appcenter-analytics/src/test/java/com/microsoft/appcenter/analytics/channel/SessionTrackerTest.java index 826484e72..c371fc2f0 100644 --- a/sdk/appcenter-analytics/src/test/java/com/microsoft/appcenter/analytics/channel/SessionTrackerTest.java +++ b/sdk/appcenter-analytics/src/test/java/com/microsoft/appcenter/analytics/channel/SessionTrackerTest.java @@ -40,13 +40,14 @@ import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; -import static org.mockito.Matchers.any; -import static org.mockito.Matchers.anyInt; -import static org.mockito.Matchers.anySetOf; -import static org.mockito.Matchers.anyString; -import static org.mockito.Matchers.argThat; -import static org.mockito.Matchers.eq; -import static org.mockito.Matchers.notNull; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.anySetOf; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.ArgumentMatchers.argThat; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.ArgumentMatchers.isA; +import static org.mockito.ArgumentMatchers.notNull; import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; @@ -58,7 +59,13 @@ import static org.powermock.api.mockito.PowerMockito.verifyStatic; @SuppressWarnings("unused") -@PrepareForTest({SessionTracker.class, SessionContext.class, SharedPreferencesManager.class, SystemClock.class, AppCenterLog.class}) +@PrepareForTest({ + AppCenterLog.class, + SessionTracker.class, + SessionContext.class, + SharedPreferencesManager.class, + SystemClock.class +}) public class SessionTrackerTest { private final static String TEST_GROUP = "group_test"; @@ -310,13 +317,7 @@ public void goBackgroundAndComeBackMuchLater() { } /* In total we sent only 2 session logs. */ - verify(mChannel, times(2)).enqueue(argThat(new ArgumentMatcher() { - - @Override - public boolean matches(Object argument) { - return argument instanceof StartSessionLog; - } - }), anyString(), eq(DEFAULTS)); + verify(mChannel, times(2)).enqueue(isA(StartSessionLog.class), anyString(), eq(DEFAULTS)); } @Test @@ -330,11 +331,11 @@ public Object answer(InvocationOnMock invocation) { startSessionLog.set((StartSessionLog) invocation.getArguments()[0]); return null; } - }).when(mChannel).enqueue(notNull(StartSessionLog.class), eq(TEST_GROUP), anyInt()); + }).when(mChannel).enqueue(isA(StartSessionLog.class), eq(TEST_GROUP), anyInt()); /* Go foreground, start session is sent. */ mSessionTracker.onActivityResumed(); - verify(mChannel, times(1)).enqueue(notNull(StartSessionLog.class), eq(TEST_GROUP), eq(DEFAULTS)); + verify(mChannel, times(1)).enqueue(isA(StartSessionLog.class), eq(TEST_GROUP), eq(DEFAULTS)); assertNotNull(startSessionLog.get()); UUID sid = startSessionLog.get().getSid(); assertNotNull(sid); @@ -344,14 +345,14 @@ public Object answer(InvocationOnMock invocation) { mSessionTracker.onActivityPaused(); spendTime(1); mSessionTracker.onActivityResumed(); - verify(mChannel, times(1)).enqueue(notNull(StartSessionLog.class), eq(TEST_GROUP), eq(DEFAULTS)); + verify(mChannel, times(1)).enqueue(isA(StartSessionLog.class), eq(TEST_GROUP), eq(DEFAULTS)); /* Go background and come back after timeout, second session. */ spendTime(1); mSessionTracker.onActivityPaused(); spendTime(30000); mSessionTracker.onActivityResumed(); - verify(mChannel, times(2)).enqueue(notNull(StartSessionLog.class), eq(TEST_GROUP), eq(DEFAULTS)); + verify(mChannel, times(2)).enqueue(isA(StartSessionLog.class), eq(TEST_GROUP), eq(DEFAULTS)); assertNotEquals(sid, startSessionLog.get().getSid()); } @@ -536,7 +537,7 @@ public void pastSessions() { /* Clear sessions. */ mSessionTracker.clearSessions(); - verifyStatic(); + verifyStatic(SharedPreferencesManager.class); SharedPreferencesManager.remove("sessions"); } @@ -647,7 +648,7 @@ public void setManualSessionTracker() { assertNull(log.getSid()); /* Verify that method was called. */ - verifyStatic(); + verifyStatic(SystemClock.class); SystemClock.elapsedRealtime(); /* Set manual session tracker. */ @@ -659,12 +660,12 @@ public void setManualSessionTracker() { assertNull(log.getSid()); /* Verify that method wasn't called. */ - verifyStatic(); + verifyStatic(SystemClock.class); SystemClock.elapsedRealtime(); /* Call pause and verify that method wasn't called again. */ mSessionTracker.onActivityPaused(); - verifyStatic(); + verifyStatic(SystemClock.class); SystemClock.elapsedRealtime(); /* Call start session and verify that log has session id. */ @@ -673,7 +674,7 @@ public void setManualSessionTracker() { assertNotNull(log.getSid()); /* Verify that method wasn't called. */ - verifyStatic(); + verifyStatic(SystemClock.class); SystemClock.elapsedRealtime(); } } diff --git a/sdk/appcenter-analytics/src/test/java/com/microsoft/appcenter/analytics/ingestion/models/json/EventLogFactoryTest.java b/sdk/appcenter-analytics/src/test/java/com/microsoft/appcenter/analytics/ingestion/models/json/EventLogFactoryTest.java index 09f367d68..4e0c0bae8 100644 --- a/sdk/appcenter-analytics/src/test/java/com/microsoft/appcenter/analytics/ingestion/models/json/EventLogFactoryTest.java +++ b/sdk/appcenter-analytics/src/test/java/com/microsoft/appcenter/analytics/ingestion/models/json/EventLogFactoryTest.java @@ -27,9 +27,9 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertSame; -import static org.mockito.Matchers.eq; -import static org.mockito.Matchers.notNull; -import static org.mockito.Matchers.same; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.ArgumentMatchers.notNull; +import static org.mockito.ArgumentMatchers.same; import static org.mockito.Mockito.times; import static org.powermock.api.mockito.PowerMockito.mockStatic; import static org.powermock.api.mockito.PowerMockito.verifyStatic; @@ -99,7 +99,7 @@ public void convertEventWithoutProperties() { for (CommonSchemaLog commonSchemaLog : convertedLogs) { /* Check name was added. */ - verifyStatic(); + verifyStatic(PartAUtils.class); PartAUtils.setName(same(commonSchemaLog), eq("test")); /* Check tag was added. */ @@ -107,13 +107,13 @@ public void convertEventWithoutProperties() { } /* Check Part A was added with target tokens. */ - verifyStatic(); + verifyStatic(PartAUtils.class); PartAUtils.addPartAFromLog(eq(log), notNull(CommonSchemaLog.class), eq("t1")); - verifyStatic(); + verifyStatic(PartAUtils.class); PartAUtils.addPartAFromLog(eq(log), notNull(CommonSchemaLog.class), eq("t2")); /* Check data was added with typed properties (and thus not old ones). */ - verifyStatic(times(2)); + verifyStatic(CommonSchemaDataUtils.class, times(2)); CommonSchemaDataUtils.addCommonSchemaData(eq(properties), notNull(CommonSchemaLog.class)); } } From 28ee3196cd970a116f6528bd856bfa178cc25442 Mon Sep 17 00:00:00 2001 From: Ivan Matkov Date: Thu, 21 Apr 2022 19:29:42 +0200 Subject: [PATCH 06/72] Fix crashes tests --- .../crashes/AbstractCrashesTest.java | 44 +-- .../appcenter/crashes/CrashesTest.java | 296 +++++++++--------- .../appcenter/crashes/HandledErrorTest.java | 234 +++++++------- .../crashes/UncaughtExceptionHandlerTest.java | 98 +++--- .../WrapperSdkExceptionManagerTest.java | 232 ++++++-------- .../models/ErrorAttachmentLogTest.java | 4 +- .../crashes/model/TestCrashExceptionTest.java | 1 - .../crashes/utils/ErrorLogHelperTest.java | 92 +++--- 8 files changed, 493 insertions(+), 508 deletions(-) diff --git a/sdk/appcenter-crashes/src/test/java/com/microsoft/appcenter/crashes/AbstractCrashesTest.java b/sdk/appcenter-crashes/src/test/java/com/microsoft/appcenter/crashes/AbstractCrashesTest.java index 1cef63a6e..1d51a569e 100644 --- a/sdk/appcenter-crashes/src/test/java/com/microsoft/appcenter/crashes/AbstractCrashesTest.java +++ b/sdk/appcenter-crashes/src/test/java/com/microsoft/appcenter/crashes/AbstractCrashesTest.java @@ -5,6 +5,14 @@ package com.microsoft.appcenter.crashes; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyBoolean; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; +import static org.powermock.api.mockito.PowerMockito.doAnswer; +import static org.powermock.api.mockito.PowerMockito.mockStatic; + import android.os.Looper; import android.os.SystemClock; @@ -23,36 +31,36 @@ import org.junit.After; import org.junit.Before; import org.junit.Rule; -import org.junit.rules.TemporaryFolder; import org.mockito.Mock; import org.mockito.invocation.InvocationOnMock; import org.mockito.stubbing.Answer; import org.powermock.core.classloader.annotations.PrepareForTest; import org.powermock.modules.junit4.rule.PowerMockRule; -import static org.mockito.Matchers.any; -import static org.mockito.Matchers.anyBoolean; -import static org.mockito.Matchers.eq; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; -import static org.powermock.api.mockito.PowerMockito.doAnswer; -import static org.powermock.api.mockito.PowerMockito.mockStatic; - -@SuppressWarnings("unused") -@PrepareForTest({ErrorLogHelper.class, SystemClock.class, FileManager.class, SharedPreferencesManager.class, AppCenterLog.class, AppCenter.class, Crashes.class, HandlerUtils.class, Looper.class, ErrorAttachmentLog.class}) +@PrepareForTest({ + AppCenter.class, + AppCenterLog.class, + Crashes.class, + ErrorAttachmentLog.class, + ErrorLogHelper.class, + FileManager.class, + HandlerUtils.class, + Looper.class, + SharedPreferencesManager.class, + SystemClock.class +}) public class AbstractCrashesTest { static final String CRASHES_ENABLED_KEY = PrefStorageConstants.KEY_ENABLED + "_" + Crashes.getInstance().getServiceName(); static final Exception EXCEPTION = new Exception("This is a test exception."); - @Rule - public final TemporaryFolder errorStorageDirectory = new TemporaryFolder(); - @Rule public PowerMockRule mPowerMockRule = new PowerMockRule(); + @Mock AppCenterHandler mAppCenterHandler; + @Mock private AppCenter mAppCenter; @@ -60,12 +68,12 @@ public class AbstractCrashesTest { public void setUp() { Thread.setDefaultUncaughtExceptionHandler(null); Crashes.unsetInstance(); - mockStatic(SystemClock.class); + mockStatic(AppCenter.class); + mockStatic(AppCenterLog.class); mockStatic(FileManager.class); mockStatic(SharedPreferencesManager.class); - mockStatic(AppCenterLog.class); + mockStatic(SystemClock.class); when(SystemClock.elapsedRealtime()).thenReturn(System.currentTimeMillis()); - mockStatic(AppCenter.class); when(AppCenter.getInstance()).thenReturn(mAppCenter); @SuppressWarnings("unchecked") @@ -101,7 +109,7 @@ public Void answer(InvocationOnMock invocation) { }; doAnswer(runNow).when(HandlerUtils.class); HandlerUtils.runOnUiThread(any(Runnable.class)); - doAnswer(runNow).when(mAppCenterHandler).post(any(Runnable.class), any(Runnable.class)); + doAnswer(runNow).when(mAppCenterHandler).post(any(Runnable.class), any()); } @After diff --git a/sdk/appcenter-crashes/src/test/java/com/microsoft/appcenter/crashes/CrashesTest.java b/sdk/appcenter-crashes/src/test/java/com/microsoft/appcenter/crashes/CrashesTest.java index 63f6a5445..975c75cdc 100644 --- a/sdk/appcenter-crashes/src/test/java/com/microsoft/appcenter/crashes/CrashesTest.java +++ b/sdk/appcenter-crashes/src/test/java/com/microsoft/appcenter/crashes/CrashesTest.java @@ -5,6 +5,50 @@ package com.microsoft.appcenter.crashes; +import static android.content.ComponentCallbacks2.TRIM_MEMORY_COMPLETE; +import static android.content.ComponentCallbacks2.TRIM_MEMORY_RUNNING_CRITICAL; +import static android.content.ComponentCallbacks2.TRIM_MEMORY_RUNNING_LOW; +import static android.content.ComponentCallbacks2.TRIM_MEMORY_RUNNING_MODERATE; +import static android.content.ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN; +import static android.util.Log.getStackTraceString; +import static com.microsoft.appcenter.Constants.WRAPPER_SDK_NAME_NDK; +import static com.microsoft.appcenter.Flags.CRITICAL; +import static com.microsoft.appcenter.Flags.DEFAULTS; +import static com.microsoft.appcenter.Flags.NORMAL; +import static com.microsoft.appcenter.crashes.Crashes.MINIDUMP_FILE; +import static com.microsoft.appcenter.crashes.Crashes.PREF_KEY_MEMORY_RUNNING_LEVEL; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyBoolean; +import static org.mockito.ArgumentMatchers.anyByte; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.anyLong; +import static org.mockito.ArgumentMatchers.anyMap; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.ArgumentMatchers.contains; +import static org.mockito.ArgumentMatchers.endsWith; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.ArgumentMatchers.isA; +import static org.mockito.ArgumentMatchers.isNull; +import static org.mockito.ArgumentMatchers.notNull; +import static org.mockito.Mockito.doNothing; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoInteractions; +import static org.mockito.Mockito.when; +import static org.powermock.api.mockito.PowerMockito.doThrow; +import static org.powermock.api.mockito.PowerMockito.mockStatic; +import static org.powermock.api.mockito.PowerMockito.verifyNoMoreInteractions; +import static org.powermock.api.mockito.PowerMockito.verifyStatic; +import static org.powermock.api.mockito.PowerMockito.whenNew; + import android.content.ComponentCallbacks; import android.content.ComponentCallbacks2; import android.content.Context; @@ -24,7 +68,6 @@ import com.microsoft.appcenter.crashes.model.ErrorReport; import com.microsoft.appcenter.crashes.model.TestCrashException; import com.microsoft.appcenter.crashes.utils.ErrorLogHelper; -import com.microsoft.appcenter.ingestion.Ingestion; import com.microsoft.appcenter.ingestion.models.Device; import com.microsoft.appcenter.ingestion.models.Log; import com.microsoft.appcenter.ingestion.models.json.DefaultLogSerializer; @@ -45,7 +88,6 @@ import org.junit.Test; import org.junit.rules.TemporaryFolder; import org.mockito.ArgumentCaptor; -import org.mockito.ArgumentMatcher; import org.mockito.Mockito; import org.mockito.invocation.InvocationOnMock; import org.mockito.stubbing.Answer; @@ -61,56 +103,13 @@ import java.util.Collection; import java.util.Collections; import java.util.Date; +import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.UUID; -import static android.content.ComponentCallbacks2.TRIM_MEMORY_COMPLETE; -import static android.content.ComponentCallbacks2.TRIM_MEMORY_RUNNING_CRITICAL; -import static android.content.ComponentCallbacks2.TRIM_MEMORY_RUNNING_LOW; -import static android.content.ComponentCallbacks2.TRIM_MEMORY_RUNNING_MODERATE; -import static android.content.ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN; -import static android.util.Log.getStackTraceString; -import static com.microsoft.appcenter.Constants.WRAPPER_SDK_NAME_NDK; -import static com.microsoft.appcenter.Flags.CRITICAL; -import static com.microsoft.appcenter.Flags.DEFAULTS; -import static com.microsoft.appcenter.Flags.NORMAL; -import static com.microsoft.appcenter.crashes.Crashes.MINIDUMP_FILE; -import static com.microsoft.appcenter.crashes.Crashes.PREF_KEY_MEMORY_RUNNING_LEVEL; -import static com.microsoft.appcenter.crashes.ingestion.models.ErrorAttachmentLog.attachmentWithBinary; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNotEquals; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertTrue; -import static org.mockito.Matchers.any; -import static org.mockito.Matchers.anyBoolean; -import static org.mockito.Matchers.anyByte; -import static org.mockito.Matchers.anyInt; -import static org.mockito.Matchers.anyLong; -import static org.mockito.Matchers.anyMapOf; -import static org.mockito.Matchers.anyString; -import static org.mockito.Matchers.argThat; -import static org.mockito.Matchers.contains; -import static org.mockito.Matchers.eq; -import static org.mockito.Matchers.isA; -import static org.mockito.Matchers.isNull; -import static org.mockito.Matchers.notNull; -import static org.mockito.Mockito.doNothing; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.never; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.verifyZeroInteractions; -import static org.mockito.Mockito.when; -import static org.powermock.api.mockito.PowerMockito.doThrow; -import static org.powermock.api.mockito.PowerMockito.mockStatic; -import static org.powermock.api.mockito.PowerMockito.verifyNoMoreInteractions; -import static org.powermock.api.mockito.PowerMockito.verifyStatic; -import static org.powermock.api.mockito.PowerMockito.whenNew; - +@PrepareForTest({android.util.Log.class}) public class CrashesTest extends AbstractCrashesTest { private static final String STACK_TRACE = "type: message"; @@ -134,7 +133,9 @@ private static void assertErrorEquals(ManagedErrorLog errorLog, ErrorReport repo @Before public void setUp() { super.setUp(); - mErrorLog = ErrorLogHelper.createErrorLog(mock(Context.class), Thread.currentThread(), new RuntimeException(), Thread.getAllStackTraces(), 0); + mockStatic(android.util.Log.class); + when(getStackTraceString(any(Throwable.class))).thenReturn(STACK_TRACE); + mErrorLog = ErrorLogHelper.createErrorLog(mock(Context.class), Thread.currentThread(), new RuntimeException(), new HashMap<>(), 0); } @Test @@ -149,20 +150,21 @@ public void initializeWhenDisabled() { Crashes crashes = Crashes.getInstance(); mockStatic(ErrorLogHelper.class); File dir = mock(File.class); - File file1 = mock(File.class); - File file2 = mock(File.class); UncaughtExceptionHandler mockHandler = mock(UncaughtExceptionHandler.class); when(ErrorLogHelper.getErrorStorageDirectory()).thenReturn(dir); when(ErrorLogHelper.getStoredErrorLogFiles()).thenReturn(new File[0]); when(ErrorLogHelper.getNewMinidumpFiles()).thenReturn(new File[0]); - when(dir.listFiles()).thenReturn(new File[]{file1, file2}); + when(dir.listFiles()).thenReturn(new File[]{ + mock(File.class), + mock(File.class) + }); crashes.setUncaughtExceptionHandler(mockHandler); when(SharedPreferencesManager.getBoolean(CRASHES_ENABLED_KEY, true)).thenReturn(false); crashes.onStarting(mAppCenterHandler); crashes.onStarted(mock(Context.class), mock(Channel.class), "", null, true); /* Test. */ - verifyStatic(times(4)); + verifyStatic(SharedPreferencesManager.class, times(4)); SharedPreferencesManager.getBoolean(CRASHES_ENABLED_KEY, true); assertFalse(Crashes.isEnabled().get()); assertEquals(crashes.getInitializeTimestamp(), -1); @@ -173,7 +175,7 @@ public void initializeWhenDisabled() { @Test public void notInit() { Crashes.notifyUserConfirmation(Crashes.SEND); - verifyStatic(); + verifyStatic(AppCenterLog.class); AppCenterLog.error(eq(AppCenter.LOG_TAG), anyString()); } @@ -213,7 +215,7 @@ public void setEnabled() { crashes.onStarting(mAppCenterHandler); crashes.onStarted(mock(Context.class), mockChannel, "", null, true); verify(mockChannel).removeGroup(eq(crashes.getGroupName())); - verify(mockChannel).addGroup(eq(crashes.getGroupName()), anyInt(), anyInt(), anyInt(), isNull(Ingestion.class), any(Channel.GroupListener.class)); + verify(mockChannel).addGroup(eq(crashes.getGroupName()), anyInt(), anyLong(), anyInt(), isNull(), any(Channel.GroupListener.class)); /* Test. */ assertTrue(Crashes.isEnabled().get()); @@ -238,7 +240,7 @@ public void setEnabled() { assertTrue(Thread.getDefaultUncaughtExceptionHandler() instanceof UncaughtExceptionHandler); Crashes.setEnabled(true); assertTrue(Crashes.isEnabled().get()); - verify(mockChannel, times(2)).addGroup(eq(crashes.getGroupName()), anyInt(), anyInt(), anyInt(), isNull(Ingestion.class), any(Channel.GroupListener.class)); + verify(mockChannel, times(2)).addGroup(eq(crashes.getGroupName()), anyInt(), anyLong(), anyInt(), isNull(), any(Channel.GroupListener.class)); Crashes.trackError(EXCEPTION); verify(mockChannel, times(1)).enqueue(isA(HandledErrorLog.class), eq(crashes.getGroupName()), eq(DEFAULTS)); } @@ -264,14 +266,14 @@ public void failToListErrorStorageDirectoryOnDisable() { crashes.onStarting(mAppCenterHandler); crashes.onStarted(context, mockChannel, "", null, true); verify(mockChannel).removeGroup(eq(crashes.getGroupName())); - verify(mockChannel).addGroup(eq(crashes.getGroupName()), anyInt(), anyInt(), anyInt(), isNull(Ingestion.class), any(Channel.GroupListener.class)); + verify(mockChannel).addGroup(eq(crashes.getGroupName()), anyInt(), anyLong(), anyInt(), isNull(), any(Channel.GroupListener.class)); /* When we disable. */ Crashes.setEnabled(false); assertFalse(Crashes.isEnabled().get()); /* Verify we recovered file listing error. */ - verify(context).unregisterComponentCallbacks(notNull(ComponentCallbacks.class)); + verify(context).unregisterComponentCallbacks(notNull()); } @Test @@ -316,7 +318,7 @@ public void queuePendingCrashesShouldProcess() throws JSONException { List errorAttachmentLogList = Arrays.asList(mockAttachment, mockAttachment, mockEmptyAttachment, null); when(mockListener.getErrorAttachments(report)).thenReturn(errorAttachmentLogList); LogSerializer logSerializer = mock(LogSerializer.class); - when(logSerializer.deserializeLog(anyString(), anyString())).thenReturn(mErrorLog); + when(logSerializer.deserializeLog(anyString(), any())).thenReturn(mErrorLog); Crashes crashes = Crashes.getInstance(); crashes.setLogSerializer(logSerializer); crashes.setInstanceListener(mockListener); @@ -327,13 +329,9 @@ public void queuePendingCrashesShouldProcess() throws JSONException { verify(mockListener).shouldProcess(report); verify(mockListener).shouldAwaitUserConfirmation(); verify(mockListener).getErrorAttachments(report); - verify(mockChannel).enqueue(argThat(new ArgumentMatcher() { - @Override - public boolean matches(Object log) { - return log.equals(mErrorLog); - } - }), eq(crashes.getGroupName()), eq(CRITICAL)); - verify(mockChannel, times(errorAttachmentLogList.size() - skipAttachmentLogsCount)).enqueue(mockAttachment, crashes.getGroupName(), DEFAULTS); + verify(mockChannel).enqueue(eq(mErrorLog), eq(crashes.getGroupName()), eq(CRITICAL)); + verify(mockChannel, times(errorAttachmentLogList.size() - skipAttachmentLogsCount)) + .enqueue(mockAttachment, crashes.getGroupName(), DEFAULTS); } @Test @@ -354,7 +352,7 @@ public void queuePendingCrashesShouldNotProcess() throws JSONException { Crashes crashes = Crashes.getInstance(); LogSerializer logSerializer = mock(LogSerializer.class); - when(logSerializer.deserializeLog(anyString(), anyString())).thenReturn(mErrorLog); + when(logSerializer.deserializeLog(anyString(), any())).thenReturn(mErrorLog); crashes.setLogSerializer(logSerializer); crashes.setInstanceListener(mockListener); @@ -401,7 +399,7 @@ public void queuePendingCrashesAlwaysSend() throws JSONException { Crashes crashes = Crashes.getInstance(); LogSerializer logSerializer = mock(LogSerializer.class); - when(logSerializer.deserializeLog(anyString(), anyString())).thenReturn(mErrorLog); + when(logSerializer.deserializeLog(anyString(), any())).thenReturn(mErrorLog); crashes.setLogSerializer(logSerializer); crashes.setInstanceListener(mockListener); @@ -412,12 +410,7 @@ public void queuePendingCrashesAlwaysSend() throws JSONException { verify(mockListener, never()).shouldAwaitUserConfirmation(); verify(mockListener).getErrorAttachments(report); - verify(mockChannel).enqueue(argThat(new ArgumentMatcher() { - @Override - public boolean matches(Object log) { - return log.equals(mErrorLog); - } - }), eq(crashes.getGroupName()), eq(CRITICAL)); + verify(mockChannel).enqueue(eq(mErrorLog), eq(crashes.getGroupName()), eq(CRITICAL)); verify(mockChannel, times(errorAttachmentLogList.size())).enqueue(mockAttachment, crashes.getGroupName(), DEFAULTS); } @@ -437,7 +430,7 @@ public void processPendingErrorsCorrupted() throws JSONException { mockException.setMessage("message"); ManagedErrorLog managedErrorLog = mock(ManagedErrorLog.class); when(managedErrorLog.getException()).thenReturn(mockException); - when(logSerializer.deserializeLog(anyString(), anyString())).thenReturn(managedErrorLog); + when(logSerializer.deserializeLog(anyString(), any())).thenReturn(managedErrorLog); crashes.setLogSerializer(logSerializer); CrashesListener listener = mock(CrashesListener.class); @@ -446,14 +439,14 @@ public void processPendingErrorsCorrupted() throws JSONException { Channel channel = mock(Channel.class); crashes.onStarting(mAppCenterHandler); crashes.onStarted(mock(Context.class), channel, "", null, true); - verifyZeroInteractions(listener); + verifyNoInteractions(listener); verify(channel, never()).enqueue(any(Log.class), anyString(), anyInt()); } @Test public void noQueueingWhenDisabled() { mockStatic(ErrorLogHelper.class); - when(ErrorLogHelper.getErrorStorageDirectory()).thenReturn(errorStorageDirectory.getRoot()); + when(ErrorLogHelper.getErrorStorageDirectory()).thenReturn(mTemporaryFolder.getRoot()); when(SharedPreferencesManager.getBoolean(CRASHES_ENABLED_KEY, true)).thenReturn(false); Channel channel = mock(Channel.class); Crashes crashes = Crashes.getInstance(); @@ -473,7 +466,7 @@ public void noQueueNullLog() throws JSONException { Crashes crashes = Crashes.getInstance(); LogSerializer logSerializer = mock(LogSerializer.class); - when(logSerializer.deserializeLog(anyString(), anyString())).thenReturn(null); + when(logSerializer.deserializeLog(anyString(), any())).thenReturn(null); crashes.setLogSerializer(logSerializer); crashes.onStarting(mAppCenterHandler); @@ -495,7 +488,7 @@ public void printErrorOnJSONException() throws JSONException { Crashes crashes = Crashes.getInstance(); LogSerializer logSerializer = mock(LogSerializer.class); - when(logSerializer.deserializeLog(anyString(), anyString())).thenThrow(jsonException); + when(logSerializer.deserializeLog(anyString(), any())).thenThrow(jsonException); crashes.setLogSerializer(logSerializer); crashes.onStarting(mAppCenterHandler); @@ -503,7 +496,7 @@ public void printErrorOnJSONException() throws JSONException { verify(mockChannel, never()).enqueue(any(Log.class), anyString(), anyInt()); - verifyStatic(); + verifyStatic(AppCenterLog.class); AppCenterLog.error(eq(Crashes.LOG_TAG), anyString(), eq(jsonException)); } @@ -540,7 +533,7 @@ public void getChannelListener() throws JSONException { when(FileManager.read(any(File.class))).thenReturn(STACK_TRACE); LogSerializer logSerializer = mock(LogSerializer.class); - when(logSerializer.deserializeLog(anyString(), anyString())).thenReturn(mErrorLog); + when(logSerializer.deserializeLog(anyString(), any())).thenReturn(mErrorLog); CrashesListener crashesListener = mock(CrashesListener.class); when(crashesListener.shouldProcess(any(ErrorReport.class))).thenReturn(true); Crashes.setListener(crashesListener); @@ -550,7 +543,7 @@ public void getChannelListener() throws JSONException { crashes.onStarted(mock(Context.class), mock(Channel.class), "", null, true); /* The error report was created and cached but device is null here. */ - verifyStatic(); + verifyStatic(ErrorLogHelper.class); ErrorLogHelper.getErrorReportFromErrorLog(mErrorLog, STACK_TRACE); assertNull(errorReport.getDevice()); @@ -571,7 +564,7 @@ public void getChannelListener() throws JSONException { assertErrorEquals(mErrorLog, errorReportCaptor.getValue()); /* No more error reports should be produced at the point. */ - verifyStatic(); + verifyStatic(ErrorLogHelper.class); ErrorLogHelper.getErrorReportFromErrorLog(mErrorLog, STACK_TRACE); /* Simulate onFailure event. */ @@ -580,7 +573,7 @@ public void getChannelListener() throws JSONException { assertErrorEquals(mErrorLog, errorReportCaptor.getValue()); /* onSuccess and onFailure invalidate the cache, so one more call is expected. */ - verifyStatic(); + verifyStatic(ErrorLogHelper.class); ErrorLogHelper.getErrorReportFromErrorLog(mErrorLog, STACK_TRACE); } @@ -600,12 +593,12 @@ public void getChannelListenerErrors() { Channel.GroupListener listener = Crashes.getInstance().getChannelListener(); listener.onBeforeSending(mErrorLog); - verifyStatic(); + verifyStatic(AppCenterLog.class); AppCenterLog.warn(eq(Crashes.LOG_TAG), anyString()); Mockito.verifyNoMoreInteractions(mockListener); listener.onSuccess(mock(Log.class)); - verifyStatic(); + verifyStatic(AppCenterLog.class); AppCenterLog.warn(eq(Crashes.LOG_TAG), contains(Log.class.getName())); Mockito.verifyNoMoreInteractions(mockListener); } @@ -628,7 +621,7 @@ public void handleUserConfirmationDoNotSend() throws JSONException { /* Prepare data. Set values. */ Crashes crashes = Crashes.getInstance(); LogSerializer logSerializer = mock(LogSerializer.class); - when(logSerializer.deserializeLog(anyString(), anyString())).thenReturn(mErrorLog); + when(logSerializer.deserializeLog(anyString(), any())).thenReturn(mErrorLog); crashes.setLogSerializer(logSerializer); crashes.setInstanceListener(mockListener); crashes.onStarting(mAppCenterHandler); @@ -637,11 +630,11 @@ public void handleUserConfirmationDoNotSend() throws JSONException { /* Verify. */ Crashes.notifyUserConfirmation(Crashes.DONT_SEND); verify(mockListener, never()).getErrorAttachments(any(ErrorReport.class)); - verifyStatic(); + verifyStatic(ErrorLogHelper.class); ErrorLogHelper.cleanPendingMinidumps(); - verifyStatic(); + verifyStatic(ErrorLogHelper.class); ErrorLogHelper.removeStoredErrorLogFile(mErrorLog.getId()); - verifyStatic(never()); + verifyStatic(ErrorLogHelper.class, never()); ErrorLogHelper.removeLostThrowableFiles(); } @@ -663,7 +656,7 @@ public void handleUserConfirmationAlwaysSend() throws JSONException { Crashes crashes = Crashes.getInstance(); LogSerializer logSerializer = mock(LogSerializer.class); - when(logSerializer.deserializeLog(anyString(), anyString())).thenReturn(mErrorLog); + when(logSerializer.deserializeLog(anyString(), any())).thenReturn(mErrorLog); crashes.setLogSerializer(logSerializer); crashes.setInstanceListener(mockListener); @@ -672,7 +665,7 @@ public void handleUserConfirmationAlwaysSend() throws JSONException { Crashes.notifyUserConfirmation(Crashes.ALWAYS_SEND); - verifyStatic(); + verifyStatic(SharedPreferencesManager.class); SharedPreferencesManager.putBoolean(Crashes.PREF_KEY_ALWAYS_SEND, true); } @@ -696,12 +689,12 @@ public void buildErrorReport() { Crashes crashes = Crashes.getInstance(); ErrorReport report = crashes.buildErrorReport(mErrorLog); assertErrorEquals(mErrorLog, report); - verifyStatic(); + verifyStatic(ErrorLogHelper.class); ErrorLogHelper.getErrorReportFromErrorLog(mErrorLog, STACK_TRACE); /* Verify the caching. */ assertEquals(report, crashes.buildErrorReport(mErrorLog)); - verifyStatic(); + verifyStatic(ErrorLogHelper.class); ErrorLogHelper.getErrorReportFromErrorLog(mErrorLog, STACK_TRACE); mErrorLog.setId(UUID.randomUUID()); @@ -768,7 +761,7 @@ public void noCrashInLastSession() { Crashes.getInstance().onStarted(mock(Context.class), mock(Channel.class), "", null, true); assertFalse(Crashes.hasCrashedInLastSession().get()); assertNull(Crashes.getLastSessionCrashReport().get()); - verifyStatic(never()); + verifyStatic(AppCenterLog.class, never()); AppCenterLog.debug(anyString(), anyString()); } @@ -792,12 +785,12 @@ public void crashInLastSession() throws JSONException, IOException { errorLog.setDevice(mock(Device.class)); LogSerializer logSerializer = mock(LogSerializer.class); - when(logSerializer.deserializeLog(anyString(), anyString())).thenReturn(errorLog); + when(logSerializer.deserializeLog(anyString(), any())).thenReturn(errorLog); final ErrorReport errorReport = ErrorLogHelper.getErrorReportFromErrorLog(errorLog, STACK_TRACE); mockStatic(ErrorLogHelper.class); - File lastErrorLogFile = errorStorageDirectory.newFile("last-error-log.json"); + File lastErrorLogFile = mTemporaryFolder.newFile("last-error-log.json"); new FileWriter(lastErrorLogFile).append("fake_data").close(); when(ErrorLogHelper.getLastErrorLogFile()).thenReturn(lastErrorLogFile); when(ErrorLogHelper.getErrorReportFromErrorLog(errorLog, STACK_TRACE)).thenReturn(errorReport); @@ -856,24 +849,24 @@ public void getThrowableDeprecated() { public void noCrashInLastSessionWhenDisabled() { mockStatic(ErrorLogHelper.class); - when(ErrorLogHelper.getErrorStorageDirectory()).thenReturn(errorStorageDirectory.getRoot()); + when(ErrorLogHelper.getErrorStorageDirectory()).thenReturn(mTemporaryFolder.getRoot()); Crashes.setEnabled(false); assertFalse(Crashes.hasCrashedInLastSession().get()); assertNull(Crashes.getLastSessionCrashReport().get()); - verifyStatic(never()); + verifyStatic(ErrorLogHelper.class, never()); ErrorLogHelper.getLastErrorLogFile(); } @Test public void failToDeserializeLastSessionCrashReport() throws JSONException, IOException { LogSerializer logSerializer = mock(LogSerializer.class); - when(logSerializer.deserializeLog(anyString(), anyString())).thenReturn(mock(ManagedErrorLog.class)); + when(logSerializer.deserializeLog(anyString(), any())).thenReturn(mock(ManagedErrorLog.class)); mockStatic(ErrorLogHelper.class); - File lastErrorLogFile = errorStorageDirectory.newFile("last-error-log.json"); + File lastErrorLogFile = mTemporaryFolder.newFile("last-error-log.json"); new FileWriter(lastErrorLogFile).append("fake_data").close(); when(ErrorLogHelper.getLastErrorLogFile()).thenReturn(lastErrorLogFile); when(ErrorLogHelper.getStoredErrorLogFiles()).thenReturn(new File[]{lastErrorLogFile}); @@ -886,7 +879,7 @@ public void failToDeserializeLastSessionCrashReport() throws JSONException, IOEx assertFalse(Crashes.hasCrashedInLastSession().get()); JSONException jsonException = new JSONException("Fake JSON exception"); - when(logSerializer.deserializeLog(anyString(), anyString())).thenThrow(jsonException); + when(logSerializer.deserializeLog(anyString(), any())).thenThrow(jsonException); /* * Last session error is only fetched upon initialization: enabled and channel ready. @@ -904,16 +897,16 @@ public void failToDeserializeLastSessionCrashReport() throws JSONException, IOEx * De-serializing fails twice: processing the log from last time as part of the bulk processing. * And loading that same file for exposing it in getLastErrorReport. */ - verifyStatic(times(2)); + verifyStatic(AppCenterLog.class, times(2)); AppCenterLog.error(eq(Crashes.LOG_TAG), anyString(), eq(jsonException)); - verifyStatic(); + verifyStatic(ErrorLogHelper.class); ErrorLogHelper.removeLostThrowableFiles(); } @Test public void crashInLastSessionCorrupted() throws IOException { mockStatic(ErrorLogHelper.class); - File file = errorStorageDirectory.newFile("last-error-log.json"); + File file = mTemporaryFolder.newFile("last-error-log.json"); new FileWriter(file).append("fake_data").close(); when(ErrorLogHelper.getStoredErrorLogFiles()).thenReturn(new File[]{file}); when(ErrorLogHelper.getNewMinidumpFiles()).thenReturn(new File[0]); @@ -946,7 +939,7 @@ public void discardHugeErrorAttachments() throws JSONException { /* Prepare a big (too big) attachment and a small one. */ ArrayList errorAttachmentLogs = new ArrayList<>(2); - ErrorAttachmentLog binaryAttachment = attachmentWithBinary(new byte[7 * 1024 * 1024 + 1], "earth.png", "image/png"); + ErrorAttachmentLog binaryAttachment = ErrorAttachmentLog.attachmentWithBinary(new byte[7 * 1024 * 1024 + 1], "earth.png", "image/png"); errorAttachmentLogs.add(binaryAttachment); ErrorAttachmentLog textAttachment = ErrorAttachmentLog.attachmentWithText("hello", "log.txt"); errorAttachmentLogs.add(textAttachment); @@ -964,7 +957,7 @@ public void discardHugeErrorAttachments() throws JSONException { when(log.getId()).thenReturn(UUID.randomUUID()); when(log.getException()).thenReturn(mockException); LogSerializer logSerializer = mock(LogSerializer.class); - when(logSerializer.deserializeLog(anyString(), anyString())).thenReturn(log); + when(logSerializer.deserializeLog(anyString(), any())).thenReturn(log); mockStatic(ErrorLogHelper.class); when(ErrorLogHelper.getStoredErrorLogFiles()).thenReturn(new File[]{mock(File.class)}); when(ErrorLogHelper.getNewMinidumpFiles()).thenReturn(new File[0]); @@ -999,7 +992,7 @@ public void manualProcessing() throws Exception { when(ErrorLogHelper.getErrorReportFromErrorLog(any(ManagedErrorLog.class), anyString())).thenReturn(report1).thenReturn(report2); when(FileManager.read(any(File.class))).thenReturn(""); LogSerializer logSerializer = mock(LogSerializer.class); - when(logSerializer.deserializeLog(anyString(), anyString())).thenAnswer(new Answer() { + when(logSerializer.deserializeLog(anyString(), any())).thenAnswer(new Answer() { @Override public ManagedErrorLog answer(InvocationOnMock invocation) { @@ -1038,20 +1031,20 @@ public ManagedErrorLog answer(InvocationOnMock invocation) { assertEquals(report2, iterator.next()); /* Listener not called yet on anything on manual processing. */ - verifyZeroInteractions(listener); + verifyNoInteractions(listener); /* Send only the first. */ assertFalse(WrapperSdkExceptionManager.sendCrashReportsOrAwaitUserConfirmation(Collections.singletonList(report1.getId())).get()); /* We used manual process function, listener not called. */ - verifyZeroInteractions(listener); + verifyNoInteractions(listener); /* No log sent until manual user confirmation in that mode (we are not in always send). */ verify(mockChannel, never()).enqueue(any(ManagedErrorLog.class), eq(crashes.getGroupName()), anyInt()); /* Confirm with always send. */ Crashes.notifyUserConfirmation(Crashes.ALWAYS_SEND); - verifyStatic(); + verifyStatic(SharedPreferencesManager.class); SharedPreferencesManager.putBoolean(Crashes.PREF_KEY_ALWAYS_SEND, true); when(SharedPreferencesManager.getBoolean(eq(Crashes.PREF_KEY_ALWAYS_SEND), anyBoolean())).thenReturn(true); @@ -1075,7 +1068,7 @@ public ManagedErrorLog answer(InvocationOnMock invocation) { verify(mockChannel, never()).enqueue(eq(mockAttachment), eq(crashes.getGroupName()), anyInt()); /* We used manual process function, listener not called and our mock channel does not send events. */ - verifyZeroInteractions(listener); + verifyNoInteractions(listener); /* Reset instance to test another tine with always send. */ Crashes.unsetInstance(); @@ -1109,7 +1102,7 @@ public void manuallyReportNullList() throws Exception { when(ErrorLogHelper.getErrorReportFromErrorLog(any(ManagedErrorLog.class), anyString())).thenReturn(report1).thenReturn(report2); when(FileManager.read(any(File.class))).thenReturn(""); LogSerializer logSerializer = mock(LogSerializer.class); - when(logSerializer.deserializeLog(anyString(), anyString())).thenAnswer(new Answer() { + when(logSerializer.deserializeLog(anyString(), any())).thenAnswer(new Answer() { @Override public ManagedErrorLog answer(InvocationOnMock invocation) { @@ -1199,7 +1192,7 @@ private ManagedErrorLog testNativeCrashLog(long appStartTime, long crashTime, bo LogSerializer logSerializer = mock(LogSerializer.class); ArgumentCaptor log = ArgumentCaptor.forClass(Log.class); when(logSerializer.serializeLog(log.capture())).thenReturn("{}"); - when(logSerializer.deserializeLog(anyString(), anyString())).thenAnswer(new Answer() { + when(logSerializer.deserializeLog(anyString(), any())).thenAnswer(new Answer() { @Override public ManagedErrorLog answer(InvocationOnMock invocation) { @@ -1295,7 +1288,6 @@ public void minidumpFilePathNull() throws Exception { /* Set up mock for the crash. */ final com.microsoft.appcenter.crashes.ingestion.models.Exception exception = mock(com.microsoft.appcenter.crashes.ingestion.models.Exception.class); DefaultLogSerializer defaultLogSerializer = mock(DefaultLogSerializer.class); - mock(ErrorAttachmentLog.class); mockStatic(ErrorLogHelper.class); mockStatic(ErrorAttachmentLog.class); ErrorReport errorReport = new ErrorReport(); @@ -1313,7 +1305,7 @@ public void minidumpFilePathNull() throws Exception { when(FileManager.read(any(File.class))).thenReturn(""); String jsonCrash = "{}"; LogSerializer logSerializer = mock(LogSerializer.class); - when(logSerializer.deserializeLog(anyString(), anyString())).thenAnswer(new Answer() { + when(logSerializer.deserializeLog(anyString(), any())).thenAnswer(new Answer() { @Override public ManagedErrorLog answer(InvocationOnMock invocation) { @@ -1338,8 +1330,8 @@ public ManagedErrorLog answer(InvocationOnMock invocation) { * Verify that attachmentWithBinary doesn't get called if minidump is missing. * This scenario used to crash before, so if the test succeeds that also tests the crash is fixed. */ - verifyStatic(never()); - attachmentWithBinary(new byte[]{anyByte()}, anyString(), anyString()); + verifyStatic(ErrorAttachmentLog.class, never()); + ErrorAttachmentLog.attachmentWithBinary(new byte[]{anyByte()}, anyString(), anyString()); } @Test @@ -1348,7 +1340,6 @@ public void minidumpStoredWithOldSDK() throws Exception { /* Set up mock for the crash. */ final com.microsoft.appcenter.crashes.ingestion.models.Exception exception = mock(com.microsoft.appcenter.crashes.ingestion.models.Exception.class); DefaultLogSerializer defaultLogSerializer = mock(DefaultLogSerializer.class); - mock(ErrorAttachmentLog.class); mockStatic(ErrorLogHelper.class); mockStatic(ErrorAttachmentLog.class); ErrorReport errorReport = new ErrorReport(); @@ -1368,7 +1359,7 @@ public void minidumpStoredWithOldSDK() throws Exception { when(FileManager.read(any(File.class))).thenReturn(""); String jsonCrash = "{}"; LogSerializer logSerializer = mock(LogSerializer.class); - when(logSerializer.deserializeLog(anyString(), anyString())).thenAnswer(new Answer() { + when(logSerializer.deserializeLog(anyString(), any())).thenAnswer(new Answer() { @Override public ManagedErrorLog answer(InvocationOnMock invocation) { @@ -1390,41 +1381,38 @@ public ManagedErrorLog answer(InvocationOnMock invocation) { crashes.onStarted(mock(Context.class), mock(Channel.class), "secret-app-mock", null, true); /* Verify that attachmentWithBinary does get sent. */ - verifyStatic(); - attachmentWithBinary(new byte[]{anyByte()}, anyString(), anyString()); + verifyStatic(ErrorAttachmentLog.class); + ErrorAttachmentLog.attachmentWithBinary(any(), anyString(), anyString()); /* Verify temporary field erased. */ - verify(exception, times(1)).setStackTrace(null); + verify(exception).setStackTrace(isNull()); } @Test - @PrepareForTest({android.util.Log.class}) public void stackOverflowOnSavingThrowable() throws Exception { /* Mock error log utils. */ mockStatic(ErrorLogHelper.class); + when(ErrorLogHelper.getModelExceptionFromThrowable(any(Throwable.class))).thenCallRealMethod(); when(ErrorLogHelper.getErrorStorageDirectory()).thenReturn(mock(File.class)); - when(ErrorLogHelper.getNewMinidumpFiles()).thenReturn(new File[]{}); - when(ErrorLogHelper.getStoredErrorLogFiles()).thenReturn(new File[]{}); - when(ErrorLogHelper.createErrorLog(any(Context.class), any(Thread.class), any(com.microsoft.appcenter.crashes.ingestion.models.Exception.class), anyMapOf(Thread.class, StackTraceElement[].class), anyLong(), anyBoolean())).thenReturn(mErrorLog); + when(ErrorLogHelper.getNewMinidumpFiles()).thenReturn(new File[0]); + when(ErrorLogHelper.getStoredErrorLogFiles()).thenReturn(new File[0]); + when(ErrorLogHelper.createErrorLog(any(Context.class), any(Thread.class), notNull(), anyMap(), anyLong(), anyBoolean())).thenReturn(mErrorLog); File throwableFile = mock(File.class); - whenNew(File.class).withParameterTypes(File.class, String.class).withArguments(any(File.class), argThat(new ArgumentMatcher() { - - @Override - public boolean matches(Object argument) { - return argument.toString().endsWith(ErrorLogHelper.THROWABLE_FILE_EXTENSION); - } - })).thenReturn(throwableFile); + whenNew(File.class).withParameterTypes(File.class, String.class) + .withArguments(any(File.class), endsWith(ErrorLogHelper.THROWABLE_FILE_EXTENSION)) + .thenReturn(throwableFile); + whenNew(File.class).withParameterTypes(File.class, String.class) + .withArguments(any(File.class), anyString()) + .thenReturn(mock(File.class)); LogSerializer logSerializer = mock(LogSerializer.class); String jsonCrash = "{}"; when(logSerializer.serializeLog(any(Log.class))).thenReturn(jsonCrash); /* Mock storage to fail on stack overflow when saving a Throwable as binary. */ - mockStatic(android.util.Log.class); - when(getStackTraceString(any(Throwable.class))).thenReturn(STACK_TRACE); Throwable throwable = new Throwable(); doThrow(new StackOverflowError()).when(FileManager.class); - FileManager.write(throwableFile, STACK_TRACE); + FileManager.write(eq(throwableFile), eq(STACK_TRACE)); /* Simulate start SDK. */ Crashes crashes = Crashes.getInstance(); @@ -1436,18 +1424,18 @@ public boolean matches(Object argument) { Crashes.getInstance().saveUncaughtException(Thread.currentThread(), throwable); /* Verify it didn't prevent saving the JSON file. */ - verifyStatic(); + verifyStatic(FileManager.class); FileManager.write(any(File.class), eq(jsonCrash)); } @Test - public void handlerMemoryWarning() throws Exception { + public void handlerMemoryWarning() { /* Mock files. */ mockStatic(ErrorLogHelper.class); - when(ErrorLogHelper.getErrorStorageDirectory()).thenReturn(mock(File.class)); - when(ErrorLogHelper.getNewMinidumpFiles()).thenReturn(new File[]{}); - when(ErrorLogHelper.getStoredErrorLogFiles()).thenReturn(new File[]{}); + when(ErrorLogHelper.getErrorStorageDirectory()).thenReturn(mTemporaryFolder.getRoot()); + when(ErrorLogHelper.getNewMinidumpFiles()).thenReturn(new File[0]); + when(ErrorLogHelper.getStoredErrorLogFiles()).thenReturn(new File[0]); /* Mock classes. */ Context mockContext = mock(Context.class); @@ -1464,14 +1452,14 @@ public void handlerMemoryWarning() throws Exception { componentCallbacks2Captor.getValue().onTrimMemory(TRIM_MEMORY_RUNNING_CRITICAL); /* Verify put data to preferences. */ - verifyStatic(); + verifyStatic(SharedPreferencesManager.class); SharedPreferencesManager.putInt(eq(PREF_KEY_MEMORY_RUNNING_LEVEL), eq(TRIM_MEMORY_RUNNING_CRITICAL)); /* Invoke callback onLowMemory. */ componentCallbacks2Captor.getValue().onLowMemory(); /* Verify put data to preferences. */ - verifyStatic(); + verifyStatic(SharedPreferencesManager.class); SharedPreferencesManager.putInt(eq(PREF_KEY_MEMORY_RUNNING_LEVEL), eq(TRIM_MEMORY_COMPLETE)); } @@ -1481,7 +1469,7 @@ public void registerAndUnregisterComponentCallbacks() { /* Mock classes. */ Context mockContext = mock(Context.class); mockStatic(ErrorLogHelper.class); - when(ErrorLogHelper.getErrorStorageDirectory()).thenReturn(errorStorageDirectory.getRoot()); + when(ErrorLogHelper.getErrorStorageDirectory()).thenReturn(mTemporaryFolder.getRoot()); when(ErrorLogHelper.getStoredErrorLogFiles()).thenReturn(new File[0]); when(ErrorLogHelper.getNewMinidumpFiles()).thenReturn(new File[0]); @@ -1491,29 +1479,29 @@ public void registerAndUnregisterComponentCallbacks() { crashes.onStarted(mockContext, mock(Channel.class), "", null, true); /* Verify register callback. */ - verify(mockContext, never()).registerComponentCallbacks(any(ComponentCallbacks2.class)); - verifyStatic(); + verify(mockContext, never()).registerComponentCallbacks(any(ComponentCallbacks.class)); + verifyStatic(SharedPreferencesManager.class); SharedPreferencesManager.remove(eq(PREF_KEY_MEMORY_RUNNING_LEVEL)); /* Enable crashes. */ crashes.setInstanceEnabled(true); - verify(mockContext).registerComponentCallbacks(any(ComponentCallbacks2.class)); + verify(mockContext).registerComponentCallbacks(isA(ComponentCallbacks2.class)); /* Disable crashes. */ crashes.setInstanceEnabled(false); /* Verify unregister callback. */ - verify(mockContext, (times(2))).unregisterComponentCallbacks(any(ComponentCallbacks2.class)); + verify(mockContext).unregisterComponentCallbacks(isA(ComponentCallbacks2.class)); - /* Verify clear preferences. */ - verifyStatic(times(2)); + /* Verify clear preferences. It's called first time during starting disabled service. */ + verifyStatic(SharedPreferencesManager.class, times(2)); SharedPreferencesManager.remove(eq(PREF_KEY_MEMORY_RUNNING_LEVEL)); } @Test public void setReceiveMemoryWarningInLastSession() { mockStatic(ErrorLogHelper.class); - when(ErrorLogHelper.getErrorStorageDirectory()).thenReturn(errorStorageDirectory.getRoot()); + when(ErrorLogHelper.getErrorStorageDirectory()).thenReturn(mTemporaryFolder.getRoot()); when(ErrorLogHelper.getStoredErrorLogFiles()).thenReturn(new File[0]); when(ErrorLogHelper.getNewMinidumpFiles()).thenReturn(new File[0]); when(FileManager.read(any(File.class))).thenReturn(""); @@ -1563,7 +1551,7 @@ public void checkStacktraceWhenThrowableFileIsEmpty() throws Exception { com.microsoft.appcenter.crashes.ingestion.models.Exception mockException = mock(com.microsoft.appcenter.crashes.ingestion.models.Exception.class); when(mockException.getType()).thenReturn("type"); when(mockException.getMessage()).thenReturn("message"); - when(mockException.getFrames()).thenReturn(Arrays.asList(stackFrame1)); + when(mockException.getFrames()).thenReturn(Collections.singletonList(stackFrame1)); /* Mock log. */ ManagedErrorLog mockLog = new ManagedErrorLog(); @@ -1580,7 +1568,7 @@ public void checkStacktraceWhenThrowableFileIsEmpty() throws Exception { /* Verify that stacktrace is null. */ ErrorReport report = crashes.buildErrorReport(mockLog); assertEquals(expectedStacktrace, report.getStackTrace()); - verifyStatic(never()); + verifyStatic(FileManager.class, never()); FileManager.read(any(File.class)); } @@ -1620,7 +1608,7 @@ public void checkStacktraceWithLegacyThrowableFile() throws Exception { /* Verify. */ assertEquals(expectedStacktrace, report.getStackTrace()); verify(crashes, never()).buildStackTrace(any(com.microsoft.appcenter.crashes.ingestion.models.Exception.class)); - verifyStatic(); + verifyStatic(FileManager.class); FileManager.read(any(File.class)); } diff --git a/sdk/appcenter-crashes/src/test/java/com/microsoft/appcenter/crashes/HandledErrorTest.java b/sdk/appcenter-crashes/src/test/java/com/microsoft/appcenter/crashes/HandledErrorTest.java index b8352054d..8277ba3d9 100644 --- a/sdk/appcenter-crashes/src/test/java/com/microsoft/appcenter/crashes/HandledErrorTest.java +++ b/sdk/appcenter-crashes/src/test/java/com/microsoft/appcenter/crashes/HandledErrorTest.java @@ -5,6 +5,26 @@ package com.microsoft.appcenter.crashes; +import static com.microsoft.appcenter.Flags.DEFAULTS; +import static com.microsoft.appcenter.test.TestUtils.generateString; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertSame; +import static org.junit.Assert.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.reset; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoInteractions; +import static org.powermock.api.mockito.PowerMockito.verifyStatic; +import static java.util.Collections.singletonList; + import android.content.Context; import com.microsoft.appcenter.AppCenter; @@ -13,7 +33,6 @@ import com.microsoft.appcenter.crashes.ingestion.models.Exception; import com.microsoft.appcenter.crashes.ingestion.models.HandledErrorLog; import com.microsoft.appcenter.crashes.ingestion.models.StackFrame; -import com.microsoft.appcenter.crashes.model.ErrorReport; import com.microsoft.appcenter.crashes.utils.ErrorLogHelper; import com.microsoft.appcenter.ingestion.models.Log; import com.microsoft.appcenter.utils.AppCenterLog; @@ -21,7 +40,7 @@ import org.junit.Test; import org.mockito.ArgumentCaptor; -import org.mockito.ArgumentMatcher; +import org.mockito.Captor; import org.mockito.Mock; import java.util.Arrays; @@ -29,26 +48,6 @@ import java.util.HashMap; import java.util.Map; -import static com.microsoft.appcenter.Flags.DEFAULTS; -import static com.microsoft.appcenter.test.TestUtils.generateString; -import static java.util.Collections.singletonList; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertSame; -import static org.junit.Assert.assertTrue; -import static org.mockito.Matchers.any; -import static org.mockito.Matchers.anyInt; -import static org.mockito.Matchers.anyString; -import static org.mockito.Matchers.argThat; -import static org.mockito.Matchers.eq; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.never; -import static org.mockito.Mockito.reset; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.verify; -import static org.powermock.api.mockito.PowerMockito.verifyStatic; - public class HandledErrorTest extends AbstractCrashesTest { private Crashes mCrashes; @@ -56,6 +55,9 @@ public class HandledErrorTest extends AbstractCrashesTest { @Mock private Channel mChannel; + @Captor + private ArgumentCaptor mLog; + private void startCrashes() { mCrashes = Crashes.getInstance(); mCrashes.onStarting(mAppCenterHandler); @@ -65,94 +67,96 @@ private void startCrashes() { @Test public void notInit() { Crashes.trackError(EXCEPTION, null, null); - verifyStatic(); + verifyStatic(AppCenterLog.class); AppCenterLog.error(eq(AppCenter.LOG_TAG), anyString()); } @Test - public void trackError() { + public void trackErrorWithoutProperties() { startCrashes(); Crashes.trackError(EXCEPTION); - verify(mChannel).enqueue(argThat(new ArgumentMatcher() { + verify(mChannel).enqueue(mLog.capture(), eq(mCrashes.getGroupName()), eq(DEFAULTS)); + assertTrue(mLog.getValue() instanceof HandledErrorLog); + HandledErrorLog errorLog = (HandledErrorLog) mLog.getValue(); + assertEquals(EXCEPTION.getMessage(), errorLog.getException().getMessage()); + assertNull(errorLog.getProperties()); + } - @Override - public boolean matches(Object item) { - return item instanceof HandledErrorLog && EXCEPTION.getMessage() != null - && EXCEPTION.getMessage().equals(((HandledErrorLog) item).getException().getMessage()); - } - }), eq(mCrashes.getGroupName()), eq(DEFAULTS)); - reset(mChannel); + @Test + public void trackErrorWithInvalidProperties() { + startCrashes(); Crashes.trackError(EXCEPTION, new HashMap() {{ put(null, null); put("", null); put(generateString(ErrorLogHelper.MAX_PROPERTY_ITEM_LENGTH + 1, '*'), null); put("1", null); }}, null); - verify(mChannel).enqueue(argThat(new ArgumentMatcher() { + verify(mChannel).enqueue(mLog.capture(), eq(mCrashes.getGroupName()), eq(DEFAULTS)); + assertTrue(mLog.getValue() instanceof HandledErrorLog); + HandledErrorLog errorLog = (HandledErrorLog) mLog.getValue(); + assertEquals(EXCEPTION.getMessage(), errorLog.getException().getMessage()); + assertNotNull(errorLog.getProperties()); + assertEquals(0, errorLog.getProperties().size()); + } - @Override - public boolean matches(Object item) { - return item instanceof HandledErrorLog && EXCEPTION.getMessage() != null - && EXCEPTION.getMessage().equals(((HandledErrorLog) item).getException().getMessage()) - && ((HandledErrorLog) item).getProperties().size() == 0; - } - }), eq(mCrashes.getGroupName()), eq(DEFAULTS)); - reset(mChannel); + @Test + public void trackErrorWithTooManyProperties() { + startCrashes(); Crashes.trackError(EXCEPTION, new HashMap() {{ for (int i = 0; i < 30; i++) { put("valid" + i, "valid"); } }}, null); - verify(mChannel).enqueue(argThat(new ArgumentMatcher() { + verify(mChannel).enqueue(mLog.capture(), eq(mCrashes.getGroupName()), eq(DEFAULTS)); + assertTrue(mLog.getValue() instanceof HandledErrorLog); + HandledErrorLog errorLog = (HandledErrorLog) mLog.getValue(); + assertEquals(EXCEPTION.getMessage(), errorLog.getException().getMessage()); + assertNotNull(errorLog.getProperties()); + assertEquals(20, errorLog.getProperties().size()); + } - @Override - public boolean matches(Object item) { - return item instanceof HandledErrorLog && EXCEPTION.getMessage() != null - && EXCEPTION.getMessage().equals(((HandledErrorLog) item).getException().getMessage()) - && ((HandledErrorLog) item).getProperties().size() == 20; - } - }), eq(mCrashes.getGroupName()), eq(DEFAULTS)); - reset(mChannel); + @Test + public void trackErrorWithTooLongProperty() { + startCrashes(); final String longerMapItem = generateString(ErrorLogHelper.MAX_PROPERTY_ITEM_LENGTH + 1, '*'); Crashes.trackError(EXCEPTION, new HashMap() {{ put(longerMapItem, longerMapItem); }}, null); - verify(mChannel).enqueue(argThat(new ArgumentMatcher() { - - @Override - public boolean matches(Object item) { - if (item instanceof HandledErrorLog) { - HandledErrorLog errorLog = (HandledErrorLog) item; - if (EXCEPTION.getMessage() != null && EXCEPTION.getMessage().equals((errorLog.getException().getMessage()))) { - if (errorLog.getProperties().size() == 1) { - Map.Entry entry = errorLog.getProperties().entrySet().iterator().next(); - return entry.getKey().length() == ErrorLogHelper.MAX_PROPERTY_ITEM_LENGTH && entry.getValue().length() == ErrorLogHelper.MAX_PROPERTY_ITEM_LENGTH; - } - } - } - return false; - } - }), eq(mCrashes.getGroupName()), eq(DEFAULTS)); + verify(mChannel).enqueue(mLog.capture(), eq(mCrashes.getGroupName()), eq(DEFAULTS)); + assertTrue(mLog.getValue() instanceof HandledErrorLog); + HandledErrorLog errorLog = (HandledErrorLog) mLog.getValue(); + assertEquals(EXCEPTION.getMessage(), errorLog.getException().getMessage()); + assertNotNull(errorLog.getProperties()); + assertEquals(1, errorLog.getProperties().size()); + Map.Entry entry = errorLog.getProperties().entrySet().iterator().next(); + assertEquals(ErrorLogHelper.MAX_PROPERTY_ITEM_LENGTH, entry.getKey().length()); + assertEquals(ErrorLogHelper.MAX_PROPERTY_ITEM_LENGTH, entry.getValue().length()); + } - HandledErrorLog mockLog = mock(HandledErrorLog.class); + @Test + public void noCallbacksOnHandledErrorLog() { + startCrashes(); CrashesListener mockListener = mock(CrashesListener.class); mCrashes.setInstanceListener(mockListener); + Channel.GroupListener channelListener = mCrashes.getChannelListener(); + HandledErrorLog errorLogLog = mock(HandledErrorLog.class); + channelListener.onBeforeSending(errorLogLog); + channelListener.onSuccess(errorLogLog); + channelListener.onFailure(errorLogLog, EXCEPTION); + verifyNoInteractions(mockListener); + } - /* mCrashes callback test for trackError. */ - mCrashes.getChannelListener().onBeforeSending(mockLog); - verify(mockListener, never()).onBeforeSending(any(ErrorReport.class)); - mCrashes.getChannelListener().onSuccess(mockLog); - verify(mockListener, never()).onSendingSucceeded(any(ErrorReport.class)); - mCrashes.getChannelListener().onFailure(mockLog, EXCEPTION); - verify(mockListener, never()).onSendingFailed(any(ErrorReport.class), eq(EXCEPTION)); - + @Test + public void noCallbacksOnErrorAttachmentLog() { + startCrashes(); + CrashesListener mockListener = mock(CrashesListener.class); + mCrashes.setInstanceListener(mockListener); + Channel.GroupListener channelListener = mCrashes.getChannelListener(); ErrorAttachmentLog attachmentLog = mock(ErrorAttachmentLog.class); - mCrashes.getChannelListener().onBeforeSending(attachmentLog); - verify(mockListener, never()).onBeforeSending(any(ErrorReport.class)); - mCrashes.getChannelListener().onSuccess(attachmentLog); - verify(mockListener, never()).onSendingSucceeded(any(ErrorReport.class)); - mCrashes.getChannelListener().onFailure(attachmentLog, EXCEPTION); - verify(mockListener, never()).onSendingFailed(any(ErrorReport.class), eq(EXCEPTION)); + channelListener.onBeforeSending(attachmentLog); + channelListener.onSuccess(attachmentLog); + channelListener.onFailure(attachmentLog, EXCEPTION); + verifyNoInteractions(mockListener); } @Test @@ -175,63 +179,53 @@ public void trackExceptionForWrapperSdk() { mCrashes.onStarting(mAppCenterHandler); mCrashes.onStarted(mock(Context.class), mChannel, "", null, true); WrapperSdkExceptionManager.trackException(exception, null, null); - verify(mChannel).enqueue(argThat(new ArgumentMatcher() { - - @Override - public boolean matches(Object item) { - return item instanceof HandledErrorLog && exception.equals(((HandledErrorLog) item).getException()); - } - }), eq(mCrashes.getGroupName()), eq(DEFAULTS)); + verify(mChannel).enqueue(mLog.capture(), eq(mCrashes.getGroupName()), eq(DEFAULTS)); + assertTrue(mLog.getValue() instanceof HandledErrorLog); + HandledErrorLog errorLog = (HandledErrorLog) mLog.getValue(); + assertEquals(exception, errorLog.getException()); + assertNull(errorLog.getProperties()); reset(mChannel); + WrapperSdkExceptionManager.trackException(exception, new HashMap() {{ put(null, null); put("", null); put(generateString(ErrorLogHelper.MAX_PROPERTY_ITEM_LENGTH + 1, '*'), null); put("1", null); }}, null); - verify(mChannel).enqueue(argThat(new ArgumentMatcher() { - - @Override - public boolean matches(Object item) { - return item instanceof HandledErrorLog && exception.equals(((HandledErrorLog) item).getException()) - && ((HandledErrorLog) item).getProperties().size() == 0; - } - }), eq(mCrashes.getGroupName()), eq(DEFAULTS)); + verify(mChannel).enqueue(mLog.capture(), eq(mCrashes.getGroupName()), eq(DEFAULTS)); + assertTrue(mLog.getValue() instanceof HandledErrorLog); + errorLog = (HandledErrorLog) mLog.getValue(); + assertEquals(exception, errorLog.getException()); + assertNotNull(errorLog.getProperties()); + assertEquals(0, errorLog.getProperties().size()); reset(mChannel); + WrapperSdkExceptionManager.trackException(exception, new HashMap() {{ for (int i = 0; i < 30; i++) { put("valid" + i, "valid"); } }}, null); - verify(mChannel).enqueue(argThat(new ArgumentMatcher() { - - @Override - public boolean matches(Object item) { - return item instanceof HandledErrorLog && exception.equals(((HandledErrorLog) item).getException()) - && ((HandledErrorLog) item).getProperties().size() == 20; - } - }), eq(mCrashes.getGroupName()), eq(DEFAULTS)); + verify(mChannel).enqueue(mLog.capture(), eq(mCrashes.getGroupName()), eq(DEFAULTS)); + assertTrue(mLog.getValue() instanceof HandledErrorLog); + errorLog = (HandledErrorLog) mLog.getValue(); + assertEquals(exception, errorLog.getException()); + assertNotNull(errorLog.getProperties()); + assertEquals(20, errorLog.getProperties().size()); reset(mChannel); + final String longerMapItem = generateString(ErrorLogHelper.MAX_PROPERTY_ITEM_LENGTH + 1, '*'); WrapperSdkExceptionManager.trackException(exception, new HashMap() {{ put(longerMapItem, longerMapItem); }}, null); - verify(mChannel).enqueue(argThat(new ArgumentMatcher() { - - @Override - public boolean matches(Object item) { - if (item instanceof HandledErrorLog) { - HandledErrorLog errorLog = (HandledErrorLog) item; - if (exception.equals((errorLog.getException()))) { - if (errorLog.getProperties().size() == 1) { - Map.Entry entry = errorLog.getProperties().entrySet().iterator().next(); - return entry.getKey().length() == ErrorLogHelper.MAX_PROPERTY_ITEM_LENGTH && entry.getValue().length() == ErrorLogHelper.MAX_PROPERTY_ITEM_LENGTH; - } - } - } - return false; - } - }), eq(mCrashes.getGroupName()), eq(DEFAULTS)); + verify(mChannel).enqueue(mLog.capture(), eq(mCrashes.getGroupName()), eq(DEFAULTS)); + assertTrue(mLog.getValue() instanceof HandledErrorLog); + errorLog = (HandledErrorLog) mLog.getValue(); + assertEquals(exception, errorLog.getException()); + assertNotNull(errorLog.getProperties()); + assertEquals(1, errorLog.getProperties().size()); + Map.Entry entry = errorLog.getProperties().entrySet().iterator().next(); + assertEquals(ErrorLogHelper.MAX_PROPERTY_ITEM_LENGTH, entry.getKey().length()); + assertEquals(ErrorLogHelper.MAX_PROPERTY_ITEM_LENGTH, entry.getValue().length()); } @Test diff --git a/sdk/appcenter-crashes/src/test/java/com/microsoft/appcenter/crashes/UncaughtExceptionHandlerTest.java b/sdk/appcenter-crashes/src/test/java/com/microsoft/appcenter/crashes/UncaughtExceptionHandlerTest.java index 9211079c0..b2e81f6c0 100644 --- a/sdk/appcenter-crashes/src/test/java/com/microsoft/appcenter/crashes/UncaughtExceptionHandlerTest.java +++ b/sdk/appcenter-crashes/src/test/java/com/microsoft/appcenter/crashes/UncaughtExceptionHandlerTest.java @@ -5,11 +5,29 @@ package com.microsoft.appcenter.crashes; +import static com.microsoft.appcenter.utils.PrefStorageConstants.KEY_ENABLED; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyBoolean; +import static org.mockito.ArgumentMatchers.anyLong; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoMoreInteractions; +import static org.powermock.api.mockito.PowerMockito.doAnswer; +import static org.powermock.api.mockito.PowerMockito.doThrow; +import static org.powermock.api.mockito.PowerMockito.mock; +import static org.powermock.api.mockito.PowerMockito.mockStatic; +import static org.powermock.api.mockito.PowerMockito.verifyStatic; +import static org.powermock.api.mockito.PowerMockito.when; + import android.content.Context; import android.os.SystemClock; import com.microsoft.appcenter.AppCenter; import com.microsoft.appcenter.AppCenterHandler; +import com.microsoft.appcenter.channel.Channel; import com.microsoft.appcenter.crashes.ingestion.models.Exception; import com.microsoft.appcenter.crashes.ingestion.models.ManagedErrorLog; import com.microsoft.appcenter.crashes.utils.ErrorLogHelper; @@ -27,38 +45,30 @@ import org.junit.Before; import org.junit.Rule; import org.junit.Test; -import org.mockito.Matchers; -import org.mockito.Mockito; -import org.mockito.internal.util.reflection.Whitebox; +import org.junit.rules.TemporaryFolder; import org.mockito.invocation.InvocationOnMock; import org.mockito.stubbing.Answer; -import org.powermock.api.mockito.PowerMockito; import org.powermock.core.classloader.annotations.PrepareForTest; import org.powermock.modules.junit4.rule.PowerMockRule; +import org.powermock.reflect.Whitebox; import java.io.File; import java.io.IOException; -import java.util.Map; import java.util.UUID; -import static com.microsoft.appcenter.utils.PrefStorageConstants.KEY_ENABLED; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNull; -import static org.mockito.Matchers.any; -import static org.mockito.Matchers.anyBoolean; -import static org.mockito.Matchers.anyLong; -import static org.mockito.Matchers.anyString; -import static org.mockito.Matchers.eq; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.verifyNoMoreInteractions; -import static org.powermock.api.mockito.PowerMockito.doAnswer; -import static org.powermock.api.mockito.PowerMockito.mock; -import static org.powermock.api.mockito.PowerMockito.mockStatic; -import static org.powermock.api.mockito.PowerMockito.verifyStatic; -import static org.powermock.api.mockito.PowerMockito.when; - @SuppressWarnings("unused") -@PrepareForTest({SystemClock.class, SharedPreferencesManager.class, FileManager.class, Crashes.class, ErrorLogHelper.class, DeviceInfoHelper.class, ShutdownHelper.class, AppCenterLog.class, AppCenter.class, HandlerUtils.class}) +@PrepareForTest({ + AppCenter.class, + AppCenterLog.class, + Crashes.class, + DeviceInfoHelper.class, + ErrorLogHelper.class, + FileManager.class, + HandlerUtils.class, + SharedPreferencesManager.class, + ShutdownHelper.class, + SystemClock.class +}) public class UncaughtExceptionHandlerTest { private static final String CRASHES_ENABLED_KEY = KEY_ENABLED + "_" + Crashes.getInstance().getServiceName(); @@ -66,6 +76,9 @@ public class UncaughtExceptionHandlerTest { @Rule public PowerMockRule mPowerMockRule = new PowerMockRule(); + @Rule + public final TemporaryFolder mErrorStorageDirectory = new TemporaryFolder(); + private Thread.UncaughtExceptionHandler mDefaultExceptionHandler; private UncaughtExceptionHandler mExceptionHandler; @@ -89,32 +102,30 @@ public void setUp() { when(SharedPreferencesManager.getBoolean(CRASHES_ENABLED_KEY, true)).thenReturn(true); /* Then simulate further changes to state. */ - PowerMockito.doAnswer(new Answer() { + doAnswer(new Answer() { @Override public Object answer(InvocationOnMock invocation) { /* Whenever the new state is persisted, make further calls return the new state. */ boolean enabled = (Boolean) invocation.getArguments()[1]; - Mockito.when(SharedPreferencesManager.getBoolean(CRASHES_ENABLED_KEY, true)).thenReturn(enabled); + when(SharedPreferencesManager.getBoolean(CRASHES_ENABLED_KEY, true)).thenReturn(enabled); return null; } }).when(SharedPreferencesManager.class); SharedPreferencesManager.putBoolean(eq(CRASHES_ENABLED_KEY), anyBoolean()); ManagedErrorLog errorLogMock = mock(ManagedErrorLog.class); - when(ErrorLogHelper.getErrorStorageDirectory()).thenReturn(new File(".")); + when(ErrorLogHelper.getModelExceptionFromThrowable(any(Throwable.class))).thenCallRealMethod(); + when(ErrorLogHelper.getErrorStorageDirectory()).thenReturn(mErrorStorageDirectory.getRoot()); when(ErrorLogHelper.getStoredErrorLogFiles()).thenReturn(new File[0]); - when(ErrorLogHelper.createErrorLog(any(Context.class), any(Thread.class), any(Exception.class), Matchers.>any(), anyLong(), anyBoolean())) + when(ErrorLogHelper.getNewMinidumpFiles()).thenReturn(new File[0]); + when(ErrorLogHelper.createErrorLog(any(Context.class), any(Thread.class), any(Exception.class), any(), anyLong(), anyBoolean())) .thenReturn(errorLogMock); when(errorLogMock.getId()).thenReturn(UUID.randomUUID()); when(errorLogMock.getException()).thenReturn(new com.microsoft.appcenter.crashes.ingestion.models.Exception()); - mDefaultExceptionHandler = mock(Thread.UncaughtExceptionHandler.class); - Thread.setDefaultUncaughtExceptionHandler(mDefaultExceptionHandler); - mExceptionHandler = new UncaughtExceptionHandler(); - /* Mock handlers. */ mockStatic(HandlerUtils.class); Answer runNow = new Answer() { @@ -128,8 +139,13 @@ public Void answer(InvocationOnMock invocation) { doAnswer(runNow).when(HandlerUtils.class); HandlerUtils.runOnUiThread(any(Runnable.class)); AppCenterHandler handler = mock(AppCenterHandler.class); - Crashes.getInstance().onStarting(handler); doAnswer(runNow).when(handler).post(any(Runnable.class), any(Runnable.class)); + Crashes.getInstance().onStarting(handler); + Crashes.getInstance().onStarted(mock(Context.class), mock(Channel.class), "mock", null, true); + + mDefaultExceptionHandler = mock(Thread.UncaughtExceptionHandler.class); + Thread.setDefaultUncaughtExceptionHandler(mDefaultExceptionHandler); + mExceptionHandler = new UncaughtExceptionHandler(); } @Test @@ -162,12 +178,12 @@ public void handleExceptionAndPassOn() { mExceptionHandler.uncaughtException(thread, exception); verify(mDefaultExceptionHandler).uncaughtException(thread, exception); - verifyStatic(); - ErrorLogHelper.createErrorLog(any(Context.class), any(Thread.class), any(Exception.class), Matchers.>any(), anyLong(), anyBoolean()); + verifyStatic(ErrorLogHelper.class); + ErrorLogHelper.createErrorLog(any(Context.class), any(Thread.class), any(Exception.class), any(), anyLong(), anyBoolean()); } @Test - public void handleExceptionAndPassOnExplicitlySetDontIgnore() { + public void handleExceptionAndPassOnExplicitlySetDonNotIgnore() { mExceptionHandler.setIgnoreDefaultExceptionHandler(false); handleExceptionAndPassOn(); } @@ -185,10 +201,8 @@ public void handleExceptionAndIgnoreDefaultHandler() { mExceptionHandler.uncaughtException(thread, exception); verifyNoMoreInteractions(mDefaultExceptionHandler); - verifyStatic(); - ErrorLogHelper.createErrorLog(any(Context.class), any(Thread.class), any(Exception.class), Matchers.>any(), anyLong(), anyBoolean()); - verifyStatic(); - System.exit(10); + verifyStatic(ErrorLogHelper.class); + ErrorLogHelper.createErrorLog(any(Context.class), any(Thread.class), any(Exception.class), any(), anyLong(), anyBoolean()); } @Test @@ -205,7 +219,7 @@ public void testInvalidJsonException() throws JSONException { final RuntimeException exception = new RuntimeException(); mExceptionHandler.uncaughtException(thread, exception); - verifyStatic(); + verifyStatic(AppCenterLog.class); AppCenterLog.error(eq(Crashes.LOG_TAG), anyString(), eq(jsonException)); verify(mDefaultExceptionHandler).uncaughtException(thread, exception); @@ -216,14 +230,14 @@ public void testIOException() throws java.lang.Exception { mExceptionHandler.register(); IOException ioException = new IOException("Fake IO exception"); - PowerMockito.doThrow(ioException).when(FileManager.class); - FileManager.write(any(File.class), anyString()); + doThrow(ioException).when(FileManager.class); + FileManager.write(any(File.class), any()); final Thread thread = Thread.currentThread(); final RuntimeException exception = new RuntimeException(); mExceptionHandler.uncaughtException(thread, exception); - verifyStatic(); + verifyStatic(AppCenterLog.class); AppCenterLog.error(eq(Crashes.LOG_TAG), anyString(), eq(ioException)); verify(mDefaultExceptionHandler).uncaughtException(thread, exception); diff --git a/sdk/appcenter-crashes/src/test/java/com/microsoft/appcenter/crashes/WrapperSdkExceptionManagerTest.java b/sdk/appcenter-crashes/src/test/java/com/microsoft/appcenter/crashes/WrapperSdkExceptionManagerTest.java index 2add05888..c3b7103f8 100644 --- a/sdk/appcenter-crashes/src/test/java/com/microsoft/appcenter/crashes/WrapperSdkExceptionManagerTest.java +++ b/sdk/appcenter-crashes/src/test/java/com/microsoft/appcenter/crashes/WrapperSdkExceptionManagerTest.java @@ -5,10 +5,35 @@ package com.microsoft.appcenter.crashes; +import static android.util.Log.getStackTraceString; +import static com.microsoft.appcenter.utils.PrefStorageConstants.KEY_ENABLED; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyBoolean; +import static org.mockito.ArgumentMatchers.anyLong; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.ArgumentMatchers.endsWith; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.ArgumentMatchers.isA; +import static org.mockito.ArgumentMatchers.isNull; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; +import static org.powermock.api.mockito.PowerMockito.doAnswer; +import static org.powermock.api.mockito.PowerMockito.doThrow; +import static org.powermock.api.mockito.PowerMockito.mockStatic; +import static org.powermock.api.mockito.PowerMockito.verifyStatic; +import static org.powermock.api.mockito.PowerMockito.whenNew; + import android.content.Context; import com.microsoft.appcenter.AppCenter; import com.microsoft.appcenter.AppCenterHandler; +import com.microsoft.appcenter.channel.Channel; import com.microsoft.appcenter.crashes.ingestion.models.Exception; import com.microsoft.appcenter.crashes.ingestion.models.ManagedErrorLog; import com.microsoft.appcenter.crashes.model.ErrorReport; @@ -27,9 +52,6 @@ import org.junit.Rule; import org.junit.Test; import org.junit.rules.TemporaryFolder; -import org.mockito.ArgumentMatcher; -import org.mockito.Matchers; -import org.mockito.Mockito; import org.mockito.invocation.InvocationOnMock; import org.mockito.stubbing.Answer; import org.powermock.core.classloader.annotations.PrepareForTest; @@ -37,60 +59,48 @@ import java.io.File; import java.io.IOException; -import java.util.Map; import java.util.UUID; -import static android.util.Log.getStackTraceString; -import static com.microsoft.appcenter.utils.PrefStorageConstants.KEY_ENABLED; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertNull; -import static org.mockito.Matchers.any; -import static org.mockito.Matchers.anyBoolean; -import static org.mockito.Matchers.anyLong; -import static org.mockito.Matchers.anyString; -import static org.mockito.Matchers.argThat; -import static org.mockito.Matchers.eq; -import static org.mockito.Matchers.isNull; -import static org.mockito.Mockito.never; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.verify; -import static org.powermock.api.mockito.PowerMockito.doAnswer; -import static org.powermock.api.mockito.PowerMockito.doThrow; -import static org.powermock.api.mockito.PowerMockito.mock; -import static org.powermock.api.mockito.PowerMockito.mockStatic; -import static org.powermock.api.mockito.PowerMockito.verifyNoMoreInteractions; -import static org.powermock.api.mockito.PowerMockito.verifyStatic; -import static org.powermock.api.mockito.PowerMockito.when; -import static org.powermock.api.mockito.PowerMockito.whenNew; - -@PrepareForTest({AppCenter.class, WrapperSdkExceptionManager.class, AppCenterLog.class, SharedPreferencesManager.class, FileManager.class, Crashes.class, ErrorLogHelper.class, HandlerUtils.class}) +@PrepareForTest({ + AppCenter.class, + AppCenterLog.class, + Crashes.class, + ErrorLogHelper.class, + FileManager.class, + HandlerUtils.class, + SharedPreferencesManager.class, + WrapperSdkExceptionManager.class +}) public class WrapperSdkExceptionManagerTest { private static final String CRASHES_ENABLED_KEY = KEY_ENABLED + "_" + Crashes.getInstance().getServiceName(); private static final String STACK_TRACE = "Sample stack trace"; + private Crashes mCrashes; + @Rule - public final PowerMockRule rule = new PowerMockRule(); + public final PowerMockRule mRule = new PowerMockRule(); @Rule - public final TemporaryFolder errorStorageDirectory = new TemporaryFolder(); + public final TemporaryFolder mErrorStorageDirectory = new TemporaryFolder(); @Before public void setUp() { Crashes.unsetInstance(); mockStatic(AppCenter.class); - mockStatic(FileManager.class); - mockStatic(SharedPreferencesManager.class); mockStatic(AppCenterLog.class); mockStatic(ErrorLogHelper.class); - when(ErrorLogHelper.getErrorStorageDirectory()).thenReturn(errorStorageDirectory.getRoot()); + mockStatic(FileManager.class); + mockStatic(SharedPreferencesManager.class); + when(ErrorLogHelper.getErrorStorageDirectory()).thenReturn(mErrorStorageDirectory.getRoot()); ManagedErrorLog errorLogMock = mock(ManagedErrorLog.class); when(errorLogMock.getId()).thenReturn(UUID.randomUUID()); when(errorLogMock.getException()).thenReturn(new com.microsoft.appcenter.crashes.ingestion.models.Exception()); - when(ErrorLogHelper.createErrorLog(any(Context.class), any(Thread.class), any(Exception.class), Matchers.>any(), anyLong(), anyBoolean())) + when(ErrorLogHelper.createErrorLog(any(Context.class), any(Thread.class), any(Exception.class), any(), anyLong(), anyBoolean())) .thenReturn(errorLogMock); + when(ErrorLogHelper.getNewMinidumpFiles()).thenReturn(new File[0]); + when(ErrorLogHelper.getStoredErrorLogFiles()).thenReturn(new File[0]); @SuppressWarnings("unchecked") AppCenterFuture future = (AppCenterFuture) mock(AppCenterFuture.class); @@ -111,10 +121,13 @@ public Void answer(InvocationOnMock invocation) { doAnswer(runNow).when(HandlerUtils.class); HandlerUtils.runOnUiThread(any(Runnable.class)); AppCenterHandler handler = mock(AppCenterHandler.class); - Crashes.getInstance().onStarting(handler); - doAnswer(runNow).when(handler).post(any(Runnable.class), any(Runnable.class)); + doAnswer(runNow).when(handler).post(any(Runnable.class), any()); + mCrashes = Crashes.getInstance(); + mCrashes.onStarting(handler); + mCrashes.onStarted(mock(Context.class), mock(Channel.class), "mock", null, true); } + @SuppressWarnings("InstantiationOfUtilityClass") @Test public void constructWrapperSdkExceptionManager() { new WrapperSdkExceptionManager(); @@ -134,9 +147,9 @@ public void deleteWrapperExceptionDataWithNullId() { /* Delete null does nothing. */ WrapperSdkExceptionManager.deleteWrapperExceptionData(null); - verifyStatic(never()); + verifyStatic(FileManager.class, never()); FileManager.delete(any(File.class)); - verifyStatic(); + verifyStatic(AppCenterLog.class); AppCenterLog.error(eq(Crashes.LOG_TAG), anyString()); } @@ -145,9 +158,9 @@ public void deleteWrapperExceptionDataWithMissingId() { /* Delete with file not found does nothing. */ WrapperSdkExceptionManager.deleteWrapperExceptionData(UUID.randomUUID()); - verifyStatic(never()); + verifyStatic(FileManager.class, never()); FileManager.delete(any(File.class)); - verifyStatic(never()); + verifyStatic(AppCenterLog.class, never()); AppCenterLog.error(eq(Crashes.LOG_TAG), anyString()); } @@ -159,26 +172,26 @@ public void deleteWrapperExceptionDataWithLoadingError() throws java.lang.Except whenNew(File.class).withAnyArguments().thenReturn(file); when(file.exists()).thenReturn(true); WrapperSdkExceptionManager.deleteWrapperExceptionData(UUID.randomUUID()); - verifyStatic(); + verifyStatic(FileManager.class); FileManager.delete(any(File.class)); - verifyStatic(); + verifyStatic(AppCenterLog.class); AppCenterLog.error(eq(Crashes.LOG_TAG), anyString()); } @Test public void saveWrapperSdkCrash() throws JSONException, IOException { - LogSerializer logSerializer = Mockito.mock(LogSerializer.class); + LogSerializer logSerializer = mock(LogSerializer.class); when(logSerializer.serializeLog(any(ManagedErrorLog.class))).thenReturn("mock"); - Crashes.getInstance().setLogSerializer(logSerializer); + mCrashes.setLogSerializer(logSerializer); String data = "d"; WrapperSdkExceptionManager.saveWrapperException(Thread.currentThread(), null, new Exception(), data); - verifyStatic(); + verifyStatic(FileManager.class); FileManager.write(any(File.class), eq(data)); /* We can't do it twice in the same process. */ data = "e"; WrapperSdkExceptionManager.saveWrapperException(Thread.currentThread(), null, new Exception(), data); - verifyStatic(never()); + verifyStatic(FileManager.class, never()); FileManager.write(any(File.class), eq(data)); } @@ -186,25 +199,25 @@ public void saveWrapperSdkCrash() throws JSONException, IOException { @PrepareForTest(android.util.Log.class) public void saveWrapperSdkCrashWithJavaThrowable() throws JSONException, IOException { String mockData = "mock"; - LogSerializer logSerializer = Mockito.mock(LogSerializer.class); + LogSerializer logSerializer = mock(LogSerializer.class); when(logSerializer.serializeLog(any(ManagedErrorLog.class))).thenReturn(mockData); - Crashes.getInstance().setLogSerializer(logSerializer); + mCrashes.setLogSerializer(logSerializer); String data = "d"; Throwable throwable = new Throwable(); mockStatic(android.util.Log.class); - Mockito.when(getStackTraceString(any(Throwable.class))).thenReturn(STACK_TRACE); + when(getStackTraceString(any(Throwable.class))).thenReturn(STACK_TRACE); WrapperSdkExceptionManager.saveWrapperException(Thread.currentThread(), throwable, new Exception(), data); - verifyStatic(); + verifyStatic(FileManager.class); FileManager.write(any(File.class), eq(data)); - verifyStatic(); + verifyStatic(FileManager.class); FileManager.write(any(File.class), eq(mockData)); /* We can't do it twice in the same process. */ data = "e"; WrapperSdkExceptionManager.saveWrapperException(Thread.currentThread(), throwable, new Exception(), data); - verifyStatic(never()); + verifyStatic(FileManager.class, never()); FileManager.write(any(File.class), eq(data)); - verifyStatic(); + verifyStatic(FileManager.class); FileManager.write(any(File.class), eq(mockData)); } @@ -212,41 +225,37 @@ public void saveWrapperSdkCrashWithJavaThrowable() throws JSONException, IOExcep @PrepareForTest({android.util.Log.class}) public void saveWrapperSdkCrashWithOnlyJavaThrowable() throws JSONException, IOException { String mockData = "mock"; - LogSerializer logSerializer = Mockito.mock(LogSerializer.class); + LogSerializer logSerializer = mock(LogSerializer.class); when(logSerializer.serializeLog(any(ManagedErrorLog.class))).thenReturn(mockData); - Crashes.getInstance().setLogSerializer(logSerializer); + mCrashes.setLogSerializer(logSerializer); Throwable throwable = new Throwable(); mockStatic(android.util.Log.class); - Mockito.when(getStackTraceString(any(Throwable.class))).thenReturn(STACK_TRACE); + when(getStackTraceString(any(Throwable.class))).thenReturn(STACK_TRACE); WrapperSdkExceptionManager.saveWrapperException(Thread.currentThread(), throwable, new Exception(), null); - verifyStatic(never()); - FileManager.write(any(File.class), isNull(String.class)); - verifyStatic(); + verifyStatic(FileManager.class, never()); + FileManager.write(any(File.class), isNull()); + verifyStatic(FileManager.class); FileManager.write(any(File.class), eq(mockData)); /* We can't do it twice in the same process. */ WrapperSdkExceptionManager.saveWrapperException(Thread.currentThread(), throwable, new Exception(), null); - verifyStatic(); + verifyStatic(FileManager.class); FileManager.write(any(File.class), eq(mockData)); } @Test public void saveWrapperSdkCrashFailsToCreateThrowablePlaceholder() throws java.lang.Exception { - LogSerializer logSerializer = Mockito.mock(LogSerializer.class); + LogSerializer logSerializer = mock(LogSerializer.class); when(logSerializer.serializeLog(any(ManagedErrorLog.class))).thenReturn("mock"); - Crashes.getInstance().setLogSerializer(logSerializer); + mCrashes.setLogSerializer(logSerializer); File throwableFile = mock(File.class); - whenNew(File.class).withParameterTypes(String.class, String.class).withArguments(anyString(), argThat(new ArgumentMatcher() { - - @Override - public boolean matches(Object argument) { - return String.valueOf(argument).endsWith(ErrorLogHelper.THROWABLE_FILE_EXTENSION); - } - })).thenReturn(throwableFile); + whenNew(File.class).withParameterTypes(String.class, String.class) + .withArguments(anyString(), endsWith(ErrorLogHelper.THROWABLE_FILE_EXTENSION)) + .thenReturn(throwableFile); when(throwableFile.createNewFile()).thenReturn(false); String data = "d"; WrapperSdkExceptionManager.saveWrapperException(Thread.currentThread(), null, new Exception(), data); - verifyStatic(times(3)); + verifyStatic(AppCenterLog.class, times(3)); AppCenterLog.debug(anyString(), anyString()); /* Second call is ignored. */ @@ -254,72 +263,48 @@ public boolean matches(Object argument) { WrapperSdkExceptionManager.saveWrapperException(Thread.currentThread(), null, new Exception(), data); /* No more error. */ - verifyStatic(times(3)); + verifyStatic(AppCenterLog.class, times(3)); AppCenterLog.debug(anyString(), anyString()); } @Test public void saveWrapperSdkCrashFailsWithJSONException() throws JSONException { - LogSerializer logSerializer = Mockito.mock(LogSerializer.class); + LogSerializer logSerializer = mock(LogSerializer.class); when(logSerializer.serializeLog(any(ManagedErrorLog.class))).thenThrow(new JSONException("mock")); - Crashes.getInstance().setLogSerializer(logSerializer); + mCrashes.setLogSerializer(logSerializer); String data = "d"; WrapperSdkExceptionManager.saveWrapperException(Thread.currentThread(), null, new Exception(), data); - verifyStatic(); - AppCenterLog.error(anyString(), anyString(), argThat(new ArgumentMatcher() { - - @Override - public boolean matches(Object argument) { - return argument instanceof JSONException; - } - })); + verifyStatic(AppCenterLog.class); + AppCenterLog.error(anyString(), anyString(), isA(JSONException.class)); /* Second call is ignored. */ data = "e"; WrapperSdkExceptionManager.saveWrapperException(Thread.currentThread(), null, new Exception(), data); /* No more error. */ - verifyStatic(); - AppCenterLog.error(anyString(), anyString(), argThat(new ArgumentMatcher() { - - @Override - public boolean matches(Object argument) { - return argument instanceof JSONException; - } - })); + verifyStatic(AppCenterLog.class); + AppCenterLog.error(anyString(), anyString(), isA(JSONException.class)); } @Test public void saveWrapperSdkCrashFailsWithIOException() throws IOException, JSONException { doThrow(new IOException()).when(FileManager.class); FileManager.write(any(File.class), anyString()); - LogSerializer logSerializer = Mockito.mock(LogSerializer.class); + LogSerializer logSerializer = mock(LogSerializer.class); when(logSerializer.serializeLog(any(ManagedErrorLog.class))).thenReturn("mock"); - Crashes.getInstance().setLogSerializer(logSerializer); + mCrashes.setLogSerializer(logSerializer); String data = "d"; WrapperSdkExceptionManager.saveWrapperException(Thread.currentThread(), null, new Exception(), data); - verifyStatic(); - AppCenterLog.error(anyString(), anyString(), argThat(new ArgumentMatcher() { - - @Override - public boolean matches(Object argument) { - return argument instanceof IOException; - } - })); + verifyStatic(AppCenterLog.class); + AppCenterLog.error(anyString(), anyString(), isA(IOException.class)); /* Second call is ignored. */ data = "e"; WrapperSdkExceptionManager.saveWrapperException(Thread.currentThread(), null, new Exception(), data); /* No more error. */ - verifyStatic(); - AppCenterLog.error(anyString(), anyString(), argThat(new ArgumentMatcher() { - - @Override - public boolean matches(Object argument) { - return argument instanceof IOException; - } - })); + verifyStatic(AppCenterLog.class); + AppCenterLog.error(anyString(), anyString(), isA(IOException.class)); } @Test @@ -327,43 +312,32 @@ public void saveWrapperSdkCrashFailsWithIOExceptionAfterLog() throws IOException String data = "d"; doThrow(new IOException()).when(FileManager.class); FileManager.write(any(File.class), eq(data)); - LogSerializer logSerializer = Mockito.mock(LogSerializer.class); + LogSerializer logSerializer = mock(LogSerializer.class); when(logSerializer.serializeLog(any(ManagedErrorLog.class))).thenReturn("mock"); - Crashes.getInstance().setLogSerializer(logSerializer); + mCrashes.setLogSerializer(logSerializer); WrapperSdkExceptionManager.saveWrapperException(Thread.currentThread(), null, new Exception(), data); - verifyStatic(); - AppCenterLog.error(anyString(), anyString(), argThat(new ArgumentMatcher() { - - @Override - public boolean matches(Object argument) { - return argument instanceof IOException; - } - })); + verifyStatic(AppCenterLog.class); + AppCenterLog.error(anyString(), anyString(), isA(IOException.class)); /* Second call is ignored. */ data = "e"; WrapperSdkExceptionManager.saveWrapperException(Thread.currentThread(), null, new Exception(), data); /* No more error. */ - verifyStatic(); - AppCenterLog.error(anyString(), anyString(), argThat(new ArgumentMatcher() { - - @Override - public boolean matches(Object argument) { - return argument instanceof IOException; - } - })); + verifyStatic(AppCenterLog.class); + AppCenterLog.error(anyString(), anyString(), isA(IOException.class)); } @Test public void saveWrapperExceptionWhenSDKDisabled() throws JSONException { when(SharedPreferencesManager.getBoolean(CRASHES_ENABLED_KEY, true)).thenReturn(false); - LogSerializer logSerializer = Mockito.mock(LogSerializer.class); - Crashes.getInstance().setLogSerializer(logSerializer); + LogSerializer logSerializer = mock(LogSerializer.class); + mCrashes.setLogSerializer(logSerializer); String data = "d"; WrapperSdkExceptionManager.saveWrapperException(Thread.currentThread(), null, new Exception(), data); verify(logSerializer, never()).serializeLog(any(Log.class)); - verifyNoMoreInteractions(ErrorLogHelper.class); + verifyStatic(ErrorLogHelper.class, never()); + ErrorLogHelper.createErrorLog(any(Context.class), any(Thread.class), any(Exception.class), any(), anyLong(), anyBoolean()); } @Test diff --git a/sdk/appcenter-crashes/src/test/java/com/microsoft/appcenter/crashes/ingestion/models/ErrorAttachmentLogTest.java b/sdk/appcenter-crashes/src/test/java/com/microsoft/appcenter/crashes/ingestion/models/ErrorAttachmentLogTest.java index 70bd7640d..41f7adae4 100644 --- a/sdk/appcenter-crashes/src/test/java/com/microsoft/appcenter/crashes/ingestion/models/ErrorAttachmentLogTest.java +++ b/sdk/appcenter-crashes/src/test/java/com/microsoft/appcenter/crashes/ingestion/models/ErrorAttachmentLogTest.java @@ -16,7 +16,6 @@ import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; -@SuppressWarnings("unused") public class ErrorAttachmentLogTest { @Test @@ -32,9 +31,8 @@ public void attachmentWithText() { @Test public void attachmentWithNullText() { - String text = null; String fileName = "1"; - ErrorAttachmentLog attachment = ErrorAttachmentLog.attachmentWithText(text, fileName); + ErrorAttachmentLog attachment = ErrorAttachmentLog.attachmentWithText(null, fileName); assertNotNull(attachment); assertEquals("", new String(attachment.getData(), CHARSET)); assertEquals(fileName, attachment.getFileName()); diff --git a/sdk/appcenter-crashes/src/test/java/com/microsoft/appcenter/crashes/model/TestCrashExceptionTest.java b/sdk/appcenter-crashes/src/test/java/com/microsoft/appcenter/crashes/model/TestCrashExceptionTest.java index 66e3e1675..4c4da6977 100644 --- a/sdk/appcenter-crashes/src/test/java/com/microsoft/appcenter/crashes/model/TestCrashExceptionTest.java +++ b/sdk/appcenter-crashes/src/test/java/com/microsoft/appcenter/crashes/model/TestCrashExceptionTest.java @@ -8,7 +8,6 @@ import org.junit.Assert; import org.junit.Test; -@SuppressWarnings("unused") public class TestCrashExceptionTest { @Test diff --git a/sdk/appcenter-crashes/src/test/java/com/microsoft/appcenter/crashes/utils/ErrorLogHelperTest.java b/sdk/appcenter-crashes/src/test/java/com/microsoft/appcenter/crashes/utils/ErrorLogHelperTest.java index d0d54f67c..0439885d2 100644 --- a/sdk/appcenter-crashes/src/test/java/com/microsoft/appcenter/crashes/utils/ErrorLogHelperTest.java +++ b/sdk/appcenter-crashes/src/test/java/com/microsoft/appcenter/crashes/utils/ErrorLogHelperTest.java @@ -5,6 +5,27 @@ package com.microsoft.appcenter.crashes.utils; +import static com.microsoft.appcenter.test.TestUtils.generateString; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyLong; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.powermock.api.mockito.PowerMockito.doThrow; +import static org.powermock.api.mockito.PowerMockito.mockStatic; +import static org.powermock.api.mockito.PowerMockito.verifyStatic; +import static org.powermock.api.mockito.PowerMockito.when; +import static org.powermock.api.mockito.PowerMockito.whenNew; + import android.app.ActivityManager; import android.app.ActivityManager.RunningAppProcessInfo; import android.content.Context; @@ -48,29 +69,15 @@ import java.util.Map; import java.util.UUID; -import static com.microsoft.appcenter.test.TestUtils.generateString; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNotEquals; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertTrue; -import static org.mockito.Matchers.any; -import static org.mockito.Matchers.anyLong; -import static org.mockito.Matchers.anyString; -import static org.mockito.Matchers.eq; -import static org.mockito.Mockito.doThrow; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.never; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.verify; -import static org.powermock.api.mockito.PowerMockito.mockStatic; -import static org.powermock.api.mockito.PowerMockito.verifyStatic; -import static org.powermock.api.mockito.PowerMockito.when; -import static org.powermock.api.mockito.PowerMockito.whenNew; - -@SuppressWarnings("unused") -@PrepareForTest({DeviceInfoHelper.class, Process.class, Build.class, ErrorLogHelper.class, FileManager.class, TextUtils.class, AppCenterLog.class}) +@PrepareForTest({ + AppCenterLog.class, + Build.class, + DeviceInfoHelper.class, + ErrorLogHelper.class, + FileManager.class, + Process.class, + TextUtils.class +}) public class ErrorLogHelperTest { @Rule @@ -92,6 +99,7 @@ public void tearDown() throws java.lang.Exception { TestUtils.setInternalState(Build.class, "CPU_ABI", null); } + @SuppressWarnings("InstantiationOfUtilityClass") @Test public void createErrorLog() throws java.lang.Exception { @@ -129,7 +137,14 @@ public Date answer(InvocationOnMock invocation) { /* Test. */ long launchTimeStamp = 2000; - ManagedErrorLog errorLog = ErrorLogHelper.createErrorLog(mockContext, java.lang.Thread.currentThread(), new RuntimeException(new IOException(new TestCrashException())), java.lang.Thread.getAllStackTraces(), launchTimeStamp); + mockStatic(java.lang.Thread.class); // Workaround for PowerMock class loading issue. + Map allStackTraces = java.lang.Thread.getAllStackTraces(); + ManagedErrorLog errorLog = ErrorLogHelper.createErrorLog( + mockContext, + java.lang.Thread.currentThread(), + new RuntimeException(new IOException(new TestCrashException())), + allStackTraces, + launchTimeStamp); assertNotNull(errorLog); assertNotNull(errorLog.getId()); assertEquals(logTimestamp, errorLog.getTimestamp()); @@ -168,7 +183,7 @@ public Date answer(InvocationOnMock invocation) { /* Check threads. */ assertNotNull(errorLog.getThreads()); - assertEquals(java.lang.Thread.getAllStackTraces().size(), errorLog.getThreads().size()); + assertEquals(allStackTraces.size(), errorLog.getThreads().size()); for (Thread thread : errorLog.getThreads()) { assertNotNull(thread); assertTrue(thread.getId() > 0); @@ -219,7 +234,7 @@ public Date answer(InvocationOnMock invocation) { /* Test. */ long launchTimeStamp = 2000; - ManagedErrorLog errorLog = ErrorLogHelper.createErrorLog(mockContext, java.lang.Thread.currentThread(), new java.lang.Exception(), java.lang.Thread.getAllStackTraces(), launchTimeStamp); + ManagedErrorLog errorLog = ErrorLogHelper.createErrorLog(mockContext, java.lang.Thread.currentThread(), new java.lang.Exception(), new HashMap<>(), launchTimeStamp); assertNotNull(errorLog); assertNotNull(errorLog.getId()); assertEquals(logTimestamp, errorLog.getTimestamp()); @@ -265,7 +280,7 @@ public Date answer(InvocationOnMock invocation) { /* Test. */ long launchTimeStamp = 2000; - ManagedErrorLog errorLog = ErrorLogHelper.createErrorLog(mockContext, java.lang.Thread.currentThread(), new java.lang.Exception(), java.lang.Thread.getAllStackTraces(), launchTimeStamp); + ManagedErrorLog errorLog = ErrorLogHelper.createErrorLog(mockContext, java.lang.Thread.currentThread(), new java.lang.Exception(), new HashMap<>(), launchTimeStamp); assertNotNull(errorLog); assertNotNull(errorLog.getId()); assertEquals(logTimestamp, errorLog.getTimestamp()); @@ -305,7 +320,7 @@ public void getErrorReportFromErrorLog() throws java.lang.Exception { TestUtils.setInternalState(Build.class, "SUPPORTED_ABIS", new String[]{"armeabi-v7a", "arm"}); /* Create an error log. */ - ManagedErrorLog errorLog = ErrorLogHelper.createErrorLog(mockContext, java.lang.Thread.currentThread(), new RuntimeException(new TestCrashException()), java.lang.Thread.getAllStackTraces(), 900); + ManagedErrorLog errorLog = ErrorLogHelper.createErrorLog(mockContext, java.lang.Thread.currentThread(), new RuntimeException(new TestCrashException()), new HashMap<>(), 900); assertNotNull(errorLog); /* Test. */ @@ -381,7 +396,7 @@ public void cleanDirectory() throws java.lang.Exception { ErrorLogHelper.cleanPendingMinidumps(); /* Verify clean function was called. */ - verifyStatic(); + verifyStatic(FileManager.class); FileManager.cleanDirectory(ErrorLogHelper.getPendingMinidumpDirectory()); /* Clean up. */ @@ -450,20 +465,15 @@ public void getStoredDeviceInfoAndUserInfoCannotRead() throws IOException { public void throwIOExceptionWhenGetMinidumpSubfolderWithDeviceInfo() throws java.lang.Exception { /* Prepare data. */ - BufferedWriter mockBufferedWriter = mock(BufferedWriter.class); - FileWriter mockFileWriter = mock(FileWriter.class); - whenNew(BufferedWriter.class).withAnyArguments().thenReturn(mockBufferedWriter); - whenNew(FileWriter.class).withAnyArguments().thenReturn(mockFileWriter); - doThrow(new IOException()).when(mockBufferedWriter).write(anyString()); - mockStatic(TextUtils.class); - when(TextUtils.isEmpty(anyString())).thenReturn(false); - when(TextUtils.getTrimmedLength(anyString())).thenReturn(1); Device mockDevice = mock(Device.class); mockStatic(DeviceInfoHelper.class); when(DeviceInfoHelper.getDeviceInfo(any(Context.class))).thenReturn(mockDevice); Context mockContext = mock(Context.class); File mockFile = mock(File.class); whenNew(File.class).withAnyArguments().thenReturn(mockFile); + mockStatic(FileManager.class); + doThrow(new IOException()).when(FileManager.class); + FileManager.write(eq(mockFile), any()); /* Verify. */ ErrorLogHelper.getNewMinidumpSubfolderWithContextData(mockContext); @@ -547,12 +557,12 @@ public void removeLostThrowableFiles() { /* Verify removing files when getErrorStorageDirectory return some files. */ ErrorLogHelper.removeLostThrowableFiles(); - verifyStatic(times(2)); + verifyStatic(FileManager.class, times(2)); FileManager.delete(any(File.class)); } @Test - public void removeLostThrowableFilesWhenListOfFilesIsEmpty() throws java.lang.Exception { + public void removeLostThrowableFilesWhenListOfFilesIsEmpty() { /* Mock FileManager class. */ mockStatic(FileManager.class); @@ -566,12 +576,12 @@ public void removeLostThrowableFilesWhenListOfFilesIsEmpty() throws java.lang.Ex /* Verify removing files when getErrorStorageDirectory return null. */ ErrorLogHelper.removeLostThrowableFiles(); - verifyStatic(never()); + verifyStatic(FileManager.class, never()); FileManager.delete(any(File.class)); /* Verify removing files when getErrorStorageDirectory return 0. */ ErrorLogHelper.removeLostThrowableFiles(); - verifyStatic(never()); + verifyStatic(FileManager.class, never()); FileManager.delete(any(File.class)); } From aaf652d79504d9669bdb577c0529e38fda44d507 Mon Sep 17 00:00:00 2001 From: Ivan Matkov Date: Thu, 21 Apr 2022 21:46:30 +0200 Subject: [PATCH 07/72] Fix distribute tests --- .../distribute/AbstractDistributeTest.java | 27 +- ...AppCenterPackageInstallerReceiverTest.java | 26 +- .../distribute/AppStoreDetectionTest.java | 9 +- .../distribute/DeepLinkActivityTest.java | 6 +- .../DistributeBeforeApiSuccessTest.java | 502 +++++++----------- .../DistributeBeforeDownloadTest.java | 194 +++---- .../DistributeCustomizationTest.java | 50 +- ...uteDisableAutomaticCheckForUpdateTest.java | 17 +- .../distribute/DistributeHttpTest.java | 45 +- .../DistributeManualCheckForUpdateTest.java | 29 +- .../DistributePlusDownloadReceiverTest.java | 2 +- .../appcenter/distribute/DistributeTest.java | 177 +++--- .../distribute/DistributeUpdateTrackTest.java | 17 +- .../distribute/DistributeUtilsTest.java | 11 +- .../DistributeWarnAlertSystemWindowsTest.java | 14 +- .../DistributeWarnUnknownSourcesTest.java | 26 +- .../DownloadManagerReceiverTest.java | 14 +- .../distribute/InstallerUtilsTest.java | 18 +- .../distribute/PermissionUtilsTest.java | 2 +- .../ReleaseDownloadListenerTest.java | 10 +- .../ReleaseInstallerListenerTest.java | 106 ++-- .../ResumeFromBackgroundTaskTest.java | 4 +- .../UnknownSourcesDetectionTest.java | 14 +- ...DownloadManagerDistributeDeadlockTest.java | 10 +- .../DownloadManagerReleaseDownloaderTest.java | 68 +-- .../DownloadManagerRequestTaskTest.java | 34 +- .../DownloadManagerUpdateTaskTest.java | 39 +- 27 files changed, 689 insertions(+), 782 deletions(-) diff --git a/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/AbstractDistributeTest.java b/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/AbstractDistributeTest.java index fdbdc8327..cec1bb214 100644 --- a/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/AbstractDistributeTest.java +++ b/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/AbstractDistributeTest.java @@ -53,15 +53,15 @@ import static com.microsoft.appcenter.distribute.DistributeConstants.PREFERENCE_KEY_DOWNLOAD_ID; import static com.microsoft.appcenter.utils.PrefStorageConstants.ALLOWED_NETWORK_REQUEST; import static com.microsoft.appcenter.utils.PrefStorageConstants.KEY_ENABLED; -import static org.mockito.Matchers.any; -import static org.mockito.Matchers.anyBoolean; -import static org.mockito.Matchers.anyInt; -import static org.mockito.Matchers.anyString; -import static org.mockito.Matchers.eq; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyBoolean; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; import static org.powermock.api.mockito.PowerMockito.doAnswer; -import static org.powermock.api.mockito.PowerMockito.doReturn; +import static org.powermock.api.mockito.PowerMockito.doCallRealMethod; import static org.powermock.api.mockito.PowerMockito.mockStatic; import static org.powermock.api.mockito.PowerMockito.spy; import static org.powermock.api.mockito.PowerMockito.whenNew; @@ -74,7 +74,6 @@ BrowserUtils.class, CryptoUtils.class, Distribute.class, - DistributeUtils.class, HandlerUtils.class, HttpUtils.class, IdHelper.class, @@ -178,7 +177,7 @@ public Void answer(InvocationOnMock invocation) { ((Runnable) invocation.getArguments()[0]).run(); return null; } - }).when(mAppCenterHandler).post(any(Runnable.class), any(Runnable.class)); + }).when(mAppCenterHandler).post(any(Runnable.class), any()); whenNew(DistributeInfoTracker.class).withAnyArguments().thenReturn(mDistributeInfoTracker); mockStatic(IdHelper.class); when(IdHelper.getInstallId()).thenReturn(mInstallId); @@ -232,14 +231,18 @@ public Void answer(InvocationOnMock invocation) { mockStatic(NetworkStateHelper.class); mNetworkStateHelper = mock(NetworkStateHelper.class, new Returns(true)); when(NetworkStateHelper.getSharedInstance(any(Context.class))).thenReturn(mNetworkStateHelper); - spy(HttpUtils.class); - doReturn(mHttpClient).when(HttpUtils.class, "createHttpClient", any(Context.class)); + mockStatic(HttpUtils.class); + when(HttpUtils.createHttpClient(any(Context.class))).thenReturn(mHttpClient); + doCallRealMethod().when(HttpUtils.class); + HttpUtils.hideSecret(anyString()); + doCallRealMethod().when(HttpUtils.class); + HttpUtils.isRecoverableError(any(Throwable.class)); /* Mock some statics. */ mockStatic(BrowserUtils.class); mockStatic(TextUtils.class); mockStatic(InstallerUtils.class); - when(TextUtils.isEmpty(any(CharSequence.class))).thenAnswer(new Answer() { + when(TextUtils.isEmpty(any())).thenAnswer(new Answer() { @Override public Boolean answer(InvocationOnMock invocation) { @@ -309,7 +312,7 @@ public Void answer(InvocationOnMock invocation) { /* Mock Release Downloader. */ mockStatic(ReleaseDownloaderFactory.class); - when(ReleaseDownloaderFactory.create(any(Context.class), any(ReleaseDetails.class), any(ReleaseDownloadListener.class))).thenReturn(mReleaseDownloader); + when(ReleaseDownloaderFactory.create(any(Context.class), any(), any())).thenReturn(mReleaseDownloader); when(mReleaseDownloader.getReleaseDetails()).thenReturn(mReleaseDetails); /* Mock Release Downloader Listener. */ diff --git a/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/AppCenterPackageInstallerReceiverTest.java b/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/AppCenterPackageInstallerReceiverTest.java index fc5f46417..a48b4ec26 100644 --- a/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/AppCenterPackageInstallerReceiverTest.java +++ b/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/AppCenterPackageInstallerReceiverTest.java @@ -9,10 +9,10 @@ import static com.microsoft.appcenter.distribute.AppCenterPackageInstallerReceiver.START_ACTION; import static com.microsoft.appcenter.distribute.DistributeConstants.LOG_TAG; -import static org.mockito.Matchers.any; -import static org.mockito.Matchers.anyInt; -import static org.mockito.Matchers.anyString; -import static org.mockito.Matchers.eq; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.spy; @@ -37,15 +37,14 @@ import org.junit.Before; import org.junit.Rule; import org.junit.Test; -import org.mockito.Matchers; import org.mockito.Mock; import org.powermock.core.classloader.annotations.PrepareForTest; import org.powermock.modules.junit4.rule.PowerMockRule; @PrepareForTest({ AppCenterLog.class, - Toast.class, - AppCenterPackageInstallerReceiver.class + AppCenterPackageInstallerReceiver.class, + Toast.class }) public class AppCenterPackageInstallerReceiverTest { @@ -87,7 +86,8 @@ public void setUp() { /* Mock methods. */ when(mContext.getPackageManager()).thenReturn(mPackageManager); - when(mContext.getPackageName()).thenReturn("com.mock.package"); + when(mContext.getPackageName()).thenReturn("com.contoso"); + when(mContext.getString(anyInt())).thenReturn("localized_message"); when(mActivity.getApplicationContext()).thenReturn(mContext); /* Mock toast. */ @@ -111,7 +111,7 @@ public void onReceiveWithActionMyPackageReplace() { mAppCenterPackageInstallerReceiver.onReceive(mContext, mIntent); /* Verify that activity was started. */ - verify(mContext).startActivity(Matchers.any()); + verify(mContext).startActivity(any()); } @Test @@ -139,7 +139,7 @@ public void onReceiveWithStartIntentWithStatusSuccess() { mAppCenterPackageInstallerReceiver.onReceive(mContext, mIntent); /* Verify. */ - verifyStatic(); + verifyStatic(AppCenterLog.class); AppCenterLog.debug(eq(LOG_TAG), eq("Application was successfully updated.")); } @@ -188,7 +188,7 @@ private void onReceiveWithStartFailureAction(int status) { mAppCenterPackageInstallerReceiver.onReceive(mContext, mIntent); /* Verify that log was called. */ - verifyStatic(); + verifyStatic(AppCenterLog.class); AppCenterLog.debug(eq(LOG_TAG), eq("Failed to install a new release with status: " + status + ". Error message: null.")); } @@ -207,7 +207,7 @@ public void onReceiveWithStartIntentWithUnrecognizedStatus() { mAppCenterPackageInstallerReceiver.onReceive(mContext, mIntent); /* Verify that log was called. */ - verifyStatic(); + verifyStatic(AppCenterLog.class); AppCenterLog.debug(eq(LOG_TAG), eq("Unrecognized status received from installer: " + status)); } @@ -219,7 +219,7 @@ public void onReceiverWithUnknownAction() { mAppCenterPackageInstallerReceiver.onReceive(mContext, mIntent); /* Verify that log was called. */ - verifyStatic(); + verifyStatic(AppCenterLog.class); AppCenterLog.debug(eq(LOG_TAG), eq("Unrecognized action UnknownAction - do nothing.")); } diff --git a/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/AppStoreDetectionTest.java b/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/AppStoreDetectionTest.java index 578ac3b0a..e80223ed0 100644 --- a/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/AppStoreDetectionTest.java +++ b/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/AppStoreDetectionTest.java @@ -19,7 +19,7 @@ import static com.microsoft.appcenter.distribute.DistributeConstants.LOG_TAG; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; -import static org.mockito.Matchers.anyString; +import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @@ -39,6 +39,7 @@ public class AppStoreDetectionTest { @Before public void setUp() { + when(mContext.getPackageName()).thenReturn("com.test.app"); when(mContext.getPackageManager()).thenReturn(mPackageManager); } @@ -49,6 +50,7 @@ public void tearDown() { Whitebox.setInternalState(InstallerUtils.class, "sInstalledFromAppStore", (Boolean) null); } + @SuppressWarnings("InstantiationOfUtilityClass") @Test public void init() { new InstallerUtils(); @@ -91,10 +93,7 @@ public void applicationInstallerIsNotStore() { String mockPackage = "mock.package.name"; /* Mock context. */ - Context mockContext = mock(Context.class); - when(mockContext.getPackageName()).thenReturn(mockPackage); - when(mockContext.getPackageManager()).thenReturn(mPackageManager); - mContext = mockContext; + when(mContext.getPackageName()).thenReturn(mockPackage); /* Mock installer package name. */ setInstallerPackageName(mockPackage); diff --git a/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/DeepLinkActivityTest.java b/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/DeepLinkActivityTest.java index d6f1f8dd8..986737dbe 100644 --- a/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/DeepLinkActivityTest.java +++ b/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/DeepLinkActivityTest.java @@ -15,8 +15,8 @@ import org.powermock.core.classloader.annotations.PrepareForTest; import org.powermock.modules.junit4.PowerMockRunner; -import static org.mockito.Matchers.any; -import static org.mockito.Matchers.anyString; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.spy; @@ -54,7 +54,7 @@ private void invalidIntent(Intent intent) { /* Check interactions. */ verify(activity).startActivity(intent); verify(activity).finish(); - verifyStatic(never()); + verifyStatic(Distribute.class, never()); Distribute.getInstance(); } diff --git a/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/DistributeBeforeApiSuccessTest.java b/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/DistributeBeforeApiSuccessTest.java index 07c43177a..5ae675168 100644 --- a/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/DistributeBeforeApiSuccessTest.java +++ b/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/DistributeBeforeApiSuccessTest.java @@ -5,6 +5,49 @@ package com.microsoft.appcenter.distribute; +import static com.microsoft.appcenter.Flags.DEFAULTS; +import static com.microsoft.appcenter.distribute.DistributeConstants.PARAMETER_ENABLE_UPDATE_SETUP_FAILURE_REDIRECT_KEY; +import static com.microsoft.appcenter.distribute.DistributeConstants.PARAMETER_INSTALL_ID; +import static com.microsoft.appcenter.distribute.DistributeConstants.PARAMETER_PLATFORM; +import static com.microsoft.appcenter.distribute.DistributeConstants.PARAMETER_PLATFORM_VALUE; +import static com.microsoft.appcenter.distribute.DistributeConstants.PARAMETER_REDIRECT_ID; +import static com.microsoft.appcenter.distribute.DistributeConstants.PARAMETER_REDIRECT_SCHEME; +import static com.microsoft.appcenter.distribute.DistributeConstants.PARAMETER_RELEASE_HASH; +import static com.microsoft.appcenter.distribute.DistributeConstants.PARAMETER_REQUEST_ID; +import static com.microsoft.appcenter.distribute.DistributeConstants.PREFERENCE_KEY_DISTRIBUTION_GROUP_ID; +import static com.microsoft.appcenter.distribute.DistributeConstants.PREFERENCE_KEY_DOWNLOADED_DISTRIBUTION_GROUP_ID; +import static com.microsoft.appcenter.distribute.DistributeConstants.PREFERENCE_KEY_DOWNLOADED_RELEASE_HASH; +import static com.microsoft.appcenter.distribute.DistributeConstants.PREFERENCE_KEY_DOWNLOADED_RELEASE_ID; +import static com.microsoft.appcenter.distribute.DistributeConstants.PREFERENCE_KEY_DOWNLOAD_STATE; +import static com.microsoft.appcenter.distribute.DistributeConstants.PREFERENCE_KEY_POSTPONE_TIME; +import static com.microsoft.appcenter.distribute.DistributeConstants.PREFERENCE_KEY_REQUEST_ID; +import static com.microsoft.appcenter.distribute.DistributeConstants.PREFERENCE_KEY_TESTER_APP_UPDATE_SETUP_FAILED_MESSAGE_KEY; +import static com.microsoft.appcenter.distribute.DistributeConstants.PREFERENCE_KEY_UPDATE_SETUP_FAILED_MESSAGE_KEY; +import static com.microsoft.appcenter.distribute.DistributeConstants.PREFERENCE_KEY_UPDATE_SETUP_FAILED_PACKAGE_HASH_KEY; +import static com.microsoft.appcenter.distribute.DistributeConstants.PREFERENCE_KEY_UPDATE_TOKEN; +import static com.microsoft.appcenter.distribute.DistributeConstants.PRIVATE_UPDATE_SETUP_PATH_FORMAT; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.anyLong; +import static org.mockito.ArgumentMatchers.anyMap; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.ArgumentMatchers.matches; +import static org.mockito.ArgumentMatchers.same; +import static org.mockito.ArgumentMatchers.startsWith; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; +import static org.powermock.api.mockito.PowerMockito.doAnswer; +import static org.powermock.api.mockito.PowerMockito.doNothing; +import static org.powermock.api.mockito.PowerMockito.mockStatic; +import static org.powermock.api.mockito.PowerMockito.verifyStatic; +import static org.powermock.api.mockito.PowerMockito.whenNew; + import android.app.Activity; import android.app.Dialog; import android.content.Context; @@ -31,13 +74,12 @@ import org.json.JSONException; import org.junit.Test; import org.mockito.ArgumentCaptor; -import org.mockito.ArgumentMatcher; -import org.mockito.internal.util.reflection.Whitebox; import org.mockito.invocation.InvocationOnMock; import org.mockito.stubbing.Answer; import org.mockito.verification.VerificationMode; import org.powermock.api.mockito.PowerMockito; import org.powermock.core.classloader.annotations.PrepareForTest; +import org.powermock.reflect.Whitebox; import java.net.URISyntaxException; import java.util.Collections; @@ -50,52 +92,15 @@ import javax.net.ssl.SSLPeerUnverifiedException; -import static com.microsoft.appcenter.Flags.DEFAULTS; -import static com.microsoft.appcenter.distribute.DistributeConstants.PARAMETER_ENABLE_UPDATE_SETUP_FAILURE_REDIRECT_KEY; -import static com.microsoft.appcenter.distribute.DistributeConstants.PARAMETER_INSTALL_ID; -import static com.microsoft.appcenter.distribute.DistributeConstants.PARAMETER_PLATFORM; -import static com.microsoft.appcenter.distribute.DistributeConstants.PARAMETER_PLATFORM_VALUE; -import static com.microsoft.appcenter.distribute.DistributeConstants.PARAMETER_REDIRECT_ID; -import static com.microsoft.appcenter.distribute.DistributeConstants.PARAMETER_REDIRECT_SCHEME; -import static com.microsoft.appcenter.distribute.DistributeConstants.PARAMETER_RELEASE_HASH; -import static com.microsoft.appcenter.distribute.DistributeConstants.PARAMETER_REQUEST_ID; -import static com.microsoft.appcenter.distribute.DistributeConstants.PREFERENCE_KEY_DISTRIBUTION_GROUP_ID; -import static com.microsoft.appcenter.distribute.DistributeConstants.PREFERENCE_KEY_DOWNLOADED_DISTRIBUTION_GROUP_ID; -import static com.microsoft.appcenter.distribute.DistributeConstants.PREFERENCE_KEY_DOWNLOADED_RELEASE_HASH; -import static com.microsoft.appcenter.distribute.DistributeConstants.PREFERENCE_KEY_DOWNLOADED_RELEASE_ID; -import static com.microsoft.appcenter.distribute.DistributeConstants.PREFERENCE_KEY_DOWNLOAD_STATE; -import static com.microsoft.appcenter.distribute.DistributeConstants.PREFERENCE_KEY_POSTPONE_TIME; -import static com.microsoft.appcenter.distribute.DistributeConstants.PREFERENCE_KEY_REQUEST_ID; -import static com.microsoft.appcenter.distribute.DistributeConstants.PREFERENCE_KEY_TESTER_APP_UPDATE_SETUP_FAILED_MESSAGE_KEY; -import static com.microsoft.appcenter.distribute.DistributeConstants.PREFERENCE_KEY_UPDATE_SETUP_FAILED_MESSAGE_KEY; -import static com.microsoft.appcenter.distribute.DistributeConstants.PREFERENCE_KEY_UPDATE_SETUP_FAILED_PACKAGE_HASH_KEY; -import static com.microsoft.appcenter.distribute.DistributeConstants.PREFERENCE_KEY_UPDATE_TOKEN; -import static com.microsoft.appcenter.distribute.DistributeConstants.PRIVATE_UPDATE_SETUP_PATH_FORMAT; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; -import static org.mockito.Matchers.any; -import static org.mockito.Matchers.anyInt; -import static org.mockito.Matchers.anyLong; -import static org.mockito.Matchers.anyMapOf; -import static org.mockito.Matchers.anyString; -import static org.mockito.Matchers.argThat; -import static org.mockito.Matchers.eq; -import static org.mockito.Matchers.same; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.never; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; -import static org.powermock.api.mockito.PowerMockito.doAnswer; -import static org.powermock.api.mockito.PowerMockito.doNothing; -import static org.powermock.api.mockito.PowerMockito.mockStatic; -import static org.powermock.api.mockito.PowerMockito.verifyStatic; -import static org.powermock.api.mockito.PowerMockito.whenNew; - /** * Cover scenarios that are happening before we see an API call success for latest release. */ -@PrepareForTest({ErrorDetails.class, DistributeUtils.class, SessionContext.class, UUID.class}) +@PrepareForTest({ + DistributeUtils.class, + ErrorDetails.class, + SessionContext.class, + UUID.class +}) public class DistributeBeforeApiSuccessTest extends AbstractDistributeTest { private void testDistributeInactiveOnPrivateTrack() throws PackageManager.NameNotFoundException { @@ -105,7 +110,7 @@ private void testDistributeInactiveOnPrivateTrack() throws PackageManager.NameNo Distribute.setUpdateTrack(UpdateTrack.PRIVATE); start(); Distribute.getInstance().onActivityResumed(mActivity); - verifyStatic(never()); + verifyStatic(BrowserUtils.class, never()); BrowserUtils.openBrowser(anyString(), any(Activity.class)); /* @@ -119,7 +124,7 @@ private void testDistributeInactiveOnPrivateTrack() throws PackageManager.NameNo Distribute.setUpdateTrack(UpdateTrack.PRIVATE); start(); Distribute.getInstance().onActivityResumed(mActivity); - verify(mHttpClient, never()).callAsync(anyString(), anyString(), anyMapOf(String.class, String.class), any(HttpClient.CallTemplate.class), any(ServiceCallback.class)); + verify(mHttpClient, never()).callAsync(anyString(), anyString(), anyMap(), any(HttpClient.CallTemplate.class), any(ServiceCallback.class)); } private void showUpdateSetupFailedDialog() { @@ -136,7 +141,7 @@ private void showUpdateSetupFailedDialog() { verify(mDialogBuilder).setTitle(R.string.appcenter_distribute_update_failed_dialog_title); verify(mDialogBuilder).setMessage(R.string.appcenter_distribute_update_failed_dialog_message); verify(mDialog).show(); - verifyStatic(); + verifyStatic(SharedPreferencesManager.class); SharedPreferencesManager.remove(PREFERENCE_KEY_UPDATE_SETUP_FAILED_MESSAGE_KEY); } @@ -171,7 +176,6 @@ public void continueWhenEnabledForDebuggableBuildSetToTrue() throws Exception { Distribute.setUpdateTrack(UpdateTrack.PRIVATE); start(); Distribute.getInstance().onActivityResumed(mActivity); - verifyStatic(); String url = "http://mock"; url += String.format(PRIVATE_UPDATE_SETUP_PATH_FORMAT, "a"); url += "?" + PARAMETER_RELEASE_HASH + "=" + TEST_HASH; @@ -181,21 +185,16 @@ public void continueWhenEnabledForDebuggableBuildSetToTrue() throws Exception { url += "&" + PARAMETER_PLATFORM + "=" + PARAMETER_PLATFORM_VALUE; url += "&" + PARAMETER_ENABLE_UPDATE_SETUP_FAILURE_REDIRECT_KEY + "=" + "true"; url += "&" + PARAMETER_INSTALL_ID + "=" + mInstallId.toString(); + verifyStatic(BrowserUtils.class); BrowserUtils.openBrowser(url, mActivity); - verifyStatic(); + verifyStatic(SharedPreferencesManager.class); SharedPreferencesManager.putString(PREFERENCE_KEY_REQUEST_ID, requestId.toString()); /* Store token. */ Distribute.getInstance().storeRedirectionParameters(requestId.toString(), "g", "some token"); HashMap headers = new HashMap<>(); headers.put(DistributeConstants.HEADER_API_TOKEN, "some token"); - verify(mHttpClient).callAsync(argThat(new ArgumentMatcher() { - - @Override - public boolean matches(Object argument) { - return argument.toString().startsWith("https://mock2"); - } - }), anyString(), eq(headers), any(HttpClient.CallTemplate.class), any(ServiceCallback.class)); + verify(mHttpClient).callAsync(startsWith("https://mock2"), anyString(), eq(headers), any(HttpClient.CallTemplate.class), any(ServiceCallback.class)); } @Test @@ -235,14 +234,14 @@ public void checkForUpdateIfIgnoredSideLoadingButSwitchedToPublicTrack() throws Distribute.getInstance().onActivityResumed(activity); /* Then we check for update directly via API call. */ - verify(mHttpClient).callAsync(anyString(), anyString(), anyMapOf(String.class, String.class), any(HttpClient.CallTemplate.class), any(ServiceCallback.class)); - verifyStatic(never()); + verify(mHttpClient).callAsync(anyString(), anyString(), anyMap(), any(HttpClient.CallTemplate.class), any(ServiceCallback.class)); + verifyStatic(DistributeUtils.class, never()); DistributeUtils.updateSetupUsingBrowser(same(activity), anyString(), anyString(), any(PackageInfo.class)); /* Check we don't reset private track ignore state. */ - verifyStatic(never()); + verifyStatic(SharedPreferencesManager.class, never()); SharedPreferencesManager.remove(PREFERENCE_KEY_UPDATE_SETUP_FAILED_PACKAGE_HASH_KEY); - verifyStatic(never()); + verifyStatic(SharedPreferencesManager.class, never()); SharedPreferencesManager.remove(PREFERENCE_KEY_UPDATE_SETUP_FAILED_MESSAGE_KEY); } @@ -260,11 +259,11 @@ public void continueIfReleaseHashNotEqualsToFailedPackageHash() throws PackageMa start(); Activity activity = mock(Activity.class); Distribute.getInstance().onActivityResumed(activity); - verifyStatic(); + verifyStatic(DistributeUtils.class); DistributeUtils.updateSetupUsingBrowser(same(activity), anyString(), anyString(), any(PackageInfo.class)); - verifyStatic(); + verifyStatic(SharedPreferencesManager.class); SharedPreferencesManager.remove(PREFERENCE_KEY_UPDATE_SETUP_FAILED_PACKAGE_HASH_KEY); - verifyStatic(); + verifyStatic(SharedPreferencesManager.class); SharedPreferencesManager.remove(PREFERENCE_KEY_UPDATE_SETUP_FAILED_MESSAGE_KEY); } @@ -274,13 +273,13 @@ public void storeUpdateSetupFailedParameterBeforeStart() { /* Setup mock. */ when(SharedPreferencesManager.getString(PREFERENCE_KEY_REQUEST_ID)).thenReturn("r"); Distribute.getInstance().storeUpdateSetupFailedParameter("r", "error_message"); - verifyStatic(never()); + verifyStatic(SharedPreferencesManager.class, never()); SharedPreferencesManager.getString(PREFERENCE_KEY_REQUEST_ID); start(); - verifyStatic(never()); + verifyStatic(SharedPreferencesManager.class, never()); SharedPreferencesManager.putString(PREFERENCE_KEY_UPDATE_SETUP_FAILED_MESSAGE_KEY, "error_message"); Distribute.getInstance().onActivityResumed(mock(Activity.class)); - verifyStatic(); + verifyStatic(SharedPreferencesManager.class); SharedPreferencesManager.putString(PREFERENCE_KEY_UPDATE_SETUP_FAILED_MESSAGE_KEY, "error_message"); } @@ -290,13 +289,13 @@ public void storeTesterAppUpdateSetupFailedParameterBeforeStart() { /* Setup mock. */ when(SharedPreferencesManager.getString(PREFERENCE_KEY_REQUEST_ID)).thenReturn("r"); Distribute.getInstance().storeTesterAppUpdateSetupFailedParameter("r", "error_message"); - verifyStatic(never()); + verifyStatic(SharedPreferencesManager.class, never()); SharedPreferencesManager.getString(PREFERENCE_KEY_REQUEST_ID); start(); - verifyStatic(never()); + verifyStatic(SharedPreferencesManager.class, never()); SharedPreferencesManager.putString(PREFERENCE_KEY_TESTER_APP_UPDATE_SETUP_FAILED_MESSAGE_KEY, "error_message"); Distribute.getInstance().onActivityResumed(mock(Activity.class)); - verifyStatic(); + verifyStatic(SharedPreferencesManager.class); SharedPreferencesManager.putString(PREFERENCE_KEY_TESTER_APP_UPDATE_SETUP_FAILED_MESSAGE_KEY, "error_message"); } @@ -308,7 +307,7 @@ public void storeUpdateSetupFailedParameterWithIncorrectRequestIdBeforeStart() { Distribute.getInstance().storeUpdateSetupFailedParameter("r2", "error_message"); start(); Distribute.getInstance().onActivityResumed(mock(Activity.class)); - verifyStatic(never()); + verifyStatic(SharedPreferencesManager.class, never()); SharedPreferencesManager.putString(PREFERENCE_KEY_UPDATE_SETUP_FAILED_MESSAGE_KEY, "error_message"); } @@ -320,7 +319,7 @@ public void storeTesterAppUpdateSetupFailedParameterWithIncorrectRequestIdBefore Distribute.getInstance().storeTesterAppUpdateSetupFailedParameter("r2", "error_message"); start(); Distribute.getInstance().onActivityResumed(mock(Activity.class)); - verifyStatic(never()); + verifyStatic(SharedPreferencesManager.class, never()); SharedPreferencesManager.putString(PREFERENCE_KEY_TESTER_APP_UPDATE_SETUP_FAILED_MESSAGE_KEY, "error_message"); } @@ -333,20 +332,20 @@ public void storePrivateRedirectionBeforeStart() { /* Store token before start, start in background, no storage access. */ Distribute.getInstance().storeRedirectionParameters("r", "g", "some token"); start(); - verifyStatic(never()); + verifyStatic(SharedPreferencesManager.class, never()); SharedPreferencesManager.putString(anyString(), anyString()); - verifyStatic(never()); + verifyStatic(SharedPreferencesManager.class, never()); SharedPreferencesManager.remove(anyString()); /* Unlock the processing by going into foreground. */ Distribute.getInstance().onActivityResumed(mock(Activity.class)); - verifyStatic(); + verifyStatic(SharedPreferencesManager.class); SharedPreferencesManager.putString(PREFERENCE_KEY_UPDATE_TOKEN, "some token"); - verifyStatic(); + verifyStatic(SharedPreferencesManager.class); SharedPreferencesManager.putString(PREFERENCE_KEY_DISTRIBUTION_GROUP_ID, "g"); - verifyStatic(); + verifyStatic(SharedPreferencesManager.class); SharedPreferencesManager.remove(PREFERENCE_KEY_REQUEST_ID); - verifyStatic(); + verifyStatic(SharedPreferencesManager.class); SharedPreferencesManager.remove(PREFERENCE_KEY_DOWNLOAD_STATE); verify(mDistributeInfoTracker).updateDistributionGroupId("g"); HashMap headers = new HashMap<>(); @@ -363,20 +362,20 @@ public void storePublicRedirectionBeforeStart() { /* Store token before start, start in background, no storage access. */ Distribute.getInstance().storeRedirectionParameters("r", "g", null); start(); - verifyStatic(never()); + verifyStatic(SharedPreferencesManager.class, never()); SharedPreferencesManager.putString(anyString(), anyString()); - verifyStatic(never()); + verifyStatic(SharedPreferencesManager.class, never()); SharedPreferencesManager.remove(anyString()); /* Unlock the processing by going into foreground. */ Distribute.getInstance().onActivityResumed(mock(Activity.class)); - verifyStatic(); + verifyStatic(SharedPreferencesManager.class); SharedPreferencesManager.remove(PREFERENCE_KEY_UPDATE_TOKEN); - verifyStatic(); + verifyStatic(SharedPreferencesManager.class); SharedPreferencesManager.putString(PREFERENCE_KEY_DISTRIBUTION_GROUP_ID, "g"); - verifyStatic(); + verifyStatic(SharedPreferencesManager.class); SharedPreferencesManager.remove(PREFERENCE_KEY_REQUEST_ID); - verifyStatic(); + verifyStatic(SharedPreferencesManager.class); SharedPreferencesManager.remove(PREFERENCE_KEY_DOWNLOAD_STATE); verify(mDistributeInfoTracker).updateDistributionGroupId("g"); HashMap headers = new HashMap<>(); @@ -392,19 +391,19 @@ public void postponeBrowserIfNoNetwork() throws Exception { Distribute.setUpdateTrack(UpdateTrack.PRIVATE); start(); Distribute.getInstance().onActivityResumed(mActivity); - verifyStatic(never()); + verifyStatic(BrowserUtils.class, never()); BrowserUtils.openBrowser(anyString(), any(Activity.class)); /* If network comes back, we don't open network unless we restart app. */ when(mNetworkStateHelper.isNetworkConnected()).thenReturn(true); Distribute.getInstance().onActivityPaused(mActivity); Distribute.getInstance().onActivityResumed(mActivity); - verifyStatic(never()); + verifyStatic(BrowserUtils.class, never()); BrowserUtils.openBrowser(anyString(), any(Activity.class)); /* Restart should open browser if still have network. */ restartResumeLauncher(mActivity); - verifyStatic(); + verifyStatic(BrowserUtils.class); BrowserUtils.openBrowser(anyString(), any(Activity.class)); } @@ -435,7 +434,7 @@ public void accept(Void aVoid) { }); runnable.get().run(); assertTrue(latch.await(0, TimeUnit.MILLISECONDS)); - verifyStatic(never()); + verifyStatic(BrowserUtils.class, never()); BrowserUtils.openBrowser(anyString(), any(Activity.class)); } @@ -475,9 +474,9 @@ public void handleFailedUpdateSetupDialogReinstallAction() throws URISyntaxExcep /* Click. */ clickListener.getValue().onClick(mDialog, DialogInterface.BUTTON_NEGATIVE); - verifyStatic(); + verifyStatic(BrowserUtils.class); BrowserUtils.appendUri(anyString(), anyString()); - verifyStatic(); + verifyStatic(SharedPreferencesManager.class); SharedPreferencesManager.remove(PREFERENCE_KEY_UPDATE_SETUP_FAILED_PACKAGE_HASH_KEY); } @@ -490,9 +489,9 @@ public void handleFailedUpdateSetupDialogReinstallActionWithException() throws U /* Click. */ clickListener.getValue().onClick(mDialog, DialogInterface.BUTTON_NEGATIVE); - verifyStatic(); + verifyStatic(BrowserUtils.class); BrowserUtils.appendUri(anyString(), anyString()); - verifyStatic(); + verifyStatic(SharedPreferencesManager.class); SharedPreferencesManager.remove(PREFERENCE_KEY_UPDATE_SETUP_FAILED_PACKAGE_HASH_KEY); } @@ -518,7 +517,7 @@ public void handleFailedUpdateSetupDialogIgnoreAction() { /* Click. */ clickListener.getValue().onClick(mDialog, DialogInterface.BUTTON_POSITIVE); - verifyStatic(); + verifyStatic(SharedPreferencesManager.class); SharedPreferencesManager.putString(PREFERENCE_KEY_UPDATE_SETUP_FAILED_PACKAGE_HASH_KEY, "some_hash"); } @@ -554,7 +553,6 @@ public void testerAppNotInstalled() throws Exception { Distribute.setUpdateTrack(UpdateTrack.PRIVATE); start(); Distribute.getInstance().onActivityResumed(mActivity); - verifyStatic(); String url = DistributeConstants.DEFAULT_INSTALL_URL; url += String.format(PRIVATE_UPDATE_SETUP_PATH_FORMAT, "a"); url += "?" + PARAMETER_RELEASE_HASH + "=" + TEST_HASH; @@ -564,8 +562,9 @@ public void testerAppNotInstalled() throws Exception { url += "&" + PARAMETER_PLATFORM + "=" + PARAMETER_PLATFORM_VALUE; url += "&" + PARAMETER_ENABLE_UPDATE_SETUP_FAILURE_REDIRECT_KEY + "=" + "true"; url += "&" + PARAMETER_INSTALL_ID + "=" + mInstallId.toString(); + verifyStatic(BrowserUtils.class); BrowserUtils.openBrowser(url, mActivity); - verifyStatic(); + verifyStatic(SharedPreferencesManager.class); SharedPreferencesManager.putString(PREFERENCE_KEY_REQUEST_ID, requestId.toString()); } @@ -584,7 +583,7 @@ public void useBrowserUpdateSetupIfAppIsTesterApp() throws Exception { Distribute.setUpdateTrack(UpdateTrack.PRIVATE); start(); Distribute.getInstance().onActivityResumed(mActivity); - verifyStatic(); + verifyStatic(BrowserUtils.class); BrowserUtils.openBrowser(anyString(), any(Activity.class)); } @@ -625,14 +624,14 @@ public void testerAppUpdateSetupFailed() throws Exception { url += "&" + PARAMETER_PLATFORM + "=" + PARAMETER_PLATFORM_VALUE; url += "&" + PARAMETER_ENABLE_UPDATE_SETUP_FAILURE_REDIRECT_KEY + "=" + "true"; url += "&" + PARAMETER_INSTALL_ID + "=" + mInstallId.toString(); - verifyStatic(); + verifyStatic(BrowserUtils.class); BrowserUtils.openBrowser(url, mActivity); /* Start and resume: open browser. */ when(SharedPreferencesManager.getString(PREFERENCE_KEY_TESTER_APP_UPDATE_SETUP_FAILED_MESSAGE_KEY)).thenReturn(null); Distribute.getInstance().onActivityPaused(mActivity); Distribute.getInstance().onActivityResumed(mActivity); - verifyStatic(); + verifyStatic(BrowserUtils.class); BrowserUtils.openBrowser(url, mActivity); } @@ -671,76 +670,58 @@ public void happyPathUsingTesterAppUpdateSetup() throws Exception { } Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(url)); verify(mActivity).startActivity(intent); - verifyStatic(); + verifyStatic(SharedPreferencesManager.class); SharedPreferencesManager.putString(PREFERENCE_KEY_REQUEST_ID, requestId.toString()); /* Verify that it was only one call. */ - verifyStatic(); + verifyStatic(SharedPreferencesManager.class); SharedPreferencesManager.putString(eq(PREFERENCE_KEY_REQUEST_ID), anyString()); /* Store token. */ Distribute.getInstance().storeRedirectionParameters(requestId.toString(), "g", "some token"); /* Verify behavior. */ - verifyStatic(); + verifyStatic(SharedPreferencesManager.class); SharedPreferencesManager.putString(PREFERENCE_KEY_UPDATE_TOKEN, "some token"); - verifyStatic(); + verifyStatic(SharedPreferencesManager.class); SharedPreferencesManager.putString(PREFERENCE_KEY_DISTRIBUTION_GROUP_ID, "g"); - verifyStatic(); + verifyStatic(SharedPreferencesManager.class); SharedPreferencesManager.remove(PREFERENCE_KEY_REQUEST_ID); - verifyStatic(); + verifyStatic(SharedPreferencesManager.class); SharedPreferencesManager.remove(PREFERENCE_KEY_DOWNLOAD_STATE); HashMap headers = new HashMap<>(); headers.put(DistributeConstants.HEADER_API_TOKEN, "some token"); - verify(mHttpClient).callAsync(argThat(new ArgumentMatcher() { - - @Override - public boolean matches(Object argument) { - return argument.toString().startsWith(DistributeConstants.DEFAULT_API_URL); - } - }), anyString(), eq(headers), any(HttpClient.CallTemplate.class), any(ServiceCallback.class)); + verify(mHttpClient).callAsync(startsWith(DistributeConstants.DEFAULT_API_URL), anyString(), eq(headers), any(HttpClient.CallTemplate.class), any(ServiceCallback.class)); /* If call already made, activity changed must not recall it. */ Distribute.getInstance().onActivityPaused(mActivity); Distribute.getInstance().onActivityResumed(mActivity); /* Verify behavior. */ - verifyStatic(); + verifyStatic(SharedPreferencesManager.class); SharedPreferencesManager.putString(PREFERENCE_KEY_UPDATE_TOKEN, "some token"); - verifyStatic(); + verifyStatic(SharedPreferencesManager.class); SharedPreferencesManager.putString(PREFERENCE_KEY_DISTRIBUTION_GROUP_ID, "g"); - verifyStatic(); + verifyStatic(SharedPreferencesManager.class); SharedPreferencesManager.remove(PREFERENCE_KEY_REQUEST_ID); - verifyStatic(); + verifyStatic(SharedPreferencesManager.class); SharedPreferencesManager.remove(PREFERENCE_KEY_DOWNLOAD_STATE); - verify(mHttpClient).callAsync(argThat(new ArgumentMatcher() { - - @Override - public boolean matches(Object argument) { - return argument.toString().startsWith(DistributeConstants.DEFAULT_API_URL); - } - }), anyString(), eq(headers), any(HttpClient.CallTemplate.class), any(ServiceCallback.class)); + verify(mHttpClient).callAsync(startsWith(DistributeConstants.DEFAULT_API_URL), anyString(), eq(headers), any(HttpClient.CallTemplate.class), any(ServiceCallback.class)); /* Call is still in progress. If we restart app, nothing happens we still wait. */ restartResumeLauncher(mActivity); /* Verify behavior not changed. */ verify(mActivity).startActivity(intent); - verifyStatic(); + verifyStatic(SharedPreferencesManager.class); SharedPreferencesManager.putString(PREFERENCE_KEY_UPDATE_TOKEN, "some token"); - verifyStatic(); + verifyStatic(SharedPreferencesManager.class); SharedPreferencesManager.putString(PREFERENCE_KEY_DISTRIBUTION_GROUP_ID, "g"); - verifyStatic(); + verifyStatic(SharedPreferencesManager.class); SharedPreferencesManager.remove(PREFERENCE_KEY_REQUEST_ID); - verifyStatic(); + verifyStatic(SharedPreferencesManager.class); SharedPreferencesManager.remove(PREFERENCE_KEY_DOWNLOAD_STATE); - verify(mHttpClient).callAsync(argThat(new ArgumentMatcher() { - - @Override - public boolean matches(Object argument) { - return argument.toString().startsWith(DistributeConstants.DEFAULT_API_URL); - } - }), anyString(), eq(headers), any(HttpClient.CallTemplate.class), any(ServiceCallback.class)); + verify(mHttpClient).callAsync(startsWith(DistributeConstants.DEFAULT_API_URL), anyString(), eq(headers), any(HttpClient.CallTemplate.class), any(ServiceCallback.class)); /* If process is restarted, a new call will be made. Need to mock storage for that. */ when(SharedPreferencesManager.getString(PREFERENCE_KEY_DISTRIBUTION_GROUP_ID)).thenReturn("g"); @@ -749,13 +730,8 @@ public boolean matches(Object argument) { Distribute.setUpdateTrack(UpdateTrack.PRIVATE); start(); Distribute.getInstance().onActivityResumed(mActivity); - verify(mHttpClient, times(2)).callAsync(argThat(new ArgumentMatcher() { - - @Override - public boolean matches(Object argument) { - return argument.toString().startsWith(DistributeConstants.DEFAULT_API_URL); - } - }), anyString(), eq(headers), any(HttpClient.CallTemplate.class), any(ServiceCallback.class)); + verify(mHttpClient, times(2)) + .callAsync(startsWith(DistributeConstants.DEFAULT_API_URL), anyString(), eq(headers), any(HttpClient.CallTemplate.class), any(ServiceCallback.class)); } @Test @@ -772,7 +748,6 @@ public void happyPathUntilHangingCallWithToken() throws Exception { Distribute.setUpdateTrack(UpdateTrack.PRIVATE); start(); Distribute.getInstance().onActivityResumed(mActivity); - verifyStatic(); String url = DistributeConstants.DEFAULT_INSTALL_URL; url += String.format(PRIVATE_UPDATE_SETUP_PATH_FORMAT, "a"); url += "?" + PARAMETER_RELEASE_HASH + "=" + TEST_HASH; @@ -782,85 +757,68 @@ public void happyPathUntilHangingCallWithToken() throws Exception { url += "&" + PARAMETER_PLATFORM + "=" + PARAMETER_PLATFORM_VALUE; url += "&" + PARAMETER_ENABLE_UPDATE_SETUP_FAILURE_REDIRECT_KEY + "=" + "true"; url += "&" + PARAMETER_INSTALL_ID + "=" + mInstallId.toString(); + verifyStatic(BrowserUtils.class); BrowserUtils.openBrowser(url, mActivity); - verifyStatic(); + verifyStatic(SharedPreferencesManager.class); SharedPreferencesManager.putString(PREFERENCE_KEY_REQUEST_ID, requestId.toString()); /* If browser already opened, activity changed must not recall it. */ Distribute.getInstance().onActivityPaused(mActivity); Distribute.getInstance().onActivityResumed(mActivity); - verifyStatic(); + verifyStatic(BrowserUtils.class); BrowserUtils.openBrowser(url, mActivity); - verifyStatic(); + verifyStatic(SharedPreferencesManager.class); SharedPreferencesManager.putString(PREFERENCE_KEY_REQUEST_ID, requestId.toString()); /* Store token. */ Distribute.getInstance().storeRedirectionParameters(requestId.toString(), "g", "some token"); /* Verify behavior. */ - verifyStatic(); + verifyStatic(SharedPreferencesManager.class); SharedPreferencesManager.putString(PREFERENCE_KEY_UPDATE_TOKEN, "some token"); - verifyStatic(); + verifyStatic(SharedPreferencesManager.class); SharedPreferencesManager.putString(PREFERENCE_KEY_DISTRIBUTION_GROUP_ID, "g"); - verifyStatic(); + verifyStatic(SharedPreferencesManager.class); SharedPreferencesManager.remove(PREFERENCE_KEY_REQUEST_ID); - verifyStatic(); + verifyStatic(SharedPreferencesManager.class); SharedPreferencesManager.remove(PREFERENCE_KEY_DOWNLOAD_STATE); verify(mDistributeInfoTracker).updateDistributionGroupId("g"); HashMap headers = new HashMap<>(); headers.put(DistributeConstants.HEADER_API_TOKEN, "some token"); - verify(mHttpClient).callAsync(argThat(new ArgumentMatcher() { - - @Override - public boolean matches(Object argument) { - return argument.toString().startsWith(DistributeConstants.DEFAULT_API_URL); - } - }), anyString(), eq(headers), any(HttpClient.CallTemplate.class), any(ServiceCallback.class)); + verify(mHttpClient).callAsync(startsWith(DistributeConstants.DEFAULT_API_URL), anyString(), eq(headers), any(HttpClient.CallTemplate.class), any(ServiceCallback.class)); /* If call already made, activity changed must not recall it. */ Distribute.getInstance().onActivityPaused(mActivity); Distribute.getInstance().onActivityResumed(mActivity); /* Verify behavior. */ - verifyStatic(); + verifyStatic(SharedPreferencesManager.class); SharedPreferencesManager.putString(PREFERENCE_KEY_UPDATE_TOKEN, "some token"); - verifyStatic(); + verifyStatic(SharedPreferencesManager.class); SharedPreferencesManager.putString(PREFERENCE_KEY_DISTRIBUTION_GROUP_ID, "g"); - verifyStatic(); + verifyStatic(SharedPreferencesManager.class); SharedPreferencesManager.remove(PREFERENCE_KEY_REQUEST_ID); - verifyStatic(); + verifyStatic(SharedPreferencesManager.class); SharedPreferencesManager.remove(PREFERENCE_KEY_DOWNLOAD_STATE); verify(mDistributeInfoTracker).updateDistributionGroupId("g"); - verify(mHttpClient).callAsync(argThat(new ArgumentMatcher() { - - @Override - public boolean matches(Object argument) { - return argument.toString().startsWith(DistributeConstants.DEFAULT_API_URL); - } - }), anyString(), eq(headers), any(HttpClient.CallTemplate.class), any(ServiceCallback.class)); + verify(mHttpClient).callAsync(startsWith(DistributeConstants.DEFAULT_API_URL), anyString(), eq(headers), any(HttpClient.CallTemplate.class), any(ServiceCallback.class)); /* Call is still in progress. If we restart app, nothing happens we still wait. */ restartResumeLauncher(mActivity); /* Verify behavior not changed. */ - verifyStatic(); + verifyStatic(BrowserUtils.class); BrowserUtils.openBrowser(url, mActivity); - verifyStatic(); + verifyStatic(SharedPreferencesManager.class); SharedPreferencesManager.putString(PREFERENCE_KEY_UPDATE_TOKEN, "some token"); - verifyStatic(); + verifyStatic(SharedPreferencesManager.class); SharedPreferencesManager.putString(PREFERENCE_KEY_DISTRIBUTION_GROUP_ID, "g"); - verifyStatic(); + verifyStatic(SharedPreferencesManager.class); SharedPreferencesManager.remove(PREFERENCE_KEY_REQUEST_ID); - verifyStatic(); + verifyStatic(SharedPreferencesManager.class); SharedPreferencesManager.remove(PREFERENCE_KEY_DOWNLOAD_STATE); verify(mDistributeInfoTracker).updateDistributionGroupId("g"); - verify(mHttpClient).callAsync(argThat(new ArgumentMatcher() { - - @Override - public boolean matches(Object argument) { - return argument.toString().startsWith(DistributeConstants.DEFAULT_API_URL); - } - }), anyString(), eq(headers), any(HttpClient.CallTemplate.class), any(ServiceCallback.class)); + verify(mHttpClient).callAsync(startsWith(DistributeConstants.DEFAULT_API_URL), anyString(), eq(headers), any(HttpClient.CallTemplate.class), any(ServiceCallback.class)); /* If process is restarted, a new call will be made. Need to mock storage for that. */ when(SharedPreferencesManager.getString(PREFERENCE_KEY_DISTRIBUTION_GROUP_ID)).thenReturn("g"); @@ -869,13 +827,7 @@ public boolean matches(Object argument) { Distribute.setUpdateTrack(UpdateTrack.PRIVATE); start(); Distribute.getInstance().onActivityResumed(mActivity); - verify(mHttpClient, times(2)).callAsync(argThat(new ArgumentMatcher() { - - @Override - public boolean matches(Object argument) { - return argument.toString().startsWith(DistributeConstants.DEFAULT_API_URL); - } - }), anyString(), eq(headers), any(HttpClient.CallTemplate.class), any(ServiceCallback.class)); + verify(mHttpClient, times(2)).callAsync(startsWith(DistributeConstants.DEFAULT_API_URL), anyString(), eq(headers), any(HttpClient.CallTemplate.class), any(ServiceCallback.class)); } @Test @@ -887,49 +839,25 @@ public void happyPathUntilHangingCallWithoutToken() { /* Verify behavior. */ HashMap headers = new HashMap<>(); - verify(mHttpClient).callAsync(argThat(new ArgumentMatcher() { - - @Override - public boolean matches(Object argument) { - return argument.toString().startsWith(DistributeConstants.DEFAULT_API_URL); - } - }), anyString(), eq(headers), any(HttpClient.CallTemplate.class), any(ServiceCallback.class)); + verify(mHttpClient).callAsync(startsWith(DistributeConstants.DEFAULT_API_URL), anyString(), eq(headers), any(HttpClient.CallTemplate.class), any(ServiceCallback.class)); /* If call already made, activity changed must not recall it. */ Distribute.getInstance().onActivityPaused(mActivity); Distribute.getInstance().onActivityResumed(mActivity); /* Verify behavior. */ - verify(mHttpClient).callAsync(argThat(new ArgumentMatcher() { - - @Override - public boolean matches(Object argument) { - return argument.toString().startsWith(DistributeConstants.DEFAULT_API_URL); - } - }), anyString(), eq(headers), any(HttpClient.CallTemplate.class), any(ServiceCallback.class)); + verify(mHttpClient).callAsync(startsWith(DistributeConstants.DEFAULT_API_URL), anyString(), eq(headers), any(HttpClient.CallTemplate.class), any(ServiceCallback.class)); /* Call is still in progress. If we restart app, nothing happens we still wait. */ restartResumeLauncher(mActivity); /* Verify behavior not changed. */ - verify(mHttpClient).callAsync(argThat(new ArgumentMatcher() { - - @Override - public boolean matches(Object argument) { - return argument.toString().startsWith(DistributeConstants.DEFAULT_API_URL); - } - }), anyString(), eq(headers), any(HttpClient.CallTemplate.class), any(ServiceCallback.class)); + verify(mHttpClient).callAsync(startsWith(DistributeConstants.DEFAULT_API_URL), anyString(), eq(headers), any(HttpClient.CallTemplate.class), any(ServiceCallback.class)); /* If process is restarted, a new call will be made. Need to mock storage for that. */ restartProcessAndSdk(); Distribute.getInstance().onActivityResumed(mActivity); - verify(mHttpClient, times(2)).callAsync(argThat(new ArgumentMatcher() { - - @Override - public boolean matches(Object argument) { - return argument.toString().startsWith(DistributeConstants.DEFAULT_API_URL); - } - }), anyString(), eq(headers), any(HttpClient.CallTemplate.class), any(ServiceCallback.class)); + verify(mHttpClient, times(2)).callAsync(startsWith(DistributeConstants.DEFAULT_API_URL), anyString(), eq(headers), any(HttpClient.CallTemplate.class), any(ServiceCallback.class)); } @Test @@ -948,7 +876,6 @@ public void setUrls() throws Exception { Distribute.setUpdateTrack(UpdateTrack.PRIVATE); start(); Distribute.getInstance().onActivityResumed(mActivity); - verifyStatic(); String url = "http://mock"; url += String.format(PRIVATE_UPDATE_SETUP_PATH_FORMAT, "a"); url += "?" + PARAMETER_RELEASE_HASH + "=" + TEST_HASH; @@ -958,21 +885,16 @@ public void setUrls() throws Exception { url += "&" + PARAMETER_PLATFORM + "=" + PARAMETER_PLATFORM_VALUE; url += "&" + PARAMETER_ENABLE_UPDATE_SETUP_FAILURE_REDIRECT_KEY + "=" + "true"; url += "&" + PARAMETER_INSTALL_ID + "=" + mInstallId.toString(); + verifyStatic(BrowserUtils.class); BrowserUtils.openBrowser(url, mActivity); - verifyStatic(); + verifyStatic(SharedPreferencesManager.class); SharedPreferencesManager.putString(PREFERENCE_KEY_REQUEST_ID, requestId.toString()); /* Store token. */ Distribute.getInstance().storeRedirectionParameters(requestId.toString(), "g", "some token"); HashMap headers = new HashMap<>(); headers.put(DistributeConstants.HEADER_API_TOKEN, "some token"); - verify(mHttpClient).callAsync(argThat(new ArgumentMatcher() { - - @Override - public boolean matches(Object argument) { - return argument.toString().startsWith("https://mock2"); - } - }), anyString(), eq(headers), any(HttpClient.CallTemplate.class), any(ServiceCallback.class)); + verify(mHttpClient).callAsync(startsWith("https://mock2"), anyString(), eq(headers), any(HttpClient.CallTemplate.class), any(ServiceCallback.class)); } @Test @@ -989,9 +911,9 @@ public void computeHashFailsWhenOpeningBrowser() throws Exception { verify(mPackageManager).getPackageInfo("com.contoso", 0); /* And verify we didn't open browser. */ - verifyStatic(never()); + verifyStatic(BrowserUtils.class, never()); BrowserUtils.openBrowser(anyString(), any(Activity.class)); - verifyStatic(never()); + verifyStatic(SharedPreferencesManager.class, never()); SharedPreferencesManager.putString(anyString(), anyString()); } @@ -1008,7 +930,6 @@ public void disableBeforeStoreToken() throws Exception { Distribute.setUpdateTrack(UpdateTrack.PRIVATE); start(); Distribute.getInstance().onActivityResumed(mActivity); - verifyStatic(); String url = DistributeConstants.DEFAULT_INSTALL_URL; url += String.format(PRIVATE_UPDATE_SETUP_PATH_FORMAT, "a"); url += "?" + PARAMETER_RELEASE_HASH + "=" + TEST_HASH; @@ -1018,8 +939,9 @@ public void disableBeforeStoreToken() throws Exception { url += "&" + PARAMETER_PLATFORM + "=" + PARAMETER_PLATFORM_VALUE; url += "&" + PARAMETER_ENABLE_UPDATE_SETUP_FAILURE_REDIRECT_KEY + "=" + "true"; url += "&" + PARAMETER_INSTALL_ID + "=" + mInstallId.toString(); + verifyStatic(BrowserUtils.class); BrowserUtils.openBrowser(url, mActivity); - verifyStatic(); + verifyStatic(SharedPreferencesManager.class); SharedPreferencesManager.putString(PREFERENCE_KEY_REQUEST_ID, requestId.toString()); /* Disable. */ @@ -1030,11 +952,11 @@ public void disableBeforeStoreToken() throws Exception { Distribute.getInstance().storeRedirectionParameters(requestId.toString(), "g", "some token"); /* Verify behavior. */ - verifyStatic(never()); + verifyStatic(SharedPreferencesManager.class, never()); SharedPreferencesManager.putString(PREFERENCE_KEY_UPDATE_TOKEN, "some token"); - verifyStatic(); + verifyStatic(SharedPreferencesManager.class); SharedPreferencesManager.remove(PREFERENCE_KEY_REQUEST_ID); - verifyStatic(); + verifyStatic(SharedPreferencesManager.class); SharedPreferencesManager.remove(PREFERENCE_KEY_DOWNLOAD_STATE); /* Since after disabling once, the request id was deleted we can enable/disable it will also ignore the request. */ @@ -1045,7 +967,7 @@ public void disableBeforeStoreToken() throws Exception { Distribute.getInstance().storeRedirectionParameters(requestId.toString(), "g", "some token"); /* Verify behavior. */ - verifyStatic(never()); + verifyStatic(SharedPreferencesManager.class, never()); SharedPreferencesManager.putString(PREFERENCE_KEY_UPDATE_TOKEN, "some token"); } @@ -1055,13 +977,13 @@ public void disableWhileCheckingRelease() { /* Mock we already have token. */ when(SharedPreferencesManager.getString(PREFERENCE_KEY_UPDATE_TOKEN)).thenReturn("some token"); ServiceCall firstCall = mock(ServiceCall.class); - when(mHttpClient.callAsync(anyString(), anyString(), anyMapOf(String.class, String.class), any(HttpClient.CallTemplate.class), any(ServiceCallback.class))).thenReturn(firstCall).thenReturn(mock(ServiceCall.class)); + when(mHttpClient.callAsync(anyString(), anyString(), anyMap(), any(HttpClient.CallTemplate.class), any(ServiceCallback.class))).thenReturn(firstCall).thenReturn(mock(ServiceCall.class)); /* The call is only triggered when app is resumed. */ start(); - verify(mHttpClient, never()).callAsync(anyString(), anyString(), eq(Collections.emptyMap()), any(HttpClient.CallTemplate.class), any(ServiceCallback.class)); + verify(mHttpClient, never()).callAsync(anyString(), anyString(), eq(Collections.emptyMap()), any(HttpClient.CallTemplate.class), any(ServiceCallback.class)); Distribute.getInstance().onActivityResumed(mActivity); - verify(mHttpClient).callAsync(anyString(), anyString(), eq(Collections.emptyMap()), any(HttpClient.CallTemplate.class), any(ServiceCallback.class)); + verify(mHttpClient).callAsync(anyString(), anyString(), eq(Collections.emptyMap()), any(HttpClient.CallTemplate.class), any(ServiceCallback.class)); /* Verify cancel on disabling. */ verify(firstCall, never()).cancel(); @@ -1079,7 +1001,7 @@ public void releaseFailureWithDifferentIds() { /* Mock we already have token. */ when(SharedPreferencesManager.getString(PREFERENCE_KEY_UPDATE_TOKEN)).thenReturn("some token"); - when(mHttpClient.callAsync(anyString(), anyString(), anyMapOf(String.class, String.class), any(HttpClient.CallTemplate.class), any(ServiceCallback.class))).thenAnswer(new Answer() { + when(mHttpClient.callAsync(anyString(), anyString(), anyMap(), any(HttpClient.CallTemplate.class), any(ServiceCallback.class))).thenAnswer(new Answer() { @Override public ServiceCall answer(InvocationOnMock invocation) { @@ -1103,7 +1025,7 @@ public ServiceCall answer(InvocationOnMock invocation) { Distribute.getInstance().onActivityResumed(mock(Activity.class)); /* Verify on failure we don't complete workflow if ids don't match. */ - verifyStatic(never()); + verifyStatic(SharedPreferencesManager.class, never()); SharedPreferencesManager.remove(PREFERENCE_KEY_DOWNLOAD_STATE); } @@ -1111,7 +1033,7 @@ private void checkReleaseFailure(final Exception exception, VerificationMode del /* Mock we already have token. */ when(SharedPreferencesManager.getString(PREFERENCE_KEY_UPDATE_TOKEN)).thenReturn("some token"); - when(mHttpClient.callAsync(anyString(), anyString(), anyMapOf(String.class, String.class), any(HttpClient.CallTemplate.class), any(ServiceCallback.class))).thenAnswer(new Answer() { + when(mHttpClient.callAsync(anyString(), anyString(), anyMap(), any(HttpClient.CallTemplate.class), any(ServiceCallback.class))).thenAnswer(new Answer() { @Override public ServiceCall answer(InvocationOnMock invocation) { @@ -1123,24 +1045,24 @@ public ServiceCall answer(InvocationOnMock invocation) { /* Trigger call. */ start(); Distribute.getInstance().onActivityResumed(mock(Activity.class)); - verify(mHttpClient).callAsync(anyString(), anyString(), eq(Collections.emptyMap()), any(HttpClient.CallTemplate.class), any(ServiceCallback.class)); + verify(mHttpClient).callAsync(anyString(), anyString(), eq(Collections.emptyMap()), any(HttpClient.CallTemplate.class), any(ServiceCallback.class)); /* Verify on failure we complete workflow. */ - verifyStatic(); + verifyStatic(SharedPreferencesManager.class); SharedPreferencesManager.remove(PREFERENCE_KEY_DOWNLOAD_STATE); /* Check token kept or not depending on the test. */ - verifyStatic(deleteTokenVerificationMode); + verifyStatic(SharedPreferencesManager.class, deleteTokenVerificationMode); SharedPreferencesManager.remove(PREFERENCE_KEY_UPDATE_TOKEN); /* Check postpone time kept or not depending on the test. */ - verifyStatic(deleteTokenVerificationMode); + verifyStatic(SharedPreferencesManager.class, deleteTokenVerificationMode); SharedPreferencesManager.remove(PREFERENCE_KEY_POSTPONE_TIME); /* After that if we resume app nothing happens. */ Distribute.getInstance().onActivityPaused(mock(Activity.class)); Distribute.getInstance().onActivityResumed(mock(Activity.class)); - verify(mHttpClient).callAsync(anyString(), anyString(), eq(Collections.emptyMap()), any(HttpClient.CallTemplate.class), any(ServiceCallback.class)); + verify(mHttpClient).callAsync(anyString(), anyString(), eq(Collections.emptyMap()), any(HttpClient.CallTemplate.class), any(ServiceCallback.class)); } @Test @@ -1184,14 +1106,14 @@ public void checkReleaseFailsWithSome404noRelease() throws Exception { @Test public void releaseSuccessDifferentIds() { when(SharedPreferencesManager.getString(PREFERENCE_KEY_UPDATE_TOKEN)).thenReturn("some token"); - when(mHttpClient.callAsync(anyString(), anyString(), anyMapOf(String.class, String.class), any(HttpClient.CallTemplate.class), any(ServiceCallback.class))).thenAnswer(new Answer() { + when(mHttpClient.callAsync(anyString(), anyString(), anyMap(), any(HttpClient.CallTemplate.class), any(ServiceCallback.class))).thenAnswer(new Answer() { @Override public ServiceCall answer(InvocationOnMock invocation) { /* Do the call so that id had changed. */ Distribute.getInstance().getLatestReleaseDetails("mockGroup", "token"); - ((ServiceCallback) invocation.getArguments()[4]).onCallSucceeded(new HttpResponse(200, "mock", Collections.emptyMap())); + ((ServiceCallback) invocation.getArguments()[4]).onCallSucceeded(new HttpResponse(200, "mock", Collections.emptyMap())); return mock(ServiceCall.class); } }).thenAnswer(new Answer() { @@ -1208,7 +1130,7 @@ public ServiceCall answer(InvocationOnMock invocation) { Distribute.getInstance().onActivityResumed(mock(Activity.class)); /* Verify on failure we don't complete workflow if ids don't match. */ - verifyStatic(never()); + verifyStatic(SharedPreferencesManager.class, never()); SharedPreferencesManager.remove(PREFERENCE_KEY_DOWNLOAD_STATE); } @@ -1218,14 +1140,14 @@ public void releaseSuccessActivityIsNull() { /* Update is more recent. */ when(mReleaseDetails.getVersion()).thenReturn(7); - when(mHttpClient.callAsync(anyString(), anyString(), anyMapOf(String.class, String.class), any(HttpClient.CallTemplate.class), any(ServiceCallback.class))).thenAnswer(new Answer() { + when(mHttpClient.callAsync(anyString(), anyString(), anyMap(), any(HttpClient.CallTemplate.class), any(ServiceCallback.class))).thenAnswer(new Answer() { @Override public ServiceCall answer(InvocationOnMock invocation) { /* Do the call so that id had changed. */ Distribute.getInstance().onActivityPaused(mActivity); - ((ServiceCallback) invocation.getArguments()[4]).onCallSucceeded(new HttpResponse(200, "mock", Collections.emptyMap())); + ((ServiceCallback) invocation.getArguments()[4]).onCallSucceeded(new HttpResponse(200, "mock", Collections.emptyMap())); return mock(ServiceCall.class); } }).thenAnswer(new Answer() { @@ -1242,7 +1164,7 @@ public ServiceCall answer(InvocationOnMock invocation) { Distribute.getInstance().onActivityResumed(mock(Activity.class)); /* Verify on failure we don't complete workflow if ids don't match. */ - verifyStatic(never()); + verifyStatic(SharedPreferencesManager.class, never()); SharedPreferencesManager.remove(PREFERENCE_KEY_DOWNLOAD_STATE); } @@ -1251,11 +1173,11 @@ public void checkReleaseFailsParsing() throws Exception { /* Mock we already have token. */ when(SharedPreferencesManager.getString(PREFERENCE_KEY_UPDATE_TOKEN)).thenReturn("some token"); - when(mHttpClient.callAsync(anyString(), anyString(), anyMapOf(String.class, String.class), any(HttpClient.CallTemplate.class), any(ServiceCallback.class))).thenAnswer(new Answer() { + when(mHttpClient.callAsync(anyString(), anyString(), anyMap(), any(HttpClient.CallTemplate.class), any(ServiceCallback.class))).thenAnswer(new Answer() { @Override public ServiceCall answer(InvocationOnMock invocation) { - ((ServiceCallback) invocation.getArguments()[4]).onCallSucceeded(new HttpResponse(200, "mock", Collections.emptyMap())); + ((ServiceCallback) invocation.getArguments()[4]).onCallSucceeded(new HttpResponse(200, "mock", Collections.emptyMap())); return mock(ServiceCall.class); } }); @@ -1268,7 +1190,7 @@ public ServiceCall answer(InvocationOnMock invocation) { verify(mHttpClient).callAsync(anyString(), anyString(), eq(headers), any(HttpClient.CallTemplate.class), any(ServiceCallback.class)); /* Verify on failure we complete workflow. */ - verifyStatic(); + verifyStatic(SharedPreferencesManager.class); SharedPreferencesManager.remove(PREFERENCE_KEY_DOWNLOAD_STATE); /* After that if we resume app nothing happens. */ @@ -1289,13 +1211,13 @@ public void disableBeforeCheckReleaseFails() { /* Disable before it fails. */ Distribute.setEnabled(false); - verifyStatic(); + verifyStatic(SharedPreferencesManager.class); SharedPreferencesManager.remove(PREFERENCE_KEY_DOWNLOAD_STATE); - verifyStatic(never()); + verifyStatic(SharedPreferencesManager.class, never()); SharedPreferencesManager.remove(PREFERENCE_KEY_UPDATE_TOKEN); /* Verify complete workflow call ignored. i.e. no more call to delete the state. */ - verifyStatic(); + verifyStatic(SharedPreferencesManager.class); SharedPreferencesManager.remove(PREFERENCE_KEY_DOWNLOAD_STATE); /* After that if we resume app nothing happens. */ @@ -1315,13 +1237,13 @@ public void disableBeforeCheckReleaseSucceed() { /* Disable before it succeeds. */ Distribute.setEnabled(false); - verifyStatic(); + verifyStatic(SharedPreferencesManager.class); SharedPreferencesManager.remove(PREFERENCE_KEY_DOWNLOAD_STATE); - verifyStatic(never()); + verifyStatic(SharedPreferencesManager.class, never()); SharedPreferencesManager.remove(PREFERENCE_KEY_UPDATE_TOKEN); /* Verify complete workflow call skipped. i.e. no more call to delete the state. */ - verifyStatic(); + verifyStatic(SharedPreferencesManager.class); SharedPreferencesManager.remove(PREFERENCE_KEY_DOWNLOAD_STATE); /* After that if we resume app nothing happens. */ @@ -1345,7 +1267,7 @@ public void storeBetterEncryptedToken() { verify(mHttpClient).callAsync(anyString(), anyString(), eq(headers), any(HttpClient.CallTemplate.class), any(ServiceCallback.class)); /* Verify storage was updated with new encrypted value. */ - verifyStatic(); + verifyStatic(SharedPreferencesManager.class); SharedPreferencesManager.putString(PREFERENCE_KEY_UPDATE_TOKEN, "some better encrypted token"); } @@ -1363,18 +1285,11 @@ public void willNotReportReleaseInstallForPrivateGroupWithoutStoredReleaseHash() Distribute.setUpdateTrack(UpdateTrack.PRIVATE); start(); Distribute.getInstance().onActivityResumed(mActivity); - ArgumentMatcher urlArg = new ArgumentMatcher() { - - @Override - public boolean matches(Object argument) { - return argument.toString().matches("^https://.*?/sdk/apps/a/releases/private/latest\\?release_hash=" + TEST_HASH + "$"); - } - }; - verify(mHttpClient).callAsync(argThat(urlArg), eq("GET"), eq(headers), any(HttpClient.CallTemplate.class), any(ServiceCallback.class)); + verify(mHttpClient).callAsync(matches("^https://.*?/sdk/apps/a/releases/private/latest\\?release_hash=" + TEST_HASH + "$"), eq("GET"), eq(headers), any(HttpClient.CallTemplate.class), any(ServiceCallback.class)); } @Test - public void willNotReportReleaseInstallForPrivateGroupWhenReleaseHashesDontMatch() { + public void willNotReportReleaseInstallForPrivateGroupWhenReleaseHashesDoNotMatch() { when(SharedPreferencesManager.getString(PREFERENCE_KEY_UPDATE_TOKEN)).thenReturn("some token"); when(SharedPreferencesManager.getString(PREFERENCE_KEY_DISTRIBUTION_GROUP_ID)).thenReturn("fake-distribution-id"); when(SharedPreferencesManager.getString(PREFERENCE_KEY_DOWNLOADED_RELEASE_HASH)).thenReturn("fake-release-hash"); @@ -1387,14 +1302,7 @@ public void willNotReportReleaseInstallForPrivateGroupWhenReleaseHashesDontMatch Distribute.setUpdateTrack(UpdateTrack.PRIVATE); start(); Distribute.getInstance().onActivityResumed(mActivity); - ArgumentMatcher urlArg = new ArgumentMatcher() { - - @Override - public boolean matches(Object argument) { - return argument.toString().matches("^https://.*?/sdk/apps/a/releases/private/latest\\?release_hash=" + TEST_HASH + "$"); - } - }; - verify(mHttpClient).callAsync(argThat(urlArg), eq("GET"), eq(headers), any(HttpClient.CallTemplate.class), any(ServiceCallback.class)); + verify(mHttpClient).callAsync(matches("^https://.*?/sdk/apps/a/releases/private/latest\\?release_hash=" + TEST_HASH + "$"), eq("GET"), eq(headers), any(HttpClient.CallTemplate.class), any(ServiceCallback.class)); } @Test @@ -1413,14 +1321,7 @@ public void reportReleaseInstallForPrivateGroupWhenReleaseHashesMatch() { Distribute.setUpdateTrack(UpdateTrack.PRIVATE); start(); Distribute.getInstance().onActivityResumed(mActivity); - ArgumentMatcher urlArg = new ArgumentMatcher() { - - @Override - public boolean matches(Object argument) { - return argument.toString().matches("^https://.*?/sdk/apps/a/releases/private/latest\\?release_hash=" + TEST_HASH + "&distribution_group_id=" + distributionGroupId + "&downloaded_release_id=4$"); - } - }; - verify(mHttpClient).callAsync(argThat(urlArg), eq("GET"), eq(headers), any(HttpClient.CallTemplate.class), any(ServiceCallback.class)); + verify(mHttpClient).callAsync(matches("^https://.*?/sdk/apps/a/releases/private/latest\\?release_hash=" + TEST_HASH + "&distribution_group_id=" + distributionGroupId + "&downloaded_release_id=4$"), eq("GET"), eq(headers), any(HttpClient.CallTemplate.class), any(ServiceCallback.class)); } @Test @@ -1437,14 +1338,7 @@ public void reportReleaseInstallForPublicGroupWhenReleaseHashesMatch() { /* Primary storage will be missing data. */ start(); Distribute.getInstance().onActivityResumed(mActivity); - ArgumentMatcher urlArg = new ArgumentMatcher() { - - @Override - public boolean matches(Object argument) { - return argument.toString().matches("^https://.*?/public/sdk/apps/a/releases/latest\\?release_hash=" + TEST_HASH + "&install_id=" + mInstallId + "&distribution_group_id=" + distributionGroupId + "&downloaded_release_id=4$"); - } - }; - verify(mHttpClient).callAsync(argThat(urlArg), eq("GET"), eq(headers), any(HttpClient.CallTemplate.class), any(ServiceCallback.class)); + verify(mHttpClient).callAsync(matches("^https://.*?/public/sdk/apps/a/releases/latest\\?release_hash=" + TEST_HASH + "&install_id=" + mInstallId + "&distribution_group_id=" + distributionGroupId + "&downloaded_release_id=4$"), eq("GET"), eq(headers), any(HttpClient.CallTemplate.class), any(ServiceCallback.class)); } @Test @@ -1468,7 +1362,7 @@ public void enqueueDistributionStartSessionLogAfterEnablingUpdates() { } @Test - public void dontEnqueueDistributionStartSessionLogIfLastSessionIdIsNull() { + public void doNotEnqueueDistributionStartSessionLogIfLastSessionIdIsNull() { /* Setup mock. */ mockStatic(SessionContext.class); @@ -1488,7 +1382,7 @@ public void dontEnqueueDistributionStartSessionLogIfLastSessionIdIsNull() { } @Test - public void dontEnqueueDistributionStartSessionLogIfNoSessionsWereLoggedBefore() { + public void doNotEnqueueDistributionStartSessionLogIfNoSessionsWereLoggedBefore() { /* Setup mock. */ mockStatic(SessionContext.class); @@ -1506,7 +1400,7 @@ public void dontEnqueueDistributionStartSessionLogIfNoSessionsWereLoggedBefore() } @Test - public void shouldChangeDistributionGroupIdIfStoredIdDoesntMatchDownloadedId() { + public void shouldChangeDistributionGroupIdIfStoredIdDoesNotMatchDownloadedId() { /* Mock release details. */ String downloadedDistributionGroupId = "fake-downloaded-id"; @@ -1520,9 +1414,9 @@ public void shouldChangeDistributionGroupIdIfStoredIdDoesntMatchDownloadedId() { start(); /* Verify group ID. */ - verifyStatic(); + verifyStatic(SharedPreferencesManager.class); SharedPreferencesManager.putString(PREFERENCE_KEY_DISTRIBUTION_GROUP_ID, downloadedDistributionGroupId); - verifyStatic(); + verifyStatic(SharedPreferencesManager.class); SharedPreferencesManager.remove(PREFERENCE_KEY_DOWNLOADED_DISTRIBUTION_GROUP_ID); } @@ -1541,9 +1435,9 @@ public void shouldChangeDistributionGroupIdIfStoredIdIsNull() { start(); /* Verify group ID. */ - verifyStatic(); + verifyStatic(SharedPreferencesManager.class); SharedPreferencesManager.putString(PREFERENCE_KEY_DISTRIBUTION_GROUP_ID, downloadedDistributionGroupId); - verifyStatic(); + verifyStatic(SharedPreferencesManager.class); SharedPreferencesManager.remove(PREFERENCE_KEY_DOWNLOADED_DISTRIBUTION_GROUP_ID); } @@ -1561,14 +1455,14 @@ public void shouldNotChangeDistributionGroupIdIfStoredIdMatchDownloadedId() { start(); /* Verify group ID. */ - verifyStatic(never()); + verifyStatic(SharedPreferencesManager.class, never()); SharedPreferencesManager.putString(eq(PREFERENCE_KEY_DISTRIBUTION_GROUP_ID), anyString()); - verifyStatic(never()); + verifyStatic(SharedPreferencesManager.class, never()); SharedPreferencesManager.remove(PREFERENCE_KEY_DOWNLOADED_DISTRIBUTION_GROUP_ID); } @Test - public void shouldNotChangeDistributionGroupIdIfAppWasntUpdated() { + public void shouldNotChangeDistributionGroupIdIfAppWasNotUpdated() { /* Mock release details. */ mockStatic(DistributeUtils.class); @@ -1581,9 +1475,9 @@ public void shouldNotChangeDistributionGroupIdIfAppWasntUpdated() { start(); /* Verify group ID. */ - verifyStatic(never()); + verifyStatic(SharedPreferencesManager.class, never()); SharedPreferencesManager.putString(eq(PREFERENCE_KEY_DISTRIBUTION_GROUP_ID), anyString()); - verifyStatic(never()); + verifyStatic(SharedPreferencesManager.class, never()); SharedPreferencesManager.remove(PREFERENCE_KEY_DOWNLOADED_DISTRIBUTION_GROUP_ID); } @@ -1596,9 +1490,9 @@ public void shouldNotChangeDistributionGroupIdIfCurrentPackageInfoIsNull() throw when(SharedPreferencesManager.getString(PREFERENCE_KEY_DOWNLOADED_RELEASE_HASH)).thenReturn("fake-hash"); /* Verify group ID. */ - verifyStatic(never()); + verifyStatic(SharedPreferencesManager.class, never()); SharedPreferencesManager.putString(eq(PREFERENCE_KEY_DISTRIBUTION_GROUP_ID), anyString()); - verifyStatic(never()); + verifyStatic(SharedPreferencesManager.class, never()); SharedPreferencesManager.remove(PREFERENCE_KEY_DOWNLOADED_DISTRIBUTION_GROUP_ID); } } diff --git a/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/DistributeBeforeDownloadTest.java b/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/DistributeBeforeDownloadTest.java index 1165d1bbe..53bd52969 100644 --- a/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/DistributeBeforeDownloadTest.java +++ b/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/DistributeBeforeDownloadTest.java @@ -5,6 +5,39 @@ package com.microsoft.appcenter.distribute; +import static com.microsoft.appcenter.Flags.DEFAULTS; +import static com.microsoft.appcenter.distribute.DistributeConstants.DOWNLOAD_STATE_AVAILABLE; +import static com.microsoft.appcenter.distribute.DistributeConstants.DOWNLOAD_STATE_COMPLETED; +import static com.microsoft.appcenter.distribute.DistributeConstants.PREFERENCE_KEY_DISTRIBUTION_GROUP_ID; +import static com.microsoft.appcenter.distribute.DistributeConstants.PREFERENCE_KEY_DOWNLOADED_RELEASE_HASH; +import static com.microsoft.appcenter.distribute.DistributeConstants.PREFERENCE_KEY_DOWNLOADED_RELEASE_ID; +import static com.microsoft.appcenter.distribute.DistributeConstants.PREFERENCE_KEY_DOWNLOAD_STATE; +import static com.microsoft.appcenter.distribute.DistributeConstants.PREFERENCE_KEY_POSTPONE_TIME; +import static com.microsoft.appcenter.distribute.DistributeConstants.PREFERENCE_KEY_RELEASE_DETAILS; +import static com.microsoft.appcenter.distribute.DistributeConstants.PREFERENCE_KEY_UPDATE_TOKEN; +import static org.junit.Assert.assertNotNull; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.anyLong; +import static org.mockito.ArgumentMatchers.anyMap; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.ArgumentMatchers.isA; +import static org.mockito.ArgumentMatchers.isNull; +import static org.mockito.ArgumentMatchers.same; +import static org.mockito.Mockito.doThrow; +import static org.mockito.Mockito.inOrder; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.reset; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; +import static org.mockito.internal.verification.VerificationModeFactory.times; +import static org.powermock.api.mockito.PowerMockito.doAnswer; +import static org.powermock.api.mockito.PowerMockito.mockStatic; +import static org.powermock.api.mockito.PowerMockito.verifyStatic; +import static org.powermock.api.mockito.PowerMockito.whenNew; + import android.app.Activity; import android.app.ProgressDialog; import android.content.ActivityNotFoundException; @@ -43,39 +76,10 @@ import java.util.UUID; import java.util.concurrent.atomic.AtomicReference; -import static com.microsoft.appcenter.Flags.DEFAULTS; -import static com.microsoft.appcenter.distribute.DistributeConstants.DOWNLOAD_STATE_AVAILABLE; -import static com.microsoft.appcenter.distribute.DistributeConstants.DOWNLOAD_STATE_COMPLETED; -import static com.microsoft.appcenter.distribute.DistributeConstants.PREFERENCE_KEY_DISTRIBUTION_GROUP_ID; -import static com.microsoft.appcenter.distribute.DistributeConstants.PREFERENCE_KEY_DOWNLOADED_RELEASE_HASH; -import static com.microsoft.appcenter.distribute.DistributeConstants.PREFERENCE_KEY_DOWNLOADED_RELEASE_ID; -import static com.microsoft.appcenter.distribute.DistributeConstants.PREFERENCE_KEY_DOWNLOAD_STATE; -import static com.microsoft.appcenter.distribute.DistributeConstants.PREFERENCE_KEY_POSTPONE_TIME; -import static com.microsoft.appcenter.distribute.DistributeConstants.PREFERENCE_KEY_RELEASE_DETAILS; -import static com.microsoft.appcenter.distribute.DistributeConstants.PREFERENCE_KEY_UPDATE_TOKEN; -import static org.junit.Assert.assertNotNull; -import static org.mockito.Matchers.any; -import static org.mockito.Matchers.anyInt; -import static org.mockito.Matchers.anyLong; -import static org.mockito.Matchers.anyMapOf; -import static org.mockito.Matchers.anyString; -import static org.mockito.Matchers.eq; -import static org.mockito.Matchers.isNull; -import static org.mockito.Matchers.same; -import static org.mockito.Mockito.doThrow; -import static org.mockito.Mockito.inOrder; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.never; -import static org.mockito.Mockito.reset; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; -import static org.mockito.internal.verification.VerificationModeFactory.times; -import static org.powermock.api.mockito.PowerMockito.doAnswer; -import static org.powermock.api.mockito.PowerMockito.mockStatic; -import static org.powermock.api.mockito.PowerMockito.verifyStatic; -import static org.powermock.api.mockito.PowerMockito.whenNew; - -@PrepareForTest({DistributeUtils.class, SessionContext.class}) +@PrepareForTest({ + DistributeUtils.class, + SessionContext.class +}) public class DistributeBeforeDownloadTest extends AbstractDistributeTest { private void mockSessionContext() { @@ -91,7 +95,7 @@ private void mockSessionContext() { public void moreRecentWithIncompatibleMinApiLevel() throws Exception { mockSessionContext(); TestUtils.setInternalState(Build.VERSION.class, "SDK_INT", Build.VERSION_CODES.LOLLIPOP); - when(mHttpClient.callAsync(anyString(), anyString(), anyMapOf(String.class, String.class), any(HttpClient.CallTemplate.class), any(ServiceCallback.class))).thenAnswer(new Answer() { + when(mHttpClient.callAsync(anyString(), anyString(), anyMap(), any(HttpClient.CallTemplate.class), any(ServiceCallback.class))).thenAnswer(new Answer() { @Override public ServiceCall answer(InvocationOnMock invocation) { @@ -114,18 +118,18 @@ public ServiceCall answer(InvocationOnMock invocation) { verify(mHttpClient).callAsync(anyString(), anyString(), eq(headers), any(HttpClient.CallTemplate.class), any(ServiceCallback.class)); /* Verify on incompatible version we complete workflow. */ - verifyStatic(never()); + verifyStatic(SharedPreferencesManager.class, never()); SharedPreferencesManager.remove(PREFERENCE_KEY_DOWNLOADED_RELEASE_HASH); - verifyStatic(); + verifyStatic(SharedPreferencesManager.class); SharedPreferencesManager.remove(PREFERENCE_KEY_DOWNLOAD_STATE); verify(mDialogBuilder, never()).create(); verify(mDialog, never()).show(); /* Verify we still track the distribution group statistics. */ - verifyStatic(); + verifyStatic(SharedPreferencesManager.class); SharedPreferencesManager.putString(PREFERENCE_KEY_DISTRIBUTION_GROUP_ID, distributionGroupId); verify(mDistributeInfoTracker).updateDistributionGroupId(distributionGroupId); - verify(mChannel).enqueue(any(DistributionStartSessionLog.class), eq(Distribute.getInstance().getGroupName()), eq(DEFAULTS)); + verify(mChannel).enqueue(isA(DistributionStartSessionLog.class), eq(Distribute.getInstance().getGroupName()), eq(DEFAULTS)); /* After that if we resume app nothing happens. */ Distribute.getInstance().onActivityPaused(mock(Activity.class)); @@ -136,7 +140,7 @@ public ServiceCall answer(InvocationOnMock invocation) { @Test public void olderVersionCode() throws Exception { mockSessionContext(); - when(mHttpClient.callAsync(anyString(), anyString(), anyMapOf(String.class, String.class), any(HttpClient.CallTemplate.class), any(ServiceCallback.class))).thenAnswer(new Answer() { + when(mHttpClient.callAsync(anyString(), anyString(), anyMap(), any(HttpClient.CallTemplate.class), any(ServiceCallback.class))).thenAnswer(new Answer() { @Override public ServiceCall answer(InvocationOnMock invocation) { @@ -160,16 +164,16 @@ public ServiceCall answer(InvocationOnMock invocation) { verify(mHttpClient).callAsync(anyString(), anyString(), eq(headers), any(HttpClient.CallTemplate.class), any(ServiceCallback.class)); /* Verify on failure we complete workflow. */ - verifyStatic(); + verifyStatic(SharedPreferencesManager.class); SharedPreferencesManager.remove(PREFERENCE_KEY_DOWNLOAD_STATE); verify(mDialogBuilder, never()).create(); verify(mDialog, never()).show(); /* Verify we still track the distribution group statistics. */ - verifyStatic(); + verifyStatic(SharedPreferencesManager.class); SharedPreferencesManager.putString(PREFERENCE_KEY_DISTRIBUTION_GROUP_ID, distributionGroupId); verify(mDistributeInfoTracker).updateDistributionGroupId(distributionGroupId); - verify(mChannel).enqueue(any(DistributionStartSessionLog.class), eq(Distribute.getInstance().getGroupName()), eq(DEFAULTS)); + verify(mChannel).enqueue(isA(DistributionStartSessionLog.class), eq(Distribute.getInstance().getGroupName()), eq(DEFAULTS)); /* After that if we resume app nothing happens. */ Distribute.getInstance().onActivityPaused(mock(Activity.class)); @@ -184,7 +188,7 @@ public ServiceCall answer(InvocationOnMock invocation) { @Test public void sameVersionCodeSameHash() throws Exception { mockSessionContext(); - when(mHttpClient.callAsync(anyString(), anyString(), anyMapOf(String.class, String.class), any(HttpClient.CallTemplate.class), any(ServiceCallback.class))).thenAnswer(new Answer() { + when(mHttpClient.callAsync(anyString(), anyString(), anyMap(), any(HttpClient.CallTemplate.class), any(ServiceCallback.class))).thenAnswer(new Answer() { @Override public ServiceCall answer(InvocationOnMock invocation) { @@ -207,16 +211,16 @@ public ServiceCall answer(InvocationOnMock invocation) { verify(mHttpClient).callAsync(anyString(), anyString(), eq(headers), any(HttpClient.CallTemplate.class), any(ServiceCallback.class)); /* Verify on failure we complete workflow. */ - verifyStatic(); + verifyStatic(SharedPreferencesManager.class); SharedPreferencesManager.remove(PREFERENCE_KEY_DOWNLOAD_STATE); verify(mDialogBuilder, never()).create(); verify(mDialog, never()).show(); /* Verify we still track the distribution group statistics. */ - verifyStatic(); + verifyStatic(SharedPreferencesManager.class); SharedPreferencesManager.putString(PREFERENCE_KEY_DISTRIBUTION_GROUP_ID, distributionGroupId); verify(mDistributeInfoTracker).updateDistributionGroupId(distributionGroupId); - verify(mChannel).enqueue(any(DistributionStartSessionLog.class), eq(Distribute.getInstance().getGroupName()), eq(DEFAULTS)); + verify(mChannel).enqueue(isA(DistributionStartSessionLog.class), eq(Distribute.getInstance().getGroupName()), eq(DEFAULTS)); /* After that if we resume app nothing happens. */ Distribute.getInstance().onActivityPaused(mock(Activity.class)); @@ -227,7 +231,7 @@ public ServiceCall answer(InvocationOnMock invocation) { @Test public void moreRecentVersionCode() throws Exception { mockSessionContext(); - when(mHttpClient.callAsync(anyString(), anyString(), anyMapOf(String.class, String.class), any(HttpClient.CallTemplate.class), any(ServiceCallback.class))).thenAnswer(new Answer() { + when(mHttpClient.callAsync(anyString(), anyString(), anyMap(), any(HttpClient.CallTemplate.class), any(ServiceCallback.class))).thenAnswer(new Answer() { @Override public ServiceCall answer(InvocationOnMock invocation) { @@ -257,10 +261,10 @@ public ServiceCall answer(InvocationOnMock invocation) { verify(mDialog).show(); /* Verify we track the distribution group statistics. */ - verifyStatic(); + verifyStatic(SharedPreferencesManager.class); SharedPreferencesManager.putString(PREFERENCE_KEY_DISTRIBUTION_GROUP_ID, distributionGroupId); verify(mDistributeInfoTracker).updateDistributionGroupId(distributionGroupId); - verify(mChannel).enqueue(any(DistributionStartSessionLog.class), eq(Distribute.getInstance().getGroupName()), eq(DEFAULTS)); + verify(mChannel).enqueue(isA(DistributionStartSessionLog.class), eq(Distribute.getInstance().getGroupName()), eq(DEFAULTS)); /* After that if we resume app we refresh dialog. */ Distribute.getInstance().onActivityPaused(mock(Activity.class)); @@ -294,7 +298,7 @@ public void sameVersionDifferentHashWithHardcodedAppName() throws Exception { when(SharedPreferencesManager.getString(PREFERENCE_KEY_DISTRIBUTION_GROUP_ID)).thenReturn("some group"); when(SharedPreferencesManager.getString(PREFERENCE_KEY_UPDATE_TOKEN)).thenReturn("some token"); mockSessionContext(); - when(mHttpClient.callAsync(anyString(), anyString(), anyMapOf(String.class, String.class), any(HttpClient.CallTemplate.class), any(ServiceCallback.class))).thenAnswer(new Answer() { + when(mHttpClient.callAsync(anyString(), anyString(), anyMap(), any(HttpClient.CallTemplate.class), any(ServiceCallback.class))).thenAnswer(new Answer() { @Override public ServiceCall answer(InvocationOnMock invocation) { @@ -330,10 +334,10 @@ public ServiceCall answer(InvocationOnMock invocation) { verify(mDialog).show(); /* Verify we didn't track the distribution group statistics since it was already done at redirection. */ - verifyStatic(never()); + verifyStatic(SharedPreferencesManager.class, never()); SharedPreferencesManager.putString(PREFERENCE_KEY_DISTRIBUTION_GROUP_ID, distributionGroupId); verify(mDistributeInfoTracker, never()).updateDistributionGroupId(distributionGroupId); - verify(mChannel, never()).enqueue(any(DistributionStartSessionLog.class), eq(Distribute.getInstance().getGroupName()), eq(DEFAULTS)); + verify(mChannel, never()).enqueue(isA(DistributionStartSessionLog.class), eq(Distribute.getInstance().getGroupName()), eq(DEFAULTS)); } @Test @@ -343,7 +347,7 @@ public void postponeDialog() throws Exception { when(SharedPreferencesManager.getString(PREFERENCE_KEY_DISTRIBUTION_GROUP_ID)).thenReturn("some group"); when(SharedPreferencesManager.getString(PREFERENCE_KEY_UPDATE_TOKEN)).thenReturn("some token"); mockSessionContext(); - when(mHttpClient.callAsync(anyString(), anyString(), anyMapOf(String.class, String.class), any(HttpClient.CallTemplate.class), any(ServiceCallback.class))).thenAnswer(new Answer() { + when(mHttpClient.callAsync(anyString(), anyString(), anyMap(), any(HttpClient.CallTemplate.class), any(ServiceCallback.class))).thenAnswer(new Answer() { @Override public ServiceCall answer(InvocationOnMock invocation) { @@ -384,24 +388,24 @@ public Void answer(InvocationOnMock invocation) { when(mDialog.isShowing()).thenReturn(false); /* Verify. */ - verifyStatic(); + verifyStatic(SharedPreferencesManager.class); SharedPreferencesManager.remove(PREFERENCE_KEY_DOWNLOAD_STATE); - verifyStatic(); + verifyStatic(SharedPreferencesManager.class); SharedPreferencesManager.remove(PREFERENCE_KEY_RELEASE_DETAILS); - verifyStatic(); + verifyStatic(SharedPreferencesManager.class); SharedPreferencesManager.putLong(eq(PREFERENCE_KEY_POSTPONE_TIME), eq(now)); /* Verify we didn't track distribution group stats since we already had redirection parameters. */ - verifyStatic(never()); + verifyStatic(SharedPreferencesManager.class, never()); SharedPreferencesManager.putString(PREFERENCE_KEY_DISTRIBUTION_GROUP_ID, distributionGroupId); verify(mDistributeInfoTracker, never()).updateDistributionGroupId(distributionGroupId); - verify(mChannel, never()).enqueue(any(DistributionStartSessionLog.class), eq(Distribute.getInstance().getGroupName()), eq(DEFAULTS)); + verify(mChannel, never()).enqueue(isA(DistributionStartSessionLog.class), eq(Distribute.getInstance().getGroupName()), eq(DEFAULTS)); /* Verify no more calls, e.g. happened only once. */ Distribute.getInstance().onActivityPaused(mock(Activity.class)); Distribute.getInstance().onActivityResumed(mock(Activity.class)); verify(mDialog).show(); - verify(mHttpClient).callAsync(anyString(), anyString(), anyMapOf(String.class, String.class), any(HttpClient.CallTemplate.class), any(ServiceCallback.class)); + verify(mHttpClient).callAsync(anyString(), anyString(), anyMap(), any(HttpClient.CallTemplate.class), any(ServiceCallback.class)); /* Restart should check release and should not show dialog again until 1 day has elapsed. */ now += DistributeConstants.POSTPONE_TIME_THRESHOLD - 1; @@ -434,7 +438,7 @@ public Void answer(InvocationOnMock invocation) { verify(mDialog, times(3)).show(); /* Set back in time to make SDK clean state and force update. */ - verifyStatic(never()); + verifyStatic(SharedPreferencesManager.class, never()); SharedPreferencesManager.remove(PREFERENCE_KEY_POSTPONE_TIME); when(releaseDetails.isMandatoryUpdate()).thenReturn(false); now = 1; @@ -442,7 +446,7 @@ public Void answer(InvocationOnMock invocation) { restartProcessAndSdk(); Distribute.getInstance().onActivityResumed(mock(Activity.class)); verify(mDialog, times(4)).show(); - verifyStatic(); + verifyStatic(SharedPreferencesManager.class); SharedPreferencesManager.remove(PREFERENCE_KEY_POSTPONE_TIME); } @@ -451,7 +455,7 @@ public void disableBeforePostponeDialog() throws Exception { /* Setup mock. */ mockSessionContext(); - when(mHttpClient.callAsync(anyString(), anyString(), anyMapOf(String.class, String.class), any(HttpClient.CallTemplate.class), any(ServiceCallback.class))).thenAnswer(new Answer() { + when(mHttpClient.callAsync(anyString(), anyString(), anyMap(), any(HttpClient.CallTemplate.class), any(ServiceCallback.class))).thenAnswer(new Answer() { @Override public ServiceCall answer(InvocationOnMock invocation) { @@ -476,14 +480,14 @@ public ServiceCall answer(InvocationOnMock invocation) { verify(mDialog).show(); /* Verify we track the distribution group statistics. */ - verifyStatic(); + verifyStatic(SharedPreferencesManager.class); SharedPreferencesManager.putString(PREFERENCE_KEY_DISTRIBUTION_GROUP_ID, distributionGroupId); verify(mDistributeInfoTracker).updateDistributionGroupId(distributionGroupId); - verify(mChannel).enqueue(any(DistributionStartSessionLog.class), eq(Distribute.getInstance().getGroupName()), eq(DEFAULTS)); + verify(mChannel).enqueue(isA(DistributionStartSessionLog.class), eq(Distribute.getInstance().getGroupName()), eq(DEFAULTS)); /* Disable. */ Distribute.setEnabled(false); - verifyStatic(); + verifyStatic(SharedPreferencesManager.class); SharedPreferencesManager.remove(PREFERENCE_KEY_DOWNLOAD_STATE); /* Postpone it. */ @@ -497,10 +501,10 @@ public ServiceCall answer(InvocationOnMock invocation) { Distribute.getInstance().onActivityPaused(mActivity); Distribute.getInstance().onActivityResumed(mActivity); verify(mDialog).show(); - verify(mHttpClient).callAsync(anyString(), anyString(), anyMapOf(String.class, String.class), any(HttpClient.CallTemplate.class), any(ServiceCallback.class)); - verifyStatic(); + verify(mHttpClient).callAsync(anyString(), anyString(), anyMap(), any(HttpClient.CallTemplate.class), any(ServiceCallback.class)); + verifyStatic(SharedPreferencesManager.class); SharedPreferencesManager.remove(PREFERENCE_KEY_DOWNLOAD_STATE); - verifyStatic(never()); + verifyStatic(SharedPreferencesManager.class, never()); SharedPreferencesManager.putLong(eq(PREFERENCE_KEY_POSTPONE_TIME), anyLong()); } @@ -511,7 +515,7 @@ public void disableBeforeDownload() throws Exception { /* Mock we already have redirection parameters. */ when(SharedPreferencesManager.getString(PREFERENCE_KEY_DISTRIBUTION_GROUP_ID)).thenReturn("some group"); when(SharedPreferencesManager.getString(PREFERENCE_KEY_UPDATE_TOKEN)).thenReturn("some token"); - when(mHttpClient.callAsync(anyString(), anyString(), anyMapOf(String.class, String.class), any(HttpClient.CallTemplate.class), any(ServiceCallback.class))).thenAnswer(new Answer() { + when(mHttpClient.callAsync(anyString(), anyString(), anyMap(), any(HttpClient.CallTemplate.class), any(ServiceCallback.class))).thenAnswer(new Answer() { @Override public ServiceCall answer(InvocationOnMock invocation) { @@ -537,7 +541,7 @@ public ServiceCall answer(InvocationOnMock invocation) { /* Disable. */ Distribute.setEnabled(false); - verifyStatic(); + verifyStatic(SharedPreferencesManager.class); SharedPreferencesManager.remove(PREFERENCE_KEY_DOWNLOAD_STATE); /* Click on download. */ @@ -551,8 +555,8 @@ public ServiceCall answer(InvocationOnMock invocation) { Distribute.getInstance().onActivityPaused(mActivity); Distribute.getInstance().onActivityResumed(mActivity); verify(mDialog).show(); - verify(mHttpClient).callAsync(anyString(), anyString(), anyMapOf(String.class, String.class), any(HttpClient.CallTemplate.class), any(ServiceCallback.class)); - verifyStatic(); + verify(mHttpClient).callAsync(anyString(), anyString(), anyMap(), any(HttpClient.CallTemplate.class), any(ServiceCallback.class)); + verifyStatic(SharedPreferencesManager.class); SharedPreferencesManager.remove(PREFERENCE_KEY_DOWNLOAD_STATE); /* Verify no download scheduled. */ @@ -570,7 +574,7 @@ public void pauseBeforeDownload() throws Exception { /* Mock we already have redirection parameters. */ when(SharedPreferencesManager.getString(PREFERENCE_KEY_DISTRIBUTION_GROUP_ID)).thenReturn("some group"); when(SharedPreferencesManager.getString(PREFERENCE_KEY_UPDATE_TOKEN)).thenReturn("some token"); - when(mHttpClient.callAsync(anyString(), anyString(), anyMapOf(String.class, String.class), any(HttpClient.CallTemplate.class), any(ServiceCallback.class))).thenAnswer(new Answer() { + when(mHttpClient.callAsync(anyString(), anyString(), anyMap(), any(HttpClient.CallTemplate.class), any(ServiceCallback.class))).thenAnswer(new Answer() { @Override public ServiceCall answer(InvocationOnMock invocation) { @@ -617,7 +621,7 @@ public void mandatoryUpdateDialogAndCacheTests() throws Exception { when(SharedPreferencesManager.getString(PREFERENCE_KEY_UPDATE_TOKEN)).thenReturn("some token"); final AtomicReference serviceCallbackRef = new AtomicReference<>(); final ServiceCall serviceCall = mock(ServiceCall.class); - when(mHttpClient.callAsync(anyString(), anyString(), anyMapOf(String.class, String.class), any(HttpClient.CallTemplate.class), any(ServiceCallback.class))).thenAnswer(new Answer() { + when(mHttpClient.callAsync(anyString(), anyString(), anyMap(), any(HttpClient.CallTemplate.class), any(ServiceCallback.class))).thenAnswer(new Answer() { @Override public ServiceCall answer(InvocationOnMock invocation) { @@ -642,9 +646,9 @@ public ServiceCall answer(InvocationOnMock invocation) { serviceCallbackRef.set(null); /* Verify release notes persisted. */ - verifyStatic(); + verifyStatic(SharedPreferencesManager.class); SharedPreferencesManager.putString(PREFERENCE_KEY_RELEASE_DETAILS, "mock"); - verifyStatic(); + verifyStatic(SharedPreferencesManager.class); SharedPreferencesManager.putInt(PREFERENCE_KEY_DOWNLOAD_STATE, DOWNLOAD_STATE_AVAILABLE); /* Verify dialog. */ @@ -657,7 +661,7 @@ public ServiceCall answer(InvocationOnMock invocation) { Distribute.getInstance().onActivityResumed(mock(Activity.class)); verify(mDialogBuilder, times(2)).setPositiveButton(eq(R.string.appcenter_distribute_update_dialog_download), any(DialogInterface.OnClickListener.class)); verify(mDialogBuilder, never()).setNegativeButton(anyInt(), any(DialogInterface.OnClickListener.class)); - verify(mHttpClient, times(2)).callAsync(anyString(), anyString(), anyMapOf(String.class, String.class), any(HttpClient.CallTemplate.class), any(ServiceCallback.class)); + verify(mHttpClient, times(2)).callAsync(anyString(), anyString(), anyMap(), any(HttpClient.CallTemplate.class), any(ServiceCallback.class)); assertNotNull(serviceCallbackRef.get()); /* Simulate network back and get same release again, should do nothing particular. */ @@ -665,7 +669,7 @@ public ServiceCall answer(InvocationOnMock invocation) { serviceCallbackRef.get().onCallSucceeded(new HttpResponse(200, "mock")); /* Check we didn't change state, e.g. happened only once. */ - verifyStatic(); + verifyStatic(SharedPreferencesManager.class); SharedPreferencesManager.putInt(PREFERENCE_KEY_DOWNLOAD_STATE, DOWNLOAD_STATE_AVAILABLE); /* Restart and this time we will detect a more recent optional release. */ @@ -673,7 +677,7 @@ public ServiceCall answer(InvocationOnMock invocation) { Distribute.getInstance().onActivityResumed(mock(Activity.class)); /* Verify call is made and that we restored again mandatory update dialog in the mean time. */ - verify(mHttpClient, times(3)).callAsync(anyString(), anyString(), anyMapOf(String.class, String.class), any(HttpClient.CallTemplate.class), any(ServiceCallback.class)); + verify(mHttpClient, times(3)).callAsync(anyString(), anyString(), anyMap(), any(HttpClient.CallTemplate.class), any(ServiceCallback.class)); verify(mDialogBuilder, times(3)).setPositiveButton(eq(R.string.appcenter_distribute_update_dialog_download), any(DialogInterface.OnClickListener.class)); verify(mDialogBuilder, never()).setNegativeButton(anyInt(), any(DialogInterface.OnClickListener.class)); @@ -686,7 +690,7 @@ public ServiceCall answer(InvocationOnMock invocation) { serviceCallbackRef.get().onCallSucceeded(new HttpResponse(200, "mock")); /* Check state updated again when we detect it. */ - verifyStatic(times(2)); + verifyStatic(SharedPreferencesManager.class, times(2)); SharedPreferencesManager.putInt(PREFERENCE_KEY_DOWNLOAD_STATE, DOWNLOAD_STATE_AVAILABLE); /* Restart SDK, even offline, should show optional dialog. */ @@ -697,7 +701,7 @@ public ServiceCall answer(InvocationOnMock invocation) { verify(mDialogBuilder).setNegativeButton(anyInt(), any(DialogInterface.OnClickListener.class)); /* And still check again for further update. */ - verify(mHttpClient, times(4)).callAsync(anyString(), anyString(), anyMapOf(String.class, String.class), any(HttpClient.CallTemplate.class), any(ServiceCallback.class)); + verify(mHttpClient, times(4)).callAsync(anyString(), anyString(), anyMap(), any(HttpClient.CallTemplate.class), any(ServiceCallback.class)); /* Unblock call with network up. */ when(mNetworkStateHelper.isNetworkConnected()).thenReturn(true); @@ -733,7 +737,7 @@ public void cancelGetReleaseCallIfDownloadingCachedDialogAfterRestart() throws E when(SharedPreferencesManager.getString(PREFERENCE_KEY_UPDATE_TOKEN)).thenReturn("some token"); final AtomicReference serviceCallbackRef = new AtomicReference<>(); final ServiceCall serviceCall = mock(ServiceCall.class); - when(mHttpClient.callAsync(anyString(), anyString(), anyMapOf(String.class, String.class), any(HttpClient.CallTemplate.class), any(ServiceCallback.class))).thenAnswer(new Answer() { + when(mHttpClient.callAsync(anyString(), anyString(), anyMap(), any(HttpClient.CallTemplate.class), any(ServiceCallback.class))).thenAnswer(new Answer() { @Override public ServiceCall answer(InvocationOnMock invocation) { @@ -764,7 +768,7 @@ public ServiceCall answer(InvocationOnMock invocation) { Distribute.getInstance().onActivityResumed(mock(Activity.class)); /* Verify dialog restored and call scheduled. */ - verify(mHttpClient, times(2)).callAsync(anyString(), anyString(), anyMapOf(String.class, String.class), any(HttpClient.CallTemplate.class), any(ServiceCallback.class)); + verify(mHttpClient, times(2)).callAsync(anyString(), anyString(), anyMap(), any(HttpClient.CallTemplate.class), any(ServiceCallback.class)); ArgumentCaptor clickListener = ArgumentCaptor.forClass(DialogInterface.OnClickListener.class); verify(mDialogBuilder, times(2)).setPositiveButton(eq(R.string.appcenter_distribute_update_dialog_download), clickListener.capture()); @@ -780,7 +784,7 @@ public void releaseNotes() throws Exception { /* Mock we already have redirection parameters. */ when(SharedPreferencesManager.getString(PREFERENCE_KEY_DISTRIBUTION_GROUP_ID)).thenReturn("some group"); when(SharedPreferencesManager.getString(PREFERENCE_KEY_UPDATE_TOKEN)).thenReturn("some token"); - when(mHttpClient.callAsync(anyString(), anyString(), anyMapOf(String.class, String.class), any(HttpClient.CallTemplate.class), any(ServiceCallback.class))).thenAnswer(new Answer() { + when(mHttpClient.callAsync(anyString(), anyString(), anyMap(), any(HttpClient.CallTemplate.class), any(ServiceCallback.class))).thenAnswer(new Answer() { @Override public ServiceCall answer(InvocationOnMock invocation) { @@ -855,7 +859,7 @@ public ServiceCall answer(InvocationOnMock invocation) { doThrow(exception).when(mActivity).startActivity(intent); clickListener.getValue().onClick(mDialog, DialogInterface.BUTTON_NEUTRAL); verify(mActivity, times(2)).startActivity(intent); - verifyStatic(); + verifyStatic(AppCenterLog.class); AppCenterLog.error(anyString(), anyString(), eq(exception)); } @@ -869,7 +873,7 @@ public void shouldRemoveReleaseHashStorageIfReportedSuccessfully() throws Except /* Mock we already have token and no group. */ when(SharedPreferencesManager.getString(PREFERENCE_KEY_UPDATE_TOKEN)).thenReturn("some token"); - when(mHttpClient.callAsync(anyString(), anyString(), anyMapOf(String.class, String.class), any(HttpClient.CallTemplate.class), any(ServiceCallback.class))).thenAnswer(new Answer() { + when(mHttpClient.callAsync(anyString(), anyString(), anyMap(), any(HttpClient.CallTemplate.class), any(ServiceCallback.class))).thenAnswer(new Answer() { @Override public ServiceCall answer(InvocationOnMock invocation) { @@ -887,9 +891,9 @@ public ServiceCall answer(InvocationOnMock invocation) { start(); Distribute.getInstance().onActivityResumed(mock(Activity.class)); - verifyStatic(); + verifyStatic(SharedPreferencesManager.class); SharedPreferencesManager.remove(PREFERENCE_KEY_DOWNLOADED_RELEASE_HASH); - verifyStatic(); + verifyStatic(SharedPreferencesManager.class); SharedPreferencesManager.remove(PREFERENCE_KEY_DOWNLOADED_RELEASE_ID); } @@ -903,7 +907,7 @@ public void shouldNotRemoveReleaseHashStorageIfHashesDontMatch() throws Exceptio /* Mock we already have token and no group. */ when(SharedPreferencesManager.getString(PREFERENCE_KEY_UPDATE_TOKEN)).thenReturn("some token"); - when(mHttpClient.callAsync(anyString(), anyString(), anyMapOf(String.class, String.class), any(HttpClient.CallTemplate.class), any(ServiceCallback.class))).thenAnswer(new Answer() { + when(mHttpClient.callAsync(anyString(), anyString(), anyMap(), any(HttpClient.CallTemplate.class), any(ServiceCallback.class))).thenAnswer(new Answer() { @Override public ServiceCall answer(InvocationOnMock invocation) { @@ -921,9 +925,9 @@ public ServiceCall answer(InvocationOnMock invocation) { start(); Distribute.getInstance().onActivityResumed(mock(Activity.class)); - verifyStatic(never()); + verifyStatic(SharedPreferencesManager.class, never()); SharedPreferencesManager.remove(PREFERENCE_KEY_DOWNLOADED_RELEASE_HASH); - verifyStatic(never()); + verifyStatic(SharedPreferencesManager.class, never()); SharedPreferencesManager.remove(PREFERENCE_KEY_DOWNLOADED_RELEASE_ID); } @@ -1004,7 +1008,7 @@ public Void answer(InvocationOnMock invocation) { SharedPreferencesManager.putString(eq(PREFERENCE_KEY_RELEASE_DETAILS), anyString()); /* Mock we receive a second update. */ - when(mHttpClient.callAsync(anyString(), anyString(), anyMapOf(String.class, String.class), any(HttpClient.CallTemplate.class), any(ServiceCallback.class))).thenAnswer(new Answer() { + when(mHttpClient.callAsync(anyString(), anyString(), anyMap(), any(HttpClient.CallTemplate.class), any(ServiceCallback.class))).thenAnswer(new Answer() { @Override public ServiceCall answer(InvocationOnMock invocation) { @@ -1016,7 +1020,7 @@ public ServiceCall answer(InvocationOnMock invocation) { /* Trigger call. */ start(); Distribute.getInstance().onActivityResumed(mock(Activity.class)); - verify(mHttpClient).callAsync(anyString(), anyString(), anyMapOf(String.class, String.class), any(HttpClient.CallTemplate.class), any(ServiceCallback.class)); + verify(mHttpClient).callAsync(anyString(), anyString(), anyMap(), any(HttpClient.CallTemplate.class), any(ServiceCallback.class)); /* Verify prompt is shown. */ verify(mDialog).show(); @@ -1055,7 +1059,7 @@ public Void answer(InvocationOnMock invocation) { /* Mock we receive a second update. */ when(SharedPreferencesManager.getString(PREFERENCE_KEY_DISTRIBUTION_GROUP_ID)).thenReturn("some group"); when(SharedPreferencesManager.getString(PREFERENCE_KEY_UPDATE_TOKEN)).thenReturn("some token"); - when(mHttpClient.callAsync(anyString(), anyString(), anyMapOf(String.class, String.class), any(HttpClient.CallTemplate.class), any(ServiceCallback.class))).thenAnswer(new Answer() { + when(mHttpClient.callAsync(anyString(), anyString(), anyMap(), any(HttpClient.CallTemplate.class), any(ServiceCallback.class))).thenAnswer(new Answer() { @Override public ServiceCall answer(InvocationOnMock invocation) { @@ -1072,7 +1076,7 @@ public ServiceCall answer(InvocationOnMock invocation) { /* Disable SDK. */ ReleaseDownloader cleanupReleaseDownloader = mock(ReleaseDownloader.class); - when(ReleaseDownloaderFactory.create(any(Context.class), isNull(ReleaseDetails.class), any(ReleaseDownloadListener.class))).thenReturn(cleanupReleaseDownloader); + when(ReleaseDownloaderFactory.create(any(Context.class), isNull(), any())).thenReturn(cleanupReleaseDownloader); Distribute.setEnabled(false).get(); Distribute.setEnabled(true).get(); diff --git a/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/DistributeCustomizationTest.java b/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/DistributeCustomizationTest.java index e0f107e25..ab0dc0c4e 100644 --- a/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/DistributeCustomizationTest.java +++ b/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/DistributeCustomizationTest.java @@ -34,14 +34,14 @@ import static com.microsoft.appcenter.distribute.DistributeConstants.PREFERENCE_KEY_DOWNLOAD_STATE; import static com.microsoft.appcenter.distribute.DistributeConstants.PREFERENCE_KEY_POSTPONE_TIME; import static com.microsoft.appcenter.distribute.DistributeConstants.PREFERENCE_KEY_UPDATE_TOKEN; -import static org.mockito.Matchers.any; -import static org.mockito.Matchers.anyInt; -import static org.mockito.Matchers.anyLong; -import static org.mockito.Matchers.anyMapOf; -import static org.mockito.Matchers.anyString; -import static org.mockito.Matchers.contains; -import static org.mockito.Matchers.eq; -import static org.mockito.Matchers.isNull; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.anyLong; +import static org.mockito.ArgumentMatchers.anyMapOf; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.ArgumentMatchers.contains; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.ArgumentMatchers.isNull; import static org.mockito.Mockito.doNothing; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; @@ -357,7 +357,7 @@ public void handleUserUpdateActionNotProceededWithoutListener() throws Exception distribute.onActivityResumed(mActivity); /* Verify the method is called by onActivityCreated. */ - verifyStatic(times(++getStoredDownloadStateCounter)); + verifyStatic(DistributeUtils.class, times(++getStoredDownloadStateCounter)); DistributeUtils.getStoredDownloadState(); /* Disable the service. */ @@ -365,7 +365,7 @@ public void handleUserUpdateActionNotProceededWithoutListener() throws Exception /* Call handleUpdateAction. */ distribute.handleUpdateAction(UpdateAction.POSTPONE); - verifyStatic(times(++appCenterLogErrorCounter)); + verifyStatic(AppCenterLog.class, times(++appCenterLogErrorCounter)); AppCenterLog.error(anyString(), anyString()); /* mReleaseListener is null. */ @@ -373,7 +373,7 @@ public void handleUserUpdateActionNotProceededWithoutListener() throws Exception distribute.handleUpdateAction(UpdateAction.POSTPONE); /* Verify the user action has NOT been processed. */ - verifyStatic(times(++appCenterLogErrorCounter)); + verifyStatic(AppCenterLog.class, times(++appCenterLogErrorCounter)); AppCenterLog.error(anyString(), anyString()); /* Enable the service. */ @@ -382,16 +382,16 @@ public void handleUserUpdateActionNotProceededWithoutListener() throws Exception distribute.setInstanceEnabled(true); /* Verify the method is called by resumeDistributeWorkflow. */ - verifyStatic(times(++getStoredDownloadStateCounter)); + verifyStatic(DistributeUtils.class, times(++getStoredDownloadStateCounter)); DistributeUtils.getStoredDownloadState(); /* Call handleUpdateAction. */ distribute.handleUpdateAction(UpdateAction.POSTPONE); /* Verify the user action has NOT been processed. */ - verifyStatic(times(++getStoredDownloadStateCounter)); + verifyStatic(DistributeUtils.class, times(++getStoredDownloadStateCounter)); DistributeUtils.getStoredDownloadState(); - verifyStatic(times(++appCenterLogErrorCounter)); + verifyStatic(AppCenterLog.class, times(++appCenterLogErrorCounter)); AppCenterLog.error(anyString(), anyString()); /* Mock the download state to DOWNLOAD_STATE_AVAILABLE. */ @@ -401,9 +401,9 @@ public void handleUserUpdateActionNotProceededWithoutListener() throws Exception distribute.handleUpdateAction(UpdateAction.POSTPONE); /* Verify the user action has NOT been processed. */ - verifyStatic(times(++getStoredDownloadStateCounter)); + verifyStatic(DistributeUtils.class, times(++getStoredDownloadStateCounter)); DistributeUtils.getStoredDownloadState(); - verifyStatic(times(++appCenterLogErrorCounter)); + verifyStatic(AppCenterLog.class, times(++appCenterLogErrorCounter)); AppCenterLog.error(anyString(), anyString()); /* Verify again to make sure the user action has NOT been processed yet. */ @@ -432,7 +432,7 @@ public void handleUserUpdateActionNotProceededWithListener() throws Exception { distribute.onActivityResumed(mActivity); /* Verify the method is called by onActivityCreated. */ - verifyStatic(times(++getStoredDownloadStateCounter)); + verifyStatic(DistributeUtils.class, times(++getStoredDownloadStateCounter)); DistributeUtils.getStoredDownloadState(); /* Disable the service. */ @@ -442,23 +442,23 @@ public void handleUserUpdateActionNotProceededWithListener() throws Exception { distribute.handleUpdateAction(UpdateAction.POSTPONE); /* Verify the user action has NOT been processed. */ - verifyStatic(times(++appCenterLogErrorCounter)); + verifyStatic(AppCenterLog.class, times(++appCenterLogErrorCounter)); AppCenterLog.error(anyString(), anyString()); /* Enable the service. */ distribute.setInstanceEnabled(true); /* Verify the method is called by resumeDistributeWorkflow. */ - verifyStatic(times(++getStoredDownloadStateCounter)); + verifyStatic(DistributeUtils.class, times(++getStoredDownloadStateCounter)); DistributeUtils.getStoredDownloadState(); /* Call handleUpdateAction. */ distribute.handleUpdateAction(UpdateAction.POSTPONE); /* Verify the user action has NOT been processed. */ - verifyStatic(times(++getStoredDownloadStateCounter)); + verifyStatic(DistributeUtils.class, times(++getStoredDownloadStateCounter)); DistributeUtils.getStoredDownloadState(); - verifyStatic(times(++appCenterLogErrorCounter)); + verifyStatic(AppCenterLog.class, times(++appCenterLogErrorCounter)); AppCenterLog.error(anyString(), anyString()); /* Mock the download state to DOWNLOAD_STATE_AVAILABLE. */ @@ -468,9 +468,9 @@ public void handleUserUpdateActionNotProceededWithListener() throws Exception { distribute.handleUpdateAction(UpdateAction.POSTPONE); /* Verify the user action has NOT been processed. */ - verifyStatic(times(++getStoredDownloadStateCounter)); + verifyStatic(DistributeUtils.class, times(++getStoredDownloadStateCounter)); DistributeUtils.getStoredDownloadState(); - verifyStatic(times(++appCenterLogErrorCounter)); + verifyStatic(AppCenterLog.class, times(++appCenterLogErrorCounter)); AppCenterLog.error(anyString(), anyString()); /* Verify again to make sure the user action has NOT been processed yet. */ @@ -680,7 +680,7 @@ public void handleUserUpdateActionPostponeForMandatoryUpdate() throws Exception /* Verify POSTPONE has NOT been processed. */ verify(distribute, never()).completeWorkflow(); - verifyStatic(never()); + verifyStatic(SharedPreferencesManager.class, never()); SharedPreferencesManager.putLong(eq(PREFERENCE_KEY_POSTPONE_TIME), anyLong()); } @@ -739,7 +739,7 @@ public void handleUserUpdateActionInvalidUserAction() throws Exception { Distribute.notifyUpdateAction(invalidUserAction); /* Verify update has NOT been processed. */ - verifyStatic(); + verifyStatic(AppCenterLog.class); AppCenterLog.error(anyString(), contains(String.valueOf(invalidUserAction))); } diff --git a/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/DistributeDisableAutomaticCheckForUpdateTest.java b/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/DistributeDisableAutomaticCheckForUpdateTest.java index 16535e8f5..628d6af69 100644 --- a/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/DistributeDisableAutomaticCheckForUpdateTest.java +++ b/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/DistributeDisableAutomaticCheckForUpdateTest.java @@ -14,14 +14,15 @@ import java.util.Collections; -import static org.mockito.Matchers.any; -import static org.mockito.Matchers.anyString; -import static org.mockito.Matchers.eq; -import static org.mockito.Matchers.notNull; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.ArgumentMatchers.notNull; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; +import static org.powermock.api.mockito.PowerMockito.when; public class DistributeDisableAutomaticCheckForUpdateTest extends AbstractDistributeTest { @@ -91,7 +92,9 @@ public void checkForUpdateAfterManualCheckCompletesDoesNotCheckForUpdatesAgain() verify(mHttpClient).callAsync(anyString(), anyString(), eq(Collections.emptyMap()), any(HttpClient.CallTemplate.class), httpCallback.capture()); /* Complete call with no new release (this will return the default mock mReleaseDetails with version 0). */ - httpCallback.getValue().onCallSucceeded(mock(HttpResponse.class)); + HttpResponse response = mock(HttpResponse.class); + when(response.getPayload()).thenReturn(""); + httpCallback.getValue().onCallSucceeded(response); /* Restart. */ restartResumeLauncher(mActivity); @@ -118,7 +121,9 @@ public void manualCheckForUpdateAfterManualCheckCompletesChecksForUpdateAgain() verify(mHttpClient).callAsync(anyString(), anyString(), eq(Collections.emptyMap()), any(HttpClient.CallTemplate.class), httpCallback.capture()); /* Complete call with no new release (this will return the default mock mReleaseDetails with version 0). */ - httpCallback.getValue().onCallSucceeded(mock(HttpResponse.class)); + HttpResponse response = mock(HttpResponse.class); + when(response.getPayload()).thenReturn(""); + httpCallback.getValue().onCallSucceeded(response); /* Manually check for updates again. */ Distribute.checkForUpdate(); diff --git a/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/DistributeHttpTest.java b/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/DistributeHttpTest.java index 997609a7e..d0367f6c7 100644 --- a/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/DistributeHttpTest.java +++ b/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/DistributeHttpTest.java @@ -5,6 +5,17 @@ package com.microsoft.appcenter.distribute; +import static com.microsoft.appcenter.distribute.DistributeConstants.HEADER_API_TOKEN; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyMap; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.ArgumentMatchers.contains; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.when; +import static org.powermock.api.mockito.PowerMockito.mock; +import static org.powermock.api.mockito.PowerMockito.mockStatic; +import static org.powermock.api.mockito.PowerMockito.verifyStatic; + import android.util.Log; import com.microsoft.appcenter.channel.Channel; @@ -13,10 +24,8 @@ import com.microsoft.appcenter.http.ServiceCall; import com.microsoft.appcenter.http.ServiceCallback; import com.microsoft.appcenter.utils.AppCenterLog; -import com.microsoft.appcenter.utils.storage.SharedPreferencesManager; import org.junit.Assert; -import org.junit.Before; import org.junit.Test; import org.mockito.invocation.InvocationOnMock; import org.mockito.stubbing.Answer; @@ -27,19 +36,6 @@ import java.util.UUID; import java.util.concurrent.atomic.AtomicReference; -import static com.microsoft.appcenter.distribute.DistributeConstants.HEADER_API_TOKEN; -import static com.microsoft.appcenter.utils.PrefStorageConstants.ALLOWED_NETWORK_REQUEST; -import static org.mockito.Matchers.any; -import static org.mockito.Matchers.anyMapOf; -import static org.mockito.Matchers.anyString; -import static org.mockito.Matchers.contains; -import static org.mockito.Mockito.never; -import static org.mockito.Mockito.when; -import static org.powermock.api.mockito.PowerMockito.mock; -import static org.powermock.api.mockito.PowerMockito.mockStatic; -import static org.powermock.api.mockito.PowerMockito.verifyStatic; - -@SuppressWarnings("unused") public class DistributeHttpTest extends AbstractDistributeTest { @Test @@ -66,17 +62,14 @@ public void onBeforeCallingWithToken() throws Exception { callTemplate.onBeforeCalling(url, headers); /* Verify url log. */ - verifyStatic(); + verifyStatic(AppCenterLog.class); AppCenterLog.verbose(anyString(), contains(obfuscatedUrlString)); /* Verify header logs. */ for (Map.Entry header : headers.entrySet()) { - verifyStatic(); - if (header.getKey().equals(HEADER_API_TOKEN)) { - AppCenterLog.verbose(anyString(), contains(obfuscatedToken)); - } else { - AppCenterLog.verbose(anyString(), contains(header.getValue())); - } + String expected = header.getKey().equals(HEADER_API_TOKEN) ? obfuscatedToken : header.getValue(); + verifyStatic(AppCenterLog.class); + AppCenterLog.verbose(anyString(), contains(expected)); } } @@ -98,12 +91,12 @@ public void onBeforeCallingWithoutToken() throws Exception { callTemplate.onBeforeCalling(url, headers); /* Verify url log. */ - verifyStatic(); + verifyStatic(AppCenterLog.class); AppCenterLog.verbose(anyString(), contains(obfuscatedUrlString)); /* Verify header log. */ for (Map.Entry header : headers.entrySet()) { - verifyStatic(); + verifyStatic(AppCenterLog.class); AppCenterLog.verbose(anyString(), contains(header.getValue())); } } @@ -124,7 +117,7 @@ public void onBeforeCallingWithAnotherLogLevel() { callTemplate.onBeforeCalling(mock(URL.class), mock(Map.class)); /* Verify. */ - verifyStatic(never()); + verifyStatic(AppCenterLog.class, never()); AppCenterLog.verbose(anyString(), anyString()); } @@ -147,7 +140,7 @@ private HttpClient.CallTemplate getCallTemplate(String appSecret, String apiToke Distribute.getInstance().onStarted(mContext, mock(Channel.class), appSecret, null, true); final ServiceCall call = mock(ServiceCall.class); final AtomicReference callTemplate = new AtomicReference<>(); - when(mHttpClient.callAsync(anyString(), anyString(), anyMapOf(String.class, String.class), any(HttpClient.CallTemplate.class), any(ServiceCallback.class))).then(new Answer() { + when(mHttpClient.callAsync(anyString(), anyString(), anyMap(), any(HttpClient.CallTemplate.class), any(ServiceCallback.class))).then(new Answer() { @Override public ServiceCall answer(InvocationOnMock invocation) { diff --git a/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/DistributeManualCheckForUpdateTest.java b/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/DistributeManualCheckForUpdateTest.java index b5a7624f4..98aed2284 100644 --- a/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/DistributeManualCheckForUpdateTest.java +++ b/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/DistributeManualCheckForUpdateTest.java @@ -14,14 +14,15 @@ import java.util.Collections; -import static org.mockito.Matchers.any; -import static org.mockito.Matchers.anyString; -import static org.mockito.Matchers.eq; -import static org.mockito.Matchers.notNull; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.ArgumentMatchers.notNull; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; +import static org.powermock.api.mockito.PowerMockito.when; public class DistributeManualCheckForUpdateTest extends AbstractDistributeTest { @@ -34,16 +35,18 @@ public void checkForUpdateAfterWorkflowCompletesChecksAgain() { /* Check http call done. */ ArgumentCaptor httpCallback = ArgumentCaptor.forClass(ServiceCallback.class); - verify(mHttpClient).callAsync(anyString(), anyString(), eq(Collections.emptyMap()), any(HttpClient.CallTemplate.class), httpCallback.capture()); + verify(mHttpClient).callAsync(anyString(), anyString(), eq(Collections.emptyMap()), any(HttpClient.CallTemplate.class), httpCallback.capture()); /* Complete call with no new release (this will return the default mock mReleaseDetails with version 0). */ - httpCallback.getValue().onCallSucceeded(mock(HttpResponse.class)); + HttpResponse response = mock(HttpResponse.class); + when(response.getPayload()).thenReturn(""); + httpCallback.getValue().onCallSucceeded(response); /* If checking for updates again. */ Distribute.checkForUpdate(); /* Then we call again. */ - verify(mHttpClient, times(2)).callAsync(anyString(), anyString(), eq(Collections.emptyMap()), any(HttpClient.CallTemplate.class), notNull(ServiceCallback.class)); + verify(mHttpClient, times(2)).callAsync(anyString(), anyString(), eq(Collections.emptyMap()), any(HttpClient.CallTemplate.class), notNull(ServiceCallback.class)); } @Test @@ -55,17 +58,19 @@ public void checkForUpdateBeforeCallCompletes() { /* Check http call done. */ ArgumentCaptor httpCallback = ArgumentCaptor.forClass(ServiceCallback.class); - verify(mHttpClient).callAsync(anyString(), anyString(), eq(Collections.emptyMap()), any(HttpClient.CallTemplate.class), httpCallback.capture()); + verify(mHttpClient).callAsync(anyString(), anyString(), eq(Collections.emptyMap()), any(HttpClient.CallTemplate.class), httpCallback.capture()); /* If checking for updates again before call completes. */ Distribute.checkForUpdate(); /* Then we don't call again. */ - verify(mHttpClient).callAsync(anyString(), anyString(), eq(Collections.emptyMap()), any(HttpClient.CallTemplate.class), notNull(ServiceCallback.class)); + verify(mHttpClient).callAsync(anyString(), anyString(), eq(Collections.emptyMap()), any(HttpClient.CallTemplate.class), notNull(ServiceCallback.class)); /* And it's not queued when current call finishes with no update available. */ - httpCallback.getValue().onCallSucceeded(mock(HttpResponse.class)); - verify(mHttpClient).callAsync(anyString(), anyString(), eq(Collections.emptyMap()), any(HttpClient.CallTemplate.class), notNull(ServiceCallback.class)); + HttpResponse response = mock(HttpResponse.class); + when(response.getPayload()).thenReturn(""); + httpCallback.getValue().onCallSucceeded(response); + verify(mHttpClient).callAsync(anyString(), anyString(), eq(Collections.emptyMap()), any(HttpClient.CallTemplate.class), notNull(ServiceCallback.class)); } @Test @@ -80,6 +85,6 @@ public void checkForUpdatesWhenDisabledDoesNotWork() { Distribute.checkForUpdate(); /* No HTTP call done. */ - verify(mHttpClient, never()).callAsync(anyString(), anyString(), eq(Collections.emptyMap()), any(HttpClient.CallTemplate.class), any(ServiceCallback.class)); + verify(mHttpClient, never()).callAsync(anyString(), anyString(), eq(Collections.emptyMap()), any(HttpClient.CallTemplate.class), any(ServiceCallback.class)); } } diff --git a/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/DistributePlusDownloadReceiverTest.java b/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/DistributePlusDownloadReceiverTest.java index 415bb8081..a572763ed 100644 --- a/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/DistributePlusDownloadReceiverTest.java +++ b/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/DistributePlusDownloadReceiverTest.java @@ -14,7 +14,7 @@ import static android.app.DownloadManager.ACTION_NOTIFICATION_CLICKED; import static com.microsoft.appcenter.distribute.DistributeConstants.PREFERENCE_KEY_UPDATE_TOKEN; -import static org.mockito.Matchers.eq; +import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; diff --git a/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/DistributeTest.java b/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/DistributeTest.java index 3d7475479..3729fc411 100644 --- a/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/DistributeTest.java +++ b/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/DistributeTest.java @@ -5,8 +5,42 @@ package com.microsoft.appcenter.distribute; +import static android.content.Context.NOTIFICATION_SERVICE; +import static android.content.pm.ApplicationInfo.FLAG_DEBUGGABLE; +import static com.microsoft.appcenter.distribute.DistributeConstants.DOWNLOAD_STATE_COMPLETED; +import static com.microsoft.appcenter.distribute.DistributeConstants.DOWNLOAD_STATE_ENQUEUED; +import static com.microsoft.appcenter.distribute.DistributeConstants.DOWNLOAD_STATE_INSTALLING; +import static com.microsoft.appcenter.distribute.DistributeConstants.DOWNLOAD_STATE_NOTIFIED; +import static com.microsoft.appcenter.distribute.DistributeConstants.LOG_TAG; +import static com.microsoft.appcenter.distribute.DistributeConstants.PREFERENCE_KEY_DOWNLOADED_DISTRIBUTION_GROUP_ID; +import static com.microsoft.appcenter.distribute.DistributeConstants.PREFERENCE_KEY_DOWNLOADED_RELEASE_HASH; +import static com.microsoft.appcenter.distribute.DistributeConstants.PREFERENCE_KEY_DOWNLOADED_RELEASE_ID; +import static com.microsoft.appcenter.distribute.DistributeConstants.PREFERENCE_KEY_DOWNLOAD_STATE; +import static com.microsoft.appcenter.distribute.DistributeConstants.PREFERENCE_KEY_DOWNLOAD_TIME; +import static com.microsoft.appcenter.distribute.DistributeConstants.PREFERENCE_KEY_RELEASE_DETAILS; +import static com.microsoft.appcenter.distribute.DistributeConstants.PREFERENCE_KEY_UPDATE_SETUP_FAILED_MESSAGE_KEY; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertSame; +import static org.junit.Assert.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.RETURNS_MOCKS; +import static org.mockito.Mockito.doAnswer; +import static org.mockito.Mockito.doNothing; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; +import static org.powermock.api.mockito.PowerMockito.mockStatic; +import static org.powermock.api.mockito.PowerMockito.verifyStatic; +import static org.powermock.api.mockito.PowerMockito.whenNew; + import android.app.Activity; -import android.app.Dialog; import android.app.Notification; import android.app.NotificationManager; import android.app.ProgressDialog; @@ -36,57 +70,21 @@ import com.microsoft.appcenter.utils.storage.SharedPreferencesManager; import org.junit.After; -import org.junit.Before; import org.junit.Test; import org.mockito.ArgumentCaptor; -import org.mockito.Matchers; import org.mockito.Mockito; -import org.mockito.internal.util.reflection.Whitebox; import org.mockito.invocation.InvocationOnMock; import org.mockito.stubbing.Answer; import org.powermock.core.classloader.annotations.PrepareForTest; +import org.powermock.reflect.Whitebox; import java.util.Collections; import java.util.Map; -import static android.content.Context.NOTIFICATION_SERVICE; -import static android.content.pm.ApplicationInfo.FLAG_DEBUGGABLE; -import static com.microsoft.appcenter.distribute.DistributeConstants.DOWNLOAD_STATE_COMPLETED; -import static com.microsoft.appcenter.distribute.DistributeConstants.DOWNLOAD_STATE_ENQUEUED; -import static com.microsoft.appcenter.distribute.DistributeConstants.DOWNLOAD_STATE_INSTALLING; -import static com.microsoft.appcenter.distribute.DistributeConstants.DOWNLOAD_STATE_NOTIFIED; -import static com.microsoft.appcenter.distribute.DistributeConstants.LOG_TAG; -import static com.microsoft.appcenter.distribute.DistributeConstants.PREFERENCE_KEY_DOWNLOADED_DISTRIBUTION_GROUP_ID; -import static com.microsoft.appcenter.distribute.DistributeConstants.PREFERENCE_KEY_DOWNLOADED_RELEASE_HASH; -import static com.microsoft.appcenter.distribute.DistributeConstants.PREFERENCE_KEY_DOWNLOADED_RELEASE_ID; -import static com.microsoft.appcenter.distribute.DistributeConstants.PREFERENCE_KEY_DOWNLOAD_STATE; -import static com.microsoft.appcenter.distribute.DistributeConstants.PREFERENCE_KEY_DOWNLOAD_TIME; -import static com.microsoft.appcenter.distribute.DistributeConstants.PREFERENCE_KEY_RELEASE_DETAILS; -import static com.microsoft.appcenter.distribute.DistributeConstants.PREFERENCE_KEY_UPDATE_SETUP_FAILED_MESSAGE_KEY; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertSame; -import static org.junit.Assert.assertTrue; -import static org.mockito.BDDMockito.willThrow; -import static org.mockito.Matchers.any; -import static org.mockito.Matchers.anyInt; -import static org.mockito.Matchers.anyString; -import static org.mockito.Matchers.eq; -import static org.mockito.Mockito.RETURNS_MOCKS; -import static org.mockito.Mockito.atLeastOnce; -import static org.mockito.Mockito.doAnswer; -import static org.mockito.Mockito.doNothing; -import static org.mockito.Mockito.doReturn; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.never; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; -import static org.powermock.api.mockito.PowerMockito.mockStatic; -import static org.powermock.api.mockito.PowerMockito.verifyStatic; -import static org.powermock.api.mockito.PowerMockito.whenNew; - -@PrepareForTest({DistributeUtils.class, HttpUtils.class, DeviceInfoHelper.class}) +@PrepareForTest({ + DistributeUtils.class, + DeviceInfoHelper.class +}) public class DistributeTest extends AbstractDistributeTest { private static final String DISTRIBUTION_GROUP_ID = "group_id"; @@ -237,9 +235,9 @@ public void setDownloadingReleaseDetailsEqualTest() { long mockTime = 1000000; Distribute.getInstance().setDownloading(mReleaseDetails, mockTime); - verifyStatic(); + verifyStatic(SharedPreferencesManager.class); SharedPreferencesManager.putInt(eq(PREFERENCE_KEY_DOWNLOAD_STATE), eq(DOWNLOAD_STATE_ENQUEUED)); - verifyStatic(); + verifyStatic(SharedPreferencesManager.class); SharedPreferencesManager.putLong(eq(PREFERENCE_KEY_DOWNLOAD_TIME), eq(mockTime)); } @@ -247,7 +245,7 @@ public void setDownloadingReleaseDetailsEqualTest() { public void startFromBackgroundTwice() { start(); Distribute.getInstance().startFromBackground(mContext); - verifyStatic(never()); + verifyStatic(SharedPreferencesManager.class, never()); SharedPreferencesManager.initialize(mContext); } @@ -255,18 +253,18 @@ public void startFromBackgroundTwice() { public void setDownloadingReleaseDetailsNotEqualTest() { long mockTime = 1000000; Distribute.getInstance().setDownloading(mReleaseDetails, mockTime); - verifyStatic(never()); + verifyStatic(SharedPreferencesManager.class, never()); SharedPreferencesManager.putInt(eq(PREFERENCE_KEY_DOWNLOAD_STATE), eq(DOWNLOAD_STATE_ENQUEUED)); - verifyStatic(never()); + verifyStatic(SharedPreferencesManager.class, never()); SharedPreferencesManager.putLong(eq(PREFERENCE_KEY_DOWNLOAD_TIME), eq(mockTime)); } @Test public void setInstallingReleaseDetailsNotEqualTest() { Distribute.getInstance().setInstalling(mReleaseDetails); - verifyStatic(never()); + verifyStatic(SharedPreferencesManager.class, never()); SharedPreferencesManager.getInt(eq(PREFERENCE_KEY_DOWNLOAD_STATE), eq(DOWNLOAD_STATE_COMPLETED)); - verifyStatic(never()); + verifyStatic(SharedPreferencesManager.class, never()); SharedPreferencesManager.remove(eq(PREFERENCE_KEY_RELEASE_DETAILS)); } @@ -279,11 +277,11 @@ public void setInstallingTest() { Distribute.getInstance().setInstalling(mReleaseDetails); /* Verify. */ - verifyStatic(); + verifyStatic(SharedPreferencesManager.class); SharedPreferencesManager.remove(eq(PREFERENCE_KEY_RELEASE_DETAILS)); - verifyStatic(); + verifyStatic(SharedPreferencesManager.class); SharedPreferencesManager.remove(eq(PREFERENCE_KEY_DOWNLOAD_STATE)); - verifyStatic(never()); + verifyStatic(SharedPreferencesManager.class, never()); SharedPreferencesManager.putInt(eq(PREFERENCE_KEY_DOWNLOAD_STATE), eq(DOWNLOAD_STATE_INSTALLING)); verifyReleaseDetailsAreStored(); } @@ -297,9 +295,9 @@ public void setInstallingMandatoryReleaseDetailsTest() { Distribute.getInstance().setInstalling(mReleaseDetails); /* Verify. */ - verifyStatic(); + verifyStatic(DistributeUtils.class); DistributeUtils.getStoredDownloadState(); - verifyStatic(); + verifyStatic(SharedPreferencesManager.class); SharedPreferencesManager.putInt(eq(PREFERENCE_KEY_DOWNLOAD_STATE), eq(DOWNLOAD_STATE_INSTALLING)); verifyReleaseDetailsAreStored(); } @@ -314,11 +312,11 @@ private void mockReleaseDetails(boolean mandatoryUpdate) { } private void verifyReleaseDetailsAreStored() { - verifyStatic(); + verifyStatic(SharedPreferencesManager.class); SharedPreferencesManager.putString(eq(PREFERENCE_KEY_DOWNLOADED_DISTRIBUTION_GROUP_ID), eq(DISTRIBUTION_GROUP_ID)); - verifyStatic(); + verifyStatic(SharedPreferencesManager.class); SharedPreferencesManager.putString(eq(PREFERENCE_KEY_DOWNLOADED_RELEASE_HASH), eq(RELEASE_HASH)); - verifyStatic(); + verifyStatic(SharedPreferencesManager.class); SharedPreferencesManager.putInt(eq(PREFERENCE_KEY_DOWNLOADED_RELEASE_ID), eq(RELEASE_ID)); } @@ -419,7 +417,7 @@ public void updateReleaseDetailsFromBackground() { /* mReleaseDownloader is null and is created. */ Distribute.getInstance().startFromBackground(mContext); - verifyStatic(); + verifyStatic(ReleaseDownloaderFactory.class); ReleaseDownloaderFactory.create(any(Context.class), any(ReleaseDetails.class), any(ReleaseDownloader.Listener.class)); /* mReleaseDetails not null but id is not equal to mReleaseDownloader details id. */ @@ -442,11 +440,11 @@ public void discardDownloadAsAppUpdateTest() { resumeWorkflow(mock(Activity.class)); /* Verify that previous tasks are cancelled. */ - verifyStatic(); + verifyStatic(SharedPreferencesManager.class); SharedPreferencesManager.remove(eq(PREFERENCE_KEY_RELEASE_DETAILS)); - verifyStatic(); + verifyStatic(SharedPreferencesManager.class); SharedPreferencesManager.remove(eq(PREFERENCE_KEY_DOWNLOAD_STATE)); - verifyStatic(); + verifyStatic(SharedPreferencesManager.class); SharedPreferencesManager.remove(eq(PREFERENCE_KEY_DOWNLOAD_TIME)); } @@ -524,7 +522,7 @@ public void doNotShowUpdateSetupFailedDialogInBackground() { /* Verify dialog. */ verify(mDialogBuilder, never()).create(); - verifyStatic(never()); + verifyStatic(SharedPreferencesManager.class, never()); SharedPreferencesManager.remove(PREFERENCE_KEY_UPDATE_SETUP_FAILED_MESSAGE_KEY); } @@ -538,7 +536,7 @@ public void showUpdateSetupFailedDialogInForeground() { /* Verify dialog. */ verify(mDialogBuilder).create(); - verifyStatic(); + verifyStatic(SharedPreferencesManager.class); SharedPreferencesManager.remove(PREFERENCE_KEY_UPDATE_SETUP_FAILED_MESSAGE_KEY); } @@ -683,22 +681,23 @@ public void getLastReleaseDetailsWithDifferentHttpClients() { DependencyConfiguration.setHttpClient(mockHttpClient); mockStatic(DistributeUtils.class); when(DistributeUtils.computeReleaseHash(any(PackageInfo.class))).thenReturn("mock-hash"); + Distribute.getInstance().startFromBackground(mContext); /* Call get last release details. */ - Distribute.getInstance().getLatestReleaseDetails(anyString(), anyString()); + Distribute.getInstance().getLatestReleaseDetails(DISTRIBUTION_GROUP_ID, "token"); /* Verify. */ - verifyStatic(never()); + verifyStatic(HttpUtils.class, never()); HttpUtils.createHttpClient(any(Context.class)); /* Clear http client. */ DependencyConfiguration.setHttpClient(null); /* Call get last release details. */ - Distribute.getInstance().getLatestReleaseDetails(anyString(), anyString()); + Distribute.getInstance().getLatestReleaseDetails(DISTRIBUTION_GROUP_ID, "token"); /* Verify. */ - verifyStatic(); + verifyStatic(HttpUtils.class); HttpUtils.createHttpClient(any(Context.class)); } @@ -770,7 +769,7 @@ public ServiceCall answer(InvocationOnMock invocationOnMock) { ((ServiceCallback) invocationOnMock.getArguments()[4]).onCallSucceeded(new HttpResponse(200, "")); return call; } - }).when(mHttpClient).callAsync(anyString(), anyString(), eq(Collections.emptyMap()), any(HttpClient.CallTemplate.class), any(ServiceCallback.class)); + }).when(mHttpClient).callAsync(anyString(), anyString(), eq(Collections.emptyMap()), any(HttpClient.CallTemplate.class), any(ServiceCallback.class)); /* Starting distribute. */ Distribute.getInstance().onStarting(mAppCenterHandler); @@ -780,7 +779,7 @@ public ServiceCall answer(InvocationOnMock invocationOnMock) { Distribute.getInstance().onActivityResumed(mActivity); /* Verify download is not checked after we reset workflow. */ - verify(mHttpClient, never()).callAsync(anyString(), anyString(), eq(Collections.emptyMap()), any(HttpClient.CallTemplate.class), any(ServiceCallback.class)); + verify(mHttpClient, never()).callAsync(anyString(), anyString(), eq(Collections.emptyMap()), any(HttpClient.CallTemplate.class), any(ServiceCallback.class)); } @Test @@ -797,7 +796,7 @@ public ServiceCall answer(InvocationOnMock invocationOnMock) { ((ServiceCallback) invocationOnMock.getArguments()[4]).onCallSucceeded(new HttpResponse(200, "")); return call; } - }).when(mHttpClient).callAsync(anyString(), anyString(), eq(Collections.emptyMap()), any(HttpClient.CallTemplate.class), any(ServiceCallback.class)); + }).when(mHttpClient).callAsync(anyString(), anyString(), eq(Collections.emptyMap()), any(HttpClient.CallTemplate.class), any(ServiceCallback.class)); /* Start distribute. */ start(); @@ -807,21 +806,21 @@ public ServiceCall answer(InvocationOnMock invocationOnMock) { Distribute.getInstance().onActivityResumed(mActivity); /* Verify download is checked after we reset workflow. */ - verify(mHttpClient).callAsync(anyString(), anyString(), eq(Collections.emptyMap()), any(HttpClient.CallTemplate.class), any(ServiceCallback.class)); + verify(mHttpClient).callAsync(anyString(), anyString(), eq(Collections.emptyMap()), any(HttpClient.CallTemplate.class), any(ServiceCallback.class)); /* Stop activity. */ Distribute.getInstance().onActivityPaused(mActivity); Distribute.getInstance().onApplicationEnterBackground(); /* Verify that all calls were completed. */ - verify(mHttpClient).callAsync(anyString(), anyString(), eq(Collections.emptyMap()), any(HttpClient.CallTemplate.class), any(ServiceCallback.class)); + verify(mHttpClient).callAsync(anyString(), anyString(), eq(Collections.emptyMap()), any(HttpClient.CallTemplate.class), any(ServiceCallback.class)); /* Enter foreground again. */ Distribute.getInstance().onApplicationEnterForeground(); Distribute.getInstance().onActivityResumed(mActivity); /* Verify download is checked after we reset workflow again. */ - verify(mHttpClient, times(2)).callAsync(anyString(), anyString(), eq(Collections.emptyMap()), any(HttpClient.CallTemplate.class), any(ServiceCallback.class)); + verify(mHttpClient, times(2)).callAsync(anyString(), anyString(), eq(Collections.emptyMap()), any(HttpClient.CallTemplate.class), any(ServiceCallback.class)); } @Test @@ -843,17 +842,19 @@ public void checkUpdateReleaseAfterInterruptDownloading() { /* Verify that check release for update was called. */ ArgumentCaptor httpCallback = ArgumentCaptor.forClass(ServiceCallback.class); - verify(mHttpClient).callAsync(anyString(), anyString(), eq(Collections.emptyMap()), any(HttpClient.CallTemplate.class), httpCallback.capture()); + verify(mHttpClient).callAsync(anyString(), anyString(), eq(Collections.emptyMap()), any(HttpClient.CallTemplate.class), httpCallback.capture()); /* Complete the first call. */ - httpCallback.getValue().onCallSucceeded(mock(HttpResponse.class)); + HttpResponse response = mock(HttpResponse.class); + when(response.getPayload()).thenReturn(""); + httpCallback.getValue().onCallSucceeded(response); /* Disable and enable distribute module. */ Distribute.setEnabled(false); Distribute.setEnabled(true); /* Verify that check release for update was called again. */ - verify(mHttpClient, times(2)).callAsync(anyString(), anyString(), eq(Collections.emptyMap()), any(HttpClient.CallTemplate.class), httpCallback.capture()); + verify(mHttpClient, times(2)).callAsync(anyString(), anyString(), eq(Collections.emptyMap()), any(HttpClient.CallTemplate.class), httpCallback.capture()); } @Test @@ -864,33 +865,33 @@ public void checkRegisterAndUnregisterReceiver() { /* Verify that when distribute disabled no receivers was registered. */ Distribute.getInstance().onActivityStarted(mActivity); Distribute.getInstance().onActivityResumed(mActivity); - verify(mContext, never()).registerReceiver(Matchers.any(), Matchers.any()); + verify(mContext, never()).registerReceiver(any(BroadcastReceiver.class), any(IntentFilter.class)); /* Start distribute. */ start(); /* Check that receiver was registered. */ - verify(mContext).registerReceiver(Matchers.any(), Matchers.any()); + verify(mContext).registerReceiver(any(BroadcastReceiver.class), any(IntentFilter.class)); /* Stop activity. */ Distribute.getInstance().onActivityPaused(mActivity); Distribute.getInstance().onActivityStopped(mActivity); /* Check that receiver was not unregistered on activity pause and stop. */ - verify(mContext, never()).unregisterReceiver(Matchers.any()); + verify(mContext, never()).unregisterReceiver(any(BroadcastReceiver.class)); /* Resume activity */ Distribute.getInstance().onActivityStarted(mActivity); Distribute.getInstance().onActivityResumed(mActivity); /* Verify that register receiver was called again after activity is resumed. */ - verify(mContext).registerReceiver(Matchers.any(), Matchers.any()); + verify(mContext).registerReceiver(any(BroadcastReceiver.class), any(IntentFilter.class)); /* Disable Distribute. */ Distribute.setEnabled(false); /* Check that receiver was unregistered. */ - verify(mContext).unregisterReceiver(Matchers.any()); + verify(mContext).unregisterReceiver(any(BroadcastReceiver.class)); } @Test @@ -900,14 +901,14 @@ public void checkRegisterReceiverWhenActivityNull() { start(); /* Verify that receiver was not registered. */ - verifyStatic(); + verifyStatic(AppCenterLog.class); AppCenterLog.warn(eq(LOG_TAG), eq("Couldn't register receiver due to activity is null.")); /* Disable Distribute. */ Distribute.setEnabled(false); /* Verify that receiver was not registered. */ - verifyStatic(); + verifyStatic(AppCenterLog.class); AppCenterLog.warn(eq(LOG_TAG), eq("Couldn't unregister due to activity is null.")); } @@ -921,7 +922,7 @@ public void checkProgressWhenActivityNull() { Distribute.getInstance().notifyInstallProgress(true); /* Verify that progress dialog was not trying to display. */ - verifyStatic(); + verifyStatic(AppCenterLog.class); AppCenterLog.warn(eq(LOG_TAG), eq("Could not display install progress dialog in the background.")); } @@ -957,7 +958,7 @@ public void checkInstallProgressState() { /* Start distribute. */ Distribute.getInstance().onStarting(mAppCenterHandler); Distribute.getInstance().onStarted(mContext, mChannel, null, null, true); - when(mReleaseInstallerListener.showInstallProgressDialog(Matchers.any())).thenReturn(mDialog); + when(mReleaseInstallerListener.showInstallProgressDialog(any(Activity.class))).thenReturn(mDialog); /* Resume activity. */ Distribute.getInstance().onActivityResumed(mActivity); @@ -966,7 +967,7 @@ public void checkInstallProgressState() { Distribute.getInstance().startFromBackground(mContext); Distribute.getInstance().notifyInstallProgress(true); verify(mReleaseInstallerListener).hideInstallProgressDialog(); - verify(mReleaseInstallerListener).showInstallProgressDialog(Matchers.any()); + verify(mReleaseInstallerListener).showInstallProgressDialog(any(Activity.class)); /* Stop installing and verify thad dialog was hide. */ Distribute.getInstance().notifyInstallProgress(false); @@ -980,7 +981,7 @@ public void showSystemSettingsDialogWhenPackageInstallerNull() { Distribute.getInstance().showSystemSettingsDialogOrStartInstalling(1L, 1L); /* Verify that log was called. */ - verifyStatic(); + verifyStatic(AppCenterLog.class); AppCenterLog.debug(eq(LOG_TAG), eq("Installing couldn't start due to the release installer wasn't initialized.")); } @@ -1028,7 +1029,7 @@ public void showUpdateDialogAfterShowingInstallReleaseDialogTest() { resumeWorkflow(mActivity); /* Verify that SDK wasn't crashed with NPE and showed dialog. */ - verifyStatic(); + verifyStatic(AppCenterLog.class); AppCenterLog.debug(eq(LOG_TAG), eq("Show default update dialog.")); } diff --git a/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/DistributeUpdateTrackTest.java b/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/DistributeUpdateTrackTest.java index 3cc9cbb8b..3a561c03e 100644 --- a/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/DistributeUpdateTrackTest.java +++ b/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/DistributeUpdateTrackTest.java @@ -14,13 +14,14 @@ import org.junit.Test; import org.mockito.ArgumentCaptor; +import org.powermock.api.mockito.PowerMockito; import java.util.Collections; import static org.junit.Assert.assertEquals; -import static org.mockito.Matchers.any; -import static org.mockito.Matchers.anyString; -import static org.mockito.Matchers.eq; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; @@ -75,15 +76,17 @@ public void switchTrack() throws PackageManager.NameNotFoundException { verify(mHttpClient).callAsync(anyString(), anyString(), eq(Collections.emptyMap()), any(HttpClient.CallTemplate.class), httpCallback.capture()); /* Without browser because its public. */ - verifyStatic(never()); + verifyStatic(BrowserUtils.class, never()); BrowserUtils.openBrowser(anyString(), any(Activity.class)); /* Complete call with no new release (this will return the default mock mReleaseDetails with version 0). */ - httpCallback.getValue().onCallSucceeded(mock(HttpResponse.class)); + HttpResponse response = mock(HttpResponse.class); + PowerMockito.when(response.getPayload()).thenReturn(""); + httpCallback.getValue().onCallSucceeded(response); /* If we switch to private, browser must not open. We disallow changes. */ Distribute.setUpdateTrack(UpdateTrack.PRIVATE); - verifyStatic(never()); + verifyStatic(BrowserUtils.class, never()); BrowserUtils.openBrowser(anyString(), eq(mActivity)); } @@ -104,7 +107,7 @@ public void switchTrackBeforeForeground() throws PackageManager.NameNotFoundExce Distribute.getInstance().onActivityResumed(mActivity); /* Verify browser is not opened. Changes are allowed only before start. */ - verifyStatic(never()); + verifyStatic(BrowserUtils.class, never()); BrowserUtils.openBrowser(anyString(), eq(mActivity)); } } diff --git a/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/DistributeUtilsTest.java b/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/DistributeUtilsTest.java index fd8a34f24..6b400ea6a 100644 --- a/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/DistributeUtilsTest.java +++ b/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/DistributeUtilsTest.java @@ -18,8 +18,8 @@ import static com.microsoft.appcenter.distribute.DistributeConstants.PREFERENCE_KEY_RELEASE_DETAILS; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNull; -import static org.mockito.Matchers.anyString; -import static org.mockito.Matchers.eq; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.when; @@ -42,6 +42,7 @@ public void setUp() { mockStatic(ReleaseDetails.class); } + @SuppressWarnings("InstantiationOfUtilityClass") @Test public void init() { new DistributeUtils(); @@ -67,7 +68,7 @@ public void loadCachedReleaseDetails() throws JSONException { /* Verify. */ assertEquals(mock, releaseDetails); - verifyStatic(never()); + verifyStatic(SharedPreferencesManager.class, never()); SharedPreferencesManager.remove(eq(PREFERENCE_KEY_RELEASE_DETAILS)); } @@ -81,7 +82,7 @@ public void loadCachedReleaseDetailsNull() { /* Verify. */ assertNull(releaseDetails); - verifyStatic(never()); + verifyStatic(SharedPreferencesManager.class, never()); SharedPreferencesManager.remove(eq(PREFERENCE_KEY_RELEASE_DETAILS)); } @@ -95,7 +96,7 @@ public void loadCachedReleaseDetailsJsonException() throws JSONException { /* Verify. */ assertNull(releaseDetails); - verifyStatic(); + verifyStatic(SharedPreferencesManager.class); SharedPreferencesManager.remove(eq(PREFERENCE_KEY_RELEASE_DETAILS)); } } diff --git a/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/DistributeWarnAlertSystemWindowsTest.java b/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/DistributeWarnAlertSystemWindowsTest.java index 42e0dc92c..f4ef6f9dd 100644 --- a/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/DistributeWarnAlertSystemWindowsTest.java +++ b/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/DistributeWarnAlertSystemWindowsTest.java @@ -8,9 +8,9 @@ import static android.content.Context.NOTIFICATION_SERVICE; import static com.microsoft.appcenter.distribute.DistributeConstants.DOWNLOAD_STATE_NOTIFIED; import static com.microsoft.appcenter.distribute.DistributeConstants.LOG_TAG; -import static org.mockito.Matchers.any; -import static org.mockito.Matchers.anyLong; -import static org.mockito.Matchers.eq; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyLong; +import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.RETURNS_MOCKS; import static org.mockito.Mockito.doCallRealMethod; import static org.mockito.Mockito.never; @@ -171,7 +171,7 @@ public void showAndEnableAlertWindowsDialogQ() { when(InstallerUtils.isSystemAlertWindowsEnabled(any(Context.class))).thenReturn(true); /* Verify that after resume do nothing. */ - verifyStatic(); + verifyStatic(AppCenterLog.class); AppCenterLog.info(eq(LOG_TAG), eq("Installing in progress...")); } @@ -296,7 +296,7 @@ public void returningToDialogFromBackgroundWhenInstallerIsNull() { Distribute.getInstance().onActivityResumed(mActivity); /* Verify that installation process is trying to resume but installer is null because release details was not loaded. */ - verifyStatic(); + verifyStatic(AppCenterLog.class); AppCenterLog.debug(eq(LOG_TAG), eq("Installing couldn't start due to the release installer wasn't initialized.")); } @@ -326,7 +326,7 @@ public void showSettingsDialogWhenActivityIsNullOnAndroidQ() throws Exception { verify(mAlertWindowsDialog, never()).show(); /* Verify system alert window is not trying to display if activity is null. */ - verifyStatic(); + verifyStatic(AppCenterLog.class); AppCenterLog.warn(eq(LOG_TAG), eq("The application is in background mode, the system alerts windows won't be displayed.")); } @@ -364,7 +364,7 @@ public void needRefreshDialogWhenStartInstallationOnAndroidQ() throws Exception verify(mAlertWindowsDialog).show(); /* Verify system alert window is not trying to display if it is already shown. */ - verifyStatic(never()); + verifyStatic(AppCenterLog.class, never()); AppCenterLog.warn(eq(LOG_TAG), eq("Show new system alerts windows dialog.")); } } diff --git a/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/DistributeWarnUnknownSourcesTest.java b/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/DistributeWarnUnknownSourcesTest.java index 4d72d2335..70218f632 100644 --- a/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/DistributeWarnUnknownSourcesTest.java +++ b/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/DistributeWarnUnknownSourcesTest.java @@ -40,10 +40,10 @@ import static com.microsoft.appcenter.distribute.DistributeConstants.PREFERENCE_KEY_DISTRIBUTION_GROUP_ID; import static com.microsoft.appcenter.distribute.DistributeConstants.PREFERENCE_KEY_DOWNLOAD_STATE; import static com.microsoft.appcenter.distribute.DistributeConstants.PREFERENCE_KEY_UPDATE_TOKEN; -import static org.mockito.Matchers.any; -import static org.mockito.Matchers.anyMapOf; -import static org.mockito.Matchers.anyString; -import static org.mockito.Matchers.eq; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyMapOf; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.doThrow; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; @@ -168,7 +168,7 @@ public void cancelDialogWithBack() { when(mUnknownSourcesDialog.isShowing()).thenReturn(false); /* Verify. */ - verifyStatic(); + verifyStatic(SharedPreferencesManager.class); SharedPreferencesManager.remove(PREFERENCE_KEY_DOWNLOAD_STATE); /* Verify no more calls, e.g. happened only once. */ @@ -191,7 +191,7 @@ public void cancelDialogWithButton() { when(mUnknownSourcesDialog.isShowing()).thenReturn(false); /* Verify. */ - verifyStatic(); + verifyStatic(SharedPreferencesManager.class); SharedPreferencesManager.remove(PREFERENCE_KEY_DOWNLOAD_STATE); /* Verify no more calls, e.g. happened only once. */ @@ -209,7 +209,7 @@ public void disableBeforeCancelWithBack() { /* Disable. */ Distribute.setEnabled(false); - verifyStatic(); + verifyStatic(SharedPreferencesManager.class); SharedPreferencesManager.remove(PREFERENCE_KEY_DOWNLOAD_STATE); /* Cancel. */ @@ -219,7 +219,7 @@ public void disableBeforeCancelWithBack() { when(mUnknownSourcesDialog.isShowing()).thenReturn(false); /* Verify cancel did nothing more. */ - verifyStatic(); + verifyStatic(SharedPreferencesManager.class); SharedPreferencesManager.remove(PREFERENCE_KEY_DOWNLOAD_STATE); /* Verify no more calls, e.g. happened only once. */ @@ -237,7 +237,7 @@ public void disableBeforeCancelWithButton() { /* Disable. */ Distribute.setEnabled(false); - verifyStatic(); + verifyStatic(SharedPreferencesManager.class); SharedPreferencesManager.remove(PREFERENCE_KEY_DOWNLOAD_STATE); /* Cancel. */ @@ -247,7 +247,7 @@ public void disableBeforeCancelWithButton() { when(mUnknownSourcesDialog.isShowing()).thenReturn(false); /* Verify cancel did nothing more. */ - verifyStatic(); + verifyStatic(SharedPreferencesManager.class); SharedPreferencesManager.remove(PREFERENCE_KEY_DOWNLOAD_STATE); /* Verify no more calls, e.g. happened only once. */ @@ -376,7 +376,7 @@ public void clickSettingsFailsToNavigate() throws Exception { verify(mFirstActivity).startActivity(intent); /* Verify failure is treated as a cancel dialog. */ - verifyStatic(); + verifyStatic(SharedPreferencesManager.class); SharedPreferencesManager.remove(PREFERENCE_KEY_DOWNLOAD_STATE); /* Verify no more calls, e.g. happened only once. */ @@ -391,7 +391,7 @@ public void disableThenClickSettingsThenFailsToNavigate() throws Exception { /* Disable. */ Distribute.setEnabled(false); - verifyStatic(); + verifyStatic(SharedPreferencesManager.class); SharedPreferencesManager.remove(PREFERENCE_KEY_DOWNLOAD_STATE); /* Click settings. */ @@ -407,7 +407,7 @@ public void disableThenClickSettingsThenFailsToNavigate() throws Exception { verify(mFirstActivity).startActivity(intent); /* Verify cleaning behavior happened only once, e.g. completeWorkflow skipped. */ - verifyStatic(); + verifyStatic(SharedPreferencesManager.class); SharedPreferencesManager.remove(PREFERENCE_KEY_DOWNLOAD_STATE); /* Verify no more calls, e.g. happened only once. */ diff --git a/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/DownloadManagerReceiverTest.java b/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/DownloadManagerReceiverTest.java index 8118c922c..21db9fd10 100644 --- a/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/DownloadManagerReceiverTest.java +++ b/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/DownloadManagerReceiverTest.java @@ -18,9 +18,9 @@ import org.powermock.core.classloader.annotations.PrepareForTest; import org.powermock.modules.junit4.rule.PowerMockRule; -import static org.mockito.Matchers.anyLong; -import static org.mockito.Matchers.anyString; -import static org.mockito.Matchers.isA; +import static org.mockito.ArgumentMatchers.anyLong; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.ArgumentMatchers.isA; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; @@ -70,7 +70,7 @@ public void onReceiveInvalidDownloadIdentifier() { downloadManagerReceiver.onReceive(mockContext, mockIntent); /* Verify. */ - verifyStatic(never()); + verifyStatic(AsyncTaskUtils.class, never()); AsyncTaskUtils.execute(anyString(), isA(ResumeFromBackgroundTask.class)); } @@ -86,7 +86,7 @@ public void onReceiveProperDownloadIdentifier() { downloadManagerReceiver.onReceive(mockContext, mockIntent); /* Verify. */ - verifyStatic(); + verifyStatic(AsyncTaskUtils.class); AsyncTaskUtils.execute(anyString(), isA(ResumeFromBackgroundTask.class)); } @@ -99,9 +99,9 @@ public void invalidIntent() { new DownloadManagerReceiver().onReceive(mock(Context.class), clickIntent); /* Verify. */ - verifyStatic(never()); + verifyStatic(Distribute.class, never()); Distribute.getInstance(); - verifyStatic(never()); + verifyStatic(AsyncTaskUtils.class, never()); AsyncTaskUtils.execute(anyString(), isA(ResumeFromBackgroundTask.class)); } } \ No newline at end of file diff --git a/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/InstallerUtilsTest.java b/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/InstallerUtilsTest.java index e89b9181c..6141fb823 100644 --- a/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/InstallerUtilsTest.java +++ b/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/InstallerUtilsTest.java @@ -8,11 +8,11 @@ import static android.app.PendingIntent.FLAG_MUTABLE; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; -import static org.mockito.Matchers.any; -import static org.mockito.Matchers.anyInt; -import static org.mockito.Matchers.anyLong; -import static org.mockito.Matchers.anyString; -import static org.mockito.Matchers.eq; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.anyLong; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; import static org.powermock.api.mockito.PowerMockito.mock; @@ -32,7 +32,6 @@ import org.junit.Before; import org.junit.Rule; import org.junit.Test; -import org.mockito.Matchers; import org.mockito.Mock; import org.mockito.invocation.InvocationOnMock; import org.mockito.stubbing.Answer; @@ -79,7 +78,7 @@ public void setUp() throws IOException { /* Mock session. */ when(mMockPackageInstaller.openSession(anyInt())).thenReturn(mSession); - when(mSession.openWrite(anyString(), anyInt(), anyInt())).thenReturn(mOutputStream); + when(mSession.openWrite(anyString(), anyLong(), anyLong())).thenReturn(mOutputStream); } @Test @@ -95,7 +94,7 @@ public void installPackage() throws IOException { PackageInstaller.SessionCallback mockSessionCallback = mock(PackageInstaller.SessionCallback.class); /* Mock data. */ - when(mData.read(Matchers.anyObject())).thenReturn(10).thenReturn(-1); + when(mData.read(any())).thenReturn(10).thenReturn(-1); /* Call install. */ InstallerUtils.installPackage(mData, mContext, mockSessionCallback); @@ -212,7 +211,7 @@ private void createIntentSender(final int expectedFlag) { when(mockIntent.getIntentSender()).thenReturn(mock(IntentSender.class)); when(PendingIntent.getBroadcast(any(Context.class), anyInt(), any(Intent.class), anyInt())).then(new Answer() { @Override - public PendingIntent answer(InvocationOnMock invocation) throws Throwable { + public PendingIntent answer(InvocationOnMock invocation) { int flag = (int)invocation.getArguments()[3]; Assert.assertEquals(flag, expectedFlag); return mockIntent; @@ -225,7 +224,6 @@ public PendingIntent answer(InvocationOnMock invocation) throws Throwable { * This is used ot set a specific Build.VERSION.SDK_INT for tests. * @param field static field * @param newValue new value for the given field - * @throws Exception */ static void setFinalStatic(Field field, Object newValue) throws Exception { field.setAccessible(true); diff --git a/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/PermissionUtilsTest.java b/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/PermissionUtilsTest.java index 74861d056..2da77b2a3 100644 --- a/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/PermissionUtilsTest.java +++ b/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/PermissionUtilsTest.java @@ -19,7 +19,7 @@ import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; -import static org.mockito.Matchers.anyString; +import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; diff --git a/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/ReleaseDownloadListenerTest.java b/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/ReleaseDownloadListenerTest.java index 7320170bb..d32b5a388 100644 --- a/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/ReleaseDownloadListenerTest.java +++ b/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/ReleaseDownloadListenerTest.java @@ -39,11 +39,11 @@ import static com.microsoft.appcenter.distribute.DistributeConstants.PREFERENCE_KEY_UPDATE_TOKEN; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; -import static org.mockito.Matchers.any; -import static org.mockito.Matchers.anyInt; -import static org.mockito.Matchers.anyLong; -import static org.mockito.Matchers.anyString; -import static org.mockito.Matchers.eq; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.anyLong; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.times; diff --git a/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/ReleaseInstallerListenerTest.java b/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/ReleaseInstallerListenerTest.java index 1ba50d09c..754c497a8 100644 --- a/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/ReleaseInstallerListenerTest.java +++ b/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/ReleaseInstallerListenerTest.java @@ -5,12 +5,13 @@ package com.microsoft.appcenter.distribute; -import static org.mockito.Matchers.any; -import static org.mockito.Matchers.anyBoolean; -import static org.mockito.Matchers.anyInt; -import static org.mockito.Matchers.anyLong; -import static org.mockito.Matchers.anyString; -import static org.mockito.Matchers.eq; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyBoolean; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.anyLong; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.ArgumentMatchers.isNull; import static org.mockito.Mockito.doNothing; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; @@ -37,35 +38,34 @@ import org.junit.Rule; import org.junit.Test; import org.mockito.ArgumentCaptor; -import org.mockito.Matchers; import org.mockito.Mock; -import org.powermock.api.mockito.PowerMockito; import org.powermock.core.classloader.annotations.PrepareForTest; import org.powermock.modules.junit4.rule.PowerMockRule; -import java.io.FileDescriptor; import java.io.FileInputStream; import java.io.FileNotFoundException; -import java.io.IOException; import java.io.InputStream; import java.text.NumberFormat; @PrepareForTest({ - ProgressDialog.class, - InstallerUtils.class, - FileInputStream.class, - PackageInstaller.SessionCallback.class, + AppCenterLog.class, Distribute.class, + FileInputStream.class, HandlerUtils.class, - AppCenterLog.class, - Toast.class, - ReleaseInstallerListener.class + InstallerUtils.class, + PackageInstaller.SessionCallback.class, + ProgressDialog.class, + ReleaseInstallerListener.class, + Toast.class }) public class ReleaseInstallerListenerTest { @Rule public PowerMockRule mPowerMockRule = new PowerMockRule(); + @Mock + private Context mContext; + @Mock private Distribute mDistribute; @@ -85,14 +85,11 @@ public class ReleaseInstallerListenerTest { @Before public void setUp() throws Exception { - /* Mock context. */ - Context mockContext = mock(Context.class); - /* Mock static classes. */ - mockStatic(InstallerUtils.class); - mockStatic(HandlerUtils.class); - mockStatic(Distribute.class); mockStatic(AppCenterLog.class); + mockStatic(Distribute.class); + mockStatic(HandlerUtils.class); + mockStatic(InstallerUtils.class); mockStatic(Toast.class); /* Mock progress dialog. */ @@ -100,6 +97,7 @@ public void setUp() throws Exception { when(mMockProgressDialog.isIndeterminate()).thenReturn(false); /* Mock toast. */ + when(mContext.getString(anyInt())).thenReturn("localized_message"); when(Toast.makeText(any(Context.class), anyString(), anyInt())).thenReturn(mToast); /* Mock Distribute. */ @@ -111,10 +109,10 @@ public void setUp() throws Exception { /* Mock download manager. */ when(mDownloadManager.openDownloadedFile(anyLong())).thenReturn(mock(ParcelFileDescriptor.class)); - when(mockContext.getSystemService(anyString())).thenReturn(mDownloadManager); + when(mContext.getSystemService(anyString())).thenReturn(mDownloadManager); /* Create installer listener. */ - mReleaseInstallerListener = new ReleaseInstallerListener(mockContext); + mReleaseInstallerListener = new ReleaseInstallerListener(mContext); /* Set downloadId. */ mReleaseInstallerListener.setDownloadId(1); @@ -123,8 +121,8 @@ public void setUp() throws Exception { mReleaseInstallerListener.showInstallProgressDialog(mock(Activity.class)); /* Verify call methods. */ - verify(mMockProgressDialog).setProgressPercentFormat(any(NumberFormat.class)); - verify(mMockProgressDialog).setProgressNumberFormat(anyString()); + verify(mMockProgressDialog).setProgressPercentFormat(isNull()); + verify(mMockProgressDialog).setProgressNumberFormat(isNull()); verify(mMockProgressDialog).setIndeterminate(anyBoolean()); } @@ -143,12 +141,12 @@ public void throwIOExceptionAfterStartInstall() throws Exception { mReleaseInstallerListener.startInstall(); /* Verify that exception was called. */ - verifyStatic(); + verifyStatic(AppCenterLog.class); AppCenterLog.error(anyString(), anyString(), any(FileNotFoundException.class)); } @Test - public void releaseInstallProcessWhenOnFinnishFailureWithContext() throws IOException { + public void releaseInstallProcessWhenOnFinnishFailureWithContext() { /* Mock progress dialog. */ when(mMockProgressDialog.isIndeterminate()).thenReturn(true); @@ -158,8 +156,8 @@ public void releaseInstallProcessWhenOnFinnishFailureWithContext() throws IOExce /* Verify that installPackage method was called. */ ArgumentCaptor sessionListener = ArgumentCaptor.forClass(PackageInstaller.SessionCallback.class); - verifyStatic(); - InstallerUtils.installPackage(Matchers.any(), Matchers.any(), sessionListener.capture()); + verifyStatic(InstallerUtils.class); + InstallerUtils.installPackage(any(InputStream.class), any(Context.class), sessionListener.capture()); /* Emulate session status. */ sessionListener.getValue().onCreated(mMockSessionId); @@ -172,7 +170,7 @@ public void releaseInstallProcessWhenOnFinnishFailureWithContext() throws IOExce sessionListener.getValue().onProgressChanged(mMockSessionId, 1); /* Verify that the handler was called and catch runnable. */ - verifyStatic(); + verifyStatic(HandlerUtils.class); HandlerUtils.runOnUiThread(any(Runnable.class)); /* Verify that progress dialog was closed after finish install process. */ @@ -180,7 +178,7 @@ public void releaseInstallProcessWhenOnFinnishFailureWithContext() throws IOExce /* Verify that the handler was called again. */ ArgumentCaptor runnable = ArgumentCaptor.forClass(Runnable.class); - verifyStatic(times(2)); + verifyStatic(HandlerUtils.class, times(2)); HandlerUtils.runOnUiThread(runnable.capture()); runnable.getValue().run(); @@ -189,15 +187,15 @@ public void releaseInstallProcessWhenOnFinnishFailureWithContext() throws IOExce } @Test - public void releaseInstallerProcessWhenProgressDialogNull() throws Exception { + public void releaseInstallerProcessWhenProgressDialogNull() { /* Start install process. */ mReleaseInstallerListener.startInstall(); /* Verify that installPackage method was called. */ ArgumentCaptor sessionListener = ArgumentCaptor.forClass(PackageInstaller.SessionCallback.class); - verifyStatic(); - InstallerUtils.installPackage(Matchers.any(), Matchers.any(), sessionListener.capture()); + verifyStatic(InstallerUtils.class); + InstallerUtils.installPackage(any(InputStream.class), any(Context.class), sessionListener.capture()); /* Emulate session status. */ sessionListener.getValue().onCreated(mMockSessionId); @@ -212,7 +210,7 @@ public void releaseInstallerProcessWhenProgressDialogNull() throws Exception { /* Verify that runnable was called. */ ArgumentCaptor runnable = ArgumentCaptor.forClass(Runnable.class); - verifyStatic(); + verifyStatic(HandlerUtils.class); HandlerUtils.runOnUiThread(runnable.capture()); runnable.getValue().run(); @@ -221,7 +219,7 @@ public void releaseInstallerProcessWhenProgressDialogNull() throws Exception { /* Verify that the handler was called and catch runnable. */ runnable = ArgumentCaptor.forClass(Runnable.class); - verifyStatic(times(2)); + verifyStatic(HandlerUtils.class, times(2)); HandlerUtils.runOnUiThread(runnable.capture()); runnable.getValue().run(); @@ -232,7 +230,7 @@ public void releaseInstallerProcessWhenProgressDialogNull() throws Exception { sessionListener.getValue().onFinished(mMockSessionId, true); /* Verify that the handler was called again. */ - verifyStatic(times(3)); + verifyStatic(HandlerUtils.class, times(3)); HandlerUtils.runOnUiThread(runnable.capture()); runnable.getValue().run(); @@ -241,7 +239,7 @@ public void releaseInstallerProcessWhenProgressDialogNull() throws Exception { } @Test - public void releaseInstallerProcessWhenDialogIsIndeterminate() throws Exception { + public void releaseInstallerProcessWhenDialogIsIndeterminate() { /* Mock progress dialog. */ when(mMockProgressDialog.isIndeterminate()).thenReturn(true); @@ -251,8 +249,8 @@ public void releaseInstallerProcessWhenDialogIsIndeterminate() throws Exception /* Verify that installPackage method was called. */ ArgumentCaptor sessionListener = ArgumentCaptor.forClass(PackageInstaller.SessionCallback.class); - verifyStatic(); - InstallerUtils.installPackage(Matchers.any(), Matchers.any(), sessionListener.capture()); + verifyStatic(InstallerUtils.class); + InstallerUtils.installPackage(any(InputStream.class), any(Context.class), sessionListener.capture()); /* Emulate session status. */ sessionListener.getValue().onCreated(mMockSessionId); @@ -267,22 +265,22 @@ public void releaseInstallerProcessWhenDialogIsIndeterminate() throws Exception /* Verify that the handler was called and catch runnable. */ ArgumentCaptor runnable = ArgumentCaptor.forClass(Runnable.class); - verifyStatic(); + verifyStatic(HandlerUtils.class); HandlerUtils.runOnUiThread(runnable.capture()); runnable.getValue().run(); /* Verify that the progress dialog was updated. */ verify(mMockProgressDialog).setProgress(anyInt()); verify(mMockProgressDialog).setMax(anyInt()); - verify(mMockProgressDialog, times(2)).setProgressPercentFormat(any(NumberFormat.class)); - verify(mMockProgressDialog, times(2)).setProgressNumberFormat(anyString()); + verify(mMockProgressDialog).setProgressPercentFormat(any(NumberFormat.class)); + verify(mMockProgressDialog).setProgressNumberFormat(anyString()); verify(mMockProgressDialog, times(2)).setIndeterminate(anyBoolean()); /* Verify that progress dialog was closed after finish install process. */ sessionListener.getValue().onFinished(mMockSessionId, true); /* Verify that the handler was called again. */ - verifyStatic(times(2)); + verifyStatic(HandlerUtils.class, times(2)); HandlerUtils.runOnUiThread(runnable.capture()); runnable.getValue().run(); @@ -291,15 +289,15 @@ public void releaseInstallerProcessWhenDialogIsIndeterminate() throws Exception } @Test - public void releaseInstallerProcessWhenWithContext() throws Exception { + public void releaseInstallerProcessWhenWithContext() { /* Start install process. */ mReleaseInstallerListener.startInstall(); /* Verify that installPackage method was called. */ ArgumentCaptor sessionListener = ArgumentCaptor.forClass(PackageInstaller.SessionCallback.class); - verifyStatic(); - InstallerUtils.installPackage(Matchers.any(), Matchers.any(), sessionListener.capture()); + verifyStatic(InstallerUtils.class); + InstallerUtils.installPackage(any(InputStream.class), any(Context.class), sessionListener.capture()); /* Emulate session status. */ sessionListener.getValue().onCreated(mMockSessionId); @@ -314,7 +312,7 @@ public void releaseInstallerProcessWhenWithContext() throws Exception { /* Verify that the handler was called and catch runnable. */ ArgumentCaptor runnable = ArgumentCaptor.forClass(Runnable.class); - verifyStatic(); + verifyStatic(HandlerUtils.class); HandlerUtils.runOnUiThread(runnable.capture()); runnable.getValue().run(); @@ -325,7 +323,7 @@ public void releaseInstallerProcessWhenWithContext() throws Exception { sessionListener.getValue().onFinished(mMockSessionId, true); /* Verify that the handler was called again. */ - verifyStatic(times(2)); + verifyStatic(HandlerUtils.class, times(2)); HandlerUtils.runOnUiThread(runnable.capture()); runnable.getValue().run(); @@ -348,8 +346,8 @@ public void startInstallWhenFileIsInvalid() throws FileNotFoundException { mReleaseInstallerListener.startInstall(); /* Verify that the install process never starts. */ - verifyStatic(never()); - InstallerUtils.installPackage(Matchers.any(), Matchers.any(), any(PackageInstaller.SessionCallback.class)); + verifyStatic(InstallerUtils.class, never()); + InstallerUtils.installPackage(any(InputStream.class), any(Context.class), any(PackageInstaller.SessionCallback.class)); } @Test @@ -365,7 +363,7 @@ public void releaseInstallerHideDialogTwice() { mReleaseInstallerListener.hideInstallProgressDialog(); /* Verify that runnable was called once only. */ - verifyStatic(); + verifyStatic(HandlerUtils.class); HandlerUtils.runOnUiThread(any(Runnable.class)); } } diff --git a/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/ResumeFromBackgroundTaskTest.java b/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/ResumeFromBackgroundTaskTest.java index 68e5e499f..b30e1ae6e 100644 --- a/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/ResumeFromBackgroundTaskTest.java +++ b/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/ResumeFromBackgroundTaskTest.java @@ -18,8 +18,8 @@ import org.powermock.core.classloader.annotations.PrepareForTest; import org.powermock.modules.junit4.rule.PowerMockRule; -import static org.mockito.Matchers.anyLong; -import static org.mockito.Matchers.anyString; +import static org.mockito.ArgumentMatchers.anyLong; +import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verifyNoMoreInteractions; import static org.mockito.Mockito.when; diff --git a/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/UnknownSourcesDetectionTest.java b/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/UnknownSourcesDetectionTest.java index f12b9a62c..b210563d5 100644 --- a/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/UnknownSourcesDetectionTest.java +++ b/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/UnknownSourcesDetectionTest.java @@ -26,8 +26,9 @@ import static com.microsoft.appcenter.distribute.InstallerUtils.INSTALL_NON_MARKET_APPS_ENABLED; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; -import static org.mockito.Matchers.any; -import static org.mockito.Matchers.eq; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.reset; import static org.mockito.Mockito.times; @@ -36,9 +37,13 @@ import static org.powermock.api.mockito.PowerMockito.mockStatic; @SuppressLint("InlinedApi") -@SuppressWarnings({"deprecation", "RedundantSuppression"}) +@SuppressWarnings({"deprecation"}) @RunWith(PowerMockRunner.class) -@PrepareForTest({Build.class, Settings.Global.class, Settings.Secure.class}) +@PrepareForTest({ + Build.class, + Settings.Global.class, + Settings.Secure.class +}) public class UnknownSourcesDetectionTest { @Mock @@ -60,6 +65,7 @@ public void setUp() { mockStatic(Settings.Secure.class); when(mContext.getPackageManager()).thenReturn(mPackageManager); when(mContext.getApplicationInfo()).thenReturn(mApplicationInfo); + when(mContext.getContentResolver()).thenReturn(mock(ContentResolver.class)); } @Test diff --git a/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/download/manager/DownloadManagerDistributeDeadlockTest.java b/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/download/manager/DownloadManagerDistributeDeadlockTest.java index 187737f6f..e3fe606ed 100644 --- a/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/download/manager/DownloadManagerDistributeDeadlockTest.java +++ b/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/download/manager/DownloadManagerDistributeDeadlockTest.java @@ -56,11 +56,11 @@ import static com.microsoft.appcenter.distribute.DistributeConstants.PREFERENCE_KEY_DOWNLOAD_ID; import static com.microsoft.appcenter.utils.PrefStorageConstants.ALLOWED_NETWORK_REQUEST; import static com.microsoft.appcenter.utils.PrefStorageConstants.KEY_ENABLED; -import static org.mockito.Matchers.any; -import static org.mockito.Matchers.anyInt; -import static org.mockito.Matchers.anyLong; -import static org.mockito.Matchers.anyString; -import static org.mockito.Matchers.eq; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.anyLong; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; import static org.powermock.api.mockito.PowerMockito.doAnswer; diff --git a/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/download/manager/DownloadManagerReleaseDownloaderTest.java b/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/download/manager/DownloadManagerReleaseDownloaderTest.java index cbffe6399..30b989d21 100644 --- a/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/download/manager/DownloadManagerReleaseDownloaderTest.java +++ b/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/download/manager/DownloadManagerReleaseDownloaderTest.java @@ -35,12 +35,12 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; -import static org.mockito.Matchers.any; -import static org.mockito.Matchers.anyInt; -import static org.mockito.Matchers.anyLong; -import static org.mockito.Matchers.anyString; -import static org.mockito.Matchers.eq; -import static org.mockito.Matchers.isA; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.anyLong; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.ArgumentMatchers.isA; import static org.mockito.Mockito.never; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; @@ -100,7 +100,7 @@ public void setUp() { @Override public Object answer(InvocationOnMock invocation) { - invocation.getArgumentAt(0, Runnable.class).run(); + invocation.getArgument(0).run(); return null; } }); @@ -127,14 +127,14 @@ public void downloadStatus() { assertEquals(DOWNLOAD_ID, mReleaseDownloader.getDownloadId()); assertEquals(DOWNLOAD_ID, mReleaseDownloader.getDownloadId()); assertTrue(mReleaseDownloader.isDownloading()); - verifyStatic(times(2)); + verifyStatic(SharedPreferencesManager.class, times(2)); SharedPreferencesManager.getLong(eq(PREFERENCE_KEY_DOWNLOAD_ID), eq(INVALID_DOWNLOAD_IDENTIFIER)); } @Test public void resumeStartsUpdateTask() { mReleaseDownloader.resume(); - verifyStatic(); + verifyStatic(AsyncTaskUtils.class); AsyncTaskUtils.execute(anyString(), isA(DownloadManagerUpdateTask.class), Mockito.anyVararg()); } @@ -142,7 +142,7 @@ public void resumeStartsUpdateTask() { public void resumeDoesNothingAfterCancellation() { mReleaseDownloader.cancel(); mReleaseDownloader.resume(); - verifyStatic(never()); + verifyStatic(AsyncTaskUtils.class, never()); AsyncTaskUtils.execute(anyString(), isA(DownloadManagerUpdateTask.class), Mockito.anyVararg()); } @@ -151,12 +151,12 @@ public void cancelClearsEverything() { /* Update status. */ mReleaseDownloader.resume(); - verifyStatic(); + verifyStatic(AsyncTaskUtils.class); AsyncTaskUtils.execute(anyString(), isA(DownloadManagerUpdateTask.class), Mockito.anyVararg()); /* Create new request. */ mReleaseDownloader.onStart(); - verifyStatic(); + verifyStatic(AsyncTaskUtils.class); AsyncTaskUtils.execute(anyString(), isA(DownloadManagerRequestTask.class), Mockito.anyVararg()); /* Cancel clears everything only once. */ @@ -166,9 +166,9 @@ public void cancelClearsEverything() { /* Verify. */ verify(mRequestTask).cancel(eq(true)); verify(mUpdateTask).cancel(eq(true)); - verifyStatic(); + verifyStatic(AsyncTaskUtils.class); AsyncTaskUtils.execute(anyString(), isA(DownloadManagerRemoveTask.class), Mockito.anyVararg()); - verifyStatic(); + verifyStatic(SharedPreferencesManager.class); SharedPreferencesManager.remove(eq(PREFERENCE_KEY_DOWNLOAD_ID)); } @@ -179,9 +179,9 @@ public void doNotTryToRemoveInvalidDownloadId() { mReleaseDownloader.cancel(); /* Verify. */ - verifyStatic(never()); + verifyStatic(AsyncTaskUtils.class, never()); AsyncTaskUtils.execute(anyString(), isA(DownloadManagerRemoveTask.class), Mockito.anyVararg()); - verifyStatic(never()); + verifyStatic(SharedPreferencesManager.class, never()); SharedPreferencesManager.remove(eq(PREFERENCE_KEY_DOWNLOAD_ID)); } @@ -190,18 +190,18 @@ public void requestNewDownloading() { /* Create new request. */ mReleaseDownloader.onStart(); - verifyStatic(); + verifyStatic(AsyncTaskUtils.class); AsyncTaskUtils.execute(anyString(), isA(DownloadManagerRequestTask.class), Mockito.anyVararg()); /* Do not duplicate requests. */ mReleaseDownloader.onStart(); - verifyStatic(); + verifyStatic(AsyncTaskUtils.class); AsyncTaskUtils.execute(anyString(), isA(DownloadManagerRequestTask.class), Mockito.anyVararg()); /* Don't do anything after cancellation. */ mReleaseDownloader.cancel(); mReleaseDownloader.onStart(); - verifyStatic(); + verifyStatic(AsyncTaskUtils.class); AsyncTaskUtils.execute(anyString(), isA(DownloadManagerRequestTask.class), Mockito.anyVararg()); } @@ -209,7 +209,7 @@ public void requestNewDownloading() { public void doNotRequestNewDownloadingAfterCancellation() { mReleaseDownloader.cancel(); mReleaseDownloader.onStart(); - verifyStatic(never()); + verifyStatic(AsyncTaskUtils.class, never()); AsyncTaskUtils.execute(anyString(), isA(DownloadManagerRequestTask.class), Mockito.anyVararg()); } @@ -223,9 +223,9 @@ public void startDownloadingMandatoryUpdate() { /* Verify. */ verify(mListener).onStart(anyLong()); - verifyStatic(); + verifyStatic(SharedPreferencesManager.class); SharedPreferencesManager.putLong(eq(PREFERENCE_KEY_DOWNLOAD_ID), eq(DOWNLOAD_ID)); - verifyStatic(); + verifyStatic(AsyncTaskUtils.class); AsyncTaskUtils.execute(anyString(), isA(DownloadManagerUpdateTask.class), Mockito.anyVararg()); } @@ -239,9 +239,9 @@ public void startDownloadingNotMandatoryUpdate() { /* Verify. */ verify(mListener).onStart(anyLong()); - verifyStatic(); + verifyStatic(SharedPreferencesManager.class); SharedPreferencesManager.putLong(eq(PREFERENCE_KEY_DOWNLOAD_ID), eq(DOWNLOAD_ID)); - verifyStatic(never()); + verifyStatic(AsyncTaskUtils.class, never()); AsyncTaskUtils.execute(anyString(), isA(DownloadManagerUpdateTask.class), Mockito.anyVararg()); } @@ -250,7 +250,7 @@ public void doNotStartDownloadingAfterCancellation() { mReleaseDownloader.cancel(); mReleaseDownloader.onDownloadStarted(DOWNLOAD_ID, 0); verify(mListener, never()).onStart(anyLong()); - verifyStatic(never()); + verifyStatic(SharedPreferencesManager.class, never()); SharedPreferencesManager.putLong(eq(PREFERENCE_KEY_DOWNLOAD_ID), eq(DOWNLOAD_ID)); } @@ -259,14 +259,14 @@ public void scheduleAnotherUpdateIfListenerWantsIt() { Cursor cursor = mock(Cursor.class); when(cursor.getLong(eq(DownloadManager.COLUMN_BYTES_DOWNLOADED_SO_FAR.hashCode()))).thenReturn(42L); when(cursor.getLong(eq(DownloadManager.COLUMN_TOTAL_SIZE_BYTES.hashCode()))).thenReturn(4242L); - when(mListener.onProgress(anyInt(), anyInt())).thenReturn(true); + when(mListener.onProgress(anyLong(), anyLong())).thenReturn(true); /* Update download progress. */ mReleaseDownloader.onDownloadProgress(cursor); /* Verify. */ - verify(mListener).onProgress(anyInt(), anyInt()); - verifyStatic(); + verify(mListener).onProgress(anyLong(), anyLong()); + verifyStatic(AsyncTaskUtils.class); AsyncTaskUtils.execute(anyString(), isA(DownloadManagerUpdateTask.class), Mockito.anyVararg()); } @@ -275,14 +275,14 @@ public void doNotScheduleAnotherUpdateIfListenerDoNotWantsIt() { Cursor cursor = mock(Cursor.class); when(cursor.getLong(eq(DownloadManager.COLUMN_BYTES_DOWNLOADED_SO_FAR.hashCode()))).thenReturn(42L); when(cursor.getLong(eq(DownloadManager.COLUMN_TOTAL_SIZE_BYTES.hashCode()))).thenReturn(4242L); - when(mListener.onProgress(anyInt(), anyInt())).thenReturn(false); + when(mListener.onProgress(anyLong(), anyLong())).thenReturn(false); /* Update download progress. */ mReleaseDownloader.onDownloadProgress(cursor); /* Verify. */ - verify(mListener).onProgress(anyInt(), anyInt()); - verifyStatic(never()); + verify(mListener).onProgress(anyLong(), anyLong()); + verifyStatic(AsyncTaskUtils.class, never()); AsyncTaskUtils.execute(anyString(), isA(DownloadManagerUpdateTask.class), Mockito.anyVararg()); } @@ -294,7 +294,7 @@ public void doNotOnDownloadProgressAfterCancellation() { mReleaseDownloader.onDownloadProgress(mock(Cursor.class)); /* Verify. */ - verify(mListener, never()).onProgress(anyInt(), anyInt()); + verify(mListener, never()).onProgress(anyLong(), anyLong()); } @Test @@ -339,7 +339,7 @@ public void completeDownloadDoesNothingAfterCancellation() { @Test public void errorDownload() { - mReleaseDownloader.onDownloadError(mock(RuntimeException.class)); + mReleaseDownloader.onDownloadError(new RuntimeException("Test")); /* Verify. */ verify(mListener).onError(anyString()); @@ -348,7 +348,7 @@ public void errorDownload() { @Test public void errorDownloadDoesNothingAfterCancellation() { mReleaseDownloader.cancel(); - mReleaseDownloader.onDownloadError(mock(RuntimeException.class)); + mReleaseDownloader.onDownloadError(new RuntimeException("Test")); /* Verify. */ verify(mListener, never()).onError(anyString()); diff --git a/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/download/manager/DownloadManagerRequestTaskTest.java b/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/download/manager/DownloadManagerRequestTaskTest.java index aec7998a8..7ac34a63d 100644 --- a/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/download/manager/DownloadManagerRequestTaskTest.java +++ b/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/download/manager/DownloadManagerRequestTaskTest.java @@ -17,23 +17,23 @@ import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; -import org.mockito.Matchers; import org.mockito.Mock; import org.mockito.invocation.InvocationOnMock; -import org.mockito.runners.MockitoJUnitRunner; +import org.mockito.junit.MockitoJUnitRunner; import org.mockito.stubbing.Answer; import org.powermock.core.classloader.annotations.PrepareForTest; import org.powermock.modules.junit4.rule.PowerMockRule; -import static org.mockito.Matchers.any; -import static org.mockito.Matchers.anyInt; -import static org.mockito.Matchers.anyLong; -import static org.mockito.Matchers.eq; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.anyLong; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.verifyZeroInteractions; import static org.mockito.Mockito.when; +import static org.powermock.api.mockito.PowerMockito.verifyNoMoreInteractions; import static org.powermock.api.mockito.PowerMockito.whenNew; @@ -79,6 +79,7 @@ public void setUp() throws Exception { when(mDownloadManager.enqueue(eq(mDownloadManagerRequest))).thenReturn(DOWNLOAD_ID); /* Mock Downloader. */ + when(mReleaseDetails.getDownloadUrl()).thenReturn(mock(Uri.class)); when(mDownloader.getReleaseDetails()).thenReturn(mReleaseDetails); when(mDownloader.getDownloadManager()).thenReturn(mDownloadManager); @@ -99,7 +100,7 @@ public void downloadStarted() { /* Verify. */ String expectedTitle = "title 1 (1)"; verify(mDownloadManagerRequest).setTitle(eq(expectedTitle)); - verifyZeroInteractions(mDownloadManagerRequest); + verifyNoMoreInteractions(mDownloadManagerRequest); verify(mDownloader).onDownloadStarted(eq(DOWNLOAD_ID), anyLong()); } @@ -148,16 +149,16 @@ public void enqueueTaskIllegalStateExceptionHandled() { @Test public void noExceptionsWhenDownloadFinishedAfterTimeout() { - when(mDownloadManager.query(Matchers.any())).thenReturn(mCursor); + when(mDownloadManager.query(any(DownloadManager.Query.class))).thenReturn(mCursor); when(mCursor.moveToFirst()).thenReturn(false); when(mRequestTask.isCancelled()).thenReturn(false); /* Run Callback immediately. */ - when(mHandler.postDelayed(Matchers.any(), anyLong())).thenAnswer(new Answer() { + when(mHandler.postDelayed(any(Runnable.class), anyLong())).thenAnswer(new Answer() { @Override public Object answer(InvocationOnMock invocation) { - ((Runnable) invocation.getArguments()[0]).run(); + invocation.getArgument(0).run(); return true; } }); @@ -172,17 +173,16 @@ public Object answer(InvocationOnMock invocation) { @Test public void throwExceptionWhenDownloadStillNotFinishedAfterTimeout() { - when(mDownloadManager.query(Matchers.any())).thenReturn(mCursor); + when(mDownloadManager.query(any(DownloadManager.Query.class))).thenReturn(mCursor); when(mCursor.moveToFirst()).thenReturn(true); - when(mCursor.getInt(anyInt())).thenReturn(1); when(mRequestTask.isCancelled()).thenReturn(false); /* Run Callback immediately. */ - when(mHandler.postDelayed(Matchers.any(), anyLong())).thenAnswer(new Answer() { + when(mHandler.postDelayed(any(Runnable.class), anyLong())).thenAnswer(new Answer() { @Override public Object answer(InvocationOnMock invocation) { - ((Runnable) invocation.getArguments()[0]).run(); + invocation.getArgument(0).run(); return true; } }); @@ -197,7 +197,7 @@ public Object answer(InvocationOnMock invocation) { @Test public void oldCallbacksRemovedBeforeCreatingNewDownload() { when(mRequestTask.isCancelled()).thenReturn(false); - when(mRequestTask.createRequest(Matchers.any())).thenCallRealMethod(); + when(mRequestTask.createRequest(any(Uri.class))).thenCallRealMethod(); /* Perform background task. Emulates creating callback, which would not be used */ mRequestTask.doInBackground(); @@ -206,6 +206,6 @@ public void oldCallbacksRemovedBeforeCreatingNewDownload() { mRequestTask.doInBackground(); /* Verify that callback was removed. */ - verify(mHandler).removeCallbacks(Matchers.any()); + verify(mHandler).removeCallbacks(any(Runnable.class)); } } diff --git a/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/download/manager/DownloadManagerUpdateTaskTest.java b/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/download/manager/DownloadManagerUpdateTaskTest.java index 113a02b50..2063d9815 100644 --- a/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/download/manager/DownloadManagerUpdateTaskTest.java +++ b/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/download/manager/DownloadManagerUpdateTaskTest.java @@ -5,28 +5,28 @@ package com.microsoft.appcenter.distribute.download.manager; -import android.app.DownloadManager; -import android.database.Cursor; - -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.ArgumentCaptor; -import org.mockito.Mock; -import org.mockito.runners.MockitoJUnitRunner; - import static com.microsoft.appcenter.distribute.DistributeConstants.INVALID_DOWNLOAD_IDENTIFIER; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; -import static org.mockito.Matchers.any; -import static org.mockito.Matchers.anyLong; -import static org.mockito.Matchers.eq; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyLong; +import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.ignoreStubs; import static org.mockito.Mockito.never; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.verifyZeroInteractions; import static org.mockito.Mockito.when; +import static org.powermock.api.mockito.PowerMockito.verifyNoMoreInteractions; + +import android.app.DownloadManager; +import android.database.Cursor; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.ArgumentCaptor; +import org.mockito.Mock; +import org.mockito.junit.MockitoJUnitRunner; @RunWith(MockitoJUnitRunner.class) public class DownloadManagerUpdateTaskTest { @@ -52,11 +52,8 @@ public void setUp() { when(mCursor.getColumnIndexOrThrow(eq(DownloadManager.COLUMN_STATUS))).thenReturn(DownloadManager.COLUMN_STATUS.hashCode()); when(mCursor.getColumnIndexOrThrow(eq(DownloadManager.COLUMN_REASON))).thenReturn(DownloadManager.COLUMN_REASON.hashCode()); - /* Mock DownloadManager. */ - when(mDownloadManager.enqueue(any(DownloadManager.Request.class))).thenReturn(DOWNLOAD_ID); - when(mDownloadManager.query(any(DownloadManager.Query.class))).thenReturn(mCursor); - - /* Mock Downloader. */ + /* Mock Downloader and DownloadManager. */ + when(mDownloadManager.query(any())).thenReturn(mCursor); when(mDownloader.getDownloadManager()).thenReturn(mDownloadManager); when(mDownloader.getDownloadId()).thenReturn(DOWNLOAD_ID); @@ -77,7 +74,7 @@ public void downloadIdIsInvalid() { @Test public void errorOnNullCursor() { - when(mDownloadManager.query(any(DownloadManager.Query.class))).thenReturn(null); + when(mDownloadManager.query(any())).thenReturn(null); /* Perform background task. */ mUpdateTask.doInBackground(); @@ -107,7 +104,7 @@ public void doNothingAfterCancellation() { mUpdateTask.doInBackground(); /* Verify. */ - verifyZeroInteractions(ignoreStubs(mDownloader)); + verifyNoMoreInteractions(ignoreStubs(mDownloader)); } @Test From 44a692b290d2139b478e27126464fd1a7edac9f0 Mon Sep 17 00:00:00 2001 From: Ivan Matkov Date: Wed, 27 Apr 2022 13:46:11 +0200 Subject: [PATCH 08/72] Run tests in parallel --- sdk/build.gradle | 3 +++ 1 file changed, 3 insertions(+) diff --git a/sdk/build.gradle b/sdk/build.gradle index df3d66b51..5035a844c 100644 --- a/sdk/build.gradle +++ b/sdk/build.gradle @@ -16,6 +16,9 @@ allprojects { tasks.withType(Test) { jacoco.includeNoLocationClasses = true jacoco.excludes = ['jdk.internal.*'] + + // Run tests in parallel. + maxParallelForks = Runtime.runtime.availableProcessors() } //noinspection GroovyAssignabilityCheck From 0bb619d12e405d4fe4fe37e317e4b81f201e3ecf Mon Sep 17 00:00:00 2001 From: Ivan Matkov Date: Wed, 27 Apr 2022 15:29:54 +0200 Subject: [PATCH 09/72] Fix warnings in distribute-play module --- .../appcenter/distribute/DistributeTest.java | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/sdk/appcenter-distribute-play/src/test/java/com/microsoft/appcenter/distribute/DistributeTest.java b/sdk/appcenter-distribute-play/src/test/java/com/microsoft/appcenter/distribute/DistributeTest.java index 77896ff4a..b7729f6ee 100644 --- a/sdk/appcenter-distribute-play/src/test/java/com/microsoft/appcenter/distribute/DistributeTest.java +++ b/sdk/appcenter-distribute-play/src/test/java/com/microsoft/appcenter/distribute/DistributeTest.java @@ -5,6 +5,9 @@ package com.microsoft.appcenter.distribute; +import static org.mockito.Mockito.mock; +import static org.powermock.api.mockito.PowerMockito.mockStatic; + import android.app.Activity; import android.content.Context; @@ -17,22 +20,21 @@ import org.powermock.core.classloader.annotations.PrepareForTest; import org.powermock.modules.junit4.PowerMockRunner; -import static org.mockito.Matchers.anyString; -import static org.mockito.Matchers.eq; -import static org.mockito.Mockito.mock; -import static org.powermock.api.mockito.PowerMockito.mockStatic; -import static org.powermock.api.mockito.PowerMockito.verifyStatic; - @RunWith(PowerMockRunner.class) @PrepareForTest(AppCenterLog.class) public class DistributeTest { @Before public void setUp() { - new DistributeConstants(); mockStatic(AppCenterLog.class); } + @SuppressWarnings("InstantiationOfUtilityClass") + @Test + public void init() { + new DistributeConstants(); + } + @Test public void isEnabledTest() { Distribute.isEnabled(); From 1718102c6cd77623c467f8eda0c7bc9d127cf1a1 Mon Sep 17 00:00:00 2001 From: Ivan Matkov Date: Wed, 27 Apr 2022 15:30:18 +0200 Subject: [PATCH 10/72] Ignore IllegalAccess warnings in tests --- sdk/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sdk/build.gradle b/sdk/build.gradle index 5035a844c..7dc18a9f0 100644 --- a/sdk/build.gradle +++ b/sdk/build.gradle @@ -65,7 +65,7 @@ subprojects { testOptions { unitTests { all { - jvmArgs '-noverify', '-Djdk.attach.allowAttachSelf=true' + jvmArgs '-noverify', '-Djdk.attach.allowAttachSelf=true', '-Djdk.module.illegalAccess.silent=true' } returnDefaultValues = true } From 4c2a9534ecb6e6b40616b6853d2c9192679783a9 Mon Sep 17 00:00:00 2001 From: Ivan Matkov Date: Thu, 28 Apr 2022 11:15:57 +0200 Subject: [PATCH 11/72] Fix warnings in tests --- .../sasquatch/activities/CrashesTest.java | 1 - .../appcenter/analytics/AnalyticsTest.java | 102 +++++++------ .../AnalyticsTransmissionTargetTest.java | 1 - .../analytics/PropertyConfiguratorTest.java | 3 +- .../AnalyticsValidatorForEventLogTest.java | 2 +- .../analytics/channel/SessionTrackerTest.java | 3 - .../utils/ErrorLogHelperAndroidTest.java | 4 +- .../crashes/utils/ErrorLogHelperTest.java | 4 - .../distribute/ReleaseDetailsTest.java | 8 +- .../distribute/BrowserUtilsTest.java | 3 +- .../distribute/AppStoreDetectionTest.java | 2 - .../DistributeBeforeApiSuccessTest.java | 10 +- .../DistributeCustomizationTest.java | 12 +- ...uteDisableAutomaticCheckForUpdateTest.java | 22 +-- .../distribute/DistributeUpdateTrackTest.java | 2 +- .../DistributeWarnAlertSystemWindowsTest.java | 1 - .../DistributeWarnUnknownSourcesTest.java | 4 +- .../distribute/PermissionUtilsTest.java | 1 + .../ReleaseDownloadListenerTest.java | 6 +- .../ReleaseDownloaderFactoryTest.java | 2 +- ...DownloadManagerDistributeDeadlockTest.java | 6 - .../DownloadManagerReleaseDownloaderTest.java | 1 - .../DownloadManagerRequestTaskTest.java | 1 - .../appcenter/http/HttpUtilsAndroidTest.java | 1 + .../models/json/JSONDateUtilsAndroidTest.java | 1 + .../models/json/JSONUtilsAndroidTest.java | 1 + .../models/json/LogSerializerAndroidTest.java | 4 +- .../one/CommonSchemaDataUtilsAndroidTest.java | 3 +- .../DatabasePersistenceAndroidTest.java | 44 +++--- .../appcenter/utils/HandlerUtilsTest.java | 1 + .../appcenter/utils/IdHelperAndroidTest.java | 2 +- .../utils/storage/SQLiteUtilsAndroidTest.java | 1 + .../appcenter/AppCenterLibraryTest.java | 34 ++--- .../microsoft/appcenter/AppCenterTest.java | 79 +++++----- .../DependencyConfigurationTest.java | 1 + .../appcenter/InstantiationTest.java | 2 +- .../DefaultChannelAlternateIngestionTest.java | 1 - .../DefaultChannelOtherOperationsTest.java | 3 - .../DefaultChannelRaceConditionTest.java | 3 - .../OneCollectorChannelListenerTest.java | 3 - .../appcenter/http/DefaultHttpClientTest.java | 136 +++++++++--------- .../HttpClientNetworkStateHandlerTest.java | 1 - .../http/Tls1_2SocketFactoryTest.java | 3 - .../ingestion/AppCenterIngestionTest.java | 9 +- .../ingestion/OneCollectorIngestionTest.java | 8 +- .../ingestion/models/AbstractLogTest.java | 1 + .../ingestion/models/one/PartAUtilsTest.java | 1 + .../properties/TypedPropertyUtilsTest.java | 1 + .../persistence/DatabasePersistenceTest.java | 14 +- .../appcenter/utils/AppNameHelperTest.java | 1 + .../appcenter/utils/AsyncTaskUtilsTest.java | 1 - .../appcenter/utils/HashUtilsTest.java | 1 + .../InstrumentationRegistryHelperTest.java | 2 +- .../appcenter/utils/TicketCacheTest.java | 1 + .../appcenter/utils/crypto/CryptoTest.java | 99 +++++++------ .../utils/storage/DatabaseManagerTest.java | 1 - 56 files changed, 315 insertions(+), 350 deletions(-) diff --git a/apps/sasquatch/src/androidTest/java/com/microsoft/appcenter/sasquatch/activities/CrashesTest.java b/apps/sasquatch/src/androidTest/java/com/microsoft/appcenter/sasquatch/activities/CrashesTest.java index 3e01f13b7..ac1bde760 100644 --- a/apps/sasquatch/src/androidTest/java/com/microsoft/appcenter/sasquatch/activities/CrashesTest.java +++ b/apps/sasquatch/src/androidTest/java/com/microsoft/appcenter/sasquatch/activities/CrashesTest.java @@ -49,7 +49,6 @@ import static androidx.test.espresso.matcher.ViewMatchers.isRoot; import static androidx.test.espresso.matcher.ViewMatchers.withChild; import static androidx.test.espresso.matcher.ViewMatchers.withText; -import static android.util.Log.getStackTraceString; import static com.microsoft.appcenter.sasquatch.activities.utils.EspressoUtils.CHECK_DELAY; import static com.microsoft.appcenter.sasquatch.activities.utils.EspressoUtils.TOAST_DELAY; import static com.microsoft.appcenter.sasquatch.activities.utils.EspressoUtils.onToast; diff --git a/sdk/appcenter-analytics/src/test/java/com/microsoft/appcenter/analytics/AnalyticsTest.java b/sdk/appcenter-analytics/src/test/java/com/microsoft/appcenter/analytics/AnalyticsTest.java index 5080bb06e..0982d34ad 100644 --- a/sdk/appcenter-analytics/src/test/java/com/microsoft/appcenter/analytics/AnalyticsTest.java +++ b/sdk/appcenter-analytics/src/test/java/com/microsoft/appcenter/analytics/AnalyticsTest.java @@ -5,6 +5,37 @@ package com.microsoft.appcenter.analytics; +import static com.microsoft.appcenter.Flags.CRITICAL; +import static com.microsoft.appcenter.Flags.DEFAULTS; +import static com.microsoft.appcenter.Flags.NORMAL; +import static com.microsoft.appcenter.analytics.Analytics.ANALYTICS_CRITICAL_GROUP; +import static com.microsoft.appcenter.analytics.Analytics.ANALYTICS_GROUP; +import static com.microsoft.appcenter.analytics.Analytics.MAXIMUM_TRANSMISSION_INTERVAL_IN_SECONDS; +import static com.microsoft.appcenter.analytics.Analytics.MINIMUM_TRANSMISSION_INTERVAL_IN_SECONDS; +import static com.microsoft.appcenter.analytics.LogNameMatcher.logName; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.anyLong; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.ArgumentMatchers.contains; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.ArgumentMatchers.isA; +import static org.mockito.ArgumentMatchers.isNull; +import static org.mockito.ArgumentMatchers.notNull; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; +import static org.powermock.api.mockito.PowerMockito.doAnswer; +import static org.powermock.api.mockito.PowerMockito.mockStatic; +import static org.powermock.api.mockito.PowerMockito.verifyStatic; + import android.content.Context; import com.microsoft.appcenter.AppCenter; @@ -21,7 +52,6 @@ import com.microsoft.appcenter.analytics.ingestion.models.one.CommonSchemaEventLog; import com.microsoft.appcenter.analytics.ingestion.models.one.json.CommonSchemaEventLogFactory; import com.microsoft.appcenter.channel.Channel; -import com.microsoft.appcenter.ingestion.Ingestion; import com.microsoft.appcenter.ingestion.models.Log; import com.microsoft.appcenter.ingestion.models.json.LogFactory; import com.microsoft.appcenter.ingestion.models.properties.BooleanTypedProperty; @@ -39,7 +69,6 @@ import org.junit.Assert; import org.junit.Test; import org.mockito.ArgumentCaptor; -import org.mockito.ArgumentMatcher; import org.mockito.invocation.InvocationOnMock; import org.mockito.stubbing.Answer; @@ -51,38 +80,6 @@ import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; -import static com.microsoft.appcenter.Flags.DEFAULTS; -import static com.microsoft.appcenter.Flags.CRITICAL; -import static com.microsoft.appcenter.Flags.NORMAL; -import static com.microsoft.appcenter.analytics.Analytics.ANALYTICS_CRITICAL_GROUP; -import static com.microsoft.appcenter.analytics.Analytics.ANALYTICS_GROUP; -import static com.microsoft.appcenter.analytics.Analytics.MAXIMUM_TRANSMISSION_INTERVAL_IN_SECONDS; -import static com.microsoft.appcenter.analytics.Analytics.MINIMUM_TRANSMISSION_INTERVAL_IN_SECONDS; -import static com.microsoft.appcenter.analytics.LogNameMatcher.logName; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertTrue; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.anyInt; -import static org.mockito.ArgumentMatchers.anyLong; -import static org.mockito.ArgumentMatchers.anyString; -import static org.mockito.ArgumentMatchers.argThat; -import static org.mockito.ArgumentMatchers.contains; -import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.ArgumentMatchers.isA; -import static org.mockito.ArgumentMatchers.isNull; -import static org.mockito.ArgumentMatchers.notNull; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.never; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; -import static org.powermock.api.mockito.PowerMockito.doAnswer; -import static org.powermock.api.mockito.PowerMockito.mockStatic; -import static org.powermock.api.mockito.PowerMockito.verifyStatic; - public class AnalyticsTest extends AbstractAnalyticsTest { @After @@ -116,13 +113,13 @@ public void notInit() { /* Just check log is discarded without throwing any exception. */ Analytics.trackEvent("test"); - Analytics.trackEvent("test", new HashMap()); + Analytics.trackEvent("test", new HashMap<>()); Analytics.trackEvent("test", (Map) null); Analytics.trackEvent("test", (Map) null, 0); Analytics.trackEvent("test", (EventProperties) null); Analytics.trackEvent("test", (EventProperties) null, 0); Analytics.trackPage("test"); - Analytics.trackPage("test", new HashMap()); + Analytics.trackPage("test", new HashMap<>()); Analytics.trackPage("test", null); /* Verify we just get an error every time. */ @@ -230,7 +227,7 @@ public void trackEventFromAppWithEmptyMapProperty() { analytics.onStarted(mock(Context.class), channel, "", null, true); /* Send event with empty Map properties. */ - Analytics.trackEvent("eventName", new HashMap()); + Analytics.trackEvent("eventName", new HashMap<>()); verify(channel).enqueue(argumentCaptor.capture(), anyString(), eq(DEFAULTS)); assertNotNull(argumentCaptor.getValue()); assertEquals("eventName", argumentCaptor.getValue().getName()); @@ -438,7 +435,6 @@ public void trackPageFromLibrary() { @Test public void setEnabled() throws InterruptedException { Channel channel = mock(Channel.class); - final ArgumentCaptor captorGroupName = ArgumentCaptor.forClass(String.class); /* Before start it does not work to change state, it's disabled. */ Analytics analytics = Analytics.getInstance(); @@ -451,8 +447,8 @@ public void setEnabled() throws InterruptedException { analytics.onStarting(mAppCenterHandler); analytics.onStarted(mock(Context.class), channel, "", null, true); verify(channel).removeGroup(eq(analytics.getGroupName())); - verify(channel).addGroup(eq(ANALYTICS_CRITICAL_GROUP), anyInt(), anyLong(), anyInt(), isNull(Ingestion.class), any(Channel.GroupListener.class)); - verify(channel).addGroup(eq(ANALYTICS_GROUP), anyInt(), anyLong(), anyInt(), isNull(Ingestion.class), any(Channel.GroupListener.class)); + verify(channel).addGroup(eq(ANALYTICS_CRITICAL_GROUP), anyInt(), anyLong(), anyInt(), isNull(), any(Channel.GroupListener.class)); + verify(channel).addGroup(eq(ANALYTICS_GROUP), anyInt(), anyLong(), anyInt(), isNull(), any(Channel.GroupListener.class)); verify(channel).addListener(isA(SessionTracker.class)); verify(channel).addListener(isA(AnalyticsValidator.class)); verify(channel).addListener(isA(AnalyticsTransmissionTarget.getChannelListener().getClass())); @@ -537,7 +533,7 @@ public void notSendingLogsOnPause() { Channel channel = mock(Channel.class); analytics.onStarting(mAppCenterHandler); analytics.onStarted(mock(Context.class), channel, "", null, true); - verify(channel).addGroup(eq(analytics.getGroupName()), anyInt(), anyLong(), anyInt(), isNull(Ingestion.class), any(Channel.GroupListener.class)); + verify(channel).addGroup(eq(analytics.getGroupName()), anyInt(), anyLong(), anyInt(), isNull(), any(Channel.GroupListener.class)); verify(channel).addListener(isA(SessionTracker.class)); verify(channel).addListener(isA(AnalyticsValidator.class)); @@ -631,7 +627,7 @@ public void pauseResumeWhileDisabled() { Channel channel = mock(Channel.class); analytics.onStarting(mAppCenterHandler); analytics.onStarted(mock(Context.class), channel, "", null, true); - verify(channel).addGroup(eq(analytics.getGroupName()), anyInt(), anyLong(), anyInt(), isNull(Ingestion.class), any(Channel.GroupListener.class)); + verify(channel).addGroup(eq(analytics.getGroupName()), anyInt(), anyLong(), anyInt(), isNull(), any(Channel.GroupListener.class)); verify(channel).addListener(isA(SessionTracker.class)); verify(channel).addListener(isA(AnalyticsValidator.class)); @@ -726,7 +722,7 @@ public void analyticsListener() { analytics.onStarting(mAppCenterHandler); analytics.onStarted(mock(Context.class), channel, "", null, true); final ArgumentCaptor captor = ArgumentCaptor.forClass(Channel.GroupListener.class); - verify(channel, times(2)).addGroup(anyString(), anyInt(), anyLong(), anyInt(), isNull(Ingestion.class), captor.capture()); + verify(channel, times(2)).addGroup(anyString(), anyInt(), anyLong(), anyInt(), isNull(), captor.capture()); doAnswer(new Answer() { @Override @@ -738,9 +734,9 @@ public Void answer(InvocationOnMock invocation) { } }).when(channel).enqueue(any(Log.class), anyString(), anyInt()); Analytics.trackEvent("name"); - verify(listener).onBeforeSending(notNull(Log.class)); - verify(listener).onSendingSucceeded(notNull(Log.class)); - verify(listener).onSendingFailed(notNull(Log.class), notNull(Exception.class)); + verify(listener).onBeforeSending(notNull()); + verify(listener).onSendingSucceeded(notNull()); + verify(listener).onSendingFailed(notNull(), notNull()); } @Test @@ -751,8 +747,8 @@ public void analyticsListenerCriticalEvent() { Channel channel = mock(Channel.class); analytics.onStarting(mAppCenterHandler); analytics.onStarted(mock(Context.class), channel, "", null, true); - verify(channel).addGroup(eq(ANALYTICS_CRITICAL_GROUP), anyInt(), anyLong(), anyInt(), isNull(Ingestion.class), any(Channel.GroupListener.class)); - verify(channel).addGroup(eq(ANALYTICS_GROUP), anyInt(), anyLong(), anyInt(), isNull(Ingestion.class), any(Channel.GroupListener.class)); + verify(channel).addGroup(eq(ANALYTICS_CRITICAL_GROUP), anyInt(), anyLong(), anyInt(), isNull(), any(Channel.GroupListener.class)); + verify(channel).addGroup(eq(ANALYTICS_GROUP), anyInt(), anyLong(), anyInt(), isNull(), any(Channel.GroupListener.class)); Analytics.trackEvent("name", generateEventProperties(), Flags.CRITICAL); verify(channel).enqueue(any(Log.class), eq(ANALYTICS_CRITICAL_GROUP), eq(Flags.CRITICAL)); } @@ -765,8 +761,8 @@ public void analyticsListenerNormalEvent() { Channel channel = mock(Channel.class); analytics.onStarting(mAppCenterHandler); analytics.onStarted(mock(Context.class), channel, "", null, true); - verify(channel).addGroup(eq(ANALYTICS_CRITICAL_GROUP), anyInt(), anyLong(), anyInt(), isNull(Ingestion.class), any(Channel.GroupListener.class)); - verify(channel).addGroup(eq(ANALYTICS_GROUP), anyInt(), anyLong(), anyInt(), isNull(Ingestion.class), any(Channel.GroupListener.class)); + verify(channel).addGroup(eq(ANALYTICS_CRITICAL_GROUP), anyInt(), anyLong(), anyInt(), isNull(), any(Channel.GroupListener.class)); + verify(channel).addGroup(eq(ANALYTICS_GROUP), anyInt(), anyLong(), anyInt(), isNull(), any(Channel.GroupListener.class)); Analytics.trackEvent("name", generateEventProperties(), Flags.NORMAL); verify(channel).enqueue(any(Log.class), eq(ANALYTICS_GROUP), eq(Flags.NORMAL)); } @@ -869,14 +865,14 @@ public void createTransmissionTargetBeforeStart() { @Test public void unableToSetTransmissionIntervalMoreThanMaximum() { - Analytics analytics = Analytics.getInstance(); + Analytics.getInstance(); boolean result = Analytics.setTransmissionInterval(MAXIMUM_TRANSMISSION_INTERVAL_IN_SECONDS + 1); assertFalse(result); } @Test public void setTransmissionInterval() { - Analytics analytics = Analytics.getInstance(); + Analytics.getInstance(); boolean result = Analytics.setTransmissionInterval(MINIMUM_TRANSMISSION_INTERVAL_IN_SECONDS + 1); assertTrue(result); assertEquals(TimeUnit.MILLISECONDS.toSeconds(Analytics.getInstance().getTriggerInterval()),MINIMUM_TRANSMISSION_INTERVAL_IN_SECONDS + 1); @@ -884,7 +880,7 @@ public void setTransmissionInterval() { @Test public void unableToSetTransmissionIntervalLessThanMinimum() { - Analytics analytics = Analytics.getInstance(); + Analytics.getInstance(); boolean result = Analytics.setTransmissionInterval(MINIMUM_TRANSMISSION_INTERVAL_IN_SECONDS - 1); assertFalse(result); } diff --git a/sdk/appcenter-analytics/src/test/java/com/microsoft/appcenter/analytics/AnalyticsTransmissionTargetTest.java b/sdk/appcenter-analytics/src/test/java/com/microsoft/appcenter/analytics/AnalyticsTransmissionTargetTest.java index 1bfff56a2..994118698 100644 --- a/sdk/appcenter-analytics/src/test/java/com/microsoft/appcenter/analytics/AnalyticsTransmissionTargetTest.java +++ b/sdk/appcenter-analytics/src/test/java/com/microsoft/appcenter/analytics/AnalyticsTransmissionTargetTest.java @@ -10,7 +10,6 @@ import com.microsoft.appcenter.AppCenter; import com.microsoft.appcenter.AppCenterHandler; import com.microsoft.appcenter.analytics.ingestion.models.EventLog; -import com.microsoft.appcenter.analytics.ingestion.models.PageLog; import com.microsoft.appcenter.analytics.ingestion.models.one.CommonSchemaEventLog; import com.microsoft.appcenter.channel.Channel; import com.microsoft.appcenter.ingestion.models.Log; diff --git a/sdk/appcenter-analytics/src/test/java/com/microsoft/appcenter/analytics/PropertyConfiguratorTest.java b/sdk/appcenter-analytics/src/test/java/com/microsoft/appcenter/analytics/PropertyConfiguratorTest.java index f44d1f59e..d4e51bf1c 100644 --- a/sdk/appcenter-analytics/src/test/java/com/microsoft/appcenter/analytics/PropertyConfiguratorTest.java +++ b/sdk/appcenter-analytics/src/test/java/com/microsoft/appcenter/analytics/PropertyConfiguratorTest.java @@ -5,7 +5,6 @@ package com.microsoft.appcenter.analytics; -import android.content.ContentResolver; import android.content.Context; import android.provider.Settings.Secure; @@ -483,7 +482,7 @@ public void trackEventWithEmptyProperties() { AnalyticsTransmissionTarget target = Analytics.getTransmissionTarget("test"); /* Track event with empty properties. */ - target.trackEvent("eventName", Collections.emptyMap()); + target.trackEvent("eventName", Collections.emptyMap()); /* Check what event was sent. */ ArgumentCaptor eventLogArg = ArgumentCaptor.forClass(EventLog.class); diff --git a/sdk/appcenter-analytics/src/test/java/com/microsoft/appcenter/analytics/channel/AnalyticsValidatorForEventLogTest.java b/sdk/appcenter-analytics/src/test/java/com/microsoft/appcenter/analytics/channel/AnalyticsValidatorForEventLogTest.java index ace8a01f2..e602b6211 100644 --- a/sdk/appcenter-analytics/src/test/java/com/microsoft/appcenter/analytics/channel/AnalyticsValidatorForEventLogTest.java +++ b/sdk/appcenter-analytics/src/test/java/com/microsoft/appcenter/analytics/channel/AnalyticsValidatorForEventLogTest.java @@ -240,7 +240,7 @@ public void shouldNotFilterNonStringTypedProperty() { BooleanTypedProperty property = new BooleanTypedProperty(); property.setName("name"); property.setValue(true); - mEventLog.setTypedProperties(Collections.singletonList(property)); + mEventLog.setTypedProperties(Collections.singletonList(property)); assertFalse(mAnalyticsValidator.shouldFilter(mEventLog)); assertEquals(validEventName, mEventLog.getName()); assertEquals(1, mEventLog.getTypedProperties().size()); diff --git a/sdk/appcenter-analytics/src/test/java/com/microsoft/appcenter/analytics/channel/SessionTrackerTest.java b/sdk/appcenter-analytics/src/test/java/com/microsoft/appcenter/analytics/channel/SessionTrackerTest.java index c371fc2f0..6c59e1d08 100644 --- a/sdk/appcenter-analytics/src/test/java/com/microsoft/appcenter/analytics/channel/SessionTrackerTest.java +++ b/sdk/appcenter-analytics/src/test/java/com/microsoft/appcenter/analytics/channel/SessionTrackerTest.java @@ -20,7 +20,6 @@ import org.junit.Before; import org.junit.Rule; import org.junit.Test; -import org.mockito.ArgumentMatcher; import org.mockito.invocation.InvocationOnMock; import org.mockito.stubbing.Answer; import org.powermock.api.mockito.PowerMockito; @@ -44,10 +43,8 @@ import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.anySetOf; import static org.mockito.ArgumentMatchers.anyString; -import static org.mockito.ArgumentMatchers.argThat; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.ArgumentMatchers.isA; -import static org.mockito.ArgumentMatchers.notNull; import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; diff --git a/sdk/appcenter-crashes/src/androidTest/java/com/microsoft/appcenter/crashes/utils/ErrorLogHelperAndroidTest.java b/sdk/appcenter-crashes/src/androidTest/java/com/microsoft/appcenter/crashes/utils/ErrorLogHelperAndroidTest.java index b17025de4..e1ebe2903 100644 --- a/sdk/appcenter-crashes/src/androidTest/java/com/microsoft/appcenter/crashes/utils/ErrorLogHelperAndroidTest.java +++ b/sdk/appcenter-crashes/src/androidTest/java/com/microsoft/appcenter/crashes/utils/ErrorLogHelperAndroidTest.java @@ -90,7 +90,7 @@ public void getStoredFile() throws Exception { /* Generate test files. */ long date = 1000000; for (int i = 0; i < 3; i++) { - File file = new File(mErrorDirectory, new UUID(0, i).toString() + ErrorLogHelper.ERROR_LOG_FILE_EXTENSION); + File file = new File(mErrorDirectory, new UUID(0, i) + ErrorLogHelper.ERROR_LOG_FILE_EXTENSION); /* * file.setLastModified does not work on most devices or emulators and returns false. @@ -103,7 +103,7 @@ public void getStoredFile() throws Exception { assertEquals(testFiles[2], ErrorLogHelper.getLastErrorLogFile()); - testFiles[3] = new File(mErrorDirectory, new UUID(0, 3).toString() + ErrorLogHelper.THROWABLE_FILE_EXTENSION); + testFiles[3] = new File(mErrorDirectory, new UUID(0, 3) + ErrorLogHelper.THROWABLE_FILE_EXTENSION); FileManager.write(testFiles[3], "contents"); /* Get all error logs stored in the file system when logs exist. */ diff --git a/sdk/appcenter-crashes/src/test/java/com/microsoft/appcenter/crashes/utils/ErrorLogHelperTest.java b/sdk/appcenter-crashes/src/test/java/com/microsoft/appcenter/crashes/utils/ErrorLogHelperTest.java index 0439885d2..f433bc3be 100644 --- a/sdk/appcenter-crashes/src/test/java/com/microsoft/appcenter/crashes/utils/ErrorLogHelperTest.java +++ b/sdk/appcenter-crashes/src/test/java/com/microsoft/appcenter/crashes/utils/ErrorLogHelperTest.java @@ -14,7 +14,6 @@ import static org.junit.Assert.assertTrue; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyLong; -import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; @@ -58,9 +57,7 @@ import org.powermock.core.classloader.annotations.PrepareForTest; import org.powermock.modules.junit4.rule.PowerMockRule; -import java.io.BufferedWriter; import java.io.File; -import java.io.FileWriter; import java.io.FilenameFilter; import java.io.IOException; import java.util.Arrays; @@ -484,7 +481,6 @@ public void throwIOExceptionWhenGetMinidumpSubfolderWithDeviceInfo() throws java public void throwDeviceInfoExceptionWhenGetMinidumpSubfolderWithDeviceInfo() throws java.lang.Exception { /* Prepare data. */ - Device mockDevice = mock(Device.class); mockStatic(DeviceInfoHelper.class); when(DeviceInfoHelper.getDeviceInfo(any(Context.class))).thenThrow(new DeviceInfoHelper.DeviceInfoException("crash", new java.lang.Exception())); Context mockContext = mock(Context.class); diff --git a/sdk/appcenter-distribute-play/src/test/java/com/microsoft/appcenter/distribute/ReleaseDetailsTest.java b/sdk/appcenter-distribute-play/src/test/java/com/microsoft/appcenter/distribute/ReleaseDetailsTest.java index 0bc263412..d0de342b4 100644 --- a/sdk/appcenter-distribute-play/src/test/java/com/microsoft/appcenter/distribute/ReleaseDetailsTest.java +++ b/sdk/appcenter-distribute-play/src/test/java/com/microsoft/appcenter/distribute/ReleaseDetailsTest.java @@ -8,6 +8,8 @@ import org.junit.Test; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNull; public class ReleaseDetailsTest { @@ -15,15 +17,15 @@ public class ReleaseDetailsTest { public void getPropertyFromReleaseDetailsTest() { ReleaseDetails releaseDetails = new ReleaseDetails(); assertEquals(releaseDetails.getDistributionGroupId(), "distributionGroupId"); - assertEquals(releaseDetails.getDownloadUrl(), null); + assertNull(releaseDetails.getDownloadUrl()); assertEquals(releaseDetails.getId(), 1); assertEquals(releaseDetails.getMinApiLevel(), 0); assertEquals(releaseDetails.getReleaseHash(), "releaseHash"); assertEquals(releaseDetails.getReleaseNotes(), "releaseNotes"); - assertEquals(releaseDetails.getReleaseNotesUrl(), null); + assertNull(releaseDetails.getReleaseNotesUrl()); assertEquals(releaseDetails.getShortVersion(), "shortVersion"); assertEquals(releaseDetails.getSize(), 0); assertEquals(releaseDetails.getVersion(), 1); - assertEquals(releaseDetails.isMandatoryUpdate(), false); + assertFalse(releaseDetails.isMandatoryUpdate()); } } diff --git a/sdk/appcenter-distribute/src/androidTest/java/com/microsoft/appcenter/distribute/BrowserUtilsTest.java b/sdk/appcenter-distribute/src/androidTest/java/com/microsoft/appcenter/distribute/BrowserUtilsTest.java index 83035730b..5381f4432 100644 --- a/sdk/appcenter-distribute/src/androidTest/java/com/microsoft/appcenter/distribute/BrowserUtilsTest.java +++ b/sdk/appcenter-distribute/src/androidTest/java/com/microsoft/appcenter/distribute/BrowserUtilsTest.java @@ -48,6 +48,7 @@ public boolean matches(Object o) { } }; + @SuppressWarnings("InstantiationOfUtilityClass") @Test public void init() { new BrowserUtils(); @@ -75,7 +76,7 @@ public void noBrowserFound() { PackageManager packageManager = mock(PackageManager.class); doThrow(new ActivityNotFoundException()).when(activity).startActivity(any(Intent.class)); when(activity.getPackageManager()).thenReturn(packageManager); - when(packageManager.queryIntentActivities(any(Intent.class), anyInt())).thenReturn(Collections.emptyList()); + when(packageManager.queryIntentActivities(any(Intent.class), anyInt())).thenReturn(Collections.emptyList()); /* Open Browser then abort. */ BrowserUtils.openBrowser(TEST_URL, activity); diff --git a/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/AppStoreDetectionTest.java b/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/AppStoreDetectionTest.java index e80223ed0..e5029c776 100644 --- a/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/AppStoreDetectionTest.java +++ b/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/AppStoreDetectionTest.java @@ -20,12 +20,10 @@ import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; import static org.mockito.ArgumentMatchers.anyString; -import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import java.util.HashSet; -import java.util.Set; @SuppressWarnings("CanBeFinal") @RunWith(PowerMockRunner.class) diff --git a/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/DistributeBeforeApiSuccessTest.java b/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/DistributeBeforeApiSuccessTest.java index 5ae675168..bbe83a833 100644 --- a/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/DistributeBeforeApiSuccessTest.java +++ b/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/DistributeBeforeApiSuccessTest.java @@ -181,7 +181,7 @@ public void continueWhenEnabledForDebuggableBuildSetToTrue() throws Exception { url += "?" + PARAMETER_RELEASE_HASH + "=" + TEST_HASH; url += "&" + PARAMETER_REDIRECT_ID + "=" + mContext.getPackageName(); url += "&" + PARAMETER_REDIRECT_SCHEME + "=" + "appcenter"; - url += "&" + PARAMETER_REQUEST_ID + "=" + requestId.toString(); + url += "&" + PARAMETER_REQUEST_ID + "=" + requestId; url += "&" + PARAMETER_PLATFORM + "=" + PARAMETER_PLATFORM_VALUE; url += "&" + PARAMETER_ENABLE_UPDATE_SETUP_FAILURE_REDIRECT_KEY + "=" + "true"; url += "&" + PARAMETER_INSTALL_ID + "=" + mInstallId.toString(); @@ -558,7 +558,7 @@ public void testerAppNotInstalled() throws Exception { url += "?" + PARAMETER_RELEASE_HASH + "=" + TEST_HASH; url += "&" + PARAMETER_REDIRECT_ID + "=" + mContext.getPackageName(); url += "&" + PARAMETER_REDIRECT_SCHEME + "=" + "appcenter"; - url += "&" + PARAMETER_REQUEST_ID + "=" + requestId.toString(); + url += "&" + PARAMETER_REQUEST_ID + "=" + requestId; url += "&" + PARAMETER_PLATFORM + "=" + PARAMETER_PLATFORM_VALUE; url += "&" + PARAMETER_ENABLE_UPDATE_SETUP_FAILURE_REDIRECT_KEY + "=" + "true"; url += "&" + PARAMETER_INSTALL_ID + "=" + mInstallId.toString(); @@ -620,7 +620,7 @@ public void testerAppUpdateSetupFailed() throws Exception { url += "?" + PARAMETER_RELEASE_HASH + "=" + TEST_HASH; url += "&" + PARAMETER_REDIRECT_ID + "=" + mContext.getPackageName(); url += "&" + PARAMETER_REDIRECT_SCHEME + "=" + "appcenter"; - url += "&" + PARAMETER_REQUEST_ID + "=" + requestId.toString(); + url += "&" + PARAMETER_REQUEST_ID + "=" + requestId; url += "&" + PARAMETER_PLATFORM + "=" + PARAMETER_PLATFORM_VALUE; url += "&" + PARAMETER_ENABLE_UPDATE_SETUP_FAILURE_REDIRECT_KEY + "=" + "true"; url += "&" + PARAMETER_INSTALL_ID + "=" + mInstallId.toString(); @@ -753,7 +753,7 @@ public void happyPathUntilHangingCallWithToken() throws Exception { url += "?" + PARAMETER_RELEASE_HASH + "=" + TEST_HASH; url += "&" + PARAMETER_REDIRECT_ID + "=" + mContext.getPackageName(); url += "&" + PARAMETER_REDIRECT_SCHEME + "=" + "appcenter"; - url += "&" + PARAMETER_REQUEST_ID + "=" + requestId.toString(); + url += "&" + PARAMETER_REQUEST_ID + "=" + requestId; url += "&" + PARAMETER_PLATFORM + "=" + PARAMETER_PLATFORM_VALUE; url += "&" + PARAMETER_ENABLE_UPDATE_SETUP_FAILURE_REDIRECT_KEY + "=" + "true"; url += "&" + PARAMETER_INSTALL_ID + "=" + mInstallId.toString(); @@ -881,7 +881,7 @@ public void setUrls() throws Exception { url += "?" + PARAMETER_RELEASE_HASH + "=" + TEST_HASH; url += "&" + PARAMETER_REDIRECT_ID + "=" + mContext.getPackageName(); url += "&" + PARAMETER_REDIRECT_SCHEME + "=" + "appcenter"; - url += "&" + PARAMETER_REQUEST_ID + "=" + requestId.toString(); + url += "&" + PARAMETER_REQUEST_ID + "=" + requestId; url += "&" + PARAMETER_PLATFORM + "=" + PARAMETER_PLATFORM_VALUE; url += "&" + PARAMETER_ENABLE_UPDATE_SETUP_FAILURE_REDIRECT_KEY + "=" + "true"; url += "&" + PARAMETER_INSTALL_ID + "=" + mInstallId.toString(); diff --git a/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/DistributeCustomizationTest.java b/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/DistributeCustomizationTest.java index ab0dc0c4e..09013af07 100644 --- a/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/DistributeCustomizationTest.java +++ b/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/DistributeCustomizationTest.java @@ -37,7 +37,7 @@ import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.anyLong; -import static org.mockito.ArgumentMatchers.anyMapOf; +import static org.mockito.ArgumentMatchers.anyMap; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.contains; import static org.mockito.ArgumentMatchers.eq; @@ -211,7 +211,7 @@ private void distributeNotRecentCoverage(DistributeListener actualListener, Dist /* Mock http call. */ ArgumentCaptor httpCallback = ArgumentCaptor.forClass(ServiceCallback.class); - when(mHttpClient.callAsync(anyString(), anyString(), anyMapOf(String.class, String.class), any(HttpClient.CallTemplate.class), httpCallback.capture())).thenReturn(mock(ServiceCall.class)); + when(mHttpClient.callAsync(anyString(), anyString(), anyMap(), any(HttpClient.CallTemplate.class), httpCallback.capture())).thenReturn(mock(ServiceCall.class)); /* Mock data model. */ mockStatic(ReleaseDetails.class); @@ -277,7 +277,7 @@ private void distributeNoReleaseAvailableCoverage(DistributeListener actualListe /* Mock http call. */ ArgumentCaptor httpCallback = ArgumentCaptor.forClass(ServiceCallback.class); - when(mHttpClient.callAsync(anyString(), anyString(), anyMapOf(String.class, String.class), any(HttpClient.CallTemplate.class), httpCallback.capture())).thenReturn(mock(ServiceCall.class)); + when(mHttpClient.callAsync(anyString(), anyString(), anyMap(), any(HttpClient.CallTemplate.class), httpCallback.capture())).thenReturn(mock(ServiceCall.class)); /* Start Distribute service. */ restartProcessAndSdk(); @@ -378,7 +378,7 @@ public void handleUserUpdateActionNotProceededWithoutListener() throws Exception /* Enable the service. */ ReleaseDownloader cleanupReleaseDownloader = mock(ReleaseDownloader.class); - Mockito.when(ReleaseDownloaderFactory.create(any(Context.class), isNull(ReleaseDetails.class), any(ReleaseDownloadListener.class))).thenReturn(cleanupReleaseDownloader); + Mockito.when(ReleaseDownloaderFactory.create(any(Context.class), isNull(), any(ReleaseDownloadListener.class))).thenReturn(cleanupReleaseDownloader); distribute.setInstanceEnabled(true); /* Verify the method is called by resumeDistributeWorkflow. */ @@ -777,7 +777,7 @@ public void notifyUserUpdateActionPostponeAndThenDownload() throws Exception { private ReleaseDetails mockForCustomizationTest(boolean mandatory) throws Exception { /* Mock http call. */ - when(mHttpClient.callAsync(anyString(), anyString(), anyMapOf(String.class, String.class), any(HttpClient.CallTemplate.class), any(ServiceCallback.class))).thenAnswer(new Answer() { + when(mHttpClient.callAsync(anyString(), anyString(), anyMap(), any(HttpClient.CallTemplate.class), any(ServiceCallback.class))).thenAnswer(new Answer() { @Override public ServiceCall answer(InvocationOnMock invocation) { @@ -805,7 +805,7 @@ private void mockHtpCallFailed(String payload) { final HttpException httpException = new HttpException(new HttpResponse(404, payload)); /* Mock http call. */ - when(mHttpClient.callAsync(anyString(), anyString(), anyMapOf(String.class, String.class), any(HttpClient.CallTemplate.class), any(ServiceCallback.class))).thenAnswer(new Answer() { + when(mHttpClient.callAsync(anyString(), anyString(), anyMap(), any(HttpClient.CallTemplate.class), any(ServiceCallback.class))).thenAnswer(new Answer() { @Override public ServiceCall answer(InvocationOnMock invocation) { diff --git a/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/DistributeDisableAutomaticCheckForUpdateTest.java b/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/DistributeDisableAutomaticCheckForUpdateTest.java index 628d6af69..73b89bc0a 100644 --- a/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/DistributeDisableAutomaticCheckForUpdateTest.java +++ b/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/DistributeDisableAutomaticCheckForUpdateTest.java @@ -35,7 +35,7 @@ public void disableAutomaticCheckForUpdateBeforeDistributeStartDoesNotCheckForUp Distribute.getInstance().onActivityResumed(mActivity); /* No HTTP call done. */ - verify(mHttpClient, never()).callAsync(anyString(), anyString(), eq(Collections.emptyMap()), any(HttpClient.CallTemplate.class), any(ServiceCallback.class)); + verify(mHttpClient, never()).callAsync(anyString(), anyString(), eq(Collections.emptyMap()), any(HttpClient.CallTemplate.class), any(ServiceCallback.class)); } @Test @@ -47,7 +47,7 @@ public void disableAutomaticCheckForUpdateAfterDistributeStartChecksForUpdate() Distribute.getInstance().onActivityResumed(mActivity); /* HTTP call done. */ - verify(mHttpClient).callAsync(anyString(), anyString(), eq(Collections.emptyMap()), any(HttpClient.CallTemplate.class), any(ServiceCallback.class)); + verify(mHttpClient).callAsync(anyString(), anyString(), eq(Collections.emptyMap()), any(HttpClient.CallTemplate.class), any(ServiceCallback.class)); } @Test @@ -56,12 +56,12 @@ public void manualCheckWhileAutomaticCheckDisabledChecksForUpdate() { /* Start then call disable automatic check for update after Distribute has started. */ Distribute.disableAutomaticCheckForUpdate(); start(); - verify(mHttpClient, never()).callAsync(anyString(), anyString(), eq(Collections.emptyMap()), any(HttpClient.CallTemplate.class), any(ServiceCallback.class)); + verify(mHttpClient, never()).callAsync(anyString(), anyString(), eq(Collections.emptyMap()), any(HttpClient.CallTemplate.class), any(ServiceCallback.class)); Distribute.checkForUpdate(); Distribute.getInstance().onActivityResumed(mActivity); /* HTTP call done. */ - verify(mHttpClient).callAsync(anyString(), anyString(), eq(Collections.emptyMap()), any(HttpClient.CallTemplate.class), any(ServiceCallback.class)); + verify(mHttpClient).callAsync(anyString(), anyString(), eq(Collections.emptyMap()), any(HttpClient.CallTemplate.class), any(ServiceCallback.class)); } @Test @@ -71,11 +71,11 @@ public void manualCheckAfterActivityResumedWhileAutomaticCheckDisabledChecksForU Distribute.disableAutomaticCheckForUpdate(); start(); Distribute.getInstance().onActivityResumed(mActivity); - verify(mHttpClient, never()).callAsync(anyString(), anyString(), eq(Collections.emptyMap()), any(HttpClient.CallTemplate.class), any(ServiceCallback.class)); + verify(mHttpClient, never()).callAsync(anyString(), anyString(), eq(Collections.emptyMap()), any(HttpClient.CallTemplate.class), any(ServiceCallback.class)); Distribute.checkForUpdate(); /* HTTP call done. */ - verify(mHttpClient).callAsync(anyString(), anyString(), eq(Collections.emptyMap()), any(HttpClient.CallTemplate.class), any(ServiceCallback.class)); + verify(mHttpClient).callAsync(anyString(), anyString(), eq(Collections.emptyMap()), any(HttpClient.CallTemplate.class), any(ServiceCallback.class)); } @Test @@ -89,7 +89,7 @@ public void checkForUpdateAfterManualCheckCompletesDoesNotCheckForUpdatesAgain() /* Check http call done. */ ArgumentCaptor httpCallback = ArgumentCaptor.forClass(ServiceCallback.class); - verify(mHttpClient).callAsync(anyString(), anyString(), eq(Collections.emptyMap()), any(HttpClient.CallTemplate.class), httpCallback.capture()); + verify(mHttpClient).callAsync(anyString(), anyString(), eq(Collections.emptyMap()), any(HttpClient.CallTemplate.class), httpCallback.capture()); /* Complete call with no new release (this will return the default mock mReleaseDetails with version 0). */ HttpResponse response = mock(HttpResponse.class); @@ -100,11 +100,11 @@ public void checkForUpdateAfterManualCheckCompletesDoesNotCheckForUpdatesAgain() restartResumeLauncher(mActivity); /* Do not call again. */ - verify(mHttpClient).callAsync(anyString(), anyString(), eq(Collections.emptyMap()), any(HttpClient.CallTemplate.class), notNull(ServiceCallback.class)); + verify(mHttpClient).callAsync(anyString(), anyString(), eq(Collections.emptyMap()), any(HttpClient.CallTemplate.class), notNull()); /* Check for update manually again and verify one more call. */ Distribute.checkForUpdate(); - verify(mHttpClient, times(2)).callAsync(anyString(), anyString(), eq(Collections.emptyMap()), any(HttpClient.CallTemplate.class), notNull(ServiceCallback.class)); + verify(mHttpClient, times(2)).callAsync(anyString(), anyString(), eq(Collections.emptyMap()), any(HttpClient.CallTemplate.class), notNull()); } @Test @@ -118,7 +118,7 @@ public void manualCheckForUpdateAfterManualCheckCompletesChecksForUpdateAgain() /* Check http call done. */ ArgumentCaptor httpCallback = ArgumentCaptor.forClass(ServiceCallback.class); - verify(mHttpClient).callAsync(anyString(), anyString(), eq(Collections.emptyMap()), any(HttpClient.CallTemplate.class), httpCallback.capture()); + verify(mHttpClient).callAsync(anyString(), anyString(), eq(Collections.emptyMap()), any(HttpClient.CallTemplate.class), httpCallback.capture()); /* Complete call with no new release (this will return the default mock mReleaseDetails with version 0). */ HttpResponse response = mock(HttpResponse.class); @@ -129,6 +129,6 @@ public void manualCheckForUpdateAfterManualCheckCompletesChecksForUpdateAgain() Distribute.checkForUpdate(); /* Http call done again. */ - verify(mHttpClient, times(2)).callAsync(anyString(), anyString(), eq(Collections.emptyMap()), any(HttpClient.CallTemplate.class), notNull(ServiceCallback.class)); + verify(mHttpClient, times(2)).callAsync(anyString(), anyString(), eq(Collections.emptyMap()), any(HttpClient.CallTemplate.class), notNull()); } } diff --git a/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/DistributeUpdateTrackTest.java b/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/DistributeUpdateTrackTest.java index 3a561c03e..9c76f7438 100644 --- a/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/DistributeUpdateTrackTest.java +++ b/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/DistributeUpdateTrackTest.java @@ -73,7 +73,7 @@ public void switchTrack() throws PackageManager.NameNotFoundException { /* Check http call done. */ ArgumentCaptor httpCallback = ArgumentCaptor.forClass(ServiceCallback.class); - verify(mHttpClient).callAsync(anyString(), anyString(), eq(Collections.emptyMap()), any(HttpClient.CallTemplate.class), httpCallback.capture()); + verify(mHttpClient).callAsync(anyString(), anyString(), eq(Collections.emptyMap()), any(HttpClient.CallTemplate.class), httpCallback.capture()); /* Without browser because its public. */ verifyStatic(BrowserUtils.class, never()); diff --git a/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/DistributeWarnAlertSystemWindowsTest.java b/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/DistributeWarnAlertSystemWindowsTest.java index f4ef6f9dd..2824306e0 100644 --- a/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/DistributeWarnAlertSystemWindowsTest.java +++ b/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/DistributeWarnAlertSystemWindowsTest.java @@ -89,7 +89,6 @@ public void setUpDialog() throws Exception { when(DistributeUtils.loadCachedReleaseDetails()).thenReturn(mReleaseDetails); /* Start distribute from background. */ - ArgumentCaptor releaseDownloadListener = ArgumentCaptor.forClass(ReleaseDownloadListener.class); Distribute.getInstance().startFromBackground(mContext); /* Start distribute module. */ diff --git a/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/DistributeWarnUnknownSourcesTest.java b/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/DistributeWarnUnknownSourcesTest.java index 70218f632..2d5986cc2 100644 --- a/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/DistributeWarnUnknownSourcesTest.java +++ b/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/DistributeWarnUnknownSourcesTest.java @@ -41,7 +41,7 @@ import static com.microsoft.appcenter.distribute.DistributeConstants.PREFERENCE_KEY_DOWNLOAD_STATE; import static com.microsoft.appcenter.distribute.DistributeConstants.PREFERENCE_KEY_UPDATE_TOKEN; import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.anyMapOf; +import static org.mockito.ArgumentMatchers.anyMap; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.doThrow; @@ -80,7 +80,7 @@ public void setUpDialog() throws Exception { /* Mock we already have redirection parameters. */ when(SharedPreferencesManager.getString(PREFERENCE_KEY_DISTRIBUTION_GROUP_ID)).thenReturn("some group"); when(SharedPreferencesManager.getString(PREFERENCE_KEY_UPDATE_TOKEN)).thenReturn("some token"); - when(mHttpClient.callAsync(anyString(), anyString(), anyMapOf(String.class, String.class), any(HttpClient.CallTemplate.class), any(ServiceCallback.class))).thenAnswer(new Answer() { + when(mHttpClient.callAsync(anyString(), anyString(), anyMap(), any(HttpClient.CallTemplate.class), any(ServiceCallback.class))).thenAnswer(new Answer() { @Override public ServiceCall answer(InvocationOnMock invocation) { diff --git a/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/PermissionUtilsTest.java b/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/PermissionUtilsTest.java index 2da77b2a3..3c7122f3f 100644 --- a/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/PermissionUtilsTest.java +++ b/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/PermissionUtilsTest.java @@ -36,6 +36,7 @@ public class PermissionUtilsTest { @Mock Context mContext; + @SuppressWarnings("InstantiationOfUtilityClass") @Test public void init() { new PermissionUtils(); diff --git a/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/ReleaseDownloadListenerTest.java b/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/ReleaseDownloadListenerTest.java index d32b5a388..155408415 100644 --- a/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/ReleaseDownloadListenerTest.java +++ b/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/ReleaseDownloadListenerTest.java @@ -386,8 +386,7 @@ public void onComplete() throws Exception { @Test public void onCompleteNotify() throws Exception { - boolean mandatoryUpdate = false; - ReleaseDetails mockReleaseDetails = mockReleaseDetails(mandatoryUpdate); + ReleaseDetails mockReleaseDetails = mockReleaseDetails(false); /* Notify the download. */ when(mDistribute.notifyDownload(mockReleaseDetails)).thenReturn(true); @@ -401,14 +400,13 @@ public void onCompleteNotify() throws Exception { @Test public void onCompleteActivityNotResolved() throws Exception { - boolean mandatoryUpdate = false; /* Mock notify download result. */ when(mDistribute.notifyDownload(any(ReleaseDetails.class))).thenReturn(true); /* Mock resolving to null activity. */ when(mInstallIntent.resolveActivity(any(PackageManager.class))).thenReturn(null); - ReleaseDetails mockReleaseDetails = mockReleaseDetails(mandatoryUpdate); + ReleaseDetails mockReleaseDetails = mockReleaseDetails(false); ReleaseDownloadListener releaseDownloadListener = new ReleaseDownloadListener(mContext, mockReleaseDetails); /* Verify that nothing is called and the method is exited early with false result. */ diff --git a/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/download/ReleaseDownloaderFactoryTest.java b/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/download/ReleaseDownloaderFactoryTest.java index 68d51d1c6..0d0202794 100644 --- a/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/download/ReleaseDownloaderFactoryTest.java +++ b/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/download/ReleaseDownloaderFactoryTest.java @@ -39,7 +39,7 @@ public void tearDown() throws Exception { TestUtils.setInternalState(Build.VERSION.class, "SDK_INT", 0); } - @SuppressWarnings("ObviousNullCheck") + @SuppressWarnings({"ObviousNullCheck", "InstantiationOfUtilityClass"}) @Test public void generatedConstructor() { diff --git a/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/download/manager/DownloadManagerDistributeDeadlockTest.java b/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/download/manager/DownloadManagerDistributeDeadlockTest.java index e3fe606ed..39ccb4f1c 100644 --- a/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/download/manager/DownloadManagerDistributeDeadlockTest.java +++ b/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/download/manager/DownloadManagerDistributeDeadlockTest.java @@ -8,7 +8,6 @@ import android.content.pm.ApplicationInfo; import android.content.pm.PackageInfo; import android.content.pm.PackageManager; -import android.database.Cursor; import android.net.Uri; import android.widget.Toast; @@ -32,11 +31,9 @@ import org.junit.runner.Description; import org.junit.runners.model.Statement; import org.junit.runners.model.TestTimedOutException; -import org.mockito.Matchers; import org.mockito.Mock; import org.mockito.invocation.InvocationOnMock; import org.mockito.stubbing.Answer; -import org.powermock.api.mockito.PowerMockito; import org.powermock.core.classloader.annotations.PrepareForTest; import org.powermock.modules.junit4.rule.PowerMockRule; import org.powermock.reflect.Whitebox; @@ -122,9 +119,6 @@ public void evaluate() throws Throwable { @Mock Context mContext; - @Mock - Cursor mCursor; - @Mock Activity mActivity; diff --git a/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/download/manager/DownloadManagerReleaseDownloaderTest.java b/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/download/manager/DownloadManagerReleaseDownloaderTest.java index 30b989d21..d92ced011 100644 --- a/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/download/manager/DownloadManagerReleaseDownloaderTest.java +++ b/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/download/manager/DownloadManagerReleaseDownloaderTest.java @@ -36,7 +36,6 @@ import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.anyLong; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.eq; diff --git a/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/download/manager/DownloadManagerRequestTaskTest.java b/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/download/manager/DownloadManagerRequestTaskTest.java index 7ac34a63d..1532e11a7 100644 --- a/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/download/manager/DownloadManagerRequestTaskTest.java +++ b/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/download/manager/DownloadManagerRequestTaskTest.java @@ -25,7 +25,6 @@ import org.powermock.modules.junit4.rule.PowerMockRule; import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.anyLong; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.mock; diff --git a/sdk/appcenter/src/androidTest/java/com/microsoft/appcenter/http/HttpUtilsAndroidTest.java b/sdk/appcenter/src/androidTest/java/com/microsoft/appcenter/http/HttpUtilsAndroidTest.java index 05acf4c53..e55c8f980 100644 --- a/sdk/appcenter/src/androidTest/java/com/microsoft/appcenter/http/HttpUtilsAndroidTest.java +++ b/sdk/appcenter/src/androidTest/java/com/microsoft/appcenter/http/HttpUtilsAndroidTest.java @@ -28,6 +28,7 @@ @SuppressWarnings("unused") public class HttpUtilsAndroidTest { + @SuppressWarnings("InstantiationOfUtilityClass") @Test public void utilsCoverage() { new HttpUtils(); diff --git a/sdk/appcenter/src/androidTest/java/com/microsoft/appcenter/ingestion/models/json/JSONDateUtilsAndroidTest.java b/sdk/appcenter/src/androidTest/java/com/microsoft/appcenter/ingestion/models/json/JSONDateUtilsAndroidTest.java index 5938bda1b..c63fff89d 100644 --- a/sdk/appcenter/src/androidTest/java/com/microsoft/appcenter/ingestion/models/json/JSONDateUtilsAndroidTest.java +++ b/sdk/appcenter/src/androidTest/java/com/microsoft/appcenter/ingestion/models/json/JSONDateUtilsAndroidTest.java @@ -15,6 +15,7 @@ @SuppressWarnings("unused") public class JSONDateUtilsAndroidTest { + @SuppressWarnings("InstantiationOfUtilityClass") @Test public void utilsCoverage() { new JSONDateUtils(); diff --git a/sdk/appcenter/src/androidTest/java/com/microsoft/appcenter/ingestion/models/json/JSONUtilsAndroidTest.java b/sdk/appcenter/src/androidTest/java/com/microsoft/appcenter/ingestion/models/json/JSONUtilsAndroidTest.java index aca13e11f..316d870dc 100644 --- a/sdk/appcenter/src/androidTest/java/com/microsoft/appcenter/ingestion/models/json/JSONUtilsAndroidTest.java +++ b/sdk/appcenter/src/androidTest/java/com/microsoft/appcenter/ingestion/models/json/JSONUtilsAndroidTest.java @@ -29,6 +29,7 @@ @SuppressWarnings("unused") public class JSONUtilsAndroidTest { + @SuppressWarnings("InstantiationOfUtilityClass") @Test public void utilsCoverage() { new JSONUtils(); diff --git a/sdk/appcenter/src/androidTest/java/com/microsoft/appcenter/ingestion/models/json/LogSerializerAndroidTest.java b/sdk/appcenter/src/androidTest/java/com/microsoft/appcenter/ingestion/models/json/LogSerializerAndroidTest.java index 3f3762fb8..46812359e 100644 --- a/sdk/appcenter/src/androidTest/java/com/microsoft/appcenter/ingestion/models/json/LogSerializerAndroidTest.java +++ b/sdk/appcenter/src/androidTest/java/com/microsoft/appcenter/ingestion/models/json/LogSerializerAndroidTest.java @@ -17,9 +17,7 @@ import java.util.ArrayList; import java.util.Collections; import java.util.Date; -import java.util.HashMap; import java.util.List; -import java.util.Map; import java.util.UUID; import static com.microsoft.appcenter.ingestion.models.json.MockLog.MOCK_LOG_TYPE; @@ -34,7 +32,7 @@ public class LogSerializerAndroidTest { @Test public void emptyLogs() throws JSONException { LogContainer expectedContainer = new LogContainer(); - expectedContainer.setLogs(Collections.emptyList()); + expectedContainer.setLogs(Collections.emptyList()); LogSerializer serializer = new DefaultLogSerializer(); String payload = serializer.serializeContainer(expectedContainer); android.util.Log.v(TAG, payload); diff --git a/sdk/appcenter/src/androidTest/java/com/microsoft/appcenter/ingestion/models/one/CommonSchemaDataUtilsAndroidTest.java b/sdk/appcenter/src/androidTest/java/com/microsoft/appcenter/ingestion/models/one/CommonSchemaDataUtilsAndroidTest.java index ee13cca19..104a35099 100644 --- a/sdk/appcenter/src/androidTest/java/com/microsoft/appcenter/ingestion/models/one/CommonSchemaDataUtilsAndroidTest.java +++ b/sdk/appcenter/src/androidTest/java/com/microsoft/appcenter/ingestion/models/one/CommonSchemaDataUtilsAndroidTest.java @@ -41,6 +41,7 @@ private static StringTypedProperty typedProperty(String key, String value) { return stringTypedProperty; } + @SuppressWarnings("InstantiationOfUtilityClass") @Test public void coverInit() { new CommonSchemaDataUtils(); @@ -57,7 +58,7 @@ public void nullProperties() { @Test public void emptyProperties() { MockCommonSchemaLog log = new MockCommonSchemaLog(); - CommonSchemaDataUtils.addCommonSchemaData(Collections.emptyList(), log); + CommonSchemaDataUtils.addCommonSchemaData(Collections.emptyList(), log); assertEquals(0, log.getData().getProperties().length()); assertNull(log.getExt()); } diff --git a/sdk/appcenter/src/androidTest/java/com/microsoft/appcenter/persistence/DatabasePersistenceAndroidTest.java b/sdk/appcenter/src/androidTest/java/com/microsoft/appcenter/persistence/DatabasePersistenceAndroidTest.java index 40430a04a..1ce06159f 100644 --- a/sdk/appcenter/src/androidTest/java/com/microsoft/appcenter/persistence/DatabasePersistenceAndroidTest.java +++ b/sdk/appcenter/src/androidTest/java/com/microsoft/appcenter/persistence/DatabasePersistenceAndroidTest.java @@ -140,7 +140,7 @@ private void putLog(int inputFlags, Integer persistedPriorityFlag) throws Persis /* Get a log from persistence. */ List outputLogs = new ArrayList<>(); - persistence.getLogs("test-p1", Collections.emptyList(), 1, outputLogs); + persistence.getLogs("test-p1", Collections.emptyList(), 1, outputLogs); assertEquals(1, outputLogs.size()); assertEquals(log, outputLogs.get(0)); assertEquals(1, persistence.countLogs("test-p1")); @@ -205,7 +205,7 @@ public void putLargeLogAndDeleteAll() throws PersistenceException { /* Get a log from persistence. */ List outputLogs = new ArrayList<>(); - persistence.getLogs("test-p1", Collections.emptyList(), 1, outputLogs); + persistence.getLogs("test-p1", Collections.emptyList(), 1, outputLogs); assertEquals(1, outputLogs.size()); assertEquals(log, outputLogs.get(0)); assertEquals(1, persistence.countLogs("test-p1")); @@ -312,7 +312,7 @@ public void putLargeLogFailsToRead() throws PersistenceException { /* We won't be able to read the log now but persistence should delete the SQLite log on error. */ List outputLogs = new ArrayList<>(); - persistence.getLogs("test-p1", Collections.emptyList(), 1, outputLogs); + persistence.getLogs("test-p1", Collections.emptyList(), 1, outputLogs); assertEquals(0, outputLogs.size()); assertEquals(0, persistence.countLogs("test-p1")); } finally { @@ -396,7 +396,7 @@ public void putTooManyLogs() throws PersistenceException { /* Get logs from persistence and check we have all the most recent logs. */ List actualLogs = new ArrayList<>(); - persistence.getLogs(group, Collections.emptyList(), allLogs.size(), actualLogs); + persistence.getLogs(group, Collections.emptyList(), allLogs.size(), actualLogs); assertEquals(expectedLogs, actualLogs); } finally { persistence.close(); @@ -444,7 +444,7 @@ public void putTooManyLogsMixedPriorities() throws PersistenceException { /* Get logs from persistence and check we have all the most recent logs. */ List actualLogs = new ArrayList<>(); - persistence.getLogs(group, Collections.emptyList(), 2000, actualLogs); + persistence.getLogs(group, Collections.emptyList(), 2000, actualLogs); assertEquals(expectedLogs, actualLogs); } finally { persistence.close(); @@ -624,7 +624,7 @@ public void putNormalLogCloseToMaxSizeKeepsCritical() throws PersistenceExceptio /* Get logs from persistence: critical were kept. */ List outputLogs = new ArrayList<>(); - persistence.getLogs("test-p1", Collections.emptyList(), expectedLogs.size() + 1, outputLogs); + persistence.getLogs("test-p1", Collections.emptyList(), expectedLogs.size() + 1, outputLogs); assertTrue(expectedLogs.size() >= persistence.countLogs("test-p1")); assertThat(expectedLogs, hasItems(outputLogs.toArray(new Log[0]))); } finally { @@ -664,7 +664,7 @@ public void putNormalLogFailsIfFullOfCritical() throws PersistenceException { /* Get logs from persistence: critical were kept. */ List outputLogs = new ArrayList<>(); - persistence.getLogs("test-p1", Collections.emptyList(), expectedLogs.size() + 1, outputLogs); + persistence.getLogs("test-p1", Collections.emptyList(), expectedLogs.size() + 1, outputLogs); assertTrue(expectedLogs.size() >= persistence.countLogs("test-p1")); assertThat(expectedLogs, hasItems(outputLogs.toArray(new Log[0]))); } finally { @@ -701,9 +701,9 @@ public void deleteLogs() throws PersistenceException { List outputLogs1 = new ArrayList<>(); List outputLogs2 = new ArrayList<>(); List outputLogs3 = new ArrayList<>(); - String id = persistence.getLogs("test-p1", Collections.emptyList(), 5, outputLogs1); - persistence.getLogs("test-p2", Collections.emptyList(), 5, outputLogs2); - persistence.getLogs("test-p3", Collections.emptyList(), 5, outputLogs3); + String id = persistence.getLogs("test-p1", Collections.emptyList(), 5, outputLogs1); + persistence.getLogs("test-p2", Collections.emptyList(), 5, outputLogs2); + persistence.getLogs("test-p3", Collections.emptyList(), 5, outputLogs3); /* Verify. */ assertNotNull(id); @@ -790,8 +790,8 @@ public void deleteLogsForGroup() throws PersistenceException { /* Get a log from persistence. */ List outputLogs = new ArrayList<>(); - String id1 = persistence.getLogs("test-p1", Collections.emptyList(), 5, outputLogs); - String id2 = persistence.getLogs("test-p2", Collections.emptyList(), 5, outputLogs); + String id1 = persistence.getLogs("test-p1", Collections.emptyList(), 5, outputLogs); + String id2 = persistence.getLogs("test-p2", Collections.emptyList(), 5, outputLogs); assertNotNull(id1); assertNotNull(id2); @@ -801,7 +801,7 @@ public void deleteLogsForGroup() throws PersistenceException { /* Try another get for verification. */ outputLogs.clear(); - persistence.getLogs("test-p3", Collections.emptyList(), 5, outputLogs); + persistence.getLogs("test-p3", Collections.emptyList(), 5, outputLogs); /* Verify. */ Map> pendingGroups = persistence.mPendingDbIdentifiersGroups; @@ -816,7 +816,7 @@ public void deleteLogsForGroup() throws PersistenceException { /* Verify one log still persists in the database. */ persistence.clearPendingLogState(); outputLogs.clear(); - persistence.getLogs("test-p2", Collections.emptyList(), 5, outputLogs); + persistence.getLogs("test-p2", Collections.emptyList(), 5, outputLogs); assertEquals(1, outputLogs.size()); assertEquals(log3, outputLogs.get(0)); @@ -865,7 +865,7 @@ public void getLogsWithNormalPriority() throws PersistenceException { /* Clear. Nothing to get after. */ persistence.mDatabaseManager.clear(); List outputLogs = new ArrayList<>(); - assertNull(persistence.getLogs("test", Collections.emptyList(), sizeForGetLogs, outputLogs)); + assertNull(persistence.getLogs("test", Collections.emptyList(), sizeForGetLogs, outputLogs)); assertTrue(outputLogs.isEmpty()); assertEquals(0, persistence.countLogs("test")); } finally { @@ -878,14 +878,14 @@ private void getAllLogs(DatabasePersistence persistence, int numberOfLogs, int s int expected = 0; do { numberOfLogs -= expected; - persistence.getLogs("test", Collections.emptyList(), sizeForGetLogs, outputLogs); + persistence.getLogs("test", Collections.emptyList(), sizeForGetLogs, outputLogs); expected = Math.min(Math.max(numberOfLogs, 0), sizeForGetLogs); assertEquals(expected, outputLogs.size()); outputLogs.clear(); } while (numberOfLogs > 0); /* Get should be 0 now. */ - persistence.getLogs("test", Collections.emptyList(), sizeForGetLogs, outputLogs); + persistence.getLogs("test", Collections.emptyList(), sizeForGetLogs, outputLogs); assertEquals(0, outputLogs.size()); } @@ -926,7 +926,7 @@ public void getLogsWithMixedPriorities() throws PersistenceException { /* Get logs and check order. */ List actualLogs = new ArrayList<>(); - persistence.getLogs("test", Collections.emptyList(), expectedLogs.size(), actualLogs); + persistence.getLogs("test", Collections.emptyList(), expectedLogs.size(), actualLogs); assertEquals(expectedLogs, actualLogs); } finally { persistence.close(); @@ -982,7 +982,7 @@ public void getLogsFilteringOutPausedTargetKeys() throws PersistenceException { /* If we try to get a second batch without filtering, we should get all disabled logs. */ outLogs.clear(); - batchId = persistence.getLogs("test", Collections.emptyList(), limit, outLogs); + batchId = persistence.getLogs("test", Collections.emptyList(), limit, outLogs); assertNotNull(batchId); assertEquals(numberOfLogsPerKey * 2, outLogs.size()); for (Log log : outLogs) { @@ -1044,7 +1044,7 @@ public void getLogsException() throws PersistenceException, JSONException { /* Get. */ List outputLogs = new ArrayList<>(); - persistence.getLogs("test", Collections.emptyList(), 10, outputLogs); + persistence.getLogs("test", Collections.emptyList(), 10, outputLogs); assertEquals(numberOfLogs / 2, outputLogs.size()); assertEquals(2, persistence.mDatabaseManager.getRowCount()); } finally { @@ -1066,7 +1066,7 @@ public void getLogsWithNullDate() throws PersistenceException { /* Get logs. */ List outputLogs = new ArrayList<>(); - persistence.getLogs("test", Collections.emptyList(), 4, outputLogs); + persistence.getLogs("test", Collections.emptyList(), 4, outputLogs); assertEquals(4, outputLogs.size()); } @@ -1157,7 +1157,7 @@ public void upgradeFromVersion5to6() throws PersistenceException, JSONException /* Get new data. */ assertEquals(1, persistence.countLogs("test/one")); List outputLogs = new ArrayList<>(); - persistence.getLogs("test/one", Collections.emptyList(), 1, outputLogs); + persistence.getLogs("test/one", Collections.emptyList(), 1, outputLogs); assertEquals(1, outputLogs.size()); assertEquals(commonSchemaLog, outputLogs.get(0)); diff --git a/sdk/appcenter/src/androidTest/java/com/microsoft/appcenter/utils/HandlerUtilsTest.java b/sdk/appcenter/src/androidTest/java/com/microsoft/appcenter/utils/HandlerUtilsTest.java index 743b7297b..1be9748a6 100644 --- a/sdk/appcenter/src/androidTest/java/com/microsoft/appcenter/utils/HandlerUtilsTest.java +++ b/sdk/appcenter/src/androidTest/java/com/microsoft/appcenter/utils/HandlerUtilsTest.java @@ -17,6 +17,7 @@ public class HandlerUtilsTest { + @SuppressWarnings("InstantiationOfUtilityClass") @Test public void init() { new HandlerUtils(); diff --git a/sdk/appcenter/src/androidTest/java/com/microsoft/appcenter/utils/IdHelperAndroidTest.java b/sdk/appcenter/src/androidTest/java/com/microsoft/appcenter/utils/IdHelperAndroidTest.java index cb4f4f964..e69e5a12a 100644 --- a/sdk/appcenter/src/androidTest/java/com/microsoft/appcenter/utils/IdHelperAndroidTest.java +++ b/sdk/appcenter/src/androidTest/java/com/microsoft/appcenter/utils/IdHelperAndroidTest.java @@ -44,7 +44,7 @@ public void getInstallId() { SharedPreferencesManager.putString(PrefStorageConstants.KEY_INSTALL_ID, expected.toString()); actual = IdHelper.getInstallId(); - assertNotEquals(wrongUUID, actual); assertNotNull(actual); + assertNotEquals(wrongUUID, actual.toString()); } } \ No newline at end of file diff --git a/sdk/appcenter/src/androidTest/java/com/microsoft/appcenter/utils/storage/SQLiteUtilsAndroidTest.java b/sdk/appcenter/src/androidTest/java/com/microsoft/appcenter/utils/storage/SQLiteUtilsAndroidTest.java index 0171c7b7c..c0e14fad2 100644 --- a/sdk/appcenter/src/androidTest/java/com/microsoft/appcenter/utils/storage/SQLiteUtilsAndroidTest.java +++ b/sdk/appcenter/src/androidTest/java/com/microsoft/appcenter/utils/storage/SQLiteUtilsAndroidTest.java @@ -12,6 +12,7 @@ @SuppressWarnings("unused") public class SQLiteUtilsAndroidTest { + @SuppressWarnings("InstantiationOfUtilityClass") @Test public void test() { new SQLiteUtils(); diff --git a/sdk/appcenter/src/test/java/com/microsoft/appcenter/AppCenterLibraryTest.java b/sdk/appcenter/src/test/java/com/microsoft/appcenter/AppCenterLibraryTest.java index e1806074b..2715af1a0 100644 --- a/sdk/appcenter/src/test/java/com/microsoft/appcenter/AppCenterLibraryTest.java +++ b/sdk/appcenter/src/test/java/com/microsoft/appcenter/AppCenterLibraryTest.java @@ -91,7 +91,7 @@ public void startFromLibraryThenFromApp() { assertEquals(1, AppCenter.getInstance().getServices().size()); assertTrue(AppCenter.getInstance().getServices().contains(DummyService.getInstance())); verify(DummyService.getInstance()).getLogFactories(); - verify(DummyService.getInstance()).onStarted(any(Context.class), any(Channel.class), isNull(String.class), isNull(String.class), eq(false)); + verify(DummyService.getInstance()).onStarted(any(Context.class), any(Channel.class), isNull(), isNull(), eq(false)); verify(mApplication).registerActivityLifecycleCallbacks(DummyService.getInstance()); verify(mChannel, never()).enqueue(eq(mStartServiceLog), eq(CORE_GROUP), anyInt()); verify(mChannel, never()).setAppSecret(anyString()); @@ -114,13 +114,13 @@ public void startFromLibraryThenFromApp() { /* Verify previous behaviors happened only once, thus not again. */ verify(DummyService.getInstance()).getLogFactories(); - verify(DummyService.getInstance()).onStarted(any(Context.class), any(Channel.class), isNull(String.class), isNull(String.class), eq(false)); + verify(DummyService.getInstance()).onStarted(any(Context.class), any(Channel.class), isNull(), isNull(), eq(false)); verify(mApplication).registerActivityLifecycleCallbacks(DummyService.getInstance()); /* Verify second service started. */ assertTrue(AppCenter.getInstance().getServices().contains(AnotherDummyService.getInstance())); verify(AnotherDummyService.getInstance()).getLogFactories(); - verify(AnotherDummyService.getInstance()).onStarted(any(Context.class), any(Channel.class), eq(DUMMY_APP_SECRET), isNull(String.class), eq(true)); + verify(AnotherDummyService.getInstance()).onStarted(any(Context.class), any(Channel.class), eq(DUMMY_APP_SECRET), isNull(), eq(true)); verify(mApplication).registerActivityLifecycleCallbacks(AnotherDummyService.getInstance()); /* And enabled. */ @@ -147,7 +147,7 @@ public void startFromLibraryThenFromAppWithTargetToken() { assertEquals(1, AppCenter.getInstance().getServices().size()); assertTrue(AppCenter.getInstance().getServices().contains(DummyService.getInstance())); verify(DummyService.getInstance()).getLogFactories(); - verify(DummyService.getInstance()).onStarted(any(Context.class), any(Channel.class), isNull(String.class), isNull(String.class), eq(false)); + verify(DummyService.getInstance()).onStarted(any(Context.class), any(Channel.class), isNull(), isNull(), eq(false)); verify(mApplication).registerActivityLifecycleCallbacks(DummyService.getInstance()); verify(mChannel, never()).enqueue(eq(mStartServiceLog), eq(CORE_GROUP), anyInt()); verify(mChannel, never()).setAppSecret(anyString()); @@ -163,13 +163,13 @@ public void startFromLibraryThenFromAppWithTargetToken() { /* Verify previous behaviors happened only once, thus not again. */ verify(DummyService.getInstance()).getLogFactories(); - verify(DummyService.getInstance()).onStarted(any(Context.class), any(Channel.class), isNull(String.class), isNull(String.class), eq(false)); + verify(DummyService.getInstance()).onStarted(any(Context.class), any(Channel.class), isNull(), isNull(), eq(false)); verify(mApplication).registerActivityLifecycleCallbacks(DummyService.getInstance()); /* Verify second service started. */ assertFalse(AppCenter.getInstance().getServices().contains(AnotherDummyService.getInstance())); verify(AnotherDummyService.getInstance(), never()).getLogFactories(); - verify(AnotherDummyService.getInstance(), never()).onStarted(any(Context.class), any(Channel.class), isNull(String.class), eq(DUMMY_TRANSMISSION_TARGET_TOKEN), eq(true)); + verify(AnotherDummyService.getInstance(), never()).onStarted(any(Context.class), any(Channel.class), isNull(), eq(DUMMY_TRANSMISSION_TARGET_TOKEN), eq(true)); verify(mApplication, never()).registerActivityLifecycleCallbacks(AnotherDummyService.getInstance()); /* Verify start service log is sent. */ @@ -193,7 +193,7 @@ public void startFromLibraryThenFromAppWithBothSecrets() { assertEquals(1, AppCenter.getInstance().getServices().size()); assertTrue(AppCenter.getInstance().getServices().contains(DummyService.getInstance())); verify(DummyService.getInstance()).getLogFactories(); - verify(DummyService.getInstance()).onStarted(any(Context.class), any(Channel.class), isNull(String.class), isNull(String.class), eq(false)); + verify(DummyService.getInstance()).onStarted(any(Context.class), any(Channel.class), isNull(), isNull(), eq(false)); verify(mApplication).registerActivityLifecycleCallbacks(DummyService.getInstance()); verify(mChannel, never()).enqueue(eq(mStartServiceLog), eq(CORE_GROUP), anyInt()); verify(mChannel, never()).setAppSecret(anyString()); @@ -210,7 +210,7 @@ public void startFromLibraryThenFromAppWithBothSecrets() { /* Verify previous behaviors happened only once, thus not again. */ verify(DummyService.getInstance()).getLogFactories(); - verify(DummyService.getInstance()).onStarted(any(Context.class), any(Channel.class), isNull(String.class), isNull(String.class), eq(false)); + verify(DummyService.getInstance()).onStarted(any(Context.class), any(Channel.class), isNull(), isNull(), eq(false)); verify(mApplication).registerActivityLifecycleCallbacks(DummyService.getInstance()); /* Verify second service started. */ @@ -242,13 +242,13 @@ public void startFromAppThenFromLibrary() { /* Verify first service started. */ assertTrue(AppCenter.getInstance().getServices().contains(DummyService.getInstance())); verify(DummyService.getInstance()).getLogFactories(); - verify(DummyService.getInstance()).onStarted(any(Context.class), any(Channel.class), eq(DUMMY_APP_SECRET), isNull(String.class), eq(true)); + verify(DummyService.getInstance()).onStarted(any(Context.class), any(Channel.class), eq(DUMMY_APP_SECRET), isNull(), eq(true)); verify(mApplication).registerActivityLifecycleCallbacks(DummyService.getInstance()); /* Verify second service started. */ assertTrue(AppCenter.getInstance().getServices().contains(AnotherDummyService.getInstance())); verify(AnotherDummyService.getInstance()).getLogFactories(); - verify(AnotherDummyService.getInstance()).onStarted(any(Context.class), any(Channel.class), eq(DUMMY_APP_SECRET), isNull(String.class), eq(true)); + verify(AnotherDummyService.getInstance()).onStarted(any(Context.class), any(Channel.class), eq(DUMMY_APP_SECRET), isNull(), eq(true)); verify(mApplication).registerActivityLifecycleCallbacks(AnotherDummyService.getInstance()); /* Verify start service log is sent. */ @@ -270,11 +270,11 @@ public void startFromAppThenFromLibrary() { assertEquals(2, AppCenter.getInstance().getServices().size()); assertTrue(AppCenter.getInstance().getServices().contains(DummyService.getInstance())); verify(DummyService.getInstance()).getLogFactories(); - verify(DummyService.getInstance()).onStarted(any(Context.class), any(Channel.class), eq(DUMMY_APP_SECRET), isNull(String.class), eq(true)); + verify(DummyService.getInstance()).onStarted(any(Context.class), any(Channel.class), eq(DUMMY_APP_SECRET), isNull(), eq(true)); verify(mApplication).registerActivityLifecycleCallbacks(DummyService.getInstance()); assertTrue(AppCenter.getInstance().getServices().contains(AnotherDummyService.getInstance())); verify(AnotherDummyService.getInstance()).getLogFactories(); - verify(AnotherDummyService.getInstance()).onStarted(any(Context.class), any(Channel.class), eq(DUMMY_APP_SECRET), isNull(String.class), eq(true)); + verify(AnotherDummyService.getInstance()).onStarted(any(Context.class), any(Channel.class), eq(DUMMY_APP_SECRET), isNull(), eq(true)); verify(mApplication).registerActivityLifecycleCallbacks(AnotherDummyService.getInstance()); verify(mChannel).enqueue(eq(mStartServiceLog), eq(CORE_GROUP), eq(DEFAULTS)); services = new ArrayList<>(); @@ -298,13 +298,13 @@ public void startWithTargetTokenThenFromLibrary() { /* Verify first service started. */ assertTrue(AppCenter.getInstance().getServices().contains(DummyService.getInstance())); verify(DummyService.getInstance()).getLogFactories(); - verify(DummyService.getInstance()).onStarted(any(Context.class), any(Channel.class), isNull(String.class), eq(DUMMY_TRANSMISSION_TARGET_TOKEN), eq(true)); + verify(DummyService.getInstance()).onStarted(any(Context.class), any(Channel.class), isNull(), eq(DUMMY_TRANSMISSION_TARGET_TOKEN), eq(true)); verify(mApplication).registerActivityLifecycleCallbacks(DummyService.getInstance()); /* Verify second service not started. */ assertFalse(AppCenter.getInstance().getServices().contains(AnotherDummyService.getInstance())); verify(AnotherDummyService.getInstance(), never()).getLogFactories(); - verify(AnotherDummyService.getInstance(), never()).onStarted(any(Context.class), any(Channel.class), isNull(String.class), eq(DUMMY_TRANSMISSION_TARGET_TOKEN), eq(true)); + verify(AnotherDummyService.getInstance(), never()).onStarted(any(Context.class), any(Channel.class), isNull(), eq(DUMMY_TRANSMISSION_TARGET_TOKEN), eq(true)); verify(mApplication, never()).registerActivityLifecycleCallbacks(AnotherDummyService.getInstance()); /* Verify start service log is sent with only first service. */ @@ -321,11 +321,11 @@ public void startWithTargetTokenThenFromLibrary() { assertEquals(1, AppCenter.getInstance().getServices().size()); assertTrue(AppCenter.getInstance().getServices().contains(DummyService.getInstance())); verify(DummyService.getInstance()).getLogFactories(); - verify(DummyService.getInstance()).onStarted(any(Context.class), any(Channel.class), isNull(String.class), eq(DUMMY_TRANSMISSION_TARGET_TOKEN), eq(true)); + verify(DummyService.getInstance()).onStarted(any(Context.class), any(Channel.class), isNull(), eq(DUMMY_TRANSMISSION_TARGET_TOKEN), eq(true)); verify(mApplication).registerActivityLifecycleCallbacks(DummyService.getInstance()); assertFalse(AppCenter.getInstance().getServices().contains(AnotherDummyService.getInstance())); verify(AnotherDummyService.getInstance(), never()).getLogFactories(); - verify(AnotherDummyService.getInstance(), never()).onStarted(any(Context.class), any(Channel.class), isNull(String.class), eq(DUMMY_TRANSMISSION_TARGET_TOKEN), eq(true)); + verify(AnotherDummyService.getInstance(), never()).onStarted(any(Context.class), any(Channel.class), isNull(), eq(DUMMY_TRANSMISSION_TARGET_TOKEN), eq(true)); verify(mApplication, never()).registerActivityLifecycleCallbacks(AnotherDummyService.getInstance()); verify(mChannel).enqueue(eq(mStartServiceLog), eq(CORE_GROUP), eq(DEFAULTS)); services = new ArrayList<>(); @@ -356,7 +356,7 @@ public void startFromLibraryDoesNotStartFromApp() { verify(DummyService.getInstance()).onConfigurationUpdated(DUMMY_APP_SECRET, DUMMY_TRANSMISSION_TARGET_TOKEN); /* And not call onStarted again (verify 1 total call). */ - verify(DummyService.getInstance()).onStarted(any(Context.class), any(Channel.class), isNull(String.class), isNull(String.class), anyBoolean()); + verify(DummyService.getInstance()).onStarted(any(Context.class), any(Channel.class), isNull(), isNull(), anyBoolean()); } @Test diff --git a/sdk/appcenter/src/test/java/com/microsoft/appcenter/AppCenterTest.java b/sdk/appcenter/src/test/java/com/microsoft/appcenter/AppCenterTest.java index 9cb84ccf0..f62a66e3a 100644 --- a/sdk/appcenter/src/test/java/com/microsoft/appcenter/AppCenterTest.java +++ b/sdk/appcenter/src/test/java/com/microsoft/appcenter/AppCenterTest.java @@ -21,13 +21,9 @@ import org.junit.Assert; import org.junit.Test; -import org.junit.runner.RunWith; import org.mockito.ArgumentCaptor; -import org.mockito.ArgumentMatcher; import org.mockito.invocation.InvocationOnMock; import org.mockito.stubbing.Answer; -import org.powermock.core.classloader.annotations.PrepareForTest; -import org.powermock.modules.junit4.PowerMockRunner; import java.util.ArrayList; import java.util.List; @@ -53,7 +49,6 @@ import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.anyLong; import static org.mockito.ArgumentMatchers.anyString; -import static org.mockito.ArgumentMatchers.argThat; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.ArgumentMatchers.isA; import static org.mockito.ArgumentMatchers.isNull; @@ -102,7 +97,7 @@ public void useDummyServiceTest() { DummyService service = DummyService.getInstance(); assertTrue(AppCenter.getInstance().getServices().contains(service)); verify(service).getLogFactories(); - verify(service).onStarted(any(Context.class), any(Channel.class), eq(DUMMY_APP_SECRET), isNull(String.class), eq(true)); + verify(service).onStarted(any(Context.class), any(Channel.class), eq(DUMMY_APP_SECRET), isNull(), eq(true)); verify(mApplication).registerActivityLifecycleCallbacks(service); verify(mChannel).enqueue(eq(mStartServiceLog), eq(CORE_GROUP), eq(DEFAULTS)); verify(mChannel).setMaxStorageSize(AppCenter.DEFAULT_MAX_STORAGE_SIZE_IN_BYTES); @@ -156,7 +151,7 @@ public void useDummyServiceTestSplitCall() { DummyService service = DummyService.getInstance(); assertTrue(AppCenter.getInstance().getServices().contains(service)); verify(service).getLogFactories(); - verify(service).onStarted(any(Context.class), any(Channel.class), eq(DUMMY_APP_SECRET), isNull(String.class), eq(true)); + verify(service).onStarted(any(Context.class), any(Channel.class), eq(DUMMY_APP_SECRET), isNull(), eq(true)); verify(mApplication).registerActivityLifecycleCallbacks(service); verify(mChannel).enqueue(eq(mStartServiceLog), eq(CORE_GROUP), eq(DEFAULTS)); List services = new ArrayList<>(); @@ -175,8 +170,8 @@ public void configureAndStartTwiceTest() { DummyService service = DummyService.getInstance(); assertTrue(AppCenter.getInstance().getServices().contains(service)); verify(service).getLogFactories(); - verify(service).onStarted(any(Context.class), any(Channel.class), eq(DUMMY_APP_SECRET), isNull(String.class), eq(true)); - verify(service, never()).onStarted(any(Context.class), any(Channel.class), eq(DUMMY_APP_SECRET + "a"), isNull(String.class), eq(true)); + verify(service).onStarted(any(Context.class), any(Channel.class), eq(DUMMY_APP_SECRET), isNull(), eq(true)); + verify(service, never()).onStarted(any(Context.class), any(Channel.class), eq(DUMMY_APP_SECRET + "a"), isNull(), eq(true)); verify(mApplication).registerActivityLifecycleCallbacks(service); verify(mChannel).enqueue(eq(mStartServiceLog), eq(CORE_GROUP), eq(DEFAULTS)); List services = new ArrayList<>(); @@ -195,8 +190,8 @@ public void startTargetTokenThenStartWithAppSecretTest() { DummyService service = DummyService.getInstance(); assertTrue(AppCenter.getInstance().getServices().contains(service)); verify(service).getLogFactories(); - verify(service).onStarted(any(Context.class), any(Channel.class), isNull(String.class), eq(DUMMY_TRANSMISSION_TARGET_TOKEN), eq(true)); - verify(service, never()).onStarted(any(Context.class), any(Channel.class), eq(DUMMY_APP_SECRET), isNull(String.class), eq(true)); + verify(service).onStarted(any(Context.class), any(Channel.class), isNull(), eq(DUMMY_TRANSMISSION_TARGET_TOKEN), eq(true)); + verify(service, never()).onStarted(any(Context.class), any(Channel.class), eq(DUMMY_APP_SECRET), isNull(), eq(true)); verify(mApplication).registerActivityLifecycleCallbacks(service); verify(mChannel).enqueue(eq(mStartServiceLog), eq(CORE_GROUP), eq(DEFAULTS)); List services = new ArrayList<>(); @@ -215,8 +210,8 @@ public void startAppSecretThenStartWithTargetTokenTest() { DummyService service = DummyService.getInstance(); assertTrue(AppCenter.getInstance().getServices().contains(service)); verify(service).getLogFactories(); - verify(service, never()).onStarted(any(Context.class), any(Channel.class), isNull(String.class), eq(DUMMY_TRANSMISSION_TARGET_TOKEN), eq(true)); - verify(service).onStarted(any(Context.class), any(Channel.class), eq(DUMMY_APP_SECRET), isNull(String.class), eq(true)); + verify(service, never()).onStarted(any(Context.class), any(Channel.class), isNull(), eq(DUMMY_TRANSMISSION_TARGET_TOKEN), eq(true)); + verify(service).onStarted(any(Context.class), any(Channel.class), eq(DUMMY_APP_SECRET), isNull(), eq(true)); verify(mApplication).registerActivityLifecycleCallbacks(service); verify(mChannel).enqueue(eq(mStartServiceLog), eq(CORE_GROUP), eq(DEFAULTS)); List services = new ArrayList<>(); @@ -236,8 +231,8 @@ public void configureTwiceTest() { DummyService service = DummyService.getInstance(); assertTrue(AppCenter.getInstance().getServices().contains(service)); verify(service).getLogFactories(); - verify(service).onStarted(any(Context.class), any(Channel.class), eq(DUMMY_APP_SECRET), isNull(String.class), eq(true)); - verify(service, never()).onStarted(any(Context.class), any(Channel.class), eq(DUMMY_APP_SECRET + "a"), isNull(String.class), eq(true)); + verify(service).onStarted(any(Context.class), any(Channel.class), eq(DUMMY_APP_SECRET), isNull(), eq(true)); + verify(service, never()).onStarted(any(Context.class), any(Channel.class), eq(DUMMY_APP_SECRET + "a"), isNull(), eq(true)); verify(mApplication).registerActivityLifecycleCallbacks(service); verify(mChannel).enqueue(eq(mStartServiceLog), eq(CORE_GROUP), eq(DEFAULTS)); List services = new ArrayList<>(); @@ -258,13 +253,13 @@ public void startTwoServicesTest() { /* Verify first service started. */ assertTrue(AppCenter.getInstance().getServices().contains(DummyService.getInstance())); verify(DummyService.getInstance()).getLogFactories(); - verify(DummyService.getInstance()).onStarted(any(Context.class), any(Channel.class), eq(DUMMY_APP_SECRET), isNull(String.class), eq(true)); + verify(DummyService.getInstance()).onStarted(any(Context.class), any(Channel.class), eq(DUMMY_APP_SECRET), isNull(), eq(true)); verify(mApplication).registerActivityLifecycleCallbacks(DummyService.getInstance()); /* Verify second service started. */ assertTrue(AppCenter.getInstance().getServices().contains(AnotherDummyService.getInstance())); verify(AnotherDummyService.getInstance()).getLogFactories(); - verify(AnotherDummyService.getInstance()).onStarted(any(Context.class), any(Channel.class), eq(DUMMY_APP_SECRET), isNull(String.class), eq(true)); + verify(AnotherDummyService.getInstance()).onStarted(any(Context.class), any(Channel.class), eq(DUMMY_APP_SECRET), isNull(), eq(true)); verify(mApplication).registerActivityLifecycleCallbacks(AnotherDummyService.getInstance()); /* Verify start service log is sent. */ @@ -285,13 +280,13 @@ public void startTwoServicesSplit() { { assertTrue(AppCenter.getInstance().getServices().contains(DummyService.getInstance())); verify(DummyService.getInstance()).getLogFactories(); - verify(DummyService.getInstance()).onStarted(any(Context.class), any(Channel.class), eq(DUMMY_APP_SECRET), isNull(String.class), eq(true)); + verify(DummyService.getInstance()).onStarted(any(Context.class), any(Channel.class), eq(DUMMY_APP_SECRET), isNull(), eq(true)); verify(mApplication).registerActivityLifecycleCallbacks(DummyService.getInstance()); } { assertTrue(AppCenter.getInstance().getServices().contains(AnotherDummyService.getInstance())); verify(AnotherDummyService.getInstance()).getLogFactories(); - verify(AnotherDummyService.getInstance()).onStarted(any(Context.class), any(Channel.class), eq(DUMMY_APP_SECRET), isNull(String.class), eq(true)); + verify(AnotherDummyService.getInstance()).onStarted(any(Context.class), any(Channel.class), eq(DUMMY_APP_SECRET), isNull(), eq(true)); verify(mApplication).registerActivityLifecycleCallbacks(AnotherDummyService.getInstance()); } verify(mChannel).enqueue(eq(mStartServiceLog), eq(CORE_GROUP), eq(DEFAULTS)); @@ -312,13 +307,13 @@ public void startTwoServicesSplitEvenMore() { { assertTrue(AppCenter.getInstance().getServices().contains(DummyService.getInstance())); verify(DummyService.getInstance()).getLogFactories(); - verify(DummyService.getInstance()).onStarted(any(Context.class), any(Channel.class), eq(DUMMY_APP_SECRET), isNull(String.class), eq(true)); + verify(DummyService.getInstance()).onStarted(any(Context.class), any(Channel.class), eq(DUMMY_APP_SECRET), isNull(), eq(true)); verify(mApplication).registerActivityLifecycleCallbacks(DummyService.getInstance()); } { assertTrue(AppCenter.getInstance().getServices().contains(AnotherDummyService.getInstance())); verify(AnotherDummyService.getInstance()).getLogFactories(); - verify(AnotherDummyService.getInstance()).onStarted(any(Context.class), any(Channel.class), eq(DUMMY_APP_SECRET), isNull(String.class), eq(true)); + verify(AnotherDummyService.getInstance()).onStarted(any(Context.class), any(Channel.class), eq(DUMMY_APP_SECRET), isNull(), eq(true)); verify(mApplication).registerActivityLifecycleCallbacks(AnotherDummyService.getInstance()); } verify(mChannel, times(2)).enqueue(any(StartServiceLog.class), eq(CORE_GROUP), eq(DEFAULTS)); @@ -339,13 +334,13 @@ public void startTwoServicesWithSomeInvalidReferences() { { assertTrue(AppCenter.getInstance().getServices().contains(DummyService.getInstance())); verify(DummyService.getInstance()).getLogFactories(); - verify(DummyService.getInstance()).onStarted(any(Context.class), any(Channel.class), eq(DUMMY_APP_SECRET), isNull(String.class), eq(true)); + verify(DummyService.getInstance()).onStarted(any(Context.class), any(Channel.class), eq(DUMMY_APP_SECRET), isNull(), eq(true)); verify(mApplication).registerActivityLifecycleCallbacks(DummyService.getInstance()); } { assertTrue(AppCenter.getInstance().getServices().contains(AnotherDummyService.getInstance())); verify(AnotherDummyService.getInstance()).getLogFactories(); - verify(AnotherDummyService.getInstance()).onStarted(any(Context.class), any(Channel.class), eq(DUMMY_APP_SECRET), isNull(String.class), eq(true)); + verify(AnotherDummyService.getInstance()).onStarted(any(Context.class), any(Channel.class), eq(DUMMY_APP_SECRET), isNull(), eq(true)); verify(mApplication).registerActivityLifecycleCallbacks(AnotherDummyService.getInstance()); } verify(mChannel).enqueue(eq(mStartServiceLog), eq(CORE_GROUP), eq(DEFAULTS)); @@ -366,13 +361,13 @@ public void startTwoServicesWithSomeInvalidReferencesSplit() { { assertTrue(AppCenter.getInstance().getServices().contains(DummyService.getInstance())); verify(DummyService.getInstance()).getLogFactories(); - verify(DummyService.getInstance()).onStarted(any(Context.class), any(Channel.class), eq(DUMMY_APP_SECRET), isNull(String.class), eq(true)); + verify(DummyService.getInstance()).onStarted(any(Context.class), any(Channel.class), eq(DUMMY_APP_SECRET), isNull(), eq(true)); verify(mApplication).registerActivityLifecycleCallbacks(DummyService.getInstance()); } { assertTrue(AppCenter.getInstance().getServices().contains(AnotherDummyService.getInstance())); verify(AnotherDummyService.getInstance()).getLogFactories(); - verify(AnotherDummyService.getInstance()).onStarted(any(Context.class), any(Channel.class), eq(DUMMY_APP_SECRET), isNull(String.class), eq(true)); + verify(AnotherDummyService.getInstance()).onStarted(any(Context.class), any(Channel.class), eq(DUMMY_APP_SECRET), isNull(), eq(true)); verify(mApplication).registerActivityLifecycleCallbacks(AnotherDummyService.getInstance()); } verify(mChannel, times(2)).enqueue(any(StartServiceLog.class), eq(CORE_GROUP), eq(DEFAULTS)); @@ -396,7 +391,7 @@ public void startServiceTwice() { DummyService service = DummyService.getInstance(); assertTrue(AppCenter.getInstance().getServices().contains(service)); verify(service).getLogFactories(); - verify(service).onStarted(any(Context.class), any(Channel.class), eq(DUMMY_APP_SECRET), isNull(String.class), eq(true)); + verify(service).onStarted(any(Context.class), any(Channel.class), eq(DUMMY_APP_SECRET), isNull(), eq(true)); verify(mApplication).registerActivityLifecycleCallbacks(service); /* Start twice, this call is ignored. */ @@ -405,7 +400,7 @@ public void startServiceTwice() { /* Verify that single service has been loaded and configured (only once interaction). */ assertEquals(1, AppCenter.getInstance().getServices().size()); verify(service).getLogFactories(); - verify(service).onStarted(any(Context.class), any(Channel.class), eq(DUMMY_APP_SECRET), isNull(String.class), eq(true)); + verify(service).onStarted(any(Context.class), any(Channel.class), eq(DUMMY_APP_SECRET), isNull(), eq(true)); verify(mApplication).registerActivityLifecycleCallbacks(service); verify(mChannel).enqueue(eq(mStartServiceLog), eq(CORE_GROUP), eq(DEFAULTS)); List services = new ArrayList<>(); @@ -508,9 +503,9 @@ public void enableTest() { /* Check factories / channel only once interactions. */ verify(dummyService).getLogFactories(); - verify(dummyService).onStarted(any(Context.class), any(Channel.class), eq(DUMMY_APP_SECRET), isNull(String.class), eq(true)); + verify(dummyService).onStarted(any(Context.class), any(Channel.class), eq(DUMMY_APP_SECRET), isNull(), eq(true)); verify(anotherDummyService).getLogFactories(); - verify(anotherDummyService).onStarted(any(Context.class), any(Channel.class), eq(DUMMY_APP_SECRET), isNull(String.class), eq(true)); + verify(anotherDummyService).onStarted(any(Context.class), any(Channel.class), eq(DUMMY_APP_SECRET), isNull(), eq(true)); } @Test @@ -661,7 +656,7 @@ private void checkNullOrEmptySecretStringForbidden() { assertFalse(AppCenter.isConfigured()); /* Verify service did not start with null secrets from application. */ - verify(DummyService.getInstance(), never()).onStarted(any(Context.class), any(Channel.class), isNull(String.class), isNull(String.class), eq(true)); + verify(DummyService.getInstance(), never()).onStarted(any(Context.class), any(Channel.class), isNull(), isNull(), eq(true)); } @Test @@ -687,7 +682,7 @@ private void checkStartedWithoutAppSecret() { assertTrue(AppCenter.isConfigured()); /* Verify service started with null secrets from application. */ - verify(DummyService.getInstance()).onStarted(any(Context.class), any(Channel.class), isNull(String.class), isNull(String.class), eq(true)); + verify(DummyService.getInstance()).onStarted(any(Context.class), any(Channel.class), isNull(), isNull(), eq(true)); /* We must not be able to reconfigure app secret from null/empty state. */ AppCenter.start(mApplication, DUMMY_APP_SECRET, DummyService.class, AnotherDummyService.class); @@ -708,28 +703,28 @@ private void checkStartedWithoutAppSecret() { public void appSecretKeyWithoutAppSecretTest() { String secret = APP_SECRET_KEY + KEY_VALUE_DELIMITER; AppCenter.start(mApplication, secret, DummyService.class); - verify(DummyService.getInstance()).onStarted(any(Context.class), any(Channel.class), isNull(String.class), isNull(String.class), eq(true)); + verify(DummyService.getInstance()).onStarted(any(Context.class), any(Channel.class), isNull(), isNull(), eq(true)); } @Test public void appSecretWithKeyValueDelimiter() { String secret = KEY_VALUE_DELIMITER + DUMMY_APP_SECRET; AppCenter.start(mApplication, secret, DummyService.class); - verify(DummyService.getInstance()).onStarted(any(Context.class), any(Channel.class), isNull(String.class), isNull(String.class), eq(true)); + verify(DummyService.getInstance()).onStarted(any(Context.class), any(Channel.class), isNull(), isNull(), eq(true)); } @Test public void appSecretWithPairDelimiterAfter() { String secret = DUMMY_APP_SECRET + PAIR_DELIMITER; AppCenter.start(mApplication, secret, DummyService.class); - verify(DummyService.getInstance()).onStarted(any(Context.class), any(Channel.class), eq(DUMMY_APP_SECRET), isNull(String.class), eq(true)); + verify(DummyService.getInstance()).onStarted(any(Context.class), any(Channel.class), eq(DUMMY_APP_SECRET), isNull(), eq(true)); } @Test public void appSecretWithPairDelimiterBefore() { String secret = PAIR_DELIMITER + DUMMY_APP_SECRET; AppCenter.start(mApplication, secret, DummyService.class); - verify(DummyService.getInstance()).onStarted(any(Context.class), any(Channel.class), eq(DUMMY_APP_SECRET), isNull(String.class), eq(true)); + verify(DummyService.getInstance()).onStarted(any(Context.class), any(Channel.class), eq(DUMMY_APP_SECRET), isNull(), eq(true)); } @Test @@ -743,14 +738,14 @@ public void appSecretWithTargetTokenTest() { public void keyedAppSecretTest() { String secret = APP_SECRET_KEY + KEY_VALUE_DELIMITER + DUMMY_APP_SECRET; AppCenter.start(mApplication, secret, DummyService.class); - verify(DummyService.getInstance()).onStarted(any(Context.class), any(Channel.class), eq(DUMMY_APP_SECRET), isNull(String.class), eq(true)); + verify(DummyService.getInstance()).onStarted(any(Context.class), any(Channel.class), eq(DUMMY_APP_SECRET), isNull(), eq(true)); } @Test public void keyedAppSecretWithDelimiterTest() { String secret = APP_SECRET_KEY + KEY_VALUE_DELIMITER + DUMMY_APP_SECRET + PAIR_DELIMITER; AppCenter.start(mApplication, secret, DummyService.class); - verify(DummyService.getInstance()).onStarted(any(Context.class), any(Channel.class), eq(DUMMY_APP_SECRET), isNull(String.class), eq(true)); + verify(DummyService.getInstance()).onStarted(any(Context.class), any(Channel.class), eq(DUMMY_APP_SECRET), isNull(), eq(true)); } @Test @@ -764,8 +759,8 @@ public void keyedAppSecretWithTargetTokenTest() { @Test public void targetTokenTest() { AppCenter.start(mApplication, DUMMY_TARGET_TOKEN_STRING, DummyService.class, AnotherDummyService.class); - verify(DummyService.getInstance()).onStarted(any(Context.class), any(Channel.class), isNull(String.class), eq(DUMMY_TRANSMISSION_TARGET_TOKEN), eq(true)); - verify(AnotherDummyService.getInstance(), never()).onStarted(any(Context.class), any(Channel.class), isNull(String.class), eq(DUMMY_TRANSMISSION_TARGET_TOKEN), eq(true)); + verify(DummyService.getInstance()).onStarted(any(Context.class), any(Channel.class), isNull(), eq(DUMMY_TRANSMISSION_TARGET_TOKEN), eq(true)); + verify(AnotherDummyService.getInstance(), never()).onStarted(any(Context.class), any(Channel.class), isNull(), eq(DUMMY_TRANSMISSION_TARGET_TOKEN), eq(true)); } @Test @@ -795,7 +790,7 @@ public void unknownKeyTest() { } @Test - public void checkStartHandlerWhenDesableRunnableIsNull() throws Exception { + public void checkStartHandlerWhenDesableRunnableIsNull() { String secret = DUMMY_APP_SECRET + PAIR_DELIMITER + DUMMY_TARGET_TOKEN_STRING + PAIR_DELIMITER + "unknown" + KEY_VALUE_DELIMITER + "value"; @@ -826,7 +821,7 @@ public void duplicateServiceTest() { AppCenter.start(mApplication, DUMMY_APP_SECRET, DummyService.class, DummyService.class); /* Verify that only one service has been loaded and configured. */ - verify(DummyService.getInstance()).onStarted(notNull(Context.class), any(Channel.class), eq(DUMMY_APP_SECRET), isNull(String.class), eq(true)); + verify(DummyService.getInstance()).onStarted(notNull(), any(Channel.class), eq(DUMMY_APP_SECRET), isNull(), eq(true)); assertEquals(1, AppCenter.getInstance().getServices().size()); } @@ -1093,7 +1088,7 @@ public void useApplicationLifecycleListener() { /* Verify that the service started. */ DummyService service = DummyService.getInstance(); - verify(service).onStarted(any(Context.class), any(Channel.class), eq(DUMMY_APP_SECRET), isNull(String.class), eq(true)); + verify(service).onStarted(any(Context.class), any(Channel.class), eq(DUMMY_APP_SECRET), isNull(), eq(true)); /* Verify that the listener is subscribed to application events. */ verify(mApplication).registerActivityLifecycleCallbacks(isA(ApplicationLifecycleListener.class)); diff --git a/sdk/appcenter/src/test/java/com/microsoft/appcenter/DependencyConfigurationTest.java b/sdk/appcenter/src/test/java/com/microsoft/appcenter/DependencyConfigurationTest.java index 9ec3ac7de..7beed6a1c 100644 --- a/sdk/appcenter/src/test/java/com/microsoft/appcenter/DependencyConfigurationTest.java +++ b/sdk/appcenter/src/test/java/com/microsoft/appcenter/DependencyConfigurationTest.java @@ -24,6 +24,7 @@ public class DependencyConfigurationTest extends AbstractAppCenterTest { + @SuppressWarnings("InstantiationOfUtilityClass") @Test public void ConstructorCoverage() { new DependencyConfiguration(); diff --git a/sdk/appcenter/src/test/java/com/microsoft/appcenter/InstantiationTest.java b/sdk/appcenter/src/test/java/com/microsoft/appcenter/InstantiationTest.java index b8c6f165d..2bf423e3b 100644 --- a/sdk/appcenter/src/test/java/com/microsoft/appcenter/InstantiationTest.java +++ b/sdk/appcenter/src/test/java/com/microsoft/appcenter/InstantiationTest.java @@ -18,7 +18,7 @@ /** * This class is to go through the code to get code coverage for constructors of utils/static classes. */ -@SuppressWarnings("unused") +@SuppressWarnings({"unused", "InstantiationOfUtilityClass"}) public class InstantiationTest { @Test diff --git a/sdk/appcenter/src/test/java/com/microsoft/appcenter/channel/DefaultChannelAlternateIngestionTest.java b/sdk/appcenter/src/test/java/com/microsoft/appcenter/channel/DefaultChannelAlternateIngestionTest.java index 98f80e28f..b4665bddb 100644 --- a/sdk/appcenter/src/test/java/com/microsoft/appcenter/channel/DefaultChannelAlternateIngestionTest.java +++ b/sdk/appcenter/src/test/java/com/microsoft/appcenter/channel/DefaultChannelAlternateIngestionTest.java @@ -24,7 +24,6 @@ import static org.mockito.ArgumentMatchers.anyCollection; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.anyList; -import static org.mockito.ArgumentMatchers.anyListOf; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.ArgumentMatchers.isNull; diff --git a/sdk/appcenter/src/test/java/com/microsoft/appcenter/channel/DefaultChannelOtherOperationsTest.java b/sdk/appcenter/src/test/java/com/microsoft/appcenter/channel/DefaultChannelOtherOperationsTest.java index 0258bfe67..40ff1a9bc 100644 --- a/sdk/appcenter/src/test/java/com/microsoft/appcenter/channel/DefaultChannelOtherOperationsTest.java +++ b/sdk/appcenter/src/test/java/com/microsoft/appcenter/channel/DefaultChannelOtherOperationsTest.java @@ -14,9 +14,7 @@ import com.microsoft.appcenter.persistence.Persistence; import org.junit.Test; -import org.mockito.Matchers; -import java.util.List; import java.util.UUID; import static org.junit.Assert.assertFalse; @@ -25,7 +23,6 @@ import static org.mockito.ArgumentMatchers.anyCollection; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.anyList; -import static org.mockito.ArgumentMatchers.anyListOf; import static org.mockito.ArgumentMatchers.anyLong; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.eq; diff --git a/sdk/appcenter/src/test/java/com/microsoft/appcenter/channel/DefaultChannelRaceConditionTest.java b/sdk/appcenter/src/test/java/com/microsoft/appcenter/channel/DefaultChannelRaceConditionTest.java index 952e5b10d..52463cfa6 100644 --- a/sdk/appcenter/src/test/java/com/microsoft/appcenter/channel/DefaultChannelRaceConditionTest.java +++ b/sdk/appcenter/src/test/java/com/microsoft/appcenter/channel/DefaultChannelRaceConditionTest.java @@ -17,7 +17,6 @@ import com.microsoft.appcenter.persistence.Persistence; import org.junit.Test; -import org.mockito.ArgumentMatcher; import org.mockito.invocation.InvocationOnMock; import org.mockito.stubbing.Answer; @@ -30,9 +29,7 @@ import static org.mockito.ArgumentMatchers.anyList; import static org.mockito.ArgumentMatchers.isA; import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.anyListOf; import static org.mockito.ArgumentMatchers.anyString; -import static org.mockito.ArgumentMatchers.argThat; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; diff --git a/sdk/appcenter/src/test/java/com/microsoft/appcenter/channel/OneCollectorChannelListenerTest.java b/sdk/appcenter/src/test/java/com/microsoft/appcenter/channel/OneCollectorChannelListenerTest.java index 891050dd4..04d6d1c88 100644 --- a/sdk/appcenter/src/test/java/com/microsoft/appcenter/channel/OneCollectorChannelListenerTest.java +++ b/sdk/appcenter/src/test/java/com/microsoft/appcenter/channel/OneCollectorChannelListenerTest.java @@ -7,7 +7,6 @@ import android.content.Context; -import com.microsoft.appcenter.ingestion.Ingestion; import com.microsoft.appcenter.ingestion.OneCollectorIngestion; import com.microsoft.appcenter.ingestion.models.Log; import com.microsoft.appcenter.ingestion.models.json.LogSerializer; @@ -17,7 +16,6 @@ import com.microsoft.appcenter.ingestion.models.one.SdkExtension; import org.junit.Test; -import org.mockito.ArgumentMatcher; import java.util.Arrays; import java.util.Collections; @@ -41,7 +39,6 @@ import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.anyString; -import static org.mockito.ArgumentMatchers.argThat; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.ArgumentMatchers.same; import static org.mockito.Mockito.mock; diff --git a/sdk/appcenter/src/test/java/com/microsoft/appcenter/http/DefaultHttpClientTest.java b/sdk/appcenter/src/test/java/com/microsoft/appcenter/http/DefaultHttpClientTest.java index 797c217b9..3c29f8cd3 100644 --- a/sdk/appcenter/src/test/java/com/microsoft/appcenter/http/DefaultHttpClientTest.java +++ b/sdk/appcenter/src/test/java/com/microsoft/appcenter/http/DefaultHttpClientTest.java @@ -5,6 +5,34 @@ package com.microsoft.appcenter.http; +import static com.microsoft.appcenter.http.DefaultHttpClient.METHOD_GET; +import static com.microsoft.appcenter.http.DefaultHttpClient.METHOD_POST; +import static org.junit.Assert.assertArrayEquals; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.fail; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.anyMap; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.ArgumentMatchers.argThat; +import static org.mockito.ArgumentMatchers.contains; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.ArgumentMatchers.isA; +import static org.mockito.Mockito.any; +import static org.mockito.Mockito.doThrow; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoInteractions; +import static org.mockito.Mockito.verifyNoMoreInteractions; +import static org.mockito.Mockito.when; +import static org.powermock.api.mockito.PowerMockito.doAnswer; +import static org.powermock.api.mockito.PowerMockito.mock; +import static org.powermock.api.mockito.PowerMockito.mockStatic; +import static org.powermock.api.mockito.PowerMockito.verifyStatic; +import static org.powermock.api.mockito.PowerMockito.whenNew; + import android.net.TrafficStats; import android.os.Build; import android.util.Log; @@ -50,34 +78,6 @@ import javax.net.ssl.HttpsURLConnection; -import static com.microsoft.appcenter.http.DefaultHttpClient.METHOD_GET; -import static com.microsoft.appcenter.http.DefaultHttpClient.METHOD_POST; -import static org.junit.Assert.assertArrayEquals; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.fail; -import static org.mockito.ArgumentMatchers.contains; -import static org.mockito.ArgumentMatchers.isA; -import static org.mockito.ArgumentMatchers.anyInt; -import static org.mockito.ArgumentMatchers.anyMapOf; -import static org.mockito.ArgumentMatchers.anyString; -import static org.mockito.ArgumentMatchers.argThat; -import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.Mockito.any; -import static org.mockito.Mockito.doThrow; -import static org.mockito.Mockito.never; -import static org.mockito.Mockito.spy; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.verifyNoInteractions; -import static org.mockito.Mockito.verifyNoMoreInteractions; -import static org.mockito.Mockito.when; -import static org.powermock.api.mockito.PowerMockito.doAnswer; -import static org.powermock.api.mockito.PowerMockito.mock; -import static org.powermock.api.mockito.PowerMockito.mockStatic; -import static org.powermock.api.mockito.PowerMockito.verifyStatic; -import static org.powermock.api.mockito.PowerMockito.whenNew; - @SuppressWarnings("unused") @PrepareForTest({ AppCenterLog.class, @@ -138,8 +138,9 @@ public DefaultHttpClientCallTask answer(InvocationOnMock invocation) { return call; } }); - whenNew(Pair.class).withArguments(anyString(), anyMapOf(String.class, String.class)).then(new Answer() { + whenNew(Pair.class).withArguments(anyString(), anyMap()).then(new Answer() { + @SuppressWarnings("rawtypes") @Override public Object answer(InvocationOnMock invocation) { Pair pair = mock(Pair.class); @@ -180,7 +181,7 @@ private void testTls1_2Setting(int apiLevel, int tlsSetExpectedCalls) throws Exc HttpsURLConnection urlConnection = mockConnection(urlString); DefaultHttpClient httpClient = new DefaultHttpClient(); TestUtils.setInternalState(Build.VERSION.class, "SDK_INT", apiLevel); - httpClient.callAsync(urlString, METHOD_POST, new HashMap(), null, mock(ServiceCallback.class)); + httpClient.callAsync(urlString, METHOD_POST, new HashMap<>(), null, mock(ServiceCallback.class)); verify(urlConnection, times(tlsSetExpectedCalls)).setSSLSocketFactory(isA(TLS1_2SocketFactory.class)); } @@ -220,7 +221,7 @@ public void post200() throws Exception { ServiceCallback serviceCallback = mock(ServiceCallback.class); mockCall(); httpClient.callAsync(urlString, METHOD_POST, headers, callTemplate, serviceCallback); - verify(serviceCallback).onCallSucceeded(new HttpResponse(200, "OK", Collections.emptyMap())); + verify(serviceCallback).onCallSucceeded(new HttpResponse(200, "OK", Collections.emptyMap())); verifyNoMoreInteractions(serviceCallback); verify(urlConnection).setRequestProperty("Content-Type", "application/json"); verify(urlConnection, never()).setRequestProperty(eq("Content-Encoding"), anyString()); @@ -229,7 +230,7 @@ public void post200() throws Exception { verify(urlConnection).setRequestMethod("POST"); verify(urlConnection).setDoOutput(true); verify(urlConnection).disconnect(); - verify(callTemplate).onBeforeCalling(any(URL.class), anyMapOf(String.class, String.class)); + verify(callTemplate).onBeforeCalling(any(URL.class), anyMap()); verify(callTemplate).buildRequestBody(); httpClient.close(); @@ -271,7 +272,7 @@ public void post200WithoutCallTemplate() throws Exception { ServiceCallback serviceCallback = mock(ServiceCallback.class); mockCall(); httpClient.callAsync(urlString, METHOD_POST, headers, null, serviceCallback); - verify(serviceCallback).onCallSucceeded(new HttpResponse(200, "OK", Collections.emptyMap())); + verify(serviceCallback).onCallSucceeded(new HttpResponse(200, "OK", Collections.emptyMap())); verifyNoMoreInteractions(serviceCallback); verify(urlConnection, never()).setRequestProperty(eq("Content-Type"), anyString()); verify(urlConnection, never()).setRequestProperty(eq("Content-Encoding"), anyString()); @@ -312,7 +313,7 @@ public void get200() throws Exception { ServiceCallback serviceCallback = mock(ServiceCallback.class); mockCall(); httpClient.callAsync(urlString, METHOD_GET, headers, callTemplate, serviceCallback); - verify(serviceCallback).onCallSucceeded(new HttpResponse(200, "OK", Collections.emptyMap())); + verify(serviceCallback).onCallSucceeded(new HttpResponse(200, "OK", Collections.emptyMap())); verifyNoMoreInteractions(serviceCallback); verify(urlConnection, never()).setRequestProperty(eq("Content-Type"), anyString()); verify(urlConnection, never()).setRequestProperty(eq("Content-Encoding"), anyString()); @@ -322,7 +323,7 @@ public void get200() throws Exception { verify(urlConnection, never()).setDoOutput(true); verify(urlConnection).disconnect(); verify(inputStream).close(); - verify(callTemplate).onBeforeCalling(any(URL.class), anyMapOf(String.class, String.class)); + verify(callTemplate).onBeforeCalling(any(URL.class), anyMap()); verify(callTemplate, never()).buildRequestBody(); httpClient.close(); } @@ -351,7 +352,7 @@ public void get2xx() throws Exception { /* Test calling code. */ ServiceCallback serviceCallback = mock(ServiceCallback.class); httpClient.callAsync(urlString, METHOD_GET, headers, null, serviceCallback); - verify(serviceCallback).onCallSucceeded(eq(new HttpResponse(statusCode, "OK", Collections.emptyMap()))); + verify(serviceCallback).onCallSucceeded(eq(new HttpResponse(statusCode, "OK", Collections.emptyMap()))); verifyNoMoreInteractions(serviceCallback); /* Reset response stream. */ @@ -407,7 +408,7 @@ public void get200WithoutCallTemplate() throws Exception { ServiceCallback serviceCallback = mock(ServiceCallback.class); mockCall(); httpClient.callAsync(urlString, METHOD_GET, headers, null, serviceCallback); - verify(serviceCallback).onCallSucceeded(new HttpResponse(200, "OK", Collections.emptyMap())); + verify(serviceCallback).onCallSucceeded(new HttpResponse(200, "OK", Collections.emptyMap())); verifyNoMoreInteractions(serviceCallback); verify(urlConnection, never()).setRequestProperty(eq("Content-Type"), anyString()); verify(urlConnection, never()).setRequestProperty(eq("Content-Encoding"), anyString()); @@ -444,13 +445,13 @@ private void testPayloadLogging(final String payload, String mimeType) throws Ex ServiceCallback serviceCallback = mock(ServiceCallback.class); mockCall(); httpClient.callAsync(urlString, METHOD_GET, headers, callTemplate, serviceCallback); - verify(serviceCallback).onCallSucceeded(new HttpResponse(200, payload, Collections.emptyMap())); + verify(serviceCallback).onCallSucceeded(new HttpResponse(200, payload, Collections.emptyMap())); verifyNoMoreInteractions(serviceCallback); verify(urlConnection).setRequestMethod("GET"); verify(urlConnection, never()).setDoOutput(true); verify(urlConnection).disconnect(); verify(inputStream).close(); - verify(callTemplate).onBeforeCalling(any(URL.class), anyMapOf(String.class, String.class)); + verify(callTemplate).onBeforeCalling(any(URL.class), anyMap()); verify(callTemplate, never()).buildRequestBody(); httpClient.close(); @@ -498,13 +499,13 @@ public void hideSecretsInResponse() throws Exception { ServiceCallback serviceCallback = mock(ServiceCallback.class); mockCall(); httpClient.callAsync(urlString, METHOD_GET, headers, callTemplate, serviceCallback); - verify(serviceCallback).onCallSucceeded(new HttpResponse(200, payload, Collections.emptyMap())); + verify(serviceCallback).onCallSucceeded(new HttpResponse(200, payload, Collections.emptyMap())); verifyNoMoreInteractions(serviceCallback); verify(urlConnection).setRequestMethod("GET"); verify(urlConnection, never()).setDoOutput(true); verify(urlConnection).disconnect(); verify(inputStream).close(); - verify(callTemplate).onBeforeCalling(any(URL.class), anyMapOf(String.class, String.class)); + verify(callTemplate).onBeforeCalling(any(URL.class), anyMap()); verify(callTemplate, never()).buildRequestBody(); httpClient.close(); @@ -552,7 +553,7 @@ public void get200image() throws Exception { verify(urlConnection, never()).setDoOutput(true); verify(urlConnection).disconnect(); verify(inputStream).close(); - verify(callTemplate).onBeforeCalling(any(URL.class), anyMapOf(String.class, String.class)); + verify(callTemplate).onBeforeCalling(any(URL.class), anyMap()); verify(callTemplate, never()).buildRequestBody(); httpClient.close(); @@ -642,7 +643,7 @@ public void cancel() throws Exception { whenNew(DefaultHttpClientCallTask.class).withAnyArguments().thenReturn(mockCall); DefaultHttpClient httpClient = new DefaultHttpClient(); ServiceCallback serviceCallback = mock(ServiceCallback.class); - ServiceCall call = httpClient.callAsync(urlString, "", new HashMap(), mock(HttpClient.CallTemplate.class), serviceCallback); + ServiceCall call = httpClient.callAsync(urlString, "", new HashMap<>(), mock(HttpClient.CallTemplate.class), serviceCallback); /* Cancel and verify. */ call.cancel(); @@ -685,7 +686,7 @@ public DefaultHttpClientCallTask answer(InvocationOnMock invocation) { /* Simulate HTTP call. */ DefaultHttpClient httpClient = new DefaultHttpClient(); ServiceCallback serviceCallback = mock(ServiceCallback.class); - httpClient.callAsync(urlString, "", new HashMap(), mock(HttpClient.CallTemplate.class), serviceCallback); + httpClient.callAsync(urlString, "", new HashMap<>(), mock(HttpClient.CallTemplate.class), serviceCallback); /* Close and verify. */ httpClient.close(); @@ -711,7 +712,7 @@ public void accept(final DefaultHttpClientCallTask call) { HttpClient.CallTemplate callTemplate = mock(HttpClient.CallTemplate.class); ServiceCallback serviceCallback = mock(ServiceCallback.class); DefaultHttpClient httpClient = new DefaultHttpClient(); - ServiceCall call = httpClient.callAsync(urlString, METHOD_GET, new HashMap(), callTemplate, serviceCallback); + ServiceCall call = httpClient.callAsync(urlString, METHOD_GET, new HashMap<>(), callTemplate, serviceCallback); verify(urlConnection, never()).getResponseCode(); verifyNoMoreInteractions(serviceCallback); assertEquals(0, httpClient.getTasks().size()); @@ -738,7 +739,7 @@ public void accept(final DefaultHttpClientCallTask call) { when(callTemplate.buildRequestBody()).thenReturn("{a:1,b:2}"); ServiceCallback serviceCallback = mock(ServiceCallback.class); DefaultHttpClient httpClient = new DefaultHttpClient(); - ServiceCall call = httpClient.callAsync(urlString, METHOD_POST, new HashMap(), callTemplate, serviceCallback); + ServiceCall call = httpClient.callAsync(urlString, METHOD_POST, new HashMap<>(), callTemplate, serviceCallback); verify(urlConnection, never()).getResponseCode(); verifyNoMoreInteractions(serviceCallback); assertEquals(0, httpClient.getTasks().size()); @@ -762,7 +763,7 @@ public void accept(final DefaultHttpClientCallTask call) { HttpClient.CallTemplate callTemplate = mock(HttpClient.CallTemplate.class); ServiceCallback serviceCallback = mock(ServiceCallback.class); DefaultHttpClient httpClient = new DefaultHttpClient(); - ServiceCall call = httpClient.callAsync(urlString, METHOD_GET, new HashMap(), callTemplate, serviceCallback); + ServiceCall call = httpClient.callAsync(urlString, METHOD_GET, new HashMap<>(), callTemplate, serviceCallback); verify(urlConnection, never()).getResponseCode(); verifyNoMoreInteractions(serviceCallback); assertEquals(0, httpClient.getTasks().size()); @@ -790,8 +791,8 @@ public void accept(final DefaultHttpClientCallTask call) { HttpClient.CallTemplate callTemplate = mock(HttpClient.CallTemplate.class); ServiceCallback serviceCallback = mock(ServiceCallback.class); DefaultHttpClient httpClient = new DefaultHttpClient(); - ServiceCall call = httpClient.callAsync(urlString, METHOD_GET, new HashMap(), callTemplate, serviceCallback); - //verify(serviceCallback).onCallSucceeded(anyString(), anyMapOf(String.class, String.class)); + ServiceCall call = httpClient.callAsync(urlString, METHOD_GET, new HashMap<>(), callTemplate, serviceCallback); + //verify(serviceCallback).onCallSucceeded(anyString(), anyMap()); assertEquals(0, httpClient.getTasks().size()); } @@ -817,7 +818,7 @@ public void accept(final DefaultHttpClientCallTask call) { HttpClient.CallTemplate callTemplate = mock(HttpClient.CallTemplate.class); ServiceCallback serviceCallback = mock(ServiceCallback.class); DefaultHttpClient httpClient = new DefaultHttpClient(); - ServiceCall call = httpClient.callAsync(urlString, METHOD_GET, new HashMap(), callTemplate, serviceCallback); + ServiceCall call = httpClient.callAsync(urlString, METHOD_GET, new HashMap<>(), callTemplate, serviceCallback); verify(serviceCallback).onCallFailed(new HttpException(new HttpResponse(503, "Busy"))); assertEquals(0, httpClient.getTasks().size()); } @@ -834,7 +835,7 @@ public void failedConnection() throws Exception { ServiceCallback serviceCallback = mock(ServiceCallback.class); DefaultHttpClient httpClient = new DefaultHttpClient(); mockCall(); - httpClient.callAsync(urlString, "", new HashMap(), callTemplate, serviceCallback); + httpClient.callAsync(urlString, "", new HashMap<>(), callTemplate, serviceCallback); verify(serviceCallback).onCallFailed(exception); verifyNoInteractions(callTemplate); verifyNoMoreInteractions(serviceCallback); @@ -857,7 +858,7 @@ public void failedToWritePayload() throws Exception { ServiceCallback serviceCallback = mock(ServiceCallback.class); DefaultHttpClient httpClient = new DefaultHttpClient(); mockCall(); - httpClient.callAsync(urlString, METHOD_POST, new HashMap(), callTemplate, serviceCallback); + httpClient.callAsync(urlString, METHOD_POST, new HashMap<>(), callTemplate, serviceCallback); verify(serviceCallback).onCallFailed(exception); verifyNoMoreInteractions(serviceCallback); verify(out).close(); @@ -886,7 +887,7 @@ public void failedToReadResponse() throws Exception { ServiceCallback serviceCallback = mock(ServiceCallback.class); DefaultHttpClient httpClient = new DefaultHttpClient(); mockCall(); - httpClient.callAsync(urlString, "", new HashMap(), callTemplate, serviceCallback); + httpClient.callAsync(urlString, "", new HashMap<>(), callTemplate, serviceCallback); verify(serviceCallback).onCallFailed(exception); verifyNoMoreInteractions(serviceCallback); verify(inputStream).close(); @@ -896,6 +897,7 @@ public void failedToReadResponse() throws Exception { TrafficStats.clearThreadStatsTag(); } + @SuppressWarnings("MaskedAssertion") @Test public void failedWithError() throws Exception { String urlString = "https://mock/get"; @@ -908,7 +910,7 @@ public void failedWithError() throws Exception { DefaultHttpClient httpClient = new DefaultHttpClient(); mockCall(); try { - httpClient.callAsync(urlString, "", new HashMap(), callTemplate, serviceCallback); + httpClient.callAsync(urlString, "", new HashMap<>(), callTemplate, serviceCallback); fail(); } catch (Error ignored) { } @@ -935,7 +937,7 @@ public void failedSerialization() throws Exception { /* Test calling code. */ ServiceCallback serviceCallback = mock(ServiceCallback.class); mockCall(); - httpClient.callAsync(urlString, METHOD_POST, new HashMap(), callTemplate, serviceCallback); + httpClient.callAsync(urlString, METHOD_POST, new HashMap<>(), callTemplate, serviceCallback); verify(serviceCallback).onCallFailed(exception); verifyNoMoreInteractions(serviceCallback); verify(urlConnection).disconnect(); @@ -978,7 +980,7 @@ public void run() { /* Test. */ ServiceCallback serviceCallback = mock(ServiceCallback.class); - assertNotNull(httpClient.callAsync("", "", new HashMap(), mock(HttpClient.CallTemplate.class), serviceCallback)); + assertNotNull(httpClient.callAsync("", "", new HashMap<>(), mock(HttpClient.CallTemplate.class), serviceCallback)); /* Verify the callback call from "main" thread. */ semaphore.acquireUninterruptibly(); @@ -1028,7 +1030,7 @@ public void sendGzipWithoutVerboseLogging() throws Exception { ServiceCallback serviceCallback = mock(ServiceCallback.class); mockCall(); httpClient.callAsync(urlString, METHOD_POST, headers, callTemplate, serviceCallback); - verify(serviceCallback).onCallSucceeded(new HttpResponse(200, "OK", Collections.emptyMap())); + verify(serviceCallback).onCallSucceeded(new HttpResponse(200, "OK", Collections.emptyMap())); verifyNoMoreInteractions(serviceCallback); verify(urlConnection).setRequestProperty("Content-Type", "custom"); @@ -1038,7 +1040,7 @@ public void sendGzipWithoutVerboseLogging() throws Exception { verify(urlConnection).setRequestMethod("POST"); verify(urlConnection).setDoOutput(true); verify(urlConnection).disconnect(); - verify(callTemplate).onBeforeCalling(any(URL.class), anyMapOf(String.class, String.class)); + verify(callTemplate).onBeforeCalling(any(URL.class), anyMap()); verify(callTemplate).buildRequestBody(); httpClient.close(); @@ -1085,7 +1087,7 @@ public void sendNoGzipWhenCompressionDisabled() throws Exception { ServiceCallback serviceCallback = mock(ServiceCallback.class); mockCall(); httpClient.callAsync(urlString, METHOD_POST, headers, callTemplate, serviceCallback); - verify(serviceCallback).onCallSucceeded(new HttpResponse(200, "OK", Collections.emptyMap())); + verify(serviceCallback).onCallSucceeded(new HttpResponse(200, "OK", Collections.emptyMap())); verifyNoMoreInteractions(serviceCallback); verify(urlConnection).setRequestProperty("Content-Type", "custom"); @@ -1095,7 +1097,7 @@ public void sendNoGzipWhenCompressionDisabled() throws Exception { verify(urlConnection).setRequestMethod("POST"); verify(urlConnection).setDoOutput(true); verify(urlConnection).disconnect(); - verify(callTemplate).onBeforeCalling(any(URL.class), anyMapOf(String.class, String.class)); + verify(callTemplate).onBeforeCalling(any(URL.class), anyMap()); verify(callTemplate).buildRequestBody(); httpClient.close(); @@ -1142,7 +1144,7 @@ public void sendNoGzipWithPlainTextVerboseLogging() throws Exception { ServiceCallback serviceCallback = mock(ServiceCallback.class); mockCall(); httpClient.callAsync(urlString, METHOD_POST, headers, callTemplate, serviceCallback); - verify(serviceCallback).onCallSucceeded(new HttpResponse(200, "OK", Collections.emptyMap())); + verify(serviceCallback).onCallSucceeded(new HttpResponse(200, "OK", Collections.emptyMap())); verifyNoMoreInteractions(serviceCallback); verify(urlConnection).setRequestProperty("Content-Type", "custom"); @@ -1152,7 +1154,7 @@ public void sendNoGzipWithPlainTextVerboseLogging() throws Exception { verify(urlConnection).setRequestMethod("POST"); verify(urlConnection).setDoOutput(true); verify(urlConnection).disconnect(); - verify(callTemplate).onBeforeCalling(any(URL.class), anyMapOf(String.class, String.class)); + verify(callTemplate).onBeforeCalling(any(URL.class), anyMap()); verify(callTemplate).buildRequestBody(); httpClient.close(); @@ -1206,7 +1208,7 @@ public void sendGzipWithHugeTextAndVerboseLogging() throws Exception { ServiceCallback serviceCallback = mock(ServiceCallback.class); mockCall(); httpClient.callAsync(urlString, METHOD_POST, headers, callTemplate, serviceCallback); - verify(serviceCallback).onCallSucceeded(new HttpResponse(200, "OK", Collections.emptyMap())); + verify(serviceCallback).onCallSucceeded(new HttpResponse(200, "OK", Collections.emptyMap())); verifyNoMoreInteractions(serviceCallback); verify(urlConnection).setRequestProperty("Content-Type", "custom"); @@ -1216,7 +1218,7 @@ public void sendGzipWithHugeTextAndVerboseLogging() throws Exception { verify(urlConnection).setRequestMethod("POST"); verify(urlConnection).setDoOutput(true); verify(urlConnection).disconnect(); - verify(callTemplate).onBeforeCalling(any(URL.class), anyMapOf(String.class, String.class)); + verify(callTemplate).onBeforeCalling(any(URL.class), anyMap()); verify(callTemplate).buildRequestBody(); httpClient.close(); @@ -1238,7 +1240,7 @@ public void failedToConnectWithHttpUrl() throws Exception { ServiceCallback serviceCallback = mock(ServiceCallback.class); DefaultHttpClient httpClient = new DefaultHttpClient(); mockCall(); - httpClient.callAsync(urlString, "", new HashMap(), callTemplate, serviceCallback); + httpClient.callAsync(urlString, "", new HashMap<>(), callTemplate, serviceCallback); verify(serviceCallback).onCallFailed(any(IOException.class)); verifyNoInteractions(callTemplate); verifyNoMoreInteractions(serviceCallback); @@ -1254,7 +1256,7 @@ public void failedToConnectWithInvalidUrl() throws Exception { ServiceCallback serviceCallback = mock(ServiceCallback.class); DefaultHttpClient httpClient = new DefaultHttpClient(); mockCall(); - httpClient.callAsync(urlString, "", new HashMap(), callTemplate, serviceCallback); + httpClient.callAsync(urlString, "", new HashMap<>(), callTemplate, serviceCallback); verify(serviceCallback).onCallFailed(any(IOException.class)); verifyNoInteractions(callTemplate); verifyNoMoreInteractions(serviceCallback); @@ -1272,7 +1274,7 @@ public void failedToConnectWithHttpConnect() throws Exception { ServiceCallback serviceCallback = mock(ServiceCallback.class); DefaultHttpClient httpClient = new DefaultHttpClient(); mockCall(); - httpClient.callAsync(urlString, "", new HashMap(), callTemplate, serviceCallback); + httpClient.callAsync(urlString, "", new HashMap<>(), callTemplate, serviceCallback); verify(serviceCallback).onCallFailed(any(IOException.class)); verifyNoInteractions(callTemplate); verifyNoMoreInteractions(serviceCallback); diff --git a/sdk/appcenter/src/test/java/com/microsoft/appcenter/http/HttpClientNetworkStateHandlerTest.java b/sdk/appcenter/src/test/java/com/microsoft/appcenter/http/HttpClientNetworkStateHandlerTest.java index 28fe8d847..3c5074bb6 100644 --- a/sdk/appcenter/src/test/java/com/microsoft/appcenter/http/HttpClientNetworkStateHandlerTest.java +++ b/sdk/appcenter/src/test/java/com/microsoft/appcenter/http/HttpClientNetworkStateHandlerTest.java @@ -8,7 +8,6 @@ import android.content.Context; import android.net.ConnectivityManager; import android.net.Network; -import android.net.NetworkRequest; import android.os.Build; import com.microsoft.appcenter.test.TestUtils; diff --git a/sdk/appcenter/src/test/java/com/microsoft/appcenter/http/Tls1_2SocketFactoryTest.java b/sdk/appcenter/src/test/java/com/microsoft/appcenter/http/Tls1_2SocketFactoryTest.java index da12c9647..f96155fce 100644 --- a/sdk/appcenter/src/test/java/com/microsoft/appcenter/http/Tls1_2SocketFactoryTest.java +++ b/sdk/appcenter/src/test/java/com/microsoft/appcenter/http/Tls1_2SocketFactoryTest.java @@ -16,14 +16,11 @@ import java.net.Socket; import java.security.KeyManagementException; import java.security.NoSuchAlgorithmException; -import java.security.SecureRandom; import javax.net.ssl.HttpsURLConnection; -import javax.net.ssl.KeyManager; import javax.net.ssl.SSLContext; import javax.net.ssl.SSLSocket; import javax.net.ssl.SSLSocketFactory; -import javax.net.ssl.TrustManager; import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertNotNull; diff --git a/sdk/appcenter/src/test/java/com/microsoft/appcenter/ingestion/AppCenterIngestionTest.java b/sdk/appcenter/src/test/java/com/microsoft/appcenter/ingestion/AppCenterIngestionTest.java index 2ef36cd71..442d934f3 100644 --- a/sdk/appcenter/src/test/java/com/microsoft/appcenter/ingestion/AppCenterIngestionTest.java +++ b/sdk/appcenter/src/test/java/com/microsoft/appcenter/ingestion/AppCenterIngestionTest.java @@ -27,7 +27,6 @@ import org.powermock.core.classloader.annotations.PrepareForTest; import org.powermock.modules.junit4.rule.PowerMockRule; -import java.io.IOException; import java.net.URL; import java.util.ArrayList; import java.util.HashMap; @@ -41,7 +40,7 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; -import static org.mockito.ArgumentMatchers.anyMapOf; +import static org.mockito.ArgumentMatchers.anyMap; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.contains; import static org.mockito.ArgumentMatchers.eq; @@ -91,7 +90,7 @@ public void sendAsync() throws Exception { /* Configure mock HTTP. */ final ServiceCall call = mock(ServiceCall.class); final AtomicReference callTemplate = new AtomicReference<>(); - when(mHttpClient.callAsync(anyString(), anyString(), anyMapOf(String.class, String.class), any(HttpClient.CallTemplate.class), any(ServiceCallback.class))).then(new Answer() { + when(mHttpClient.callAsync(anyString(), anyString(), anyMap(), any(HttpClient.CallTemplate.class), any(ServiceCallback.class))).then(new Answer() { @Override public ServiceCall answer(InvocationOnMock invocation) { callTemplate.set((HttpClient.CallTemplate) invocation.getArguments()[3]); @@ -140,7 +139,7 @@ public void failedSerialization() throws Exception { /* Configure mock HTTP. */ final ServiceCall call = mock(ServiceCall.class); final AtomicReference callTemplate = new AtomicReference<>(); - when(mHttpClient.callAsync(anyString(), anyString(), anyMapOf(String.class, String.class), any(HttpClient.CallTemplate.class), any(ServiceCallback.class))).then(new Answer() { + when(mHttpClient.callAsync(anyString(), anyString(), anyMap(), any(HttpClient.CallTemplate.class), any(ServiceCallback.class))).then(new Answer() { @Override public ServiceCall answer(InvocationOnMock invocation) { @@ -266,7 +265,7 @@ private HttpClient.CallTemplate getCallTemplate(String appSecret) { /* Configure mock HTTP to get an instance of IngestionCallTemplate. */ final ServiceCall call = mock(ServiceCall.class); final AtomicReference callTemplate = new AtomicReference<>(); - when(mHttpClient.callAsync(anyString(), anyString(), anyMapOf(String.class, String.class), any(HttpClient.CallTemplate.class), any(ServiceCallback.class))).then(new Answer() { + when(mHttpClient.callAsync(anyString(), anyString(), anyMap(), any(HttpClient.CallTemplate.class), any(ServiceCallback.class))).then(new Answer() { @Override public ServiceCall answer(InvocationOnMock invocation) { diff --git a/sdk/appcenter/src/test/java/com/microsoft/appcenter/ingestion/OneCollectorIngestionTest.java b/sdk/appcenter/src/test/java/com/microsoft/appcenter/ingestion/OneCollectorIngestionTest.java index 3aa98ebea..9aca4f794 100644 --- a/sdk/appcenter/src/test/java/com/microsoft/appcenter/ingestion/OneCollectorIngestionTest.java +++ b/sdk/appcenter/src/test/java/com/microsoft/appcenter/ingestion/OneCollectorIngestionTest.java @@ -55,7 +55,7 @@ import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; -import static org.mockito.ArgumentMatchers.anyMapOf; +import static org.mockito.ArgumentMatchers.anyMap; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.contains; import static org.mockito.ArgumentMatchers.eq; @@ -157,7 +157,7 @@ public void sendAsync() throws Exception { /* Configure mock HTTP. */ ServiceCall call = mock(ServiceCall.class); ArgumentCaptor callTemplate = ArgumentCaptor.forClass(HttpClient.CallTemplate.class); - when(mHttpClient.callAsync(anyString(), anyString(), anyMapOf(String.class, String.class), callTemplate.capture(), any(ServiceCallback.class))).thenReturn(call); + when(mHttpClient.callAsync(anyString(), anyString(), anyMap(), callTemplate.capture(), any(ServiceCallback.class))).thenReturn(call); /* Test calling code. */ OneCollectorIngestion ingestion = new OneCollectorIngestion(mHttpClient, serializer); @@ -304,7 +304,7 @@ public void failedSerialization() throws Exception { /* Configure mock HTTP. */ ServiceCall call = mock(ServiceCall.class); ArgumentCaptor callTemplate = ArgumentCaptor.forClass(HttpClient.CallTemplate.class); - when(mHttpClient.callAsync(anyString(), anyString(), anyMapOf(String.class, String.class), callTemplate.capture(), any(ServiceCallback.class))).thenReturn(call); + when(mHttpClient.callAsync(anyString(), anyString(), anyMap(), callTemplate.capture(), any(ServiceCallback.class))).thenReturn(call); /* Test calling code. */ OneCollectorIngestion ingestion = new OneCollectorIngestion(mHttpClient, serializer); @@ -392,7 +392,7 @@ private HttpClient.CallTemplate getCallTemplate() { /* Configure mock HTTP to get an instance of IngestionCallTemplate. */ ServiceCall call = mock(ServiceCall.class); ArgumentCaptor callTemplate = ArgumentCaptor.forClass(HttpClient.CallTemplate.class); - when(mHttpClient.callAsync(anyString(), anyString(), anyMapOf(String.class, String.class), callTemplate.capture(), any(ServiceCallback.class))).thenReturn(call); + when(mHttpClient.callAsync(anyString(), anyString(), anyMap(), callTemplate.capture(), any(ServiceCallback.class))).thenReturn(call); OneCollectorIngestion ingestion = new OneCollectorIngestion(mHttpClient, mock(LogSerializer.class)); ingestion.setLogUrl("http://mock"); assertEquals(call, ingestion.sendAsync(null, null, mock(LogContainer.class), mock(ServiceCallback.class))); diff --git a/sdk/appcenter/src/test/java/com/microsoft/appcenter/ingestion/models/AbstractLogTest.java b/sdk/appcenter/src/test/java/com/microsoft/appcenter/ingestion/models/AbstractLogTest.java index 5691ccc36..8e6a0cd77 100644 --- a/sdk/appcenter/src/test/java/com/microsoft/appcenter/ingestion/models/AbstractLogTest.java +++ b/sdk/appcenter/src/test/java/com/microsoft/appcenter/ingestion/models/AbstractLogTest.java @@ -28,6 +28,7 @@ @SuppressWarnings("unused") public class AbstractLogTest { + @SuppressWarnings("InstantiationOfUtilityClass") @Test public void utilsCoverage() { new CommonProperties(); diff --git a/sdk/appcenter/src/test/java/com/microsoft/appcenter/ingestion/models/one/PartAUtilsTest.java b/sdk/appcenter/src/test/java/com/microsoft/appcenter/ingestion/models/one/PartAUtilsTest.java index abf59d037..bbf86e4cd 100644 --- a/sdk/appcenter/src/test/java/com/microsoft/appcenter/ingestion/models/one/PartAUtilsTest.java +++ b/sdk/appcenter/src/test/java/com/microsoft/appcenter/ingestion/models/one/PartAUtilsTest.java @@ -40,6 +40,7 @@ private static void testValidName(String name) { assertEquals(name, log.getName()); } + @SuppressWarnings("InstantiationOfUtilityClass") @Test public void coverInit() { new PartAUtils(); diff --git a/sdk/appcenter/src/test/java/com/microsoft/appcenter/ingestion/models/properties/TypedPropertyUtilsTest.java b/sdk/appcenter/src/test/java/com/microsoft/appcenter/ingestion/models/properties/TypedPropertyUtilsTest.java index c3e8aa2f2..3a70b822d 100644 --- a/sdk/appcenter/src/test/java/com/microsoft/appcenter/ingestion/models/properties/TypedPropertyUtilsTest.java +++ b/sdk/appcenter/src/test/java/com/microsoft/appcenter/ingestion/models/properties/TypedPropertyUtilsTest.java @@ -21,6 +21,7 @@ public void createValidTypedProperty() throws JSONException { assertTrue(TypedPropertyUtils.create(StringTypedProperty.TYPE) instanceof StringTypedProperty); } + @SuppressWarnings("InstantiationOfUtilityClass") @Test(expected = JSONException.class) public void createInvalidTypedProperty() throws JSONException { //noinspection AccessStaticViaInstance to cover default constructor diff --git a/sdk/appcenter/src/test/java/com/microsoft/appcenter/persistence/DatabasePersistenceTest.java b/sdk/appcenter/src/test/java/com/microsoft/appcenter/persistence/DatabasePersistenceTest.java index 1ba2465e9..4842d802f 100644 --- a/sdk/appcenter/src/test/java/com/microsoft/appcenter/persistence/DatabasePersistenceTest.java +++ b/sdk/appcenter/src/test/java/com/microsoft/appcenter/persistence/DatabasePersistenceTest.java @@ -123,7 +123,7 @@ public void clearPendingLogState() throws Exception { /* Get logs. */ for (int i = 0; i < groupCount; i++) { - persistence.getLogs(String.valueOf(i), Collections.emptyList(), logCount, new ArrayList()); + persistence.getLogs(String.valueOf(i), Collections.emptyList(), logCount, new ArrayList()); } /* Verify there are 4 pending groups. */ @@ -150,7 +150,7 @@ public void getLogsWithGetCursorException() throws Exception { /* Try to get logs. */ ArrayList outLogs = new ArrayList<>(); - persistence.getLogs("mock", Collections.emptyList(), 50, outLogs); + persistence.getLogs("mock", Collections.emptyList(), 50, outLogs); assertEquals(0, outLogs.size()); /* There is an error log. */ @@ -173,7 +173,7 @@ public void getLogsWithMoveNextException() throws Exception { /* Try to get logs. */ ArrayList outLogs = new ArrayList<>(); - persistence.getLogs("mock", Collections.emptyList(), 50, outLogs); + persistence.getLogs("mock", Collections.emptyList(), 50, outLogs); assertEquals(0, outLogs.size()); /* There is an error log. */ @@ -212,7 +212,7 @@ public void getLogsWithGetCorruptedIdsException() throws Exception { /* Get logs and verify we get only non corrupted logs. */ DatabasePersistence persistence = new DatabasePersistence(mock(Context.class)); ArrayList outLogs = new ArrayList<>(); - persistence.getLogs("mock", Collections.emptyList(), 50, outLogs); + persistence.getLogs("mock", Collections.emptyList(), 50, outLogs); assertEquals(0, outLogs.size()); /* There is an error log. */ @@ -288,7 +288,7 @@ public Log answer(InvocationOnMock invocation) { /* Get logs and verify we get only non corrupted logs. */ ArrayList outLogs = new ArrayList<>(); - persistence.getLogs("mock", Collections.emptyList(), 50, outLogs); + persistence.getLogs("mock", Collections.emptyList(), 50, outLogs); assertEquals(logCount - 1, outLogs.size()); assertEquals("first", outLogs.get(0).getType()); assertEquals("last", outLogs.get(1).getType()); @@ -298,7 +298,7 @@ public Log answer(InvocationOnMock invocation) { /* Verify next call is empty logs as they are pending. */ outLogs = new ArrayList<>(); - persistence.getLogs("mock", Collections.emptyList(), 50, outLogs); + persistence.getLogs("mock", Collections.emptyList(), 50, outLogs); assertEquals(0, outLogs.size()); /* @@ -369,7 +369,7 @@ public void close() { /* Verify next call is only the new valid log as others are marked pending. */ outLogs = new ArrayList<>(); - persistence.getLogs("mock", Collections.emptyList(), 50, outLogs); + persistence.getLogs("mock", Collections.emptyList(), 50, outLogs); assertEquals(1, outLogs.size()); assertEquals("true last", outLogs.get(0).getType()); diff --git a/sdk/appcenter/src/test/java/com/microsoft/appcenter/utils/AppNameHelperTest.java b/sdk/appcenter/src/test/java/com/microsoft/appcenter/utils/AppNameHelperTest.java index 94fb94c73..c826a05bd 100644 --- a/sdk/appcenter/src/test/java/com/microsoft/appcenter/utils/AppNameHelperTest.java +++ b/sdk/appcenter/src/test/java/com/microsoft/appcenter/utils/AppNameHelperTest.java @@ -17,6 +17,7 @@ public class AppNameHelperTest { + @SuppressWarnings("InstantiationOfUtilityClass") @Test public void init() { new AppNameHelper(); diff --git a/sdk/appcenter/src/test/java/com/microsoft/appcenter/utils/AsyncTaskUtilsTest.java b/sdk/appcenter/src/test/java/com/microsoft/appcenter/utils/AsyncTaskUtilsTest.java index ac1143242..50e84e6a4 100644 --- a/sdk/appcenter/src/test/java/com/microsoft/appcenter/utils/AsyncTaskUtilsTest.java +++ b/sdk/appcenter/src/test/java/com/microsoft/appcenter/utils/AsyncTaskUtilsTest.java @@ -12,7 +12,6 @@ import org.powermock.core.classloader.annotations.PrepareForTest; import org.powermock.modules.junit4.PowerMockRunner; -import java.util.concurrent.Executor; import java.util.concurrent.RejectedExecutionException; import static org.junit.Assert.assertSame; diff --git a/sdk/appcenter/src/test/java/com/microsoft/appcenter/utils/HashUtilsTest.java b/sdk/appcenter/src/test/java/com/microsoft/appcenter/utils/HashUtilsTest.java index 32f77d933..536cf8614 100644 --- a/sdk/appcenter/src/test/java/com/microsoft/appcenter/utils/HashUtilsTest.java +++ b/sdk/appcenter/src/test/java/com/microsoft/appcenter/utils/HashUtilsTest.java @@ -25,6 +25,7 @@ public class HashUtilsTest { @Rule public PowerMockRule mPowerMockRule = new PowerMockRule(); + @SuppressWarnings("InstantiationOfUtilityClass") @Test public void init() { new HashUtils(); diff --git a/sdk/appcenter/src/test/java/com/microsoft/appcenter/utils/InstrumentationRegistryHelperTest.java b/sdk/appcenter/src/test/java/com/microsoft/appcenter/utils/InstrumentationRegistryHelperTest.java index 7c4dc2015..e8b9184d9 100644 --- a/sdk/appcenter/src/test/java/com/microsoft/appcenter/utils/InstrumentationRegistryHelperTest.java +++ b/sdk/appcenter/src/test/java/com/microsoft/appcenter/utils/InstrumentationRegistryHelperTest.java @@ -16,7 +16,6 @@ import static org.junit.Assert.assertFalse; import static org.mockito.ArgumentMatchers.any; -import static org.mockito.Mockito.when; import static org.powermock.api.mockito.PowerMockito.doThrow; import static org.powermock.api.mockito.PowerMockito.mockStatic; import static org.powermock.api.mockito.PowerMockito.spy; @@ -30,6 +29,7 @@ public class InstrumentationRegistryHelperTest { @Rule public PowerMockRule rule = new PowerMockRule(); + @SuppressWarnings("InstantiationOfUtilityClass") @Test public void instrumentationRegistryHelperCoverage() { new InstrumentationRegistryHelper(); diff --git a/sdk/appcenter/src/test/java/com/microsoft/appcenter/utils/TicketCacheTest.java b/sdk/appcenter/src/test/java/com/microsoft/appcenter/utils/TicketCacheTest.java index 928eda910..f204c078a 100644 --- a/sdk/appcenter/src/test/java/com/microsoft/appcenter/utils/TicketCacheTest.java +++ b/sdk/appcenter/src/test/java/com/microsoft/appcenter/utils/TicketCacheTest.java @@ -24,6 +24,7 @@ public void tearDown() { TicketCache.clear(); } + @SuppressWarnings("InstantiationOfUtilityClass") @Test public void coverInit() { new TicketCache(); diff --git a/sdk/appcenter/src/test/java/com/microsoft/appcenter/utils/crypto/CryptoTest.java b/sdk/appcenter/src/test/java/com/microsoft/appcenter/utils/crypto/CryptoTest.java index b4354e4e0..da54d3049 100644 --- a/sdk/appcenter/src/test/java/com/microsoft/appcenter/utils/crypto/CryptoTest.java +++ b/sdk/appcenter/src/test/java/com/microsoft/appcenter/utils/crypto/CryptoTest.java @@ -5,6 +5,35 @@ package com.microsoft.appcenter.utils.crypto; +import static com.microsoft.appcenter.utils.crypto.CryptoConstants.AES_KEY_SIZE; +import static com.microsoft.appcenter.utils.crypto.CryptoConstants.ALGORITHM_DATA_SEPARATOR; +import static com.microsoft.appcenter.utils.crypto.CryptoConstants.ANDROID_KEY_STORE; +import static com.microsoft.appcenter.utils.crypto.CryptoConstants.CIPHER_AES; +import static com.microsoft.appcenter.utils.crypto.CryptoConstants.CIPHER_RSA; +import static com.microsoft.appcenter.utils.crypto.CryptoConstants.RSA_KEY_SIZE; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotEquals; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.ArgumentMatchers.contains; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.ArgumentMatchers.isNull; +import static org.mockito.ArgumentMatchers.notNull; +import static org.mockito.Mockito.atLeastOnce; +import static org.mockito.Mockito.doAnswer; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.powermock.api.mockito.PowerMockito.doThrow; +import static org.powermock.api.mockito.PowerMockito.mock; +import static org.powermock.api.mockito.PowerMockito.mockStatic; +import static org.powermock.api.mockito.PowerMockito.verifyStatic; +import static org.powermock.api.mockito.PowerMockito.when; +import static org.powermock.api.mockito.PowerMockito.whenNew; + import android.annotation.SuppressLint; import android.content.Context; import android.os.Build; @@ -17,8 +46,6 @@ import org.junit.Rule; import org.junit.Test; import org.mockito.ArgumentCaptor; -import org.mockito.ArgumentMatcher; -import org.mockito.Matchers; import org.mockito.Mock; import org.mockito.invocation.InvocationOnMock; import org.mockito.stubbing.Answer; @@ -26,7 +53,6 @@ import org.powermock.core.classloader.annotations.PrepareForTest; import org.powermock.modules.junit4.rule.PowerMockRule; -import java.io.UnsupportedEncodingException; import java.math.BigInteger; import java.nio.ByteBuffer; import java.nio.CharBuffer; @@ -53,36 +79,6 @@ import javax.crypto.spec.SecretKeySpec; import javax.security.auth.x500.X500Principal; -import static com.microsoft.appcenter.utils.crypto.CryptoConstants.AES_KEY_SIZE; -import static com.microsoft.appcenter.utils.crypto.CryptoConstants.ALGORITHM_DATA_SEPARATOR; -import static com.microsoft.appcenter.utils.crypto.CryptoConstants.ANDROID_KEY_STORE; -import static com.microsoft.appcenter.utils.crypto.CryptoConstants.CIPHER_AES; -import static com.microsoft.appcenter.utils.crypto.CryptoConstants.CIPHER_RSA; -import static com.microsoft.appcenter.utils.crypto.CryptoConstants.RSA_KEY_SIZE; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotEquals; -import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertTrue; -import static org.mockito.ArgumentMatchers.contains; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.anyInt; -import static org.mockito.ArgumentMatchers.anyString; -import static org.mockito.ArgumentMatchers.argThat; -import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.ArgumentMatchers.isNull; -import static org.mockito.ArgumentMatchers.notNull; -import static org.mockito.Mockito.atLeastOnce; -import static org.mockito.Mockito.doAnswer; -import static org.mockito.Mockito.spy; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.verify; -import static org.powermock.api.mockito.PowerMockito.doThrow; -import static org.powermock.api.mockito.PowerMockito.mock; -import static org.powermock.api.mockito.PowerMockito.mockStatic; -import static org.powermock.api.mockito.PowerMockito.verifyStatic; -import static org.powermock.api.mockito.PowerMockito.when; -import static org.powermock.api.mockito.PowerMockito.whenNew; - @SuppressLint("NewApi") @PowerMockIgnore({"javax.security.auth.x500.*"}) @PrepareForTest({KeyStore.class, KeyPairGenerator.class, Base64.class, CryptoUtils.class, CryptoRsaHandler.class, CryptoAesHandler.class, CryptoAesAndEtmHandler.class, MessageDigest.class}) @@ -132,7 +128,7 @@ public String answer(InvocationOnMock invocation) throws CharacterCodingExceptio when(Base64.decode(anyString(), anyInt())).thenAnswer(new Answer() { @Override - public byte[] answer(InvocationOnMock invocation) throws UnsupportedEncodingException, CharacterCodingException { + public byte[] answer(InvocationOnMock invocation) throws CharacterCodingException { CharsetEncoder encoder = StandardCharsets.ISO_8859_1.newEncoder(); return encoder.encode(CharBuffer.wrap(invocation.getArguments()[0].toString())) .array(); @@ -338,12 +334,11 @@ public byte[] answer(InvocationOnMock invocation) { @Override public byte[] answer(InvocationOnMock invocation) { - byte[] input = (byte[]) invocation.getArguments()[0]; - return input; + return invocation.getArgument(0); } }); int expectedKeyStoreCalls = 3; - verify(mKeyStore, times(expectedKeyStoreCalls)).getEntry(notNull(String.class), isNull(KeyStore.ProtectionParameter.class)); + verify(mKeyStore, times(expectedKeyStoreCalls)).getEntry(notNull(), isNull()); /* Verify we can decrypt with retry on expired key. */ CryptoUtils.DecryptedData decryptedData = cryptoUtils.decrypt(encryptedData); @@ -353,7 +348,7 @@ public byte[] answer(InvocationOnMock invocation) { /* Verify the second alias was picked for decryption. */ expectedKeyStoreCalls += 2; ArgumentCaptor aliasCaptor = ArgumentCaptor.forClass(String.class); - verify(mKeyStore, times(expectedKeyStoreCalls)).getEntry(aliasCaptor.capture(), isNull(KeyStore.ProtectionParameter.class)); + verify(mKeyStore, times(expectedKeyStoreCalls)).getEntry(aliasCaptor.capture(), isNull()); List aliases = aliasCaptor.getAllValues(); /* Check last calls: first we tried to read with the second alias (after rotation). */ @@ -392,7 +387,7 @@ public byte[] answer(InvocationOnMock invocation) { } }); int expectedKeyStoreCalls = 3; - verify(mKeyStore, times(expectedKeyStoreCalls)).getEntry(notNull(String.class), isNull(KeyStore.ProtectionParameter.class)); + verify(mKeyStore, times(expectedKeyStoreCalls)).getEntry(notNull(), isNull()); /* Verify we can decrypt with retry on expired key. */ CryptoUtils.DecryptedData decryptedData = cryptoUtils.decrypt(encryptedData); @@ -402,7 +397,7 @@ public byte[] answer(InvocationOnMock invocation) { /* Verify the second alias was picked for decryption. */ expectedKeyStoreCalls += 2; ArgumentCaptor aliasCaptor = ArgumentCaptor.forClass(String.class); - verify(mKeyStore, times(expectedKeyStoreCalls)).getEntry(aliasCaptor.capture(), isNull(KeyStore.ProtectionParameter.class)); + verify(mKeyStore, times(expectedKeyStoreCalls)).getEntry(aliasCaptor.capture(), isNull()); List aliases = aliasCaptor.getAllValues(); /* Check last calls: first we tried to read with the second alias (after rotation). */ @@ -541,7 +536,6 @@ public void aesPreferredInM() throws Exception { assertEquals(oldAesSourceText, oldDecryptedAesData.getDecryptedData()); assertTrue(oldDecryptedAesData.getNewEncryptedData().contains(expectedPrefix) && oldDecryptedAesData.getNewEncryptedData().contains(oldAesSourceText)); oldDecryptedAesData = cryptoUtils.decrypt(CIPHER_AES + "/" + AES_KEY_SIZE + ALGORITHM_DATA_SEPARATOR + "IV" + oldAesSourceText); - String t = oldDecryptedAesData.getDecryptedData(); assertEquals(oldAesSourceText, oldDecryptedAesData.getDecryptedData()); assertTrue(oldDecryptedAesData.getNewEncryptedData().contains(expectedPrefix) && oldDecryptedAesData.getNewEncryptedData().contains(oldAesSourceText)); @@ -560,6 +554,7 @@ public void aesPreferredInM() throws Exception { assertTrue(alias.getValue().contains(CIPHER_AES)); } + @SuppressWarnings("rawtypes") @Test public void verifyEncryptAes() throws Exception { @@ -624,6 +619,7 @@ public void verifyEncryptAesWithEtm() { assertEquals(decryptionString, sourceText); } + @SuppressWarnings({"unchecked", "rawtypes"}) @Test public void verifyExceptionDuringCreateSubkeyWhenOutputLengthLessOne() throws Exception { @@ -658,7 +654,7 @@ public byte[] answer(InvocationOnMock invocation) throws Throwable { spyCryptoAesAndEtmHandler.getSubkey((SecretKey)invocation.getArguments()[0], 0); return new byte[0]; } - }).when(spyCryptoAesAndEtmHandler).getSubkey(Matchers.any(), eq(16)); + }).when(spyCryptoAesAndEtmHandler).getSubkey(any(), eq(16)); // Start encryption. String encryptedText = cryptoUtils.encrypt(sourceText); @@ -667,6 +663,7 @@ public byte[] answer(InvocationOnMock invocation) throws Throwable { assertEquals(encryptedText, sourceText); } + @SuppressWarnings({"unchecked", "rawtypes"}) @Test public void verifyExceptionDuringCreateSubkeyWithTooMuchOperations() throws Exception { @@ -701,7 +698,7 @@ public byte[] answer(InvocationOnMock invocation) throws Throwable { spyCryptoAesAndEtmHandler.getSubkey((SecretKey)invocation.getArguments()[0], 999999999); return new byte[0]; } - }).when(spyCryptoAesAndEtmHandler).getSubkey(Matchers.any(), eq(16)); + }).when(spyCryptoAesAndEtmHandler).getSubkey(any(), eq(16)); // Start encryption. String encryptedText = cryptoUtils.encrypt(sourceText); @@ -711,28 +708,28 @@ public byte[] answer(InvocationOnMock invocation) throws Throwable { } @Test - public void verifyDecryptAesWithEtmCouldNotAuthenticateMac() throws Exception { - verifyExceptionDuringDecryptionAesWithEtm(16, 16, 32, SecurityException.class); + public void verifyDecryptAesWithEtmCouldNotAuthenticateMac() { + verifyExceptionDuringDecryptionAesWithEtm(16, 32); } @Test - public void verifyDecryptAesWithEtmWhenIvLengthInvalid() throws Exception { - verifyExceptionDuringDecryptionAesWithEtm(0, 16, 32, IllegalArgumentException.class); + public void verifyDecryptAesWithEtmWhenIvLengthInvalid() { + verifyExceptionDuringDecryptionAesWithEtm(0, 32); } @Test - public void verifyDecryptAesWithEtmWhenMacLengthInvalid() throws Exception { - verifyExceptionDuringDecryptionAesWithEtm(16, 16, 0, IllegalArgumentException.class); + public void verifyDecryptAesWithEtmWhenMacLengthInvalid() { + verifyExceptionDuringDecryptionAesWithEtm(16, 0); } - private void verifyExceptionDuringDecryptionAesWithEtm(int ivLength, int cipherOutputLength, int hMacLength, Class exceptionClass) throws Exception { + private void verifyExceptionDuringDecryptionAesWithEtm(int ivLength, int hMacLength) { // Mock MessageDigest. mockStatic(MessageDigest.class); // Prepare data. byte[] iv = new byte[ivLength]; - byte[] cipherOutput = new byte[cipherOutputLength]; + byte[] cipherOutput = new byte[16]; byte[] hMac = new byte[hMacLength]; // Start encryption. diff --git a/sdk/appcenter/src/test/java/com/microsoft/appcenter/utils/storage/DatabaseManagerTest.java b/sdk/appcenter/src/test/java/com/microsoft/appcenter/utils/storage/DatabaseManagerTest.java index 3e592cd73..f8e0e014a 100644 --- a/sdk/appcenter/src/test/java/com/microsoft/appcenter/utils/storage/DatabaseManagerTest.java +++ b/sdk/appcenter/src/test/java/com/microsoft/appcenter/utils/storage/DatabaseManagerTest.java @@ -20,7 +20,6 @@ import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; -import org.mockito.internal.stubbing.answers.Returns; import org.powermock.core.classloader.annotations.PrepareForTest; import org.powermock.modules.junit4.PowerMockRunner; From 76ee83e366eb104171d7950aa3e50fb6dd328eb2 Mon Sep 17 00:00:00 2001 From: Ivan Matkov Date: Thu, 28 Apr 2022 11:29:25 +0200 Subject: [PATCH 12/72] Fix more warnings in tests --- .../analytics/channel/SessionTrackerTest.java | 6 +- .../models/json/EventLogFactoryTest.java | 8 +-- .../DistributeManualCheckForUpdateTest.java | 6 +- .../ReleaseDownloaderFactoryTest.java | 9 ++- .../DownloadManagerReleaseDownloaderTest.java | 67 +++++++++---------- .../DownloadManagerRemoveTaskTest.java | 2 +- .../ingestion/AppCenterIngestionTest.java | 6 +- .../ingestion/OneCollectorIngestionTest.java | 2 +- .../ApplicationLifecycleListenerTest.java | 2 +- .../utils/context/UserIdContextTest.java | 6 +- 10 files changed, 56 insertions(+), 58 deletions(-) diff --git a/sdk/appcenter-analytics/src/test/java/com/microsoft/appcenter/analytics/channel/SessionTrackerTest.java b/sdk/appcenter-analytics/src/test/java/com/microsoft/appcenter/analytics/channel/SessionTrackerTest.java index 6c59e1d08..28af7f9a9 100644 --- a/sdk/appcenter-analytics/src/test/java/com/microsoft/appcenter/analytics/channel/SessionTrackerTest.java +++ b/sdk/appcenter-analytics/src/test/java/com/microsoft/appcenter/analytics/channel/SessionTrackerTest.java @@ -41,7 +41,7 @@ import static org.junit.Assert.assertTrue; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyInt; -import static org.mockito.ArgumentMatchers.anySetOf; +import static org.mockito.ArgumentMatchers.anySet; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.ArgumentMatchers.isA; @@ -107,7 +107,7 @@ public Void answer(InvocationOnMock invocation) { return null; } }).when(SharedPreferencesManager.class); - SharedPreferencesManager.putStringSet(anyString(), anySetOf(String.class)); + SharedPreferencesManager.putStringSet(anyString(), anySet()); when(SharedPreferencesManager.getStringSet(anyString())).thenReturn(null); SessionContext.unsetInstance(); spendTime(1000); @@ -321,7 +321,7 @@ public void goBackgroundAndComeBackMuchLater() { public void startSessionWithoutLogs() { final AtomicReference startSessionLog = new AtomicReference<>(); - doAnswer(new Answer() { + doAnswer(new Answer() { @Override public Object answer(InvocationOnMock invocation) { diff --git a/sdk/appcenter-analytics/src/test/java/com/microsoft/appcenter/analytics/ingestion/models/json/EventLogFactoryTest.java b/sdk/appcenter-analytics/src/test/java/com/microsoft/appcenter/analytics/ingestion/models/json/EventLogFactoryTest.java index 4e0c0bae8..23bacdf95 100644 --- a/sdk/appcenter-analytics/src/test/java/com/microsoft/appcenter/analytics/ingestion/models/json/EventLogFactoryTest.java +++ b/sdk/appcenter-analytics/src/test/java/com/microsoft/appcenter/analytics/ingestion/models/json/EventLogFactoryTest.java @@ -45,7 +45,7 @@ public void createEvent() { } @Test - public void dontConvertEventWithoutTargetTokens() { + public void doNotConvertEventWithoutTargetTokens() { /* Create event log with just a name and no target. */ EventLog log = new EventLog(); @@ -108,12 +108,12 @@ public void convertEventWithoutProperties() { /* Check Part A was added with target tokens. */ verifyStatic(PartAUtils.class); - PartAUtils.addPartAFromLog(eq(log), notNull(CommonSchemaLog.class), eq("t1")); + PartAUtils.addPartAFromLog(eq(log), notNull(), eq("t1")); verifyStatic(PartAUtils.class); - PartAUtils.addPartAFromLog(eq(log), notNull(CommonSchemaLog.class), eq("t2")); + PartAUtils.addPartAFromLog(eq(log), notNull(), eq("t2")); /* Check data was added with typed properties (and thus not old ones). */ verifyStatic(CommonSchemaDataUtils.class, times(2)); - CommonSchemaDataUtils.addCommonSchemaData(eq(properties), notNull(CommonSchemaLog.class)); + CommonSchemaDataUtils.addCommonSchemaData(eq(properties), notNull()); } } diff --git a/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/DistributeManualCheckForUpdateTest.java b/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/DistributeManualCheckForUpdateTest.java index 98aed2284..525bbfd97 100644 --- a/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/DistributeManualCheckForUpdateTest.java +++ b/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/DistributeManualCheckForUpdateTest.java @@ -46,7 +46,7 @@ public void checkForUpdateAfterWorkflowCompletesChecksAgain() { Distribute.checkForUpdate(); /* Then we call again. */ - verify(mHttpClient, times(2)).callAsync(anyString(), anyString(), eq(Collections.emptyMap()), any(HttpClient.CallTemplate.class), notNull(ServiceCallback.class)); + verify(mHttpClient, times(2)).callAsync(anyString(), anyString(), eq(Collections.emptyMap()), any(HttpClient.CallTemplate.class), notNull()); } @Test @@ -64,13 +64,13 @@ public void checkForUpdateBeforeCallCompletes() { Distribute.checkForUpdate(); /* Then we don't call again. */ - verify(mHttpClient).callAsync(anyString(), anyString(), eq(Collections.emptyMap()), any(HttpClient.CallTemplate.class), notNull(ServiceCallback.class)); + verify(mHttpClient).callAsync(anyString(), anyString(), eq(Collections.emptyMap()), any(HttpClient.CallTemplate.class), notNull()); /* And it's not queued when current call finishes with no update available. */ HttpResponse response = mock(HttpResponse.class); when(response.getPayload()).thenReturn(""); httpCallback.getValue().onCallSucceeded(response); - verify(mHttpClient).callAsync(anyString(), anyString(), eq(Collections.emptyMap()), any(HttpClient.CallTemplate.class), notNull(ServiceCallback.class)); + verify(mHttpClient).callAsync(anyString(), anyString(), eq(Collections.emptyMap()), any(HttpClient.CallTemplate.class), notNull()); } @Test diff --git a/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/download/ReleaseDownloaderFactoryTest.java b/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/download/ReleaseDownloaderFactoryTest.java index 0d0202794..95c8e802b 100644 --- a/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/download/ReleaseDownloaderFactoryTest.java +++ b/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/download/ReleaseDownloaderFactoryTest.java @@ -5,6 +5,9 @@ package com.microsoft.appcenter.distribute.download; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; + import android.content.Context; import android.os.Build; @@ -18,10 +21,6 @@ import org.mockito.Mock; import org.powermock.modules.junit4.PowerMockRunner; -import static org.hamcrest.CoreMatchers.instanceOf; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertThat; - @RunWith(PowerMockRunner.class) public class ReleaseDownloaderFactoryTest { @@ -51,6 +50,6 @@ public void generatedConstructor() { public void createOnLollipop() throws Exception { TestUtils.setInternalState(Build.VERSION.class, "SDK_INT", Build.VERSION_CODES.LOLLIPOP); ReleaseDownloader releaseDownloader = ReleaseDownloaderFactory.create(mockContext, mockReleaseDetails, mockReleaseDownloaderListener); - assertThat(releaseDownloader, instanceOf(DownloadManagerReleaseDownloader.class)); + assertTrue(releaseDownloader instanceof DownloadManagerReleaseDownloader); } } \ No newline at end of file diff --git a/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/download/manager/DownloadManagerReleaseDownloaderTest.java b/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/download/manager/DownloadManagerReleaseDownloaderTest.java index d92ced011..e9b7db422 100644 --- a/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/download/manager/DownloadManagerReleaseDownloaderTest.java +++ b/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/download/manager/DownloadManagerReleaseDownloaderTest.java @@ -5,6 +5,25 @@ package com.microsoft.appcenter.distribute.download.manager; +import static android.content.Context.DOWNLOAD_SERVICE; +import static com.microsoft.appcenter.distribute.DistributeConstants.INVALID_DOWNLOAD_IDENTIFIER; +import static com.microsoft.appcenter.distribute.DistributeConstants.PREFERENCE_KEY_DOWNLOAD_ID; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyLong; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.ArgumentMatchers.isA; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; +import static org.powermock.api.mockito.PowerMockito.mock; +import static org.powermock.api.mockito.PowerMockito.mockStatic; +import static org.powermock.api.mockito.PowerMockito.verifyStatic; + import android.app.DownloadManager; import android.content.Context; import android.database.Cursor; @@ -22,32 +41,12 @@ import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; -import org.mockito.Mockito; import org.mockito.invocation.InvocationOnMock; import org.mockito.stubbing.Answer; import org.powermock.core.classloader.annotations.PrepareForTest; import org.powermock.modules.junit4.PowerMockRunner; import org.powermock.reflect.Whitebox; -import static android.content.Context.DOWNLOAD_SERVICE; -import static com.microsoft.appcenter.distribute.DistributeConstants.INVALID_DOWNLOAD_IDENTIFIER; -import static com.microsoft.appcenter.distribute.DistributeConstants.PREFERENCE_KEY_DOWNLOAD_ID; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.anyLong; -import static org.mockito.ArgumentMatchers.anyString; -import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.ArgumentMatchers.isA; -import static org.mockito.Mockito.never; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; -import static org.powermock.api.mockito.PowerMockito.mock; -import static org.powermock.api.mockito.PowerMockito.mockStatic; -import static org.powermock.api.mockito.PowerMockito.verifyStatic; - @PrepareForTest({ AsyncTaskUtils.class, Build.VERSION.class, @@ -134,7 +133,7 @@ public void downloadStatus() { public void resumeStartsUpdateTask() { mReleaseDownloader.resume(); verifyStatic(AsyncTaskUtils.class); - AsyncTaskUtils.execute(anyString(), isA(DownloadManagerUpdateTask.class), Mockito.anyVararg()); + AsyncTaskUtils.execute(anyString(), isA(DownloadManagerUpdateTask.class), any()); } @Test @@ -142,7 +141,7 @@ public void resumeDoesNothingAfterCancellation() { mReleaseDownloader.cancel(); mReleaseDownloader.resume(); verifyStatic(AsyncTaskUtils.class, never()); - AsyncTaskUtils.execute(anyString(), isA(DownloadManagerUpdateTask.class), Mockito.anyVararg()); + AsyncTaskUtils.execute(anyString(), isA(DownloadManagerUpdateTask.class), any()); } @Test @@ -151,12 +150,12 @@ public void cancelClearsEverything() { /* Update status. */ mReleaseDownloader.resume(); verifyStatic(AsyncTaskUtils.class); - AsyncTaskUtils.execute(anyString(), isA(DownloadManagerUpdateTask.class), Mockito.anyVararg()); + AsyncTaskUtils.execute(anyString(), isA(DownloadManagerUpdateTask.class), any()); /* Create new request. */ mReleaseDownloader.onStart(); verifyStatic(AsyncTaskUtils.class); - AsyncTaskUtils.execute(anyString(), isA(DownloadManagerRequestTask.class), Mockito.anyVararg()); + AsyncTaskUtils.execute(anyString(), isA(DownloadManagerRequestTask.class), any()); /* Cancel clears everything only once. */ mReleaseDownloader.cancel(); @@ -166,7 +165,7 @@ public void cancelClearsEverything() { verify(mRequestTask).cancel(eq(true)); verify(mUpdateTask).cancel(eq(true)); verifyStatic(AsyncTaskUtils.class); - AsyncTaskUtils.execute(anyString(), isA(DownloadManagerRemoveTask.class), Mockito.anyVararg()); + AsyncTaskUtils.execute(anyString(), isA(DownloadManagerRemoveTask.class), any()); verifyStatic(SharedPreferencesManager.class); SharedPreferencesManager.remove(eq(PREFERENCE_KEY_DOWNLOAD_ID)); } @@ -179,7 +178,7 @@ public void doNotTryToRemoveInvalidDownloadId() { /* Verify. */ verifyStatic(AsyncTaskUtils.class, never()); - AsyncTaskUtils.execute(anyString(), isA(DownloadManagerRemoveTask.class), Mockito.anyVararg()); + AsyncTaskUtils.execute(anyString(), isA(DownloadManagerRemoveTask.class), any()); verifyStatic(SharedPreferencesManager.class, never()); SharedPreferencesManager.remove(eq(PREFERENCE_KEY_DOWNLOAD_ID)); } @@ -190,18 +189,18 @@ public void requestNewDownloading() { /* Create new request. */ mReleaseDownloader.onStart(); verifyStatic(AsyncTaskUtils.class); - AsyncTaskUtils.execute(anyString(), isA(DownloadManagerRequestTask.class), Mockito.anyVararg()); + AsyncTaskUtils.execute(anyString(), isA(DownloadManagerRequestTask.class), any()); /* Do not duplicate requests. */ mReleaseDownloader.onStart(); verifyStatic(AsyncTaskUtils.class); - AsyncTaskUtils.execute(anyString(), isA(DownloadManagerRequestTask.class), Mockito.anyVararg()); + AsyncTaskUtils.execute(anyString(), isA(DownloadManagerRequestTask.class), any()); /* Don't do anything after cancellation. */ mReleaseDownloader.cancel(); mReleaseDownloader.onStart(); verifyStatic(AsyncTaskUtils.class); - AsyncTaskUtils.execute(anyString(), isA(DownloadManagerRequestTask.class), Mockito.anyVararg()); + AsyncTaskUtils.execute(anyString(), isA(DownloadManagerRequestTask.class), any()); } @Test @@ -209,7 +208,7 @@ public void doNotRequestNewDownloadingAfterCancellation() { mReleaseDownloader.cancel(); mReleaseDownloader.onStart(); verifyStatic(AsyncTaskUtils.class, never()); - AsyncTaskUtils.execute(anyString(), isA(DownloadManagerRequestTask.class), Mockito.anyVararg()); + AsyncTaskUtils.execute(anyString(), isA(DownloadManagerRequestTask.class), any()); } @Test @@ -225,7 +224,7 @@ public void startDownloadingMandatoryUpdate() { verifyStatic(SharedPreferencesManager.class); SharedPreferencesManager.putLong(eq(PREFERENCE_KEY_DOWNLOAD_ID), eq(DOWNLOAD_ID)); verifyStatic(AsyncTaskUtils.class); - AsyncTaskUtils.execute(anyString(), isA(DownloadManagerUpdateTask.class), Mockito.anyVararg()); + AsyncTaskUtils.execute(anyString(), isA(DownloadManagerUpdateTask.class), any()); } @Test @@ -241,7 +240,7 @@ public void startDownloadingNotMandatoryUpdate() { verifyStatic(SharedPreferencesManager.class); SharedPreferencesManager.putLong(eq(PREFERENCE_KEY_DOWNLOAD_ID), eq(DOWNLOAD_ID)); verifyStatic(AsyncTaskUtils.class, never()); - AsyncTaskUtils.execute(anyString(), isA(DownloadManagerUpdateTask.class), Mockito.anyVararg()); + AsyncTaskUtils.execute(anyString(), isA(DownloadManagerUpdateTask.class), any()); } @Test @@ -266,7 +265,7 @@ public void scheduleAnotherUpdateIfListenerWantsIt() { /* Verify. */ verify(mListener).onProgress(anyLong(), anyLong()); verifyStatic(AsyncTaskUtils.class); - AsyncTaskUtils.execute(anyString(), isA(DownloadManagerUpdateTask.class), Mockito.anyVararg()); + AsyncTaskUtils.execute(anyString(), isA(DownloadManagerUpdateTask.class), any()); } @Test @@ -282,7 +281,7 @@ public void doNotScheduleAnotherUpdateIfListenerDoNotWantsIt() { /* Verify. */ verify(mListener).onProgress(anyLong(), anyLong()); verifyStatic(AsyncTaskUtils.class, never()); - AsyncTaskUtils.execute(anyString(), isA(DownloadManagerUpdateTask.class), Mockito.anyVararg()); + AsyncTaskUtils.execute(anyString(), isA(DownloadManagerUpdateTask.class), any()); } @Test diff --git a/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/download/manager/DownloadManagerRemoveTaskTest.java b/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/download/manager/DownloadManagerRemoveTaskTest.java index 7da0c71d2..c731cf568 100644 --- a/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/download/manager/DownloadManagerRemoveTaskTest.java +++ b/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/download/manager/DownloadManagerRemoveTaskTest.java @@ -11,7 +11,7 @@ import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; -import org.mockito.runners.MockitoJUnitRunner; +import org.mockito.junit.MockitoJUnitRunner; import static android.content.Context.DOWNLOAD_SERVICE; import static org.mockito.Mockito.verify; diff --git a/sdk/appcenter/src/test/java/com/microsoft/appcenter/ingestion/AppCenterIngestionTest.java b/sdk/appcenter/src/test/java/com/microsoft/appcenter/ingestion/AppCenterIngestionTest.java index 442d934f3..44032cafc 100644 --- a/sdk/appcenter/src/test/java/com/microsoft/appcenter/ingestion/AppCenterIngestionTest.java +++ b/sdk/appcenter/src/test/java/com/microsoft/appcenter/ingestion/AppCenterIngestionTest.java @@ -110,7 +110,7 @@ public ServiceCall answer(InvocationOnMock invocation) { HashMap expectedHeaders = new HashMap<>(); expectedHeaders.put(Constants.APP_SECRET, appSecret); expectedHeaders.put(AppCenterIngestion.INSTALL_ID, installId.toString()); - verify(mHttpClient).callAsync(eq("http://mock" + AppCenterIngestion.API_PATH), eq(METHOD_POST), eq(expectedHeaders), notNull(HttpClient.CallTemplate.class), eq(serviceCallback)); + verify(mHttpClient).callAsync(eq("http://mock" + AppCenterIngestion.API_PATH), eq(METHOD_POST), eq(expectedHeaders), notNull(), eq(serviceCallback)); assertNotNull(callTemplate.get()); assertEquals("mockPayload", callTemplate.get().buildRequestBody()); @@ -160,7 +160,7 @@ public ServiceCall answer(InvocationOnMock invocation) { HashMap expectedHeaders = new HashMap<>(); expectedHeaders.put(Constants.APP_SECRET, appSecret); expectedHeaders.put(AppCenterIngestion.INSTALL_ID, installId.toString()); - verify(mHttpClient).callAsync(eq("http://mock/logs?api-version=1.0.0"), eq(METHOD_POST), eq(expectedHeaders), notNull(HttpClient.CallTemplate.class), eq(serviceCallback)); + verify(mHttpClient).callAsync(eq("http://mock/logs?api-version=1.0.0"), eq(METHOD_POST), eq(expectedHeaders), notNull(), eq(serviceCallback)); assertNotNull(callTemplate.get()); try { @@ -257,7 +257,7 @@ public void sendLogsWhenIngestionDisable() throws JSONException { HashMap expectedHeaders = new HashMap<>(); expectedHeaders.put(Constants.APP_SECRET, appSecret); expectedHeaders.put(AppCenterIngestion.INSTALL_ID, installId.toString()); - verify(mHttpClient, never()).callAsync(eq("http://mock" + AppCenterIngestion.API_PATH), eq(METHOD_POST), eq(expectedHeaders), notNull(HttpClient.CallTemplate.class), eq(serviceCallback)); + verify(mHttpClient, never()).callAsync(eq("http://mock" + AppCenterIngestion.API_PATH), eq(METHOD_POST), eq(expectedHeaders), notNull(), eq(serviceCallback)); } private HttpClient.CallTemplate getCallTemplate(String appSecret) { diff --git a/sdk/appcenter/src/test/java/com/microsoft/appcenter/ingestion/OneCollectorIngestionTest.java b/sdk/appcenter/src/test/java/com/microsoft/appcenter/ingestion/OneCollectorIngestionTest.java index 9aca4f794..1fd4ad374 100644 --- a/sdk/appcenter/src/test/java/com/microsoft/appcenter/ingestion/OneCollectorIngestionTest.java +++ b/sdk/appcenter/src/test/java/com/microsoft/appcenter/ingestion/OneCollectorIngestionTest.java @@ -171,7 +171,7 @@ public void sendAsync() throws Exception { expectedHeaders.put(OneCollectorIngestion.CLIENT_VERSION_KEY, String.format("ACS-Android-Java-no-%s-no", VERSION_NAME)); expectedHeaders.put(OneCollectorIngestion.UPLOAD_TIME_KEY, "1234"); expectedHeaders.put(DefaultHttpClient.CONTENT_TYPE_KEY, "application/x-json-stream; charset=utf-8"); - verify(mHttpClient).callAsync(eq("http://mock"), eq(METHOD_POST), eq(expectedHeaders), notNull(HttpClient.CallTemplate.class), eq(serviceCallback)); + verify(mHttpClient).callAsync(eq("http://mock"), eq(METHOD_POST), eq(expectedHeaders), notNull(), eq(serviceCallback)); assertNotNull(callTemplate.getValue()); assertEquals("mockPayload1\nmockPayload2\n", callTemplate.getValue().buildRequestBody()); diff --git a/sdk/appcenter/src/test/java/com/microsoft/appcenter/utils/ApplicationLifecycleListenerTest.java b/sdk/appcenter/src/test/java/com/microsoft/appcenter/utils/ApplicationLifecycleListenerTest.java index 96123dd2f..f00e78e5b 100644 --- a/sdk/appcenter/src/test/java/com/microsoft/appcenter/utils/ApplicationLifecycleListenerTest.java +++ b/sdk/appcenter/src/test/java/com/microsoft/appcenter/utils/ApplicationLifecycleListenerTest.java @@ -14,7 +14,7 @@ import org.junit.runner.RunWith; import org.mockito.ArgumentCaptor; import org.mockito.Mock; -import org.mockito.runners.MockitoJUnitRunner; +import org.mockito.junit.MockitoJUnitRunner; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyLong; diff --git a/sdk/appcenter/src/test/java/com/microsoft/appcenter/utils/context/UserIdContextTest.java b/sdk/appcenter/src/test/java/com/microsoft/appcenter/utils/context/UserIdContextTest.java index 00caceb9b..241540125 100644 --- a/sdk/appcenter/src/test/java/com/microsoft/appcenter/utils/context/UserIdContextTest.java +++ b/sdk/appcenter/src/test/java/com/microsoft/appcenter/utils/context/UserIdContextTest.java @@ -113,7 +113,7 @@ public void setUserIdUserEquals() { /* Setting to null is an update "sign out". */ userIdContext.setUserId(null); - verify(listener).onNewUserId(isNull(String.class)); + verify(listener).onNewUserId(isNull()); assertNull(userIdContext.getUserId()); } @@ -139,7 +139,7 @@ public void setUserIdUserNotEquals() { /* Setting to null is an update "sign out". */ userIdContext.setUserId(null); - verify(listener).onNewUserId(isNull(String.class)); + verify(listener).onNewUserId(isNull()); assertNull(userIdContext.getUserId()); } @@ -173,7 +173,7 @@ public void addAndRemoveListeners() { /* Verify we now get called with update, here is a null update example. */ userIdContext.setUserId(null); - verify(listener).onNewUserId(isNull(String.class)); + verify(listener).onNewUserId(isNull()); assertNull(userIdContext.getUserId()); } From c8f820b91dcb90de55ad62ed721f2ce3bd907466 Mon Sep 17 00:00:00 2001 From: Ivan Matkov Date: Thu, 28 Apr 2022 11:48:21 +0200 Subject: [PATCH 13/72] Increase timeout for distribute tests --- .../microsoft/appcenter/distribute/AbstractDistributeTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/AbstractDistributeTest.java b/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/AbstractDistributeTest.java index cec1bb214..6c04597c2 100644 --- a/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/AbstractDistributeTest.java +++ b/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/AbstractDistributeTest.java @@ -100,7 +100,7 @@ public class AbstractDistributeTest { * Use a timeout to fail test if deadlocks happen due to a code change. */ @Rule - public Timeout mGlobalTimeout = Timeout.seconds(10); + public Timeout mGlobalTimeout = Timeout.seconds(15); @Mock Context mContext; From ab6de9b8fe8378db22b83dde63d4cc8b86a7ad86 Mon Sep 17 00:00:00 2001 From: Ivan Matkov Date: Thu, 5 May 2022 14:23:18 +0200 Subject: [PATCH 14/72] Fix coverage collection --- build.gradle | 2 +- sdk/build.gradle | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/build.gradle b/build.gradle index 4f8cbbaff..790cd758b 100644 --- a/build.gradle +++ b/build.gradle @@ -19,7 +19,7 @@ buildscript { dependencies { classpath 'com.android.tools.build:gradle:7.1.3' - classpath 'org.kt3k.gradle.plugin:coveralls-gradle-plugin:2.8.2' + classpath 'gradle.plugin.org.kt3k.gradle.plugin:coveralls-gradle-plugin:2.12.0' } } diff --git a/sdk/build.gradle b/sdk/build.gradle index 7dc18a9f0..f989044ec 100644 --- a/sdk/build.gradle +++ b/sdk/build.gradle @@ -120,8 +120,8 @@ subprojects { fileTree(dir: "$buildDir/intermediates/javac/debug/classes", excludes: fileFilter) ]) executionData.from = fileTree(dir: buildDir, includes: [ - 'jacoco/testDebugUnitTest.exec', - 'outputs/code_coverage/debugAndroidTest/connected/*coverage.ec' + 'outputs/unit_test_code_coverage/debugUnitTest/testDebugUnitTest.exec', + 'outputs/code_coverage/debugAndroidTest/connected/*/coverage.ec' ]) /* Exclude coverage report result for appcenter-distribute-play module from the main coverage report. */ From 16482a1412d4493fcbe3daa91febe57320263ce3 Mon Sep 17 00:00:00 2001 From: Ivan Matkov Date: Thu, 5 May 2022 17:52:22 +0200 Subject: [PATCH 15/72] Revert to Gradle 6.x --- build.gradle | 3 +- gradle.properties | 3 - gradle/wrapper/gradle-wrapper.properties | 4 +- sdk/build.gradle | 87 +++++++++++++++++++++--- test/build.gradle | 4 +- 5 files changed, 86 insertions(+), 15 deletions(-) diff --git a/build.gradle b/build.gradle index 790cd758b..573e030d1 100644 --- a/build.gradle +++ b/build.gradle @@ -18,7 +18,8 @@ buildscript { } dependencies { - classpath 'com.android.tools.build:gradle:7.1.3' + classpath 'com.android.tools.build:gradle:4.2.2' + classpath 'com.github.dcendents:android-maven-gradle-plugin:2.1' classpath 'gradle.plugin.org.kt3k.gradle.plugin:coveralls-gradle-plugin:2.12.0' } } diff --git a/gradle.properties b/gradle.properties index b6513c826..5bac8ac50 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,4 +1 @@ android.useAndroidX=true - -# Software Components will not be created automatically for Maven publishing from Android Gradle Plugin 8.0. -android.disableAutomaticComponentCreation=true diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 49c73ad4f..aacf6ad3c 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -4,7 +4,7 @@ # distributionBase=GRADLE_USER_HOME +distributionUrl=https\://services.gradle.org/distributions/gradle-6.9.2-bin.zip distributionPath=wrapper/dists -zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-7.2-all.zip +zipStoreBase=GRADLE_USER_HOME diff --git a/sdk/build.gradle b/sdk/build.gradle index f989044ec..35936a3e0 100644 --- a/sdk/build.gradle +++ b/sdk/build.gradle @@ -11,14 +11,11 @@ allprojects { apply plugin: 'jacoco' jacoco { - toolVersion '0.8.8' + toolVersion '0.8.3' } tasks.withType(Test) { jacoco.includeNoLocationClasses = true jacoco.excludes = ['jdk.internal.*'] - - // Run tests in parallel. - maxParallelForks = Runtime.runtime.availableProcessors() } //noinspection GroovyAssignabilityCheck @@ -29,6 +26,7 @@ allprojects { subprojects { apply plugin: 'com.android.library' + apply plugin: 'com.github.dcendents.android-maven' apply plugin: 'maven-publish' apply plugin: 'signing' @@ -65,7 +63,7 @@ subprojects { testOptions { unitTests { all { - jvmArgs '-noverify', '-Djdk.attach.allowAttachSelf=true', '-Djdk.module.illegalAccess.silent=true' + jvmArgs '-noverify', '-Djdk.attach.allowAttachSelf=true' } returnDefaultValues = true } @@ -120,8 +118,8 @@ subprojects { fileTree(dir: "$buildDir/intermediates/javac/debug/classes", excludes: fileFilter) ]) executionData.from = fileTree(dir: buildDir, includes: [ - 'outputs/unit_test_code_coverage/debugUnitTest/testDebugUnitTest.exec', - 'outputs/code_coverage/debugAndroidTest/connected/*/coverage.ec' + 'jacoco/testDebugUnitTest.exec', + 'outputs/code_coverage/debugAndroidTest/connected/*coverage.ec' ]) /* Exclude coverage report result for appcenter-distribute-play module from the main coverage report. */ @@ -132,6 +130,13 @@ subprojects { } } + task sourcesJar(type: Jar) { + afterEvaluate { + from android.sourceSets.main.java.srcDirs + classifier = 'sources' + } + } + task javadoc(type: Javadoc) { afterEvaluate { source = android.sourceSets.main.java.srcDirs @@ -149,7 +154,73 @@ subprojects { } } - // FIXME: Add publishing back + task javadocJar(type: Jar, dependsOn: javadoc) { + classifier = 'javadoc' + //noinspection GroovyAccessibility + from javadoc.destinationDir + } + + // Set artifacts for publish. + artifacts { + archives javadocJar + archives sourcesJar + } + + afterEvaluate { project -> + uploadArchives { + repositories { + mavenDeployer { + beforeDeployment { MavenDeployment deployment -> signing.signPom(deployment) } + + repository(url: ext.mavenRepoUrl) { + authentication(userName: ext.mavenUser, password: ext.mavenKey) + } + + pom.project { + + // Set base information about assemble. + packaging = 'aar' + name = project.name + description = project.description + url = ext.siteUrl + + // Set identifiers of assemble. + groupId = ext.groupId + artifactId = project.name + + // Set license information. + licenses { + license { + name = ext.licenseName + url = ext.licenseSite + } + } + + // Set information about developers. + developers { + developer { + id = ext.developerId + name = ext.developerName + email = ext.developerEmail + } + } + + // Set information about connection with developers. + scm { + connection = ext.gitUrl + developerConnection = ext.gitUrl + url = ext.siteUrl + } + } + } + } + + signing { + required { gradle.taskGraph.hasTask("uploadArchives") } + sign configurations.archives + } + } + } } // :sdk:coverageReport which combines all coverageReports generated by sub projects diff --git a/test/build.gradle b/test/build.gradle index 678d13dc6..707170f0a 100644 --- a/test/build.gradle +++ b/test/build.gradle @@ -6,7 +6,9 @@ apply plugin: 'com.android.library' android { - lint { + + //noinspection GroovyMissingReturnStatement + lintOptions { disable 'InvalidPackage' } } From 634ea0c0dee3127e65eda6dbe9e11d12da7c9cf4 Mon Sep 17 00:00:00 2001 From: Ivan Matkov Date: Thu, 5 May 2022 19:23:10 +0200 Subject: [PATCH 16/72] Fix javadoc task --- .../appcenter/crashes/utils/ErrorLogHelper.java | 1 - .../com/microsoft/appcenter/utils/AppCenterLog.java | 13 +++---------- 2 files changed, 3 insertions(+), 11 deletions(-) diff --git a/sdk/appcenter-crashes/src/main/java/com/microsoft/appcenter/crashes/utils/ErrorLogHelper.java b/sdk/appcenter-crashes/src/main/java/com/microsoft/appcenter/crashes/utils/ErrorLogHelper.java index 2341cfecf..a76d54fbd 100644 --- a/sdk/appcenter-crashes/src/main/java/com/microsoft/appcenter/crashes/utils/ErrorLogHelper.java +++ b/sdk/appcenter-crashes/src/main/java/com/microsoft/appcenter/crashes/utils/ErrorLogHelper.java @@ -22,7 +22,6 @@ import com.microsoft.appcenter.crashes.ingestion.models.Thread; import com.microsoft.appcenter.crashes.model.ErrorReport; import com.microsoft.appcenter.ingestion.models.Device; -import com.microsoft.appcenter.ingestion.models.Log; import com.microsoft.appcenter.utils.AppCenterLog; import com.microsoft.appcenter.utils.DeviceInfoHelper; import com.microsoft.appcenter.utils.context.UserIdContext; diff --git a/sdk/appcenter/src/main/java/com/microsoft/appcenter/utils/AppCenterLog.java b/sdk/appcenter/src/main/java/com/microsoft/appcenter/utils/AppCenterLog.java index b1a5de278..afcc9daf4 100644 --- a/sdk/appcenter/src/main/java/com/microsoft/appcenter/utils/AppCenterLog.java +++ b/sdk/appcenter/src/main/java/com/microsoft/appcenter/utils/AppCenterLog.java @@ -5,21 +5,14 @@ package com.microsoft.appcenter.utils; -import androidx.annotation.IntRange; -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; +import static android.util.Log.VERBOSE; import android.util.Log; -import static android.util.Log.VERBOSE; - -import com.microsoft.appcenter.BuildConfig; +import androidx.annotation.IntRange; +import androidx.annotation.NonNull; -import java.util.logging.ConsoleHandler; -import java.util.logging.Handler; import java.util.logging.Level; -import java.util.logging.LogManager; -import java.util.logging.LogRecord; import java.util.logging.Logger; /** From 48fb309c2118c5fb7052365f3052828ce5b0a95d Mon Sep 17 00:00:00 2001 From: Ivan Matkov Date: Thu, 5 May 2022 19:24:06 +0200 Subject: [PATCH 17/72] Force update jacoco --- build.gradle | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/build.gradle b/build.gradle index 573e030d1..bf0b1fd48 100644 --- a/build.gradle +++ b/build.gradle @@ -21,6 +21,11 @@ buildscript { classpath 'com.android.tools.build:gradle:4.2.2' classpath 'com.github.dcendents:android-maven-gradle-plugin:2.1' classpath 'gradle.plugin.org.kt3k.gradle.plugin:coveralls-gradle-plugin:2.12.0' + + /* Resolves issue of incorrect version use in one of jacoco/android plugin inner tasks (can be removed on AGP 7.2+). */ + classpath 'org.jacoco:org.jacoco.core:0.8.3' + classpath 'org.jacoco:org.jacoco.report:0.8.3' + classpath 'org.jacoco:org.jacoco.agent:0.8.3' } } @@ -36,6 +41,18 @@ allprojects { } subprojects { + + /* Force Jacoco Agent version upgrade (can be removed on AGP 7.2+). */ + configurations.all { + resolutionStrategy { + eachDependency { details -> + if ('org.jacoco' == details.requested.group) { + details.useVersion '0.8.3' + } + } + } + } + afterEvaluate { project -> if (project.hasProperty('android')) { def config = android.defaultConfig From f741ef3b39c9a523fe82bd7fad1e630d8d719611 Mon Sep 17 00:00:00 2001 From: Ivan Matkov Date: Thu, 5 May 2022 22:21:20 +0200 Subject: [PATCH 18/72] Fix getCursor argument matcher --- .../persistence/DatabasePersistenceTest.java | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/sdk/appcenter/src/test/java/com/microsoft/appcenter/persistence/DatabasePersistenceTest.java b/sdk/appcenter/src/test/java/com/microsoft/appcenter/persistence/DatabasePersistenceTest.java index 4842d802f..5f2449645 100644 --- a/sdk/appcenter/src/test/java/com/microsoft/appcenter/persistence/DatabasePersistenceTest.java +++ b/sdk/appcenter/src/test/java/com/microsoft/appcenter/persistence/DatabasePersistenceTest.java @@ -49,7 +49,11 @@ import java.util.List; @SuppressWarnings("unused") -@PrepareForTest({AppCenterLog.class, DatabaseManager.class, DatabasePersistence.class}) +@PrepareForTest({ + AppCenterLog.class, + DatabaseManager.class, + DatabasePersistence.class +}) public class DatabasePersistenceTest { @Rule @@ -123,7 +127,7 @@ public void clearPendingLogState() throws Exception { /* Get logs. */ for (int i = 0; i < groupCount; i++) { - persistence.getLogs(String.valueOf(i), Collections.emptyList(), logCount, new ArrayList()); + persistence.getLogs(String.valueOf(i), Collections.emptyList(), logCount, new ArrayList<>()); } /* Verify there are 4 pending groups. */ @@ -279,7 +283,7 @@ public Log answer(InvocationOnMock invocation) { /* Hack serializer to return type = payload to simplify checking. */ Log log = mock(Log.class); - when(log.getType()).thenReturn((String) invocation.getArguments()[0]); + when(log.getType()).thenReturn(invocation.getArgument(0)); return log; } }); @@ -346,7 +350,8 @@ public void close() { } }; mockCursor.mockBuildValues(databaseManager); - when(databaseManager.getCursor(any(SQLiteQueryBuilder.class), isNull(), any(String[].class), anyString())).thenReturn(mockCursor); + when(databaseManager.getCursor(any(SQLiteQueryBuilder.class), isNull(), any(String[].class), anyString())) + .thenReturn(mockCursor); idValues = new ArrayList<>(4); /* Here the id cursor will also skip the new corrupted log which id would be 3. */ @@ -365,7 +370,8 @@ public void close() { } }; mockIdCursor.mockBuildValues(databaseManager); - when(databaseManager.getCursor(any(SQLiteQueryBuilder.class), isNotNull(), any(String[].class), anyString())).thenReturn(mockIdCursor); + when(databaseManager.getCursor(any(SQLiteQueryBuilder.class), isNotNull(), any(String[].class), isNull())) + .thenReturn(mockIdCursor); /* Verify next call is only the new valid log as others are marked pending. */ outLogs = new ArrayList<>(); From 2d8d5101036f4155601793b6058b611b4640d5ab Mon Sep 17 00:00:00 2001 From: Ivan Matkov Date: Thu, 5 May 2022 22:29:54 +0200 Subject: [PATCH 19/72] Use Java 11 in CodeQL --- .github/workflows/codeql.yml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index 1e4c0a686..20d67a4ba 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -20,6 +20,12 @@ jobs: - name: Checkout repository uses: actions/checkout@v2 + - name: Use Java 11 + uses: actions/setup-java@v3 + with: + distribution: 'temurin' + java-version: '11' + # Initializes the CodeQL tools for scanning. - name: Initialize CodeQL uses: github/codeql-action/init@v1 From e0bd99684b1ee6fdd41216ef98c1f9d59c4364d6 Mon Sep 17 00:00:00 2001 From: Ivan Matkov Date: Mon, 11 Apr 2022 23:39:12 +0200 Subject: [PATCH 20/72] Resume app on notification click --- .../appcenter/distribute/Distribute.java | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/Distribute.java b/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/Distribute.java index 4c6a3e7f9..5c68b03a6 100644 --- a/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/Distribute.java +++ b/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/Distribute.java @@ -13,6 +13,7 @@ import android.app.Notification; import android.app.NotificationChannel; import android.app.NotificationManager; +import android.app.PendingIntent; import android.content.ActivityNotFoundException; import android.content.Context; import android.content.DialogInterface; @@ -1999,22 +2000,26 @@ synchronized boolean notifyDownload(ReleaseDetails releaseDetails) { NotificationChannel channel = new NotificationChannel(NOTIFICATION_CHANNEL_ID, mContext.getString(R.string.appcenter_distribute_notification_category), NotificationManager.IMPORTANCE_DEFAULT); - - //noinspection ConstantConditions notificationManager.createNotificationChannel(channel); builder = new Notification.Builder(mContext, NOTIFICATION_CHANNEL_ID); } else { builder = getOldNotificationBuilder(); } + + // TODO: Combine this and code from resumeApp. + Intent intent = new Intent(mContext, DeepLinkActivity.class); + intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + PendingIntent pendingIntent = PendingIntent.getActivity( + mContext, 0, intent, 0); + builder.setTicker(mContext.getString(R.string.appcenter_distribute_install_ready_title)) .setContentTitle(mContext.getString(R.string.appcenter_distribute_install_ready_title)) .setContentText(getInstallReadyMessage()) - .setSmallIcon(mContext.getApplicationInfo().icon); - builder.setStyle(new Notification.BigTextStyle().bigText(getInstallReadyMessage())); + .setSmallIcon(mContext.getApplicationInfo().icon) + .setStyle(new Notification.BigTextStyle().bigText(getInstallReadyMessage())) + .setContentIntent(pendingIntent); Notification notification = builder.build(); notification.flags |= Notification.FLAG_AUTO_CANCEL; - - //noinspection ConstantConditions notificationManager.notify(DistributeUtils.getNotificationId(), notification); SharedPreferencesManager.putInt(PREFERENCE_KEY_DOWNLOAD_STATE, DOWNLOAD_STATE_NOTIFIED); From 2dea53794992392aaeb3f0d384c1cfeb1164ea92 Mon Sep 17 00:00:00 2001 From: Ivan Matkov Date: Tue, 12 Apr 2022 13:42:46 +0200 Subject: [PATCH 21/72] Move posting notification to utils --- .../appcenter/distribute/Distribute.java | 50 ++--------------- .../distribute/DistributeConstants.java | 5 -- .../appcenter/distribute/DistributeUtils.java | 55 ++++++++++++++++++- .../distribute/ReleaseDownloadListener.java | 9 ++- 4 files changed, 67 insertions(+), 52 deletions(-) diff --git a/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/Distribute.java b/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/Distribute.java index 5c68b03a6..8371ec0db 100644 --- a/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/Distribute.java +++ b/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/Distribute.java @@ -10,10 +10,7 @@ import android.app.AlertDialog; import android.app.Dialog; import android.app.DownloadManager; -import android.app.Notification; -import android.app.NotificationChannel; import android.app.NotificationManager; -import android.app.PendingIntent; import android.content.ActivityNotFoundException; import android.content.Context; import android.content.DialogInterface; @@ -81,7 +78,6 @@ import static com.microsoft.appcenter.distribute.DistributeConstants.GET_LATEST_PUBLIC_RELEASE_PATH_FORMAT; import static com.microsoft.appcenter.distribute.DistributeConstants.HEADER_API_TOKEN; import static com.microsoft.appcenter.distribute.DistributeConstants.LOG_TAG; -import static com.microsoft.appcenter.distribute.DistributeConstants.NOTIFICATION_CHANNEL_ID; import static com.microsoft.appcenter.distribute.DistributeConstants.PARAMETER_DISTRIBUTION_GROUP_ID; import static com.microsoft.appcenter.distribute.DistributeConstants.PARAMETER_INSTALL_ID; import static com.microsoft.appcenter.distribute.DistributeConstants.PARAMETER_RELEASE_ID; @@ -1064,11 +1060,8 @@ synchronized void completeWorkflow(ReleaseDetails releaseDetails) { */ private synchronized void cancelNotification() { if (getStoredDownloadState() == DOWNLOAD_STATE_NOTIFIED) { - AppCenterLog.debug(LOG_TAG, "Delete notification"); - NotificationManager notificationManager = (NotificationManager) mContext.getSystemService(Context.NOTIFICATION_SERVICE); - - //noinspection ConstantConditions - notificationManager.cancel(DistributeUtils.getNotificationId()); + AppCenterLog.debug(LOG_TAG, "Cancel download notification."); + DistributeUtils.cancelDownloadNotification(mContext); } } @@ -1969,10 +1962,11 @@ synchronized void showSystemSettingsDialogOrStartInstalling(long downloadId, lon * when download completes, it will not notify and return that the install U.I. should be shown now. * * @param releaseDetails release details to check state. + * @param intent notification click intent. * @return false if install U.I should be shown now, true if a notification was posted or if the task was canceled. */ @UiThread - synchronized boolean notifyDownload(ReleaseDetails releaseDetails) { + synchronized boolean notifyDownload(ReleaseDetails releaseDetails, Intent intent) { /* Check state. */ if (releaseDetails != mReleaseDetails) { @@ -1992,35 +1986,7 @@ synchronized boolean notifyDownload(ReleaseDetails releaseDetails) { /* Post notification. */ AppCenterLog.debug(LOG_TAG, "Post a notification as the download finished in background."); - NotificationManager notificationManager = (NotificationManager) mContext.getSystemService(Context.NOTIFICATION_SERVICE); - Notification.Builder builder; - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { - - /* Create or update notification channel (mandatory on Android 8 target). */ - NotificationChannel channel = new NotificationChannel(NOTIFICATION_CHANNEL_ID, - mContext.getString(R.string.appcenter_distribute_notification_category), - NotificationManager.IMPORTANCE_DEFAULT); - notificationManager.createNotificationChannel(channel); - builder = new Notification.Builder(mContext, NOTIFICATION_CHANNEL_ID); - } else { - builder = getOldNotificationBuilder(); - } - - // TODO: Combine this and code from resumeApp. - Intent intent = new Intent(mContext, DeepLinkActivity.class); - intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); - PendingIntent pendingIntent = PendingIntent.getActivity( - mContext, 0, intent, 0); - - builder.setTicker(mContext.getString(R.string.appcenter_distribute_install_ready_title)) - .setContentTitle(mContext.getString(R.string.appcenter_distribute_install_ready_title)) - .setContentText(getInstallReadyMessage()) - .setSmallIcon(mContext.getApplicationInfo().icon) - .setStyle(new Notification.BigTextStyle().bigText(getInstallReadyMessage())) - .setContentIntent(pendingIntent); - Notification notification = builder.build(); - notification.flags |= Notification.FLAG_AUTO_CANCEL; - notificationManager.notify(DistributeUtils.getNotificationId(), notification); + DistributeUtils.postDownloadNotification(mContext, getInstallReadyMessage(), intent); SharedPreferencesManager.putInt(PREFERENCE_KEY_DOWNLOAD_STATE, DOWNLOAD_STATE_NOTIFIED); /* Reset check download flag to show install U.I. on resume if notification ignored. */ @@ -2028,12 +1994,6 @@ synchronized boolean notifyDownload(ReleaseDetails releaseDetails) { return true; } - @NonNull - @SuppressWarnings({"deprecation", "RedundantSuppression"}) - private Notification.Builder getOldNotificationBuilder() { - return new Notification.Builder(mContext); - } - /** * Show download progress (used only for mandatory updates). */ diff --git a/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/DistributeConstants.java b/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/DistributeConstants.java index 633205ac0..ed517dcad 100644 --- a/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/DistributeConstants.java +++ b/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/DistributeConstants.java @@ -200,11 +200,6 @@ public final class DistributeConstants { */ static final long POSTPONE_TIME_THRESHOLD = 24 * 60 * 60 * 1000; - /** - * Notification channel identifier. - */ - static final String NOTIFICATION_CHANNEL_ID = "appcenter.distribute"; - /** * Base key for stored preferences. */ diff --git a/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/DistributeUtils.java b/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/DistributeUtils.java index 52b4097ec..b8c5fdf15 100644 --- a/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/DistributeUtils.java +++ b/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/DistributeUtils.java @@ -6,9 +6,16 @@ package com.microsoft.appcenter.distribute; import android.app.Activity; +import android.app.Notification; +import android.app.NotificationChannel; +import android.app.NotificationManager; +import android.app.PendingIntent; +import android.content.Context; import android.content.Intent; import android.content.pm.PackageInfo; import android.net.Uri; +import android.os.Build; + import androidx.annotation.NonNull; import com.microsoft.appcenter.utils.AppCenterLog; @@ -47,15 +54,61 @@ class DistributeUtils { */ static final String TESTER_APP_PACKAGE_NAME = "com.microsoft.hockeyapp.testerapp"; + /** + * Notification channel identifier. + */ + static final String NOTIFICATION_CHANNEL_ID = "appcenter.distribute"; + /** * Get the notification identifier for downloads. * * @return notification identifier for downloads. */ - static int getNotificationId() { + private static int getNotificationId() { return Distribute.class.getName().hashCode(); } + static void postDownloadNotification(@NonNull Context context, String message, Intent intent) { + NotificationManager notificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE); + Notification.Builder builder; + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + + /* Create or update notification channel (mandatory on Android 8 target). */ + NotificationChannel channel = new NotificationChannel(NOTIFICATION_CHANNEL_ID, + context.getString(R.string.appcenter_distribute_notification_category), + NotificationManager.IMPORTANCE_DEFAULT); + notificationManager.createNotificationChannel(channel); + builder = new Notification.Builder(context, NOTIFICATION_CHANNEL_ID); + } else { + builder = getOldNotificationBuilder(context); + } + int pendingIntentFlag = 0; + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { + pendingIntentFlag = PendingIntent.FLAG_IMMUTABLE; + } + PendingIntent pendingIntent = PendingIntent.getActivity(context, 0, intent, pendingIntentFlag); + builder.setTicker(context.getString(R.string.appcenter_distribute_install_ready_title)) + .setContentTitle(context.getString(R.string.appcenter_distribute_install_ready_title)) + .setContentText(message) + .setSmallIcon(context.getApplicationInfo().icon) + .setStyle(new Notification.BigTextStyle().bigText(message)) + .setContentIntent(pendingIntent); + Notification notification = builder.build(); + notification.flags |= Notification.FLAG_AUTO_CANCEL; + notificationManager.notify(DistributeUtils.getNotificationId(), notification); + } + + static void cancelDownloadNotification(@NonNull Context context) { + NotificationManager notificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE); + notificationManager.cancel(DistributeUtils.getNotificationId()); + } + + @NonNull + @SuppressWarnings({"deprecation", "RedundantSuppression"}) + private static Notification.Builder getOldNotificationBuilder(@NonNull Context context) { + return new Notification.Builder(context); + } + /** * Get download state from storage. * diff --git a/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/ReleaseDownloadListener.java b/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/ReleaseDownloadListener.java index 2e6b80f13..95fb05fc3 100644 --- a/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/ReleaseDownloadListener.java +++ b/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/ReleaseDownloadListener.java @@ -13,6 +13,8 @@ import androidx.annotation.UiThread; import androidx.annotation.WorkerThread; +import android.content.Intent; +import android.net.Uri; import android.widget.Toast; import com.microsoft.appcenter.distribute.download.ReleaseDownloader; @@ -96,8 +98,13 @@ public void onComplete(@NonNull final long downloadId, final long totalSize) { @Override public void run() { + /* Use intent to just resume app instead on install intent. */ + // TODO: Combine with Distribute.resumeApp() + Intent intent = new Intent(mContext, DeepLinkActivity.class); + intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + /* Check if app should install now. */ - if (!Distribute.getInstance().notifyDownload(mReleaseDetails)) { + if (!Distribute.getInstance().notifyDownload(mReleaseDetails, intent)) { AppCenterLog.info(LOG_TAG, "Release is downloaded. Starting to install it."); Distribute.getInstance().setInstalling(mReleaseDetails); Distribute.getInstance().showSystemSettingsDialogOrStartInstalling(downloadId, totalSize); From 334753c297e51fc7cb33847181a362be1cd9281b Mon Sep 17 00:00:00 2001 From: Ivan Matkov Date: Wed, 13 Apr 2022 12:07:22 +0200 Subject: [PATCH 22/72] Move resume intent creation to utils --- .../microsoft/appcenter/distribute/Distribute.java | 8 +------- .../appcenter/distribute/DistributeUtils.java | 11 +++++++++++ .../appcenter/distribute/ReleaseDownloadListener.java | 4 +--- 3 files changed, 13 insertions(+), 10 deletions(-) diff --git a/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/Distribute.java b/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/Distribute.java index 8371ec0db..869685d72 100644 --- a/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/Distribute.java +++ b/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/Distribute.java @@ -1883,13 +1883,7 @@ synchronized void resumeApp(@NonNull Context context) { /* Nothing to do if already in foreground. */ if (mForegroundActivity == null) { - - /* - * Use our deep link activity with no parameter just to resume app correctly - * without duplicating activities or clearing task. - */ - Intent intent = new Intent(context, DeepLinkActivity.class); - intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + Intent intent = DistributeUtils.getResumeAppIntent(context); context.startActivity(intent); } } diff --git a/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/DistributeUtils.java b/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/DistributeUtils.java index b8c5fdf15..614155317 100644 --- a/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/DistributeUtils.java +++ b/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/DistributeUtils.java @@ -118,6 +118,17 @@ static int getStoredDownloadState() { return SharedPreferencesManager.getInt(PREFERENCE_KEY_DOWNLOAD_STATE, DOWNLOAD_STATE_COMPLETED); } + static Intent getResumeAppIntent(@NonNull Context context) { + + /* + * Use our deep link activity with no parameter just to resume app correctly + * without duplicating activities or clearing task. + */ + Intent intent = new Intent(context, DeepLinkActivity.class); + intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + return intent; + } + @NonNull static String computeReleaseHash(@NonNull PackageInfo packageInfo) { return HashUtils.sha256(packageInfo.packageName + ":" + packageInfo.versionName + ":" + DeviceInfoHelper.getVersionCode(packageInfo)); diff --git a/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/ReleaseDownloadListener.java b/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/ReleaseDownloadListener.java index 95fb05fc3..7042b0984 100644 --- a/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/ReleaseDownloadListener.java +++ b/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/ReleaseDownloadListener.java @@ -99,9 +99,7 @@ public void onComplete(@NonNull final long downloadId, final long totalSize) { public void run() { /* Use intent to just resume app instead on install intent. */ - // TODO: Combine with Distribute.resumeApp() - Intent intent = new Intent(mContext, DeepLinkActivity.class); - intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + Intent intent = DistributeUtils.getResumeAppIntent(mContext); /* Check if app should install now. */ if (!Distribute.getInstance().notifyDownload(mReleaseDetails, intent)) { From 60779ba654cd0e0ec0ae32ac74f8c20914abd40e Mon Sep 17 00:00:00 2001 From: Ivan Matkov Date: Mon, 18 Apr 2022 09:55:42 +0200 Subject: [PATCH 23/72] Fix tests --- .../appcenter/distribute/DistributeUtils.java | 4 ++- .../appcenter/distribute/DistributeTest.java | 30 +++++++++++-------- .../distribute/DistributeUtilsTest.java | 8 ----- .../ReleaseDownloadListenerTest.java | 15 +++++++--- 4 files changed, 32 insertions(+), 25 deletions(-) diff --git a/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/DistributeUtils.java b/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/DistributeUtils.java index 614155317..764629178 100644 --- a/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/DistributeUtils.java +++ b/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/DistributeUtils.java @@ -17,6 +17,7 @@ import android.os.Build; import androidx.annotation.NonNull; +import androidx.annotation.VisibleForTesting; import com.microsoft.appcenter.utils.AppCenterLog; import com.microsoft.appcenter.utils.DeviceInfoHelper; @@ -64,7 +65,8 @@ class DistributeUtils { * * @return notification identifier for downloads. */ - private static int getNotificationId() { + @VisibleForTesting + static int getNotificationId() { return Distribute.class.getName().hashCode(); } diff --git a/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/DistributeTest.java b/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/DistributeTest.java index 3729fc411..02e3ff058 100644 --- a/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/DistributeTest.java +++ b/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/DistributeTest.java @@ -37,6 +37,7 @@ import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import static org.powermock.api.mockito.PowerMockito.mockStatic; +import static org.powermock.api.mockito.PowerMockito.spy; import static org.powermock.api.mockito.PowerMockito.verifyStatic; import static org.powermock.api.mockito.PowerMockito.whenNew; @@ -75,6 +76,7 @@ import org.mockito.Mockito; import org.mockito.invocation.InvocationOnMock; import org.mockito.stubbing.Answer; +import org.powermock.api.mockito.PowerMockito; import org.powermock.core.classloader.annotations.PrepareForTest; import org.powermock.reflect.Whitebox; @@ -322,9 +324,11 @@ private void verifyReleaseDetailsAreStored() { @Test public void cancelingNotification() { - mockStatic(DistributeUtils.class); - when(DistributeUtils.getStoredDownloadState()).thenReturn(DOWNLOAD_STATE_NOTIFIED); - when(DistributeUtils.getNotificationId()).thenReturn(2); + spy(DistributeUtils.class); + PowerMockito.doReturn(DOWNLOAD_STATE_NOTIFIED).when(DistributeUtils.class); + DistributeUtils.getStoredDownloadState(); + PowerMockito.doReturn(0).when(DistributeUtils.class); + DistributeUtils.getNotificationId(); NotificationManager manager = mock(NotificationManager.class); when(mContext.getSystemService(NOTIFICATION_SERVICE)).thenReturn(manager); Distribute.getInstance().onStarted(mContext, mChannel, "a", null, true); @@ -355,7 +359,7 @@ public void notifyDownloadNoReleaseDetails() throws Exception { ReleaseDetails mockReleaseDetails = mock(ReleaseDetails.class); /* Call notify download. */ - boolean notifyDownloadResult = Distribute.getInstance().notifyDownload(mockReleaseDetails); + boolean notifyDownloadResult = Distribute.getInstance().notifyDownload(mockReleaseDetails, null); /* Verify. */ assertTrue(notifyDownloadResult); @@ -375,7 +379,7 @@ public void notifyDownloadStateNotified() throws Exception { when(DistributeUtils.getStoredDownloadState()).thenReturn(DOWNLOAD_STATE_NOTIFIED); /* Call notify download. */ - boolean notifyDownloadResult = Distribute.getInstance().notifyDownload(mReleaseDetails); + boolean notifyDownloadResult = Distribute.getInstance().notifyDownload(mReleaseDetails, null); /* Verify. */ assertFalse(notifyDownloadResult); @@ -395,7 +399,7 @@ public void notifyDownloadForegroundActivity() throws Exception { Distribute.getInstance().onActivityResumed(mock(Activity.class)); /* Call notify download. */ - boolean notifyDownloadResult = Distribute.getInstance().notifyDownload(mReleaseDetails); + boolean notifyDownloadResult = Distribute.getInstance().notifyDownload(mReleaseDetails, null); /* Verify. */ assertFalse(notifyDownloadResult); @@ -407,7 +411,7 @@ public void checkNotificationState() { mockStatic(DistributeUtils.class); when(DistributeUtils.loadCachedReleaseDetails()).thenReturn(mReleaseDetails); Distribute.getInstance().startFromBackground(mContext); - assertTrue(Distribute.getInstance().notifyDownload(mock(ReleaseDetails.class))); + assertTrue(Distribute.getInstance().notifyDownload(mock(ReleaseDetails.class), null)); } @Test @@ -1035,16 +1039,18 @@ public void showUpdateDialogAfterShowingInstallReleaseDialogTest() { private void firstDownloadNotification(int apiLevel) throws Exception { TestUtils.setInternalState(Build.VERSION.class, "SDK_INT", apiLevel); - mockStatic(DistributeUtils.class); + spy(DistributeUtils.class); NotificationManager manager = mock(NotificationManager.class); whenNew(Notification.Builder.class).withAnyArguments() .thenReturn(mock(Notification.Builder.class, RETURNS_MOCKS)); - when(DistributeUtils.loadCachedReleaseDetails()).thenReturn(mReleaseDetails); - when(DistributeUtils.getNotificationId()).thenReturn(0); + PowerMockito.doReturn(mReleaseDetails).when(DistributeUtils.class); + DistributeUtils.loadCachedReleaseDetails(); + PowerMockito.doReturn(0).when(DistributeUtils.class); + DistributeUtils.getNotificationId(); when(mContext.getSystemService(NOTIFICATION_SERVICE)).thenReturn(manager); Distribute.getInstance().startFromBackground(mContext); - Distribute.getInstance().onStarted(mContext, mChannel, "0", "Anna", false); - Distribute.getInstance().notifyDownload(mReleaseDetails); + Distribute.getInstance().onStarted(mContext, mChannel, "0", "test", false); + Distribute.getInstance().notifyDownload(mReleaseDetails, null); verify(manager).notify(eq(DistributeUtils.getNotificationId()), any(Notification.class)); } } diff --git a/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/DistributeUtilsTest.java b/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/DistributeUtilsTest.java index 6b400ea6a..0196fd1a4 100644 --- a/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/DistributeUtilsTest.java +++ b/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/DistributeUtilsTest.java @@ -49,14 +49,6 @@ public void init() { new DistributeConstants(); } - @Test - public void getNotificationId() { - - /* Coverage fix. */ - int notificationId = DistributeUtils.getNotificationId(); - Assert.assertNotEquals(0, notificationId); - } - @Test public void loadCachedReleaseDetails() throws JSONException { ReleaseDetails mock = mock(ReleaseDetails.class); diff --git a/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/ReleaseDownloadListenerTest.java b/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/ReleaseDownloadListenerTest.java index 155408415..a14d02341 100644 --- a/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/ReleaseDownloadListenerTest.java +++ b/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/ReleaseDownloadListenerTest.java @@ -57,6 +57,7 @@ AppCenter.class, AppCenterLog.class, Distribute.class, + DistributeUtils.class, HandlerUtils.class, InstallerUtils.class, ProgressDialog.class, @@ -196,7 +197,7 @@ private void mockHandler() { @Override public Void answer(InvocationOnMock invocation) { - ((Runnable) invocation.getArguments()[0]).run(); + invocation.getArgument(0).run(); return null; } }).when(HandlerUtils.class); @@ -372,10 +373,12 @@ public void onErrorNullMessage() throws Exception { @Test public void onComplete() throws Exception { + mockStatic(DistributeUtils.class); + when(DistributeUtils.getResumeAppIntent(eq(mContext))).thenReturn(mock(Intent.class)); ReleaseDetails mockReleaseDetails = mockReleaseDetails(true); /* Do not notify the download. */ - when(mDistribute.notifyDownload(mockReleaseDetails)).thenReturn(false); + when(mDistribute.notifyDownload(eq(mockReleaseDetails), any(Intent.class))).thenReturn(false); ReleaseDownloadListener releaseDownloadListener = new ReleaseDownloadListener(mContext, mockReleaseDetails); releaseDownloadListener.onComplete(1L, 1L); @@ -386,10 +389,12 @@ public void onComplete() throws Exception { @Test public void onCompleteNotify() throws Exception { + mockStatic(DistributeUtils.class); + when(DistributeUtils.getResumeAppIntent(eq(mContext))).thenReturn(mock(Intent.class)); ReleaseDetails mockReleaseDetails = mockReleaseDetails(false); /* Notify the download. */ - when(mDistribute.notifyDownload(mockReleaseDetails)).thenReturn(true); + when(mDistribute.notifyDownload(eq(mockReleaseDetails), any(Intent.class))).thenReturn(true); ReleaseDownloadListener releaseDownloadListener = new ReleaseDownloadListener(mContext, mockReleaseDetails); releaseDownloadListener.onComplete(1L, 1L); @@ -400,9 +405,11 @@ public void onCompleteNotify() throws Exception { @Test public void onCompleteActivityNotResolved() throws Exception { + mockStatic(DistributeUtils.class); + when(DistributeUtils.getResumeAppIntent(eq(mContext))).thenReturn(mock(Intent.class)); /* Mock notify download result. */ - when(mDistribute.notifyDownload(any(ReleaseDetails.class))).thenReturn(true); + when(mDistribute.notifyDownload(any(ReleaseDetails.class), any(Intent.class))).thenReturn(true); /* Mock resolving to null activity. */ when(mInstallIntent.resolveActivity(any(PackageManager.class))).thenReturn(null); From bec80d1f4faffba94a6d727d9bf3678831b248ab Mon Sep 17 00:00:00 2001 From: Ivan Matkov Date: Mon, 9 May 2022 12:42:30 +0200 Subject: [PATCH 24/72] Fix NPE on register receiver if service is not started --- .../com/microsoft/appcenter/distribute/Distribute.java | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/Distribute.java b/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/Distribute.java index 4c6a3e7f9..4c7101259 100644 --- a/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/Distribute.java +++ b/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/Distribute.java @@ -613,9 +613,13 @@ protected synchronized void applyEnabledState(boolean enabled) { /** * Register package installer receiver. */ - private synchronized void registerReceiver(Activity activity) { + private synchronized void registerReceiver(@Nullable Activity activity) { + if (mChannel == null) { + AppCenterLog.debug(LOG_TAG, "Couldn't register receiver because App Center has not started yet."); + return; + } if (!isInstanceEnabled()) { - AppCenterLog.warn(LOG_TAG, "Couldn't register receiver due to Distribute module is disabled."); + AppCenterLog.debug(LOG_TAG, "Couldn't register receiver due to Distribute module is disabled."); return; } if (activity != null) { From 9685d82d6a7a471caf52242fffcbc79b01f162a6 Mon Sep 17 00:00:00 2001 From: Ivan Matkov Date: Mon, 9 May 2022 12:45:00 +0200 Subject: [PATCH 25/72] Wrap checking service state to getter --- .../com/microsoft/appcenter/analytics/Analytics.java | 4 ++-- .../microsoft/appcenter/distribute/Distribute.java | 8 ++++---- .../microsoft/appcenter/AbstractAppCenterService.java | 11 ++++++++++- 3 files changed, 16 insertions(+), 7 deletions(-) diff --git a/sdk/appcenter-analytics/src/main/java/com/microsoft/appcenter/analytics/Analytics.java b/sdk/appcenter-analytics/src/main/java/com/microsoft/appcenter/analytics/Analytics.java index 215ea3a91..37e0adbb4 100644 --- a/sdk/appcenter-analytics/src/main/java/com/microsoft/appcenter/analytics/Analytics.java +++ b/sdk/appcenter-analytics/src/main/java/com/microsoft/appcenter/analytics/Analytics.java @@ -820,7 +820,7 @@ private void queuePage(String name, Map properties) { * Implements {@link #enableManualSessionTracker()}. */ private synchronized void enableManualSessionTrackerAsync() { - if (mChannel != null) { + if (isStarted()) { AppCenterLog.debug(AppCenterLog.LOG_TAG, "The manual session tracker state should be set before the App Center start."); return; } @@ -967,7 +967,7 @@ private void setDefaultTransmissionTarget(String transmissionTargetToken) { * @return true if the interval is set, false otherwise. */ private boolean setInstanceTransmissionInterval(int seconds) { - if (mChannel != null) { + if (isStarted()) { AppCenterLog.error(LOG_TAG, "Transmission interval should be set before the service is started."); return false; } diff --git a/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/Distribute.java b/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/Distribute.java index 4c7101259..cc4b4bc1a 100644 --- a/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/Distribute.java +++ b/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/Distribute.java @@ -456,7 +456,7 @@ public static void disableAutomaticCheckForUpdate() { * Implements {@link #disableAutomaticCheckForUpdate()}. */ private synchronized void instanceDisableAutomaticCheckForUpdate() { - if (mChannel != null) { + if (isStarted()) { AppCenterLog.error(LOG_TAG, "Automatic check for update cannot be disabled after Distribute is started."); return; } @@ -550,7 +550,7 @@ public synchronized void onActivityResumed(Activity activity) { mForegroundActivity = activity; /* If started, resume now, otherwise this will be called by onStarted. */ - if (mChannel != null) { + if (isStarted()) { resumeDistributeWorkflow(); } } @@ -567,7 +567,7 @@ public synchronized void onActivityPaused(Activity activity) { @Override public void onApplicationEnterForeground() { - if (mChannel != null) { + if (isStarted()) { AppCenterLog.debug(LOG_TAG, "Resetting workflow on entering foreground."); tryResetWorkflow(); } @@ -614,7 +614,7 @@ protected synchronized void applyEnabledState(boolean enabled) { * Register package installer receiver. */ private synchronized void registerReceiver(@Nullable Activity activity) { - if (mChannel == null) { + if (!isStarted()) { AppCenterLog.debug(LOG_TAG, "Couldn't register receiver because App Center has not started yet."); return; } diff --git a/sdk/appcenter/src/main/java/com/microsoft/appcenter/AbstractAppCenterService.java b/sdk/appcenter/src/main/java/com/microsoft/appcenter/AbstractAppCenterService.java index f24d51944..1afe809e7 100644 --- a/sdk/appcenter/src/main/java/com/microsoft/appcenter/AbstractAppCenterService.java +++ b/sdk/appcenter/src/main/java/com/microsoft/appcenter/AbstractAppCenterService.java @@ -167,7 +167,7 @@ public synchronized void setInstanceEnabled(boolean enabled) { AppCenterLog.info(getLoggerTag(), String.format("%s service has been %s.", getServiceName(), enabled ? "enabled" : "disabled")); /* Don't call it before the service starts. */ - if (mChannel != null) { + if (isStarted()) { /* Allow sub-class to handle state change. */ applyEnabledState(enabled); @@ -242,6 +242,15 @@ public Map getLogFactories() { */ protected abstract String getLoggerTag(); + /** + * Gets a state of the service. + * + * @return true if already started, false otherwise. + */ + protected boolean isStarted() { + return mChannel != null; + } + @NonNull protected String getEnabledPreferenceKey() { return KEY_ENABLED + PREFERENCE_KEY_SEPARATOR + getServiceName(); From 6d212d3bfe468a20113c0bb673d397673f0ecf4f Mon Sep 17 00:00:00 2001 From: Ivan Matkov Date: Mon, 9 May 2022 13:02:56 +0200 Subject: [PATCH 26/72] Remove extra flags --- .../AppCenterPackageInstallerReceiver.java | 36 +++++++------------ .../appcenter/distribute/Distribute.java | 30 ++++++++-------- 2 files changed, 28 insertions(+), 38 deletions(-) diff --git a/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/AppCenterPackageInstallerReceiver.java b/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/AppCenterPackageInstallerReceiver.java index 4fbcabb07..70a346e46 100644 --- a/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/AppCenterPackageInstallerReceiver.java +++ b/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/AppCenterPackageInstallerReceiver.java @@ -7,7 +7,6 @@ import static com.microsoft.appcenter.distribute.DistributeConstants.LOG_TAG; -import android.app.Activity; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; @@ -16,7 +15,7 @@ import android.os.Bundle; import android.widget.Toast; -import androidx.annotation.NonNull; +import androidx.annotation.VisibleForTesting; import com.microsoft.appcenter.utils.AppCenterLog; @@ -27,9 +26,18 @@ */ public class AppCenterPackageInstallerReceiver extends BroadcastReceiver { - public static final String START_ACTION = "com.microsoft.appcenter.action.START"; - public static final String MY_PACKAGE_REPLACED_ACTION = "android.intent.action.MY_PACKAGE_REPLACED"; - private boolean isReceiverRegistered; + @VisibleForTesting + static final String START_ACTION = "com.microsoft.appcenter.action.START"; + + @VisibleForTesting + static final String MY_PACKAGE_REPLACED_ACTION = "android.intent.action.MY_PACKAGE_REPLACED"; + + public IntentFilter getInstallerReceiverFilter() { + IntentFilter installerReceiverFilter = new IntentFilter(); + installerReceiverFilter.addAction(START_ACTION); + installerReceiverFilter.addAction(MY_PACKAGE_REPLACED_ACTION); + return installerReceiverFilter; + } @Override public void onReceive(Context context, Intent intent) { @@ -70,22 +78,4 @@ public void onReceive(Context context, Intent intent) { AppCenterLog.debug(LOG_TAG, String.format(Locale.ENGLISH, "Unrecognized action %s - do nothing.", intent.getAction())); } } - - public void tryRegisterReceiver(@NonNull Context context, @NonNull IntentFilter intentFilter) { - if (isReceiverRegistered) { - return; - } - isReceiverRegistered = true; - context.registerReceiver(this, intentFilter); - AppCenterLog.debug(LOG_TAG, "The receiver for installing a new release was registered."); - } - - public void tryUnregisterReceiver(@NonNull Context context) { - if (!isReceiverRegistered) { - return; - } - isReceiverRegistered = false; - context.unregisterReceiver(this); - AppCenterLog.debug(LOG_TAG, "The receiver for installing a new release was unregistered."); - } } diff --git a/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/Distribute.java b/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/Distribute.java index cc4b4bc1a..ff16521c4 100644 --- a/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/Distribute.java +++ b/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/Distribute.java @@ -256,11 +256,6 @@ public class Distribute extends AbstractAppCenterService { */ private AppCenterPackageInstallerReceiver mAppCenterPackageInstallerReceiver; - /** - * Intent filter of receiver for a new release. - */ - private IntentFilter mPackageInstallerReceiverFilter; - /** * Remember if we checked download since our own process restarted. */ @@ -317,10 +312,6 @@ public class Distribute extends AbstractAppCenterService { private Distribute() { mFactories = new HashMap<>(); mFactories.put(DistributionStartSessionLog.TYPE, new DistributionStartSessionLogFactory()); - mAppCenterPackageInstallerReceiver = new AppCenterPackageInstallerReceiver(); - mPackageInstallerReceiverFilter = new IntentFilter(); - mPackageInstallerReceiverFilter.addAction(AppCenterPackageInstallerReceiver.START_ACTION); - mPackageInstallerReceiverFilter.addAction(AppCenterPackageInstallerReceiver.MY_PACKAGE_REPLACED_ACTION); } /** @@ -622,10 +613,15 @@ private synchronized void registerReceiver(@Nullable Activity activity) { AppCenterLog.debug(LOG_TAG, "Couldn't register receiver due to Distribute module is disabled."); return; } - if (activity != null) { - mAppCenterPackageInstallerReceiver.tryRegisterReceiver(activity.getApplicationContext(), mPackageInstallerReceiverFilter); - } else { + if (activity == null) { AppCenterLog.warn(LOG_TAG, "Couldn't register receiver due to activity is null."); + return; + } + if (mAppCenterPackageInstallerReceiver == null) { + mAppCenterPackageInstallerReceiver = new AppCenterPackageInstallerReceiver(); + activity.getApplicationContext().registerReceiver(mAppCenterPackageInstallerReceiver, + mAppCenterPackageInstallerReceiver.getInstallerReceiverFilter()); + AppCenterLog.debug(LOG_TAG, "The receiver for installing a new release was registered."); } } @@ -633,10 +629,14 @@ private synchronized void registerReceiver(@Nullable Activity activity) { * Unregister package installer receiver. */ private synchronized void unregisterReceiver(Activity activity) { - if (activity != null) { - mAppCenterPackageInstallerReceiver.tryUnregisterReceiver(activity.getApplicationContext()); - } else { + if (activity == null) { AppCenterLog.warn(LOG_TAG, "Couldn't unregister due to activity is null."); + return; + } + if (mAppCenterPackageInstallerReceiver != null) { + activity.getApplicationContext().unregisterReceiver(mAppCenterPackageInstallerReceiver); + mAppCenterPackageInstallerReceiver = null; + AppCenterLog.debug(LOG_TAG, "The receiver for installing a new release was unregistered."); } } From 0c03db74e51951ac2672434107a6cf7878297533 Mon Sep 17 00:00:00 2001 From: Ivan Matkov Date: Mon, 9 May 2022 14:15:43 +0200 Subject: [PATCH 27/72] Simplify receiver registering --- .../appcenter/distribute/Distribute.java | 48 +++++-------------- 1 file changed, 11 insertions(+), 37 deletions(-) diff --git a/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/Distribute.java b/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/Distribute.java index ff16521c4..804ccb964 100644 --- a/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/Distribute.java +++ b/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/Distribute.java @@ -530,12 +530,6 @@ private boolean tryResetWorkflow() { return false; } - @Override - public void onActivityStarted(Activity activity) { - super.onActivityStarted(activity); - registerReceiver(activity); - } - @Override public synchronized void onActivityResumed(Activity activity) { mForegroundActivity = activity; @@ -578,7 +572,7 @@ protected synchronized void applyEnabledState(boolean enabled) { resumeWorkflowIfForeground(); /* Register package installer receiver. */ - registerReceiver(mForegroundActivity); + registerReceiver(); } else { /* Clean all state on disabling, cancel everything. Keep only redirection parameters. */ @@ -597,47 +591,27 @@ protected synchronized void applyEnabledState(boolean enabled) { mDistributeInfoTracker = null; /* Unregister package installer receiver. */ - unregisterReceiver(mForegroundActivity); + unregisterReceiver(); } } /** * Register package installer receiver. */ - private synchronized void registerReceiver(@Nullable Activity activity) { - if (!isStarted()) { - AppCenterLog.debug(LOG_TAG, "Couldn't register receiver because App Center has not started yet."); - return; - } - if (!isInstanceEnabled()) { - AppCenterLog.debug(LOG_TAG, "Couldn't register receiver due to Distribute module is disabled."); - return; - } - if (activity == null) { - AppCenterLog.warn(LOG_TAG, "Couldn't register receiver due to activity is null."); - return; - } - if (mAppCenterPackageInstallerReceiver == null) { - mAppCenterPackageInstallerReceiver = new AppCenterPackageInstallerReceiver(); - activity.getApplicationContext().registerReceiver(mAppCenterPackageInstallerReceiver, - mAppCenterPackageInstallerReceiver.getInstallerReceiverFilter()); - AppCenterLog.debug(LOG_TAG, "The receiver for installing a new release was registered."); - } + private void registerReceiver() { + mAppCenterPackageInstallerReceiver = new AppCenterPackageInstallerReceiver(); + mContext.registerReceiver(mAppCenterPackageInstallerReceiver, + mAppCenterPackageInstallerReceiver.getInstallerReceiverFilter()); + AppCenterLog.debug(LOG_TAG, "The receiver for installing a new release was registered."); } /** * Unregister package installer receiver. */ - private synchronized void unregisterReceiver(Activity activity) { - if (activity == null) { - AppCenterLog.warn(LOG_TAG, "Couldn't unregister due to activity is null."); - return; - } - if (mAppCenterPackageInstallerReceiver != null) { - activity.getApplicationContext().unregisterReceiver(mAppCenterPackageInstallerReceiver); - mAppCenterPackageInstallerReceiver = null; - AppCenterLog.debug(LOG_TAG, "The receiver for installing a new release was unregistered."); - } + private void unregisterReceiver() { + mContext.unregisterReceiver(mAppCenterPackageInstallerReceiver); + mAppCenterPackageInstallerReceiver = null; + AppCenterLog.debug(LOG_TAG, "The receiver for installing a new release was unregistered."); } @WorkerThread From c483c0c154244c6d663872f4fb33a5f6ba81498f Mon Sep 17 00:00:00 2001 From: Ivan Matkov Date: Mon, 9 May 2022 14:36:44 +0200 Subject: [PATCH 28/72] Update tests --- .../distribute/AbstractDistributeTest.java | 4 +- ...AppCenterPackageInstallerReceiverTest.java | 63 ------------------- ...va => DistributeDownloadReceiverTest.java} | 12 ++-- .../appcenter/distribute/DistributeTest.java | 45 +------------ 4 files changed, 11 insertions(+), 113 deletions(-) rename sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/{DistributePlusDownloadReceiverTest.java => DistributeDownloadReceiverTest.java} (85%) diff --git a/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/AbstractDistributeTest.java b/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/AbstractDistributeTest.java index 6c04597c2..0770aa0fa 100644 --- a/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/AbstractDistributeTest.java +++ b/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/AbstractDistributeTest.java @@ -174,7 +174,7 @@ public void setUp() throws Exception { @Override public Void answer(InvocationOnMock invocation) { - ((Runnable) invocation.getArguments()[0]).run(); + invocation.getArgument(0).run(); return null; } }).when(mAppCenterHandler).post(any(Runnable.class), any()); @@ -194,7 +194,7 @@ public Void answer(InvocationOnMock invocation) { public Void answer(InvocationOnMock invocation) { /* Whenever the new state is persisted, make further calls return the new state. */ - boolean enabled = (Boolean) invocation.getArguments()[1]; + boolean enabled = invocation.getArgument(1); when(SharedPreferencesManager.getBoolean(DISTRIBUTE_ENABLED_KEY, true)).thenReturn(enabled); return null; } diff --git a/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/AppCenterPackageInstallerReceiverTest.java b/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/AppCenterPackageInstallerReceiverTest.java index a48b4ec26..235ab43ef 100644 --- a/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/AppCenterPackageInstallerReceiverTest.java +++ b/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/AppCenterPackageInstallerReceiverTest.java @@ -8,15 +8,12 @@ import static com.microsoft.appcenter.distribute.AppCenterPackageInstallerReceiver.MY_PACKAGE_REPLACED_ACTION; import static com.microsoft.appcenter.distribute.AppCenterPackageInstallerReceiver.START_ACTION; import static com.microsoft.appcenter.distribute.DistributeConstants.LOG_TAG; - import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.never; import static org.mockito.Mockito.spy; -import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import static org.powermock.api.mockito.PowerMockito.mockStatic; @@ -25,7 +22,6 @@ import android.app.Activity; import android.content.Context; import android.content.Intent; -import android.content.IntentFilter; import android.content.pm.PackageInstaller; import android.content.pm.PackageManager; import android.os.Bundle; @@ -60,9 +56,6 @@ public class AppCenterPackageInstallerReceiverTest { @Mock private Intent mIntent; - @Mock - private IntentFilter mIntentFilter; - @Mock private PackageManager mPackageManager; @@ -222,61 +215,5 @@ public void onReceiverWithUnknownAction() { verifyStatic(AppCenterLog.class); AppCenterLog.debug(eq(LOG_TAG), eq("Unrecognized action UnknownAction - do nothing.")); } - - @Test - public void tryRegisterAndUnregisterReceiver() { - - /* Register receiver. */ - mAppCenterPackageInstallerReceiver.tryRegisterReceiver(mActivity, mIntentFilter); - verify(mActivity).registerReceiver(eq(mAppCenterPackageInstallerReceiver), eq(mIntentFilter)); - - /* Unregister receiver again. */ - mAppCenterPackageInstallerReceiver.tryUnregisterReceiver(mActivity); - verify(mActivity).unregisterReceiver(eq(mAppCenterPackageInstallerReceiver)); - - /* Register receiver again. */ - mAppCenterPackageInstallerReceiver.tryRegisterReceiver(mActivity, mIntentFilter); - verify(mActivity, times(2)).registerReceiver(eq(mAppCenterPackageInstallerReceiver), eq(mIntentFilter)); - - /* Unregister receiver again. */ - mAppCenterPackageInstallerReceiver.tryUnregisterReceiver(mActivity); - verify(mActivity, times(2)).unregisterReceiver(eq(mAppCenterPackageInstallerReceiver)); - } - - @Test - public void tryDoubleUnregisterReceiver() { - - /* Unregister receiver and verify that nothing is happened. */ - mAppCenterPackageInstallerReceiver.tryUnregisterReceiver(mActivity); - verify(mActivity, never()).unregisterReceiver(eq(mAppCenterPackageInstallerReceiver)); - - /* Register receiver. */ - mAppCenterPackageInstallerReceiver.tryRegisterReceiver(mActivity, mIntentFilter); - verify(mActivity).registerReceiver(eq(mAppCenterPackageInstallerReceiver), eq(mIntentFilter)); - - /* Unregister receiver again. */ - mAppCenterPackageInstallerReceiver.tryUnregisterReceiver(mActivity); - verify(mActivity).unregisterReceiver(eq(mAppCenterPackageInstallerReceiver)); - - /* Unregister receiver again and verify that it wasn't re-unregister. */ - mAppCenterPackageInstallerReceiver.tryUnregisterReceiver(mActivity); - verify(mActivity).unregisterReceiver(eq(mAppCenterPackageInstallerReceiver)); - } - - @Test - public void tryDoubleRegisterReceiver() { - - /* Register receiver. */ - mAppCenterPackageInstallerReceiver.tryRegisterReceiver(mActivity, mIntentFilter); - verify(mActivity).registerReceiver(eq(mAppCenterPackageInstallerReceiver), eq(mIntentFilter)); - - /* Register receiver again and verify that it wasn't re-register. */ - mAppCenterPackageInstallerReceiver.tryRegisterReceiver(mActivity, mIntentFilter); - verify(mActivity).registerReceiver(eq(mAppCenterPackageInstallerReceiver), eq(mIntentFilter)); - - /* Unregister receiver. */ - mAppCenterPackageInstallerReceiver.tryUnregisterReceiver(mActivity); - verify(mActivity).unregisterReceiver(eq(mAppCenterPackageInstallerReceiver)); - } } diff --git a/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/DistributePlusDownloadReceiverTest.java b/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/DistributeDownloadReceiverTest.java similarity index 85% rename from sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/DistributePlusDownloadReceiverTest.java rename to sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/DistributeDownloadReceiverTest.java index a572763ed..1e0108f28 100644 --- a/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/DistributePlusDownloadReceiverTest.java +++ b/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/DistributeDownloadReceiverTest.java @@ -21,7 +21,9 @@ import static org.mockito.Mockito.when; import static org.powermock.api.mockito.PowerMockito.whenNew; -public class DistributePlusDownloadReceiverTest extends AbstractDistributeTest { +public class DistributeDownloadReceiverTest extends AbstractDistributeTest { + + DownloadManagerReceiver mDownloadManagerReceiver = new DownloadManagerReceiver(); @Test public void resumeAppBeforeStart() throws Exception { @@ -29,7 +31,7 @@ public void resumeAppBeforeStart() throws Exception { when(clickIntent.getAction()).thenReturn(ACTION_NOTIFICATION_CLICKED); Intent startIntent = mock(Intent.class); whenNew(Intent.class).withArguments(mContext, DeepLinkActivity.class).thenReturn(startIntent); - new DownloadManagerReceiver().onReceive(mContext, clickIntent); + mDownloadManagerReceiver.onReceive(mContext, clickIntent); verify(startIntent).addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); verify(mContext).startActivity(startIntent); } @@ -41,7 +43,7 @@ public void resumeAfterBeforeStartButBackground() throws Exception { start(); Intent startIntent = mock(Intent.class); whenNew(Intent.class).withArguments(mContext, DeepLinkActivity.class).thenReturn(startIntent); - new DownloadManagerReceiver().onReceive(mContext, clickIntent); + mDownloadManagerReceiver.onReceive(mContext, clickIntent); verify(startIntent).addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); verify(mContext).startActivity(startIntent); } @@ -55,12 +57,12 @@ public void resumeForegroundThenPause() throws Exception { Intent startIntent = mock(Intent.class); whenNew(Intent.class).withArguments(mContext, DeepLinkActivity.class).thenReturn(startIntent); Distribute.getInstance().onActivityResumed(mock(Activity.class)); - new DownloadManagerReceiver().onReceive(mContext, clickIntent); + mDownloadManagerReceiver.onReceive(mContext, clickIntent); verify(mContext, never()).startActivity(startIntent); /* Then pause and test again. */ Distribute.getInstance().onActivityPaused(mock(Activity.class)); - new DownloadManagerReceiver().onReceive(mContext, clickIntent); + mDownloadManagerReceiver.onReceive(mContext, clickIntent); verify(startIntent).addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); verify(mContext).startActivity(startIntent); } diff --git a/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/DistributeTest.java b/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/DistributeTest.java index 3729fc411..de46d1ae8 100644 --- a/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/DistributeTest.java +++ b/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/DistributeTest.java @@ -858,33 +858,10 @@ public void checkUpdateReleaseAfterInterruptDownloading() { } @Test - public void checkRegisterAndUnregisterReceiver() { - mockStatic(SharedPreferencesManager.class); - when(SharedPreferencesManager.getBoolean(DISTRIBUTE_ENABLED_KEY, true)).thenReturn(false).thenReturn(true); - - /* Verify that when distribute disabled no receivers was registered. */ - Distribute.getInstance().onActivityStarted(mActivity); - Distribute.getInstance().onActivityResumed(mActivity); - verify(mContext, never()).registerReceiver(any(BroadcastReceiver.class), any(IntentFilter.class)); - - /* Start distribute. */ + public void applyEnableStateRegistersInstallReceiver() { start(); - /* Check that receiver was registered. */ - verify(mContext).registerReceiver(any(BroadcastReceiver.class), any(IntentFilter.class)); - - /* Stop activity. */ - Distribute.getInstance().onActivityPaused(mActivity); - Distribute.getInstance().onActivityStopped(mActivity); - - /* Check that receiver was not unregistered on activity pause and stop. */ - verify(mContext, never()).unregisterReceiver(any(BroadcastReceiver.class)); - - /* Resume activity */ - Distribute.getInstance().onActivityStarted(mActivity); - Distribute.getInstance().onActivityResumed(mActivity); - - /* Verify that register receiver was called again after activity is resumed. */ + /* Verify that register receiver was called after activity is resumed. */ verify(mContext).registerReceiver(any(BroadcastReceiver.class), any(IntentFilter.class)); /* Disable Distribute. */ @@ -894,24 +871,6 @@ public void checkRegisterAndUnregisterReceiver() { verify(mContext).unregisterReceiver(any(BroadcastReceiver.class)); } - @Test - public void checkRegisterReceiverWhenActivityNull() { - - /* Start Distribute. */ - start(); - - /* Verify that receiver was not registered. */ - verifyStatic(AppCenterLog.class); - AppCenterLog.warn(eq(LOG_TAG), eq("Couldn't register receiver due to activity is null.")); - - /* Disable Distribute. */ - Distribute.setEnabled(false); - - /* Verify that receiver was not registered. */ - verifyStatic(AppCenterLog.class); - AppCenterLog.warn(eq(LOG_TAG), eq("Couldn't unregister due to activity is null.")); - } - @Test public void checkProgressWhenActivityNull() { From a98ecb9acea5d3cf35840bc1451b2bfa85519d51 Mon Sep 17 00:00:00 2001 From: Ivan Matkov Date: Mon, 9 May 2022 14:59:52 +0200 Subject: [PATCH 29/72] Fix coverage --- .../microsoft/appcenter/distribute/DistributeUtilsTest.java | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/DistributeUtilsTest.java b/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/DistributeUtilsTest.java index 0196fd1a4..27c0bb0ef 100644 --- a/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/DistributeUtilsTest.java +++ b/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/DistributeUtilsTest.java @@ -17,6 +17,7 @@ import static com.microsoft.appcenter.distribute.DistributeConstants.PREFERENCE_KEY_RELEASE_DETAILS; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotEquals; import static org.junit.Assert.assertNull; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.eq; @@ -49,6 +50,11 @@ public void init() { new DistributeConstants(); } + @Test + public void notificationId() { + assertNotEquals(0, DistributeUtils.getNotificationId()); + } + @Test public void loadCachedReleaseDetails() throws JSONException { ReleaseDetails mock = mock(ReleaseDetails.class); From ff44f5d58c46773a4a763e170741c4719b6cdfd2 Mon Sep 17 00:00:00 2001 From: Ivan Matkov Date: Tue, 10 May 2022 14:44:44 +0200 Subject: [PATCH 30/72] Update changelog --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0f73d8b0e..08752e36a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,10 @@ ## Version 4.4.4 (Under active development) +### App Center Distribute + +* **[Fix]** Fix clicks on the download notification. + ___ ## Version 4.4.3 From 16333a08f15002752a8a40552914012afd1bc3c7 Mon Sep 17 00:00:00 2001 From: Ivan Matkov Date: Tue, 10 May 2022 14:47:15 +0200 Subject: [PATCH 31/72] Update changelog --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0f73d8b0e..0e9bb21f7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,10 @@ ## Version 4.4.4 (Under active development) +### App Center Distribute + +* **[Fix]** Fix possible crash on resume event before initialization. + ___ ## Version 4.4.3 From 9625b14b5b0f8a5b31d9df3f320cf3428a758944 Mon Sep 17 00:00:00 2001 From: Ivan Matkov Date: Fri, 1 Apr 2022 16:00:40 +0200 Subject: [PATCH 32/72] Cleanup package installing code --- .../appcenter/distribute/InstallerUtils.java | 46 +++++++++++-------- .../distribute/ReleaseInstallerListener.java | 12 ++--- 2 files changed, 32 insertions(+), 26 deletions(-) diff --git a/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/InstallerUtils.java b/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/InstallerUtils.java index 0669f0cf8..981b617d5 100644 --- a/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/InstallerUtils.java +++ b/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/InstallerUtils.java @@ -14,12 +14,15 @@ import android.content.IntentSender; import android.content.pm.PackageInstaller; import android.os.Build; +import android.os.ParcelFileDescriptor; import android.provider.Settings; import androidx.annotation.NonNull; import androidx.annotation.VisibleForTesting; +import androidx.annotation.WorkerThread; import com.microsoft.appcenter.utils.AppCenterLog; +import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; @@ -45,7 +48,7 @@ public class InstallerUtils { /** * Buffer capacity of package installer. */ - private static final int sBufferCapacity = 16384; + private static final int BUFFER_CAPACITY = 64 * 1024; /** * Installer package names that are not app stores. @@ -84,7 +87,7 @@ public class InstallerUtils { * @param context any context. * @return true if the application was installed from an app store, false if it was installed via adb or via unknown source. */ - static synchronized boolean isInstalledFromAppStore(@NonNull String logTag, @NonNull Context context) { + static boolean isInstalledFromAppStore(@NonNull String logTag, @NonNull Context context) { if (sInstalledFromAppStore == null) { String installer = context.getPackageManager().getInstallerPackageName(context.getPackageName()); AppCenterLog.debug(logTag, "InstallerPackageName=" + installer); @@ -130,7 +133,7 @@ public static boolean isUnknownSourcesEnabled(@NonNull Context context) { * @param context any context. * @return true if start activity from the background is enabled, false otherwise. */ - public synchronized static boolean isSystemAlertWindowsEnabled(@NonNull Context context) { + public static boolean isSystemAlertWindowsEnabled(@NonNull Context context) { /* * From Android 10 (29 API level) or higher we have to @@ -145,10 +148,9 @@ public synchronized static boolean isSystemAlertWindowsEnabled(@NonNull Context /** * Install a new release. - * - * @param data input stream data from the installing apk file. */ - public synchronized static void installPackage(@NonNull InputStream data, Context context, PackageInstaller.SessionCallback sessionCallback) { + @WorkerThread + public static void installPackage(ParcelFileDescriptor fileDescriptor, Context context, PackageInstaller.SessionCallback sessionCallback) { PackageInstaller.Session session = null; try { @@ -162,17 +164,9 @@ public synchronized static void installPackage(@NonNull InputStream data, Contex /* Prepare session. */ int sessionId = packageInstaller.createSession(params); session = packageInstaller.openSession(sessionId); + addFileToInstallSession(fileDescriptor, session); /* Start to install a new release. */ - OutputStream out = session.openWrite(sOutputStreamName, 0, -1); - byte[] buffer = new byte[sBufferCapacity]; - int c; - while ((c = data.read(buffer)) != -1) { - out.write(buffer, 0, c); - } - session.fsync(out); - data.close(); - out.close(); session.commit(createIntentSender(context, sessionId)); session.close(); } catch (IOException e) { @@ -190,16 +184,30 @@ public synchronized static void installPackage(@NonNull InputStream data, Contex * @param sessionId install sessionId. * @return IntentSender with receiver. */ - public synchronized static IntentSender createIntentSender(Context context, int sessionId) { - int flag = 0; + public static IntentSender createIntentSender(Context context, int sessionId) { + int broadcastFlags = 0; if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { - flag = FLAG_MUTABLE; + broadcastFlags |= FLAG_MUTABLE; } PendingIntent pendingIntent = PendingIntent.getBroadcast( context, sessionId, new Intent(AppCenterPackageInstallerReceiver.START_ACTION), - flag); + broadcastFlags); return pendingIntent.getIntentSender(); } + + @WorkerThread + private static void addFileToInstallSession(ParcelFileDescriptor fileDescriptor, PackageInstaller.Session session) + throws IOException { + try (OutputStream out = session.openWrite(sOutputStreamName, 0, fileDescriptor.getStatSize()); + InputStream in = new FileInputStream(fileDescriptor.getFileDescriptor())) { + byte[] buffer = new byte[BUFFER_CAPACITY]; + int read; + while ((read = in.read(buffer)) >= 0) { + out.write(buffer, 0, read); + } + session.fsync(out); + } + } } diff --git a/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/ReleaseInstallerListener.java b/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/ReleaseInstallerListener.java index 84a9d8496..16ab2fd26 100644 --- a/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/ReleaseInstallerListener.java +++ b/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/ReleaseInstallerListener.java @@ -35,7 +35,7 @@ public class ReleaseInstallerListener extends PackageInstaller.SessionCallback { /** * Total progress size. */ - private final int mTotalProgressSize = 100; + private static final int TOTAL_PROGRESS_SIZE = 100; /** * Context. @@ -88,17 +88,15 @@ public synchronized void setTotalSize(long totalSize) { */ public synchronized void startInstall() { AppCenterLog.debug(AppCenterLog.LOG_TAG, "Start installing new release..."); - ParcelFileDescriptor pfd; try { DownloadManager downloadManager = (DownloadManager) mContext.getSystemService(DOWNLOAD_SERVICE); - pfd = downloadManager.openDownloadedFile(mDownloadId); - if (pfd.getStatSize() != mTotalSize) { + ParcelFileDescriptor fileDescriptor = downloadManager.openDownloadedFile(mDownloadId); + if (fileDescriptor.getStatSize() != mTotalSize) { AppCenterLog.error(AppCenterLog.LOG_TAG, "Failed to start installing new release. The file is invalid."); Toast.makeText(mContext, mContext.getString(R.string.appcenter_distribute_failed_file_during_install_update), Toast.LENGTH_SHORT).show(); return; } - InputStream data = new FileInputStream(pfd.getFileDescriptor()); - InstallerUtils.installPackage(data, mContext, this); + InstallerUtils.installPackage(fileDescriptor, mContext, this); } catch (IOException e) { AppCenterLog.error(AppCenterLog.LOG_TAG, "Update can't be installed.", e); } @@ -188,7 +186,7 @@ private synchronized void updateInstallProgressDialog(final int currentSize) { mProgressDialog.setProgressPercentFormat(NumberFormat.getPercentInstance()); mProgressDialog.setProgressNumberFormat(mContext.getString(R.string.appcenter_distribute_install_progress_number_format)); mProgressDialog.setIndeterminate(false); - mProgressDialog.setMax(mTotalProgressSize); + mProgressDialog.setMax(TOTAL_PROGRESS_SIZE); } mProgressDialog.setProgress(currentSize); } From a9b058ac9787795004726d240a346b8a15ce0e29 Mon Sep 17 00:00:00 2001 From: Ivan Matkov Date: Fri, 1 Apr 2022 16:08:39 +0200 Subject: [PATCH 33/72] Call installer from background thread --- .../com/microsoft/appcenter/distribute/Distribute.java | 10 ++++++++-- .../appcenter/distribute/ReleaseInstallerListener.java | 3 ++- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/Distribute.java b/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/Distribute.java index 05ce03acd..230631122 100644 --- a/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/Distribute.java +++ b/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/Distribute.java @@ -1898,12 +1898,18 @@ synchronized void notifyInstallProgress(boolean isInProgress) { /** * Start to install a new update. */ - synchronized private void installUpdate() { + @UiThread + private synchronized void installUpdate() { if (mReleaseInstallerListener == null) { AppCenterLog.debug(LOG_TAG, "Installing couldn't start due to the release installer wasn't initialized."); return; } - mReleaseInstallerListener.startInstall(); + post(new Runnable() { + @Override + public void run() { + mReleaseInstallerListener.startInstall(); + } + }); } /** diff --git a/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/ReleaseInstallerListener.java b/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/ReleaseInstallerListener.java index 16ab2fd26..f26138024 100644 --- a/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/ReleaseInstallerListener.java +++ b/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/ReleaseInstallerListener.java @@ -93,7 +93,8 @@ public synchronized void startInstall() { ParcelFileDescriptor fileDescriptor = downloadManager.openDownloadedFile(mDownloadId); if (fileDescriptor.getStatSize() != mTotalSize) { AppCenterLog.error(AppCenterLog.LOG_TAG, "Failed to start installing new release. The file is invalid."); - Toast.makeText(mContext, mContext.getString(R.string.appcenter_distribute_failed_file_during_install_update), Toast.LENGTH_SHORT).show(); + // FIXME: Call from UI thread + // Toast.makeText(mContext, mContext.getString(R.string.appcenter_distribute_failed_file_during_install_update), Toast.LENGTH_SHORT).show(); return; } InstallerUtils.installPackage(fileDescriptor, mContext, this); From 81d91b7dac5646f011c2560f89e97d9965f9e538 Mon Sep 17 00:00:00 2001 From: Ivan Matkov Date: Mon, 11 Apr 2022 23:18:49 +0200 Subject: [PATCH 34/72] Add FIXME comments and some logs --- .../AppCenterPackageInstallerReceiver.java | 2 ++ .../appcenter/distribute/Distribute.java | 3 +- .../distribute/DownloadManagerReceiver.java | 2 ++ .../distribute/ReleaseDownloadListener.java | 2 +- .../distribute/ReleaseInstallerListener.java | 29 +++++++++++++------ .../download/ReleaseDownloader.java | 2 +- .../manager/DownloadManagerRequestTask.java | 2 ++ 7 files changed, 30 insertions(+), 12 deletions(-) diff --git a/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/AppCenterPackageInstallerReceiver.java b/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/AppCenterPackageInstallerReceiver.java index 70a346e46..004a0b793 100644 --- a/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/AppCenterPackageInstallerReceiver.java +++ b/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/AppCenterPackageInstallerReceiver.java @@ -68,10 +68,12 @@ public void onReceive(Context context, Intent intent) { case PackageInstaller.STATUS_FAILURE_INVALID: case PackageInstaller.STATUS_FAILURE_STORAGE: AppCenterLog.debug(LOG_TAG, String.format(Locale.ENGLISH, "Failed to install a new release with status: %s. Error message: %s.", status, message)); + // FIXME: StrictMode policy violation: android.os.strictmode.IncorrectContextUseViolation Toast.makeText(context, context.getString(R.string.appcenter_distribute_something_went_wrong_during_installing_new_release), Toast.LENGTH_SHORT).show(); break; default: AppCenterLog.debug(LOG_TAG, String.format(Locale.ENGLISH, "Unrecognized status received from installer: %s", status)); + // FIXME: StrictMode policy violation: android.os.strictmode.IncorrectContextUseViolation Toast.makeText(context, context.getString(R.string.appcenter_distribute_something_went_wrong_during_installing_new_release), Toast.LENGTH_SHORT).show(); } } else { diff --git a/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/Distribute.java b/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/Distribute.java index 230631122..028a1544a 100644 --- a/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/Distribute.java +++ b/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/Distribute.java @@ -1461,6 +1461,7 @@ private boolean canUpdateNow(ReleaseDetails releaseDetails) { * @param dialog existing dialog if any, always returning true when null. * @return true if a new dialog should be displayed, false otherwise. */ + @SuppressWarnings("BooleanMethodIsAlwaysInverted") private boolean shouldRefreshDialog(@Nullable Dialog dialog) { /* We could be in another activity now, refresh dialog. */ @@ -1867,7 +1868,6 @@ synchronized void resumeApp(@NonNull Context context) { } @UiThread - @VisibleForTesting synchronized void notifyInstallProgress(boolean isInProgress) { mInstallInProgress = isInProgress; if (isInProgress) { @@ -1905,6 +1905,7 @@ private synchronized void installUpdate() { return; } post(new Runnable() { + @Override public void run() { mReleaseInstallerListener.startInstall(); diff --git a/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/DownloadManagerReceiver.java b/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/DownloadManagerReceiver.java index 7c9f14c39..f7c86985a 100644 --- a/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/DownloadManagerReceiver.java +++ b/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/DownloadManagerReceiver.java @@ -10,6 +10,7 @@ import android.content.Context; import android.content.Intent; +import com.microsoft.appcenter.utils.AppCenterLog; import com.microsoft.appcenter.utils.AsyncTaskUtils; import static com.microsoft.appcenter.distribute.DistributeConstants.INVALID_DOWNLOAD_IDENTIFIER; @@ -29,6 +30,7 @@ public void onReceive(Context context, Intent intent) { * Another option would be to open download list. */ String action = intent.getAction(); + AppCenterLog.debug(LOG_TAG, "Receive broadcast action: " + action); if (DownloadManager.ACTION_NOTIFICATION_CLICKED.equals(action)) { Distribute.getInstance().resumeApp(context); } diff --git a/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/ReleaseDownloadListener.java b/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/ReleaseDownloadListener.java index 7042b0984..6fcf33646 100644 --- a/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/ReleaseDownloadListener.java +++ b/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/ReleaseDownloadListener.java @@ -92,7 +92,7 @@ public void run() { @WorkerThread @Override - public void onComplete(@NonNull final long downloadId, final long totalSize) { + public void onComplete(final long downloadId, final long totalSize) { HandlerUtils.runOnUiThread(new Runnable() { @Override diff --git a/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/ReleaseInstallerListener.java b/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/ReleaseInstallerListener.java index f26138024..dbfb86809 100644 --- a/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/ReleaseInstallerListener.java +++ b/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/ReleaseInstallerListener.java @@ -70,7 +70,7 @@ public ReleaseInstallerListener(Context context) { * * @param downloadId downloadId of the downloaded file. */ - public synchronized void setDownloadId(long downloadId) { + public void setDownloadId(long downloadId) { mDownloadId = downloadId; } @@ -79,22 +79,21 @@ public synchronized void setDownloadId(long downloadId) { * * @param totalSize downloadId of the downloaded file. */ - public synchronized void setTotalSize(long totalSize) { + public void setTotalSize(long totalSize) { mTotalSize = totalSize; } /** * Start to install a new release. */ - public synchronized void startInstall() { + public void startInstall() { AppCenterLog.debug(AppCenterLog.LOG_TAG, "Start installing new release..."); try { DownloadManager downloadManager = (DownloadManager) mContext.getSystemService(DOWNLOAD_SERVICE); + // FIXME: android.os.strictmode.LeakedClosableViolation: A resource was acquired at attached stack trace but never released. ParcelFileDescriptor fileDescriptor = downloadManager.openDownloadedFile(mDownloadId); if (fileDescriptor.getStatSize() != mTotalSize) { - AppCenterLog.error(AppCenterLog.LOG_TAG, "Failed to start installing new release. The file is invalid."); - // FIXME: Call from UI thread - // Toast.makeText(mContext, mContext.getString(R.string.appcenter_distribute_failed_file_during_install_update), Toast.LENGTH_SHORT).show(); + onInvalidFile(); return; } InstallerUtils.installPackage(fileDescriptor, mContext, this); @@ -140,6 +139,7 @@ public void onFinished(int sessionId, final boolean success) { @Override public void run() { if (!success) { + // FIXME: StrictMode policy violation: android.os.strictmode.IncorrectContextUseViolation Toast.makeText(mContext, mContext.getString(R.string.appcenter_distribute_something_went_wrong_during_installing_new_release), Toast.LENGTH_SHORT).show(); } Distribute.getInstance().notifyInstallProgress(false); @@ -147,10 +147,21 @@ public void run() { }); } + private void onInvalidFile() { + AppCenterLog.error(AppCenterLog.LOG_TAG, "Failed to start installing new release. The file is invalid."); + HandlerUtils.runOnUiThread(new Runnable() { + + @Override + public void run() { + Toast.makeText(mContext, mContext.getString(R.string.appcenter_distribute_failed_file_during_install_update), Toast.LENGTH_SHORT).show(); + } + }); + } + /** * Hide the install progress dialog. */ - public synchronized void hideInstallProgressDialog() { + public void hideInstallProgressDialog() { AppCenterLog.debug(LOG_TAG, "Hide the install progress dialog."); if (mProgressDialog != null) { final android.app.ProgressDialog progressDialog = mProgressDialog; @@ -174,7 +185,7 @@ public void run() { */ @SuppressWarnings({"deprecation", "RedundantSuppression"}) @UiThread - private synchronized void updateInstallProgressDialog(final int currentSize) { + private void updateInstallProgressDialog(final int currentSize) { AppCenterLog.debug(LOG_TAG, "Update the install progress dialog."); /* If file size is known update downloadProgress bar. */ @@ -200,7 +211,7 @@ private synchronized void updateInstallProgressDialog(final int currentSize) { * @return install progress dialog. */ @UiThread - public synchronized Dialog showInstallProgressDialog(Activity foregroundActivity) { + public Dialog showInstallProgressDialog(Activity foregroundActivity) { AppCenterLog.debug(LOG_TAG, "Show the install progress dialog."); mProgressDialog = new android.app.ProgressDialog(foregroundActivity); mProgressDialog.setTitle(mContext.getString(R.string.appcenter_distribute_install_dialog)); diff --git a/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/download/ReleaseDownloader.java b/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/download/ReleaseDownloader.java index d4d82a063..b51cb78cb 100644 --- a/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/download/ReleaseDownloader.java +++ b/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/download/ReleaseDownloader.java @@ -76,7 +76,7 @@ interface Listener { * @param totalSize total size of downloaded file. */ @WorkerThread - void onComplete(@NonNull long downloadId, long totalSize); + void onComplete(long downloadId, long totalSize); /** * Called when an error occurs during the downloading. diff --git a/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/download/manager/DownloadManagerRequestTask.java b/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/download/manager/DownloadManagerRequestTask.java index 095fd2b22..fc690fd10 100644 --- a/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/download/manager/DownloadManagerRequestTask.java +++ b/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/download/manager/DownloadManagerRequestTask.java @@ -65,6 +65,8 @@ protected Void doInBackground(Void... params) { public void run() { DownloadManager.Query query = new DownloadManager.Query(); query.setFilterByStatus(DownloadManager.STATUS_PENDING); + // FIXME: android.os.strictmode.LeakedClosableViolation: A resource was acquired at attached stack trace but never released + // FIXME: StrictMode policy violation; ~duration=6 ms: android.os.strictmode.DiskReadViolation Cursor c = downloadManager.query(query); if (c.moveToFirst()) { downloadManager.remove(downloadId); From 2b986978fcbe2fdc71904284be01eec71f356c3b Mon Sep 17 00:00:00 2001 From: Ivan Matkov Date: Tue, 12 Apr 2022 13:07:23 +0200 Subject: [PATCH 35/72] Restore abstraction from DownloadManager --- .../appcenter/distribute/Distribute.java | 14 ++--- .../appcenter/distribute/InstallerUtils.java | 7 ++- .../distribute/ReleaseDownloadListener.java | 5 +- .../distribute/ReleaseInstallerListener.java | 58 ------------------- .../download/ReleaseDownloader.java | 7 ++- .../DownloadManagerReleaseDownloader.java | 16 ++++- .../manager/DownloadManagerUpdateTask.java | 2 +- 7 files changed, 35 insertions(+), 74 deletions(-) diff --git a/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/Distribute.java b/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/Distribute.java index 028a1544a..f611d7dbd 100644 --- a/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/Distribute.java +++ b/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/Distribute.java @@ -1908,24 +1908,24 @@ private synchronized void installUpdate() { @Override public void run() { - mReleaseInstallerListener.startInstall(); + AppCenterLog.debug(AppCenterLog.LOG_TAG, "Start installing new release..."); + + // TODO: Get localUri + InstallerUtils.installPackage(null, mContext, mReleaseInstallerListener); } }); } /** * Ask permission on start application after update or start to install a new update. - * - * @param downloadId downloadId of downloaded file. - * @param totalSize total size of downloaded file. */ - synchronized void showSystemSettingsDialogOrStartInstalling(long downloadId, long totalSize) { + @UiThread + synchronized void showSystemSettingsDialogOrStartInstalling(@NonNull Uri localUri) { if (mReleaseInstallerListener == null) { AppCenterLog.debug(LOG_TAG, "Installing couldn't start due to the release installer wasn't initialized."); return; } - mReleaseInstallerListener.setDownloadId(downloadId); - mReleaseInstallerListener.setTotalSize(totalSize); + // TODO: Remember localUri /* Check permission on start application after update. */ if (InstallerUtils.isSystemAlertWindowsEnabled(mContext)) { diff --git a/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/InstallerUtils.java b/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/InstallerUtils.java index 981b617d5..815cee5a4 100644 --- a/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/InstallerUtils.java +++ b/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/InstallerUtils.java @@ -13,6 +13,7 @@ import android.content.Intent; import android.content.IntentSender; import android.content.pm.PackageInstaller; +import android.net.Uri; import android.os.Build; import android.os.ParcelFileDescriptor; import android.provider.Settings; @@ -150,7 +151,7 @@ public static boolean isSystemAlertWindowsEnabled(@NonNull Context context) { * Install a new release. */ @WorkerThread - public static void installPackage(ParcelFileDescriptor fileDescriptor, Context context, PackageInstaller.SessionCallback sessionCallback) { + public static void installPackage(@NonNull Uri localUri, Context context, PackageInstaller.SessionCallback sessionCallback) { PackageInstaller.Session session = null; try { @@ -164,7 +165,11 @@ public static void installPackage(ParcelFileDescriptor fileDescriptor, Context c /* Prepare session. */ int sessionId = packageInstaller.createSession(params); session = packageInstaller.openSession(sessionId); + + // FIXME: android.os.strictmode.LeakedClosableViolation: A resource was acquired at attached stack trace but never released. + ParcelFileDescriptor fileDescriptor = context.getContentResolver().openFileDescriptor(localUri, "r"); addFileToInstallSession(fileDescriptor, session); + fileDescriptor.close(); // TODO: finally /* Start to install a new release. */ session.commit(createIntentSender(context, sessionId)); diff --git a/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/ReleaseDownloadListener.java b/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/ReleaseDownloadListener.java index 6fcf33646..2f5d5d87b 100644 --- a/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/ReleaseDownloadListener.java +++ b/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/ReleaseDownloadListener.java @@ -92,7 +92,7 @@ public void run() { @WorkerThread @Override - public void onComplete(final long downloadId, final long totalSize) { + public void onComplete(@NonNull final Uri localUri) { HandlerUtils.runOnUiThread(new Runnable() { @Override @@ -104,8 +104,8 @@ public void run() { /* Check if app should install now. */ if (!Distribute.getInstance().notifyDownload(mReleaseDetails, intent)) { AppCenterLog.info(LOG_TAG, "Release is downloaded. Starting to install it."); + Distribute.getInstance().showSystemSettingsDialogOrStartInstalling(localUri); Distribute.getInstance().setInstalling(mReleaseDetails); - Distribute.getInstance().showSystemSettingsDialogOrStartInstalling(downloadId, totalSize); } } }); @@ -122,6 +122,7 @@ public void onError(@Nullable String errorMessage) { @Override public void run() { + // FIXME: StrictMode policy violation: android.os.strictmode.IncorrectContextUseViolation Toast.makeText(mContext, R.string.appcenter_distribute_downloading_error, Toast.LENGTH_SHORT).show(); Distribute.getInstance().completeWorkflow(mReleaseDetails); } diff --git a/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/ReleaseInstallerListener.java b/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/ReleaseInstallerListener.java index dbfb86809..59bffeba0 100644 --- a/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/ReleaseInstallerListener.java +++ b/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/ReleaseInstallerListener.java @@ -42,16 +42,6 @@ public class ReleaseInstallerListener extends PackageInstaller.SessionCallback { */ private final Context mContext; - /** - * Download id. - */ - private long mDownloadId; - - /** - * Total size of the file. - */ - private long mTotalSize; - /** * Last download progress dialog that was shown. * Android 8 deprecates this dialog but only reason is that they want us to use a non modal @@ -65,43 +55,6 @@ public ReleaseInstallerListener(Context context) { mContext = context; } - /** - * Set the downloadId of the downloaded file to be installed. - * - * @param downloadId downloadId of the downloaded file. - */ - public void setDownloadId(long downloadId) { - mDownloadId = downloadId; - } - - /** - * Set the total size of the downloaded file. - * - * @param totalSize downloadId of the downloaded file. - */ - public void setTotalSize(long totalSize) { - mTotalSize = totalSize; - } - - /** - * Start to install a new release. - */ - public void startInstall() { - AppCenterLog.debug(AppCenterLog.LOG_TAG, "Start installing new release..."); - try { - DownloadManager downloadManager = (DownloadManager) mContext.getSystemService(DOWNLOAD_SERVICE); - // FIXME: android.os.strictmode.LeakedClosableViolation: A resource was acquired at attached stack trace but never released. - ParcelFileDescriptor fileDescriptor = downloadManager.openDownloadedFile(mDownloadId); - if (fileDescriptor.getStatSize() != mTotalSize) { - onInvalidFile(); - return; - } - InstallerUtils.installPackage(fileDescriptor, mContext, this); - } catch (IOException e) { - AppCenterLog.error(AppCenterLog.LOG_TAG, "Update can't be installed.", e); - } - } - @Override public void onCreated(int sessionId) { AppCenterLog.debug(LOG_TAG, "The install session was created."); @@ -147,17 +100,6 @@ public void run() { }); } - private void onInvalidFile() { - AppCenterLog.error(AppCenterLog.LOG_TAG, "Failed to start installing new release. The file is invalid."); - HandlerUtils.runOnUiThread(new Runnable() { - - @Override - public void run() { - Toast.makeText(mContext, mContext.getString(R.string.appcenter_distribute_failed_file_during_install_update), Toast.LENGTH_SHORT).show(); - } - }); - } - /** * Hide the install progress dialog. */ diff --git a/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/download/ReleaseDownloader.java b/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/download/ReleaseDownloader.java index b51cb78cb..6d85a412d 100644 --- a/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/download/ReleaseDownloader.java +++ b/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/download/ReleaseDownloader.java @@ -5,6 +5,8 @@ package com.microsoft.appcenter.distribute.download; +import android.net.Uri; + import androidx.annotation.AnyThread; import androidx.annotation.NonNull; import androidx.annotation.Nullable; @@ -72,11 +74,10 @@ interface Listener { /** * Called when the downloading is completed. * - * @param downloadId downloadId of downloaded file. - * @param totalSize total size of downloaded file. + * @param localUri The local URI of the file. */ @WorkerThread - void onComplete(long downloadId, long totalSize); + void onComplete(@NonNull Uri localUri); /** * Called when an error occurs during the downloading. diff --git a/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/download/manager/DownloadManagerReleaseDownloader.java b/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/download/manager/DownloadManagerReleaseDownloader.java index f6fcc248f..e77361385 100644 --- a/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/download/manager/DownloadManagerReleaseDownloader.java +++ b/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/download/manager/DownloadManagerReleaseDownloader.java @@ -8,7 +8,11 @@ import android.app.DownloadManager; import android.content.Context; import android.database.Cursor; +import android.net.Uri; +import android.os.Build; import android.os.SystemClock; +import android.widget.Toast; + import androidx.annotation.AnyThread; import androidx.annotation.NonNull; import androidx.annotation.WorkerThread; @@ -176,12 +180,20 @@ public void run() { } @WorkerThread - synchronized void onDownloadComplete(long totalSize) { + synchronized void onDownloadComplete() { if (isCancelled()) { return; } + // TODO: Add file check (mReleaseDetails.size) + // AppCenterLog.error(AppCenterLog.LOG_TAG, "Failed to start installing new release. The file is invalid."); + // Toast.makeText(mContext, mContext.getString(R.string.appcenter_distribute_failed_file_during_install_update), Toast.LENGTH_SHORT).show(); AppCenterLog.debug(LOG_TAG, "Download was successful for id=" + mDownloadId); - mListener.onComplete(mDownloadId, totalSize); + Uri localUri = getDownloadManager().getUriForDownloadedFile(mDownloadId); + if (localUri != null) { + mListener.onComplete(localUri); + } else { + mListener.onError("Downloaded file not found"); + } } @WorkerThread diff --git a/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/download/manager/DownloadManagerUpdateTask.java b/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/download/manager/DownloadManagerUpdateTask.java index dbcbe3dc7..b7ba8df92 100644 --- a/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/download/manager/DownloadManagerUpdateTask.java +++ b/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/download/manager/DownloadManagerUpdateTask.java @@ -58,7 +58,7 @@ protected Void doInBackground(Void... params) { } /* Complete download. */ - mDownloader.onDownloadComplete(cursor.getLong(cursor.getColumnIndexOrThrow(DownloadManager.COLUMN_TOTAL_SIZE_BYTES))); + mDownloader.onDownloadComplete(); } finally { cursor.close(); } From b3e2612c271ddd6eaf8f6f1d10350217cdbdb9e3 Mon Sep 17 00:00:00 2001 From: Ivan Matkov Date: Wed, 13 Apr 2022 10:16:13 +0200 Subject: [PATCH 36/72] Resolve TODOs --- .../appcenter/distribute/Distribute.java | 36 +++++++++---------- .../appcenter/distribute/InstallerUtils.java | 10 +++--- .../DownloadManagerReleaseDownloader.java | 19 +++++++--- .../manager/DownloadManagerRemoveTask.java | 1 - .../main/res/values/appcenter_distribute.xml | 1 - 5 files changed, 37 insertions(+), 30 deletions(-) diff --git a/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/Distribute.java b/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/Distribute.java index f611d7dbd..eec418dab 100644 --- a/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/Distribute.java +++ b/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/Distribute.java @@ -10,7 +10,6 @@ import android.app.AlertDialog; import android.app.Dialog; import android.app.DownloadManager; -import android.app.NotificationManager; import android.content.ActivityNotFoundException; import android.content.Context; import android.content.DialogInterface; @@ -21,14 +20,15 @@ import android.net.Uri; import android.os.Build; import android.provider.Settings; +import android.text.TextUtils; +import android.widget.Toast; + import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.annotation.RequiresApi; import androidx.annotation.UiThread; import androidx.annotation.VisibleForTesting; import androidx.annotation.WorkerThread; -import android.text.TextUtils; -import android.widget.Toast; import com.microsoft.appcenter.AbstractAppCenterService; import com.microsoft.appcenter.Flags; @@ -97,8 +97,6 @@ import static com.microsoft.appcenter.distribute.DistributeConstants.PREFERENCE_KEY_UPDATE_SETUP_FAILED_PACKAGE_HASH_KEY; import static com.microsoft.appcenter.distribute.DistributeConstants.PREFERENCE_KEY_UPDATE_TOKEN; import static com.microsoft.appcenter.distribute.DistributeConstants.SERVICE_NAME; -import static com.microsoft.appcenter.distribute.DistributeUtils.computeReleaseHash; -import static com.microsoft.appcenter.distribute.DistributeUtils.getStoredDownloadState; /** * Distribute service. @@ -253,6 +251,8 @@ public class Distribute extends AbstractAppCenterService { */ private AppCenterPackageInstallerReceiver mAppCenterPackageInstallerReceiver; + private Uri mDownloadedPackageFileUri; + /** * Remember if we checked download since our own process restarted. */ @@ -380,8 +380,6 @@ public static void addStores(Set stores) { /** * Get the current update track (public vs private). */ - // TODO Remove suppress when app uses it without reflection on jCenter - @SuppressWarnings("WeakerAccess") public static int getUpdateTrack() { return getInstance().getInstanceUpdateTrack(); } @@ -519,7 +517,7 @@ synchronized void startFromBackground(Context context) { * @return true if workflow was reset, false otherwise. */ private boolean tryResetWorkflow() { - if (getStoredDownloadState() == DOWNLOAD_STATE_COMPLETED && mCheckReleaseCallId == null) { + if (DistributeUtils.getStoredDownloadState() == DOWNLOAD_STATE_COMPLETED && mCheckReleaseCallId == null) { mWorkflowCompleted = false; mBrowserOpenedOrAborted = false; return true; @@ -645,7 +643,7 @@ public void accept(Boolean enabled) { return; } boolean isDownloading = mReleaseDownloader != null && mReleaseDownloader.isDownloading(); - if (getStoredDownloadState() != DOWNLOAD_STATE_AVAILABLE || isDownloading) { + if (DistributeUtils.getStoredDownloadState() != DOWNLOAD_STATE_AVAILABLE || isDownloading) { AppCenterLog.error(LOG_TAG, "Cannot handle user update action at this time."); return; } @@ -852,7 +850,7 @@ private synchronized void resumeDistributeWorkflow() { } /* Load cached release details if process restarted and we have such a cache. */ - int downloadState = getStoredDownloadState(); + int downloadState = DistributeUtils.getStoredDownloadState(); if (mReleaseDetails == null && downloadState != DOWNLOAD_STATE_COMPLETED) { updateReleaseDetails(DistributeUtils.loadCachedReleaseDetails()); @@ -1037,7 +1035,7 @@ synchronized void completeWorkflow(ReleaseDetails releaseDetails) { * Cancel notification if needed. */ private synchronized void cancelNotification() { - if (getStoredDownloadState() == DOWNLOAD_STATE_NOTIFIED) { + if (DistributeUtils.getStoredDownloadState() == DOWNLOAD_STATE_NOTIFIED) { AppCenterLog.debug(LOG_TAG, "Cancel download notification."); DistributeUtils.cancelDownloadNotification(mContext); } @@ -1147,7 +1145,7 @@ private void processDistributionGroupId(@NonNull String distributionGroupId) { @VisibleForTesting synchronized void getLatestReleaseDetails(final String distributionGroupId, String updateToken) { AppCenterLog.debug(LOG_TAG, "Get latest release details..."); - String releaseHash = computeReleaseHash(mPackageInfo); + String releaseHash = DistributeUtils.computeReleaseHash(mPackageInfo); String url = mApiUrl; if (updateToken == null) { url += String.format(GET_LATEST_PUBLIC_RELEASE_PATH_FORMAT, mAppSecret, releaseHash, getReportingParametersForUpdatedRelease(true, distributionGroupId)); @@ -1407,7 +1405,7 @@ private boolean isCurrentReleaseWasUpdated(String lastDownloadedReleaseHash) { if (mPackageInfo == null || TextUtils.isEmpty(lastDownloadedReleaseHash)) { return false; } - String currentInstalledReleaseHash = computeReleaseHash(mPackageInfo); + String currentInstalledReleaseHash = DistributeUtils.computeReleaseHash(mPackageInfo); return currentInstalledReleaseHash.equals(lastDownloadedReleaseHash); } @@ -1904,14 +1902,16 @@ private synchronized void installUpdate() { AppCenterLog.debug(LOG_TAG, "Installing couldn't start due to the release installer wasn't initialized."); return; } + if (mDownloadedPackageFileUri == null) { + AppCenterLog.debug(LOG_TAG, "Installing couldn't start because the package file is missing."); + return; + } post(new Runnable() { @Override public void run() { AppCenterLog.debug(AppCenterLog.LOG_TAG, "Start installing new release..."); - - // TODO: Get localUri - InstallerUtils.installPackage(null, mContext, mReleaseInstallerListener); + InstallerUtils.installPackage(mDownloadedPackageFileUri, mContext, mReleaseInstallerListener); } }); } @@ -1925,7 +1925,7 @@ synchronized void showSystemSettingsDialogOrStartInstalling(@NonNull Uri localUr AppCenterLog.debug(LOG_TAG, "Installing couldn't start due to the release installer wasn't initialized."); return; } - // TODO: Remember localUri + mDownloadedPackageFileUri = localUri; /* Check permission on start application after update. */ if (InstallerUtils.isSystemAlertWindowsEnabled(mContext)) { @@ -1959,7 +1959,7 @@ synchronized boolean notifyDownload(ReleaseDetails releaseDetails, Intent intent * We should not hold the install any longer now, even if the async task was long enough * for app to be in background again, we should show install U.I. now. */ - if (mForegroundActivity != null || getStoredDownloadState() == DOWNLOAD_STATE_NOTIFIED) { + if (mForegroundActivity != null || DistributeUtils.getStoredDownloadState() == DOWNLOAD_STATE_NOTIFIED) { return false; } diff --git a/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/InstallerUtils.java b/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/InstallerUtils.java index 815cee5a4..357d827df 100644 --- a/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/InstallerUtils.java +++ b/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/InstallerUtils.java @@ -33,7 +33,7 @@ /** * Installer utils. */ -public class InstallerUtils { +class InstallerUtils { /** * Value when {@link Settings.Secure#INSTALL_NON_MARKET_APPS} setting is enabled. @@ -165,11 +165,9 @@ public static void installPackage(@NonNull Uri localUri, Context context, Packag /* Prepare session. */ int sessionId = packageInstaller.createSession(params); session = packageInstaller.openSession(sessionId); - - // FIXME: android.os.strictmode.LeakedClosableViolation: A resource was acquired at attached stack trace but never released. - ParcelFileDescriptor fileDescriptor = context.getContentResolver().openFileDescriptor(localUri, "r"); - addFileToInstallSession(fileDescriptor, session); - fileDescriptor.close(); // TODO: finally + try (ParcelFileDescriptor fileDescriptor = context.getContentResolver().openFileDescriptor(localUri, "r")) { + addFileToInstallSession(fileDescriptor, session); + } /* Start to install a new release. */ session.commit(createIntentSender(context, sessionId)); diff --git a/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/download/manager/DownloadManagerReleaseDownloader.java b/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/download/manager/DownloadManagerReleaseDownloader.java index e77361385..7107aabee 100644 --- a/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/download/manager/DownloadManagerReleaseDownloader.java +++ b/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/download/manager/DownloadManagerReleaseDownloader.java @@ -10,6 +10,7 @@ import android.database.Cursor; import android.net.Uri; import android.os.Build; +import android.os.ParcelFileDescriptor; import android.os.SystemClock; import android.widget.Toast; @@ -32,6 +33,9 @@ import static com.microsoft.appcenter.distribute.DistributeConstants.PREFERENCE_KEY_DOWNLOAD_ID; import static com.microsoft.appcenter.distribute.DistributeConstants.UPDATE_PROGRESS_TIME_THRESHOLD; +import java.io.FileNotFoundException; +import java.io.IOException; + public class DownloadManagerReleaseDownloader extends AbstractReleaseDownloader { public DownloadManagerReleaseDownloader(@NonNull Context context, @NonNull ReleaseDetails releaseDetails, @NonNull Listener listener) { @@ -184,11 +188,18 @@ synchronized void onDownloadComplete() { if (isCancelled()) { return; } - // TODO: Add file check (mReleaseDetails.size) - // AppCenterLog.error(AppCenterLog.LOG_TAG, "Failed to start installing new release. The file is invalid."); - // Toast.makeText(mContext, mContext.getString(R.string.appcenter_distribute_failed_file_during_install_update), Toast.LENGTH_SHORT).show(); + DownloadManager downloadManager = getDownloadManager(); + try (ParcelFileDescriptor fileDescriptor = downloadManager.openDownloadedFile(mDownloadId)) { + if (fileDescriptor.getStatSize() != mReleaseDetails.getSize()) { + mListener.onError("The file is invalid"); + return; + } + } catch (IOException e) { + mListener.onError("Cannot check file size: " + e.getMessage()); + return; + } AppCenterLog.debug(LOG_TAG, "Download was successful for id=" + mDownloadId); - Uri localUri = getDownloadManager().getUriForDownloadedFile(mDownloadId); + Uri localUri = downloadManager.getUriForDownloadedFile(mDownloadId); if (localUri != null) { mListener.onComplete(localUri); } else { diff --git a/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/download/manager/DownloadManagerRemoveTask.java b/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/download/manager/DownloadManagerRemoveTask.java index a8a9f665b..fc6351891 100644 --- a/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/download/manager/DownloadManagerRemoveTask.java +++ b/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/download/manager/DownloadManagerRemoveTask.java @@ -37,7 +37,6 @@ class DownloadManagerRemoveTask extends AsyncTask { mDownloadId = downloadId; } - @SuppressWarnings("ConstantConditions") @Override protected Void doInBackground(Void... params) { diff --git a/sdk/appcenter-distribute/src/main/res/values/appcenter_distribute.xml b/sdk/appcenter-distribute/src/main/res/values/appcenter_distribute.xml index 78aa7e9db..b2a6784fd 100644 --- a/sdk/appcenter-distribute/src/main/res/values/appcenter_distribute.xml +++ b/sdk/appcenter-distribute/src/main/res/values/appcenter_distribute.xml @@ -23,7 +23,6 @@ %1$d MB of %2$d MB %1$d of %2$d Something went wrong. Update was not installed. - Failed to start installing new release. The file is invalid. Installing... App update downloaded %1$s %2$s (%3$d) has been downloaded and is ready to be installed. From ccd81e83e6a5f8613aa72043fe55ff430a6e81fc Mon Sep 17 00:00:00 2001 From: Ivan Matkov Date: Thu, 12 May 2022 09:42:32 +0200 Subject: [PATCH 37/72] Fix tests (part 1, build errors only) --- .../appcenter/distribute/DistributeTest.java | 8 +- .../DistributeWarnAlertSystemWindowsTest.java | 44 +-- .../distribute/InstallerUtilsTest.java | 15 +- .../ReleaseDownloadListenerTest.java | 10 +- .../ReleaseInstallerListenerTest.java | 369 ------------------ ...DownloadManagerDistributeDeadlockTest.java | 5 +- .../DownloadManagerReleaseDownloaderTest.java | 12 +- .../DownloadManagerUpdateTaskTest.java | 2 +- 8 files changed, 40 insertions(+), 425 deletions(-) delete mode 100644 sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/ReleaseInstallerListenerTest.java diff --git a/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/DistributeTest.java b/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/DistributeTest.java index 0ce8f4c0b..0aa08949d 100644 --- a/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/DistributeTest.java +++ b/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/DistributeTest.java @@ -52,6 +52,7 @@ import android.content.Intent; import android.content.IntentFilter; import android.content.pm.PackageInfo; +import android.net.Uri; import android.os.Build; import com.microsoft.appcenter.DependencyConfiguration; @@ -941,7 +942,7 @@ public void checkInstallProgressState() { public void showSystemSettingsDialogWhenPackageInstallerNull() { /* Try to show dialog. */ - Distribute.getInstance().showSystemSettingsDialogOrStartInstalling(1L, 1L); + Distribute.getInstance().showSystemSettingsDialogOrStartInstalling(mock(Uri.class)); /* Verify that log was called. */ verifyStatic(AppCenterLog.class); @@ -976,7 +977,7 @@ public void showUpdateDialogAfterShowingInstallReleaseDialogTest() { /* Show install settings dialog. */ Distribute.getInstance().startFromBackground(mContext); resumeWorkflow(mActivity); - Distribute.getInstance().showSystemSettingsDialogOrStartInstalling(1L, 1L); + Distribute.getInstance().showSystemSettingsDialogOrStartInstalling(mock(Uri.class)); /* Emulate that settings was applied. */ Distribute.getInstance().onActivityPaused(mActivity); @@ -984,7 +985,8 @@ public void showUpdateDialogAfterShowingInstallReleaseDialogTest() { resumeWorkflow(mActivity); /* Verify that install progress was started. */ - verify(mReleaseInstallerListener).startInstall(); + verifyStatic(InstallerUtils.class); + InstallerUtils.installPackage(any(Uri.class), any(Context.class), any(ReleaseInstallerListener.class)); /* Emulate system confirmation dialog about installing new release. */ Distribute.getInstance().onActivityPaused(mActivity); diff --git a/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/DistributeWarnAlertSystemWindowsTest.java b/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/DistributeWarnAlertSystemWindowsTest.java index 2824306e0..eb23cc17c 100644 --- a/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/DistributeWarnAlertSystemWindowsTest.java +++ b/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/DistributeWarnAlertSystemWindowsTest.java @@ -9,7 +9,6 @@ import static com.microsoft.appcenter.distribute.DistributeConstants.DOWNLOAD_STATE_NOTIFIED; import static com.microsoft.appcenter.distribute.DistributeConstants.LOG_TAG; import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.anyLong; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.RETURNS_MOCKS; import static org.mockito.Mockito.doCallRealMethod; @@ -32,6 +31,7 @@ import android.content.Context; import android.content.DialogInterface; import android.content.Intent; +import android.net.Uri; import android.os.Build; import android.provider.Settings; @@ -78,7 +78,7 @@ public void setUpDialog() throws Exception { mockStatic(AppCenterLog.class); /* Reset mock release listener methods. */ - doCallRealMethod().when(mReleaseDownloaderListener).onComplete(anyLong(), anyLong()); + doCallRealMethod().when(mReleaseDownloaderListener).onComplete(any(Uri.class)); when(mReleaseInstallerListener.showInstallProgressDialog(any(Activity.class))).thenReturn(mock(Dialog.class)); /* Mock release installer listener. */ @@ -129,10 +129,7 @@ public void showAndEnableAlertWindowsDialogQ() { when(Settings.canDrawOverlays(any(Context.class))).thenReturn(false); /* Notify about complete download. */ - mReleaseDownloaderListener.onComplete(1L, 1L); - - /* Verify that downloadId was set. */ - verify(mReleaseInstallerListener).setDownloadId(anyLong()); + mReleaseDownloaderListener.onComplete(mock(Uri.class)); /* Verify that dialog was shown after complete download. */ verify(mAlertWindowsDialog).show(); @@ -156,7 +153,8 @@ public void showAndEnableAlertWindowsDialogQ() { Distribute.getInstance().onActivityResumed(mActivity); /* Verify that after enabling permissions the install process was started. */ - verify(mReleaseInstallerListener).startInstall(); + verifyStatic(InstallerUtils.class); + InstallerUtils.installPackage(any(Uri.class), any(Context.class), any(ReleaseInstallerListener.class)); /* Emulate that install a new release was started. */ Distribute.getInstance().notifyInstallProgress(true); @@ -191,10 +189,7 @@ private void showAndEnableAlertWindowsDialogLowQ(boolean isEnabled) { when(Settings.canDrawOverlays(any(Context.class))).thenReturn(isEnabled); /* Notify about complete download. */ - mReleaseDownloaderListener.onComplete(1L, 1L); - - /* Verify that downloadId was set. */ - verify(mReleaseInstallerListener).setDownloadId(anyLong()); + mReleaseDownloaderListener.onComplete(mock(Uri.class)); /* Verify that dialog was not shown. */ verify(mAlertWindowsDialog, never()).show(); @@ -211,7 +206,8 @@ private void showAndEnableAlertWindowsDialogLowQ(boolean isEnabled) { Distribute.getInstance().onActivityResumed(mActivity); /* Verify that after enabling permissions the install process was started. */ - verify(mReleaseInstallerListener).startInstall(); + verifyStatic(InstallerUtils.class); + InstallerUtils.installPackage(any(Uri.class), any(Context.class), any(ReleaseInstallerListener.class)); /* Emulate that install a new release was started. */ Distribute.getInstance().notifyInstallProgress(true); @@ -226,10 +222,7 @@ public void showAndDisableAlertWindowsDialogQ() { when(Settings.canDrawOverlays(any(Context.class))).thenReturn(false); /* Notify about complete download. */ - mReleaseDownloaderListener.onComplete(1L, 1L); - - /* Verify that downloadId was set. */ - verify(mReleaseInstallerListener).setDownloadId(anyLong()); + mReleaseDownloaderListener.onComplete(mock(Uri.class)); /* Verify that dialog was shown after complete download. */ verify(mAlertWindowsDialog).show(); @@ -240,7 +233,8 @@ public void showAndDisableAlertWindowsDialogQ() { clickListener.getValue().onClick(mDialog, DialogInterface.BUTTON_NEGATIVE); /* Verify that after enabling permissions the install process was started. */ - verify(mReleaseInstallerListener).startInstall(); + verifyStatic(InstallerUtils.class); + InstallerUtils.installPackage(any(Uri.class), any(Context.class), any(ReleaseInstallerListener.class)); } @Test @@ -251,10 +245,7 @@ public void showAndCancelAlertWindowsDialog() { when(Settings.canDrawOverlays(any(Context.class))).thenReturn(false); /* Notify about complete download. */ - mReleaseDownloaderListener.onComplete(1L, 1L); - - /* Verify that downloadId was set. */ - verify(mReleaseInstallerListener).setDownloadId(anyLong()); + mReleaseDownloaderListener.onComplete(mock(Uri.class)); /* Verify that dialog was shown after complete download. */ verify(mAlertWindowsDialog).show(); @@ -265,7 +256,8 @@ public void showAndCancelAlertWindowsDialog() { cancelListener.getValue().onCancel(mAlertWindowsDialog); /* Verify that after enabling permissions the install process was started. */ - verify(mReleaseInstallerListener).startInstall(); + verifyStatic(InstallerUtils.class); + InstallerUtils.installPackage(any(Uri.class), any(Context.class), any(ReleaseInstallerListener.class)); } @Test @@ -276,7 +268,7 @@ public void returningToDialogFromBackgroundWhenInstallerIsNull() { when(Settings.canDrawOverlays(any(Context.class))).thenReturn(false); /* Notify about complete download. */ - mReleaseDownloaderListener.onComplete(1L, 1L); + mReleaseDownloaderListener.onComplete(mock(Uri.class)); /* Emulate behavior that settings was enabled via dialog. */ Distribute.getInstance().onActivityPaused(mActivity); @@ -319,7 +311,7 @@ public void showSettingsDialogWhenActivityIsNullOnAndroidQ() throws Exception { Mockito.when(mContext.getSystemService(NOTIFICATION_SERVICE)).thenReturn(manager); /* Notify about complete download. */ - mReleaseDownloaderListener.onComplete(1L, 1L); + mReleaseDownloaderListener.onComplete(mock(Uri.class)); /* Verify that dialog was not shown. */ verify(mAlertWindowsDialog, never()).show(); @@ -346,7 +338,7 @@ public void needRefreshDialogWhenStartInstallationOnAndroidQ() throws Exception Mockito.when(mContext.getSystemService(NOTIFICATION_SERVICE)).thenReturn(manager); /* Notify about complete download. */ - mReleaseDownloaderListener.onComplete(1L, 1L); + mReleaseDownloaderListener.onComplete(mock(Uri.class)); /* Start distribute with app secret NULL to make sure updateReleaseDetails is called on startFromBackground. */ Distribute.getInstance().onStarting(mAppCenterHandler); @@ -357,7 +349,7 @@ public void needRefreshDialogWhenStartInstallationOnAndroidQ() throws Exception Mockito.when(mReleaseDetails.isMandatoryUpdate()).thenReturn(true); /* Start installation */ - mReleaseDownloaderListener.onComplete(1L, 1L); + mReleaseDownloaderListener.onComplete(mock(Uri.class)); /* Verify that dialog was shown once only. */ verify(mAlertWindowsDialog).show(); diff --git a/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/InstallerUtilsTest.java b/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/InstallerUtilsTest.java index 6141fb823..080629979 100644 --- a/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/InstallerUtilsTest.java +++ b/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/InstallerUtilsTest.java @@ -25,6 +25,7 @@ import android.content.IntentSender; import android.content.pm.PackageInstaller; import android.content.pm.PackageManager; +import android.net.Uri; import android.os.Build; import android.provider.Settings; @@ -65,9 +66,6 @@ public class InstallerUtilsTest { @Mock private PackageInstaller.Session mSession; - @Mock - private InputStream mData; - @Before public void setUp() throws IOException { @@ -93,15 +91,11 @@ public void installPackage() throws IOException { /* Mock session callback. */ PackageInstaller.SessionCallback mockSessionCallback = mock(PackageInstaller.SessionCallback.class); - /* Mock data. */ - when(mData.read(any())).thenReturn(10).thenReturn(-1); - /* Call install. */ - InstallerUtils.installPackage(mData, mContext, mockSessionCallback); + InstallerUtils.installPackage(mock(Uri.class), mContext, mockSessionCallback); /* Verify. */ verify(mMockPackageInstaller).registerSessionCallback(eq(mockSessionCallback)); - verify(mData).close(); verify(mOutputStream).close(); verify(mSession).commit(any(IntentSender.class)); verify(mSession, never()).abandon(); @@ -115,10 +109,9 @@ public void throwIOExceptionWhenTryToOpenWriteSession() throws IOException { when(mSession.openWrite(anyString(), anyLong(), anyLong())).thenThrow(new IOException()); /* Call install method. */ - InstallerUtils.installPackage(mData, mContext, null); + InstallerUtils.installPackage(mock(Uri.class), mContext, null); /* Verify. */ - verify(mData, never()).close(); verify(mSession).abandon(); } @@ -129,7 +122,7 @@ public void throwIOExceptionWhenTryToCreateSession() throws IOException { when(mMockPackageInstaller.createSession(any(PackageInstaller.SessionParams.class))).thenThrow(new IOException()); /* Call install method. */ - InstallerUtils.installPackage(mData, mContext, null); + InstallerUtils.installPackage(mock(Uri.class), mContext, null); /* Verify that the session wasn't created. */ verify(mMockPackageInstaller, never()).openSession(anyInt()); diff --git a/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/ReleaseDownloadListenerTest.java b/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/ReleaseDownloadListenerTest.java index a14d02341..720afcd23 100644 --- a/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/ReleaseDownloadListenerTest.java +++ b/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/ReleaseDownloadListenerTest.java @@ -380,11 +380,11 @@ public void onComplete() throws Exception { /* Do not notify the download. */ when(mDistribute.notifyDownload(eq(mockReleaseDetails), any(Intent.class))).thenReturn(false); ReleaseDownloadListener releaseDownloadListener = new ReleaseDownloadListener(mContext, mockReleaseDetails); - releaseDownloadListener.onComplete(1L, 1L); + releaseDownloadListener.onComplete(mock(Uri.class)); /* Verify that setInstalling() is called on mandatory update. */ verify(mDistribute).setInstalling(mockReleaseDetails); - verify(mDistribute).showSystemSettingsDialogOrStartInstalling(anyLong(), anyLong()); + verify(mDistribute).showSystemSettingsDialogOrStartInstalling(any(Uri.class)); } @Test @@ -396,7 +396,7 @@ public void onCompleteNotify() throws Exception { /* Notify the download. */ when(mDistribute.notifyDownload(eq(mockReleaseDetails), any(Intent.class))).thenReturn(true); ReleaseDownloadListener releaseDownloadListener = new ReleaseDownloadListener(mContext, mockReleaseDetails); - releaseDownloadListener.onComplete(1L, 1L); + releaseDownloadListener.onComplete(mock(Uri.class)); /* Verify that startActivity() and setInstalling() are not called here. */ verify(mContext, never()).startActivity(any(Intent.class)); @@ -417,8 +417,8 @@ public void onCompleteActivityNotResolved() throws Exception { ReleaseDownloadListener releaseDownloadListener = new ReleaseDownloadListener(mContext, mockReleaseDetails); /* Verify that nothing is called and the method is exited early with false result. */ - releaseDownloadListener.onComplete(1L, 1L); - verify(mDistribute, never()).showSystemSettingsDialogOrStartInstalling(anyLong(), anyLong()); + releaseDownloadListener.onComplete(mock(Uri.class)); + verify(mDistribute, never()).showSystemSettingsDialogOrStartInstalling(any(Uri.class)); verify(mDistribute, never()).setInstalling(mockReleaseDetails); } } diff --git a/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/ReleaseInstallerListenerTest.java b/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/ReleaseInstallerListenerTest.java deleted file mode 100644 index 754c497a8..000000000 --- a/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/ReleaseInstallerListenerTest.java +++ /dev/null @@ -1,369 +0,0 @@ -/* - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. - */ - -package com.microsoft.appcenter.distribute; - -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.anyBoolean; -import static org.mockito.ArgumentMatchers.anyInt; -import static org.mockito.ArgumentMatchers.anyLong; -import static org.mockito.ArgumentMatchers.anyString; -import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.ArgumentMatchers.isNull; -import static org.mockito.Mockito.doNothing; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.never; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; -import static org.powermock.api.mockito.PowerMockito.mockStatic; -import static org.powermock.api.mockito.PowerMockito.verifyStatic; -import static org.powermock.api.mockito.PowerMockito.whenNew; - -import android.app.Activity; -import android.app.DownloadManager; -import android.app.ProgressDialog; -import android.content.Context; -import android.content.pm.PackageInstaller; -import android.os.ParcelFileDescriptor; -import android.widget.Toast; - -import com.microsoft.appcenter.utils.AppCenterLog; -import com.microsoft.appcenter.utils.HandlerUtils; - -import org.junit.After; -import org.junit.Before; -import org.junit.Rule; -import org.junit.Test; -import org.mockito.ArgumentCaptor; -import org.mockito.Mock; -import org.powermock.core.classloader.annotations.PrepareForTest; -import org.powermock.modules.junit4.rule.PowerMockRule; - -import java.io.FileInputStream; -import java.io.FileNotFoundException; -import java.io.InputStream; -import java.text.NumberFormat; - -@PrepareForTest({ - AppCenterLog.class, - Distribute.class, - FileInputStream.class, - HandlerUtils.class, - InstallerUtils.class, - PackageInstaller.SessionCallback.class, - ProgressDialog.class, - ReleaseInstallerListener.class, - Toast.class -}) -public class ReleaseInstallerListenerTest { - - @Rule - public PowerMockRule mPowerMockRule = new PowerMockRule(); - - @Mock - private Context mContext; - - @Mock - private Distribute mDistribute; - - @Mock - private Toast mToast; - - @Mock - private DownloadManager mDownloadManager; - - @Mock - private android.app.ProgressDialog mMockProgressDialog; - - private final int mMockSessionId = 1; - - private ReleaseInstallerListener mReleaseInstallerListener; - - @Before - public void setUp() throws Exception { - - /* Mock static classes. */ - mockStatic(AppCenterLog.class); - mockStatic(Distribute.class); - mockStatic(HandlerUtils.class); - mockStatic(InstallerUtils.class); - mockStatic(Toast.class); - - /* Mock progress dialog. */ - whenNew(android.app.ProgressDialog.class).withAnyArguments().thenReturn(mMockProgressDialog); - when(mMockProgressDialog.isIndeterminate()).thenReturn(false); - - /* Mock toast. */ - when(mContext.getString(anyInt())).thenReturn("localized_message"); - when(Toast.makeText(any(Context.class), anyString(), anyInt())).thenReturn(mToast); - - /* Mock Distribute. */ - when(Distribute.getInstance()).thenReturn(mDistribute); - doNothing().when(mDistribute).notifyInstallProgress(anyBoolean()); - - /* Mock constructors and classes. */ - whenNew(FileInputStream.class).withAnyArguments().thenReturn(mock(FileInputStream.class)); - - /* Mock download manager. */ - when(mDownloadManager.openDownloadedFile(anyLong())).thenReturn(mock(ParcelFileDescriptor.class)); - when(mContext.getSystemService(anyString())).thenReturn(mDownloadManager); - - /* Create installer listener. */ - mReleaseInstallerListener = new ReleaseInstallerListener(mContext); - - /* Set downloadId. */ - mReleaseInstallerListener.setDownloadId(1); - - /* Init install progress dialog. */ - mReleaseInstallerListener.showInstallProgressDialog(mock(Activity.class)); - - /* Verify call methods. */ - verify(mMockProgressDialog).setProgressPercentFormat(isNull()); - verify(mMockProgressDialog).setProgressNumberFormat(isNull()); - verify(mMockProgressDialog).setIndeterminate(anyBoolean()); - } - - @After - public void cleanUp() { - mReleaseInstallerListener = null; - } - - @Test - public void throwIOExceptionAfterStartInstall() throws Exception { - - /* Throw exception. */ - when(mDownloadManager.openDownloadedFile(anyLong())).thenThrow(new FileNotFoundException()); - - /* Start install process. */ - mReleaseInstallerListener.startInstall(); - - /* Verify that exception was called. */ - verifyStatic(AppCenterLog.class); - AppCenterLog.error(anyString(), anyString(), any(FileNotFoundException.class)); - } - - @Test - public void releaseInstallProcessWhenOnFinnishFailureWithContext() { - - /* Mock progress dialog. */ - when(mMockProgressDialog.isIndeterminate()).thenReturn(true); - - /* Start install process. */ - mReleaseInstallerListener.startInstall(); - - /* Verify that installPackage method was called. */ - ArgumentCaptor sessionListener = ArgumentCaptor.forClass(PackageInstaller.SessionCallback.class); - verifyStatic(InstallerUtils.class); - InstallerUtils.installPackage(any(InputStream.class), any(Context.class), sessionListener.capture()); - - /* Emulate session status. */ - sessionListener.getValue().onCreated(mMockSessionId); - - /* Verify that installer process was triggered in the Distribute. */ - sessionListener.getValue().onActiveChanged(mMockSessionId, true); - verify(mDistribute).notifyInstallProgress(eq(true)); - - /* Verity that progress dialog was updated. */ - sessionListener.getValue().onProgressChanged(mMockSessionId, 1); - - /* Verify that the handler was called and catch runnable. */ - verifyStatic(HandlerUtils.class); - HandlerUtils.runOnUiThread(any(Runnable.class)); - - /* Verify that progress dialog was closed after finish install process. */ - sessionListener.getValue().onFinished(mMockSessionId, false); - - /* Verify that the handler was called again. */ - ArgumentCaptor runnable = ArgumentCaptor.forClass(Runnable.class); - verifyStatic(HandlerUtils.class, times(2)); - HandlerUtils.runOnUiThread(runnable.capture()); - runnable.getValue().run(); - - /* Verify that installer process was triggered in the Distribute again. */ - verify(mToast).show(); - } - - @Test - public void releaseInstallerProcessWhenProgressDialogNull() { - - /* Start install process. */ - mReleaseInstallerListener.startInstall(); - - /* Verify that installPackage method was called. */ - ArgumentCaptor sessionListener = ArgumentCaptor.forClass(PackageInstaller.SessionCallback.class); - verifyStatic(InstallerUtils.class); - InstallerUtils.installPackage(any(InputStream.class), any(Context.class), sessionListener.capture()); - - /* Emulate session status. */ - sessionListener.getValue().onCreated(mMockSessionId); - sessionListener.getValue().onBadgingChanged(mMockSessionId); - - /* Verify that installer process was triggered in the Distribute. */ - sessionListener.getValue().onActiveChanged(mMockSessionId, true); - verify(mDistribute).notifyInstallProgress(eq(true)); - - /* Hide dialog. */ - mReleaseInstallerListener.hideInstallProgressDialog(); - - /* Verify that runnable was called. */ - ArgumentCaptor runnable = ArgumentCaptor.forClass(Runnable.class); - verifyStatic(HandlerUtils.class); - HandlerUtils.runOnUiThread(runnable.capture()); - runnable.getValue().run(); - - /* Verity that progress dialog was updated. */ - sessionListener.getValue().onProgressChanged(mMockSessionId, 1); - - /* Verify that the handler was called and catch runnable. */ - runnable = ArgumentCaptor.forClass(Runnable.class); - verifyStatic(HandlerUtils.class, times(2)); - HandlerUtils.runOnUiThread(runnable.capture()); - runnable.getValue().run(); - - /* Verify that the progress dialog was updated. */ - verify(mMockProgressDialog, never()).setProgress(anyInt()); - - /* Verify that progress dialog was closed after finish install process. */ - sessionListener.getValue().onFinished(mMockSessionId, true); - - /* Verify that the handler was called again. */ - verifyStatic(HandlerUtils.class, times(3)); - HandlerUtils.runOnUiThread(runnable.capture()); - runnable.getValue().run(); - - /* Verify that installer process was triggered in the Distribute again. */ - verify(mDistribute).notifyInstallProgress(eq(false)); - } - - @Test - public void releaseInstallerProcessWhenDialogIsIndeterminate() { - - /* Mock progress dialog. */ - when(mMockProgressDialog.isIndeterminate()).thenReturn(true); - - /* Start install process. */ - mReleaseInstallerListener.startInstall(); - - /* Verify that installPackage method was called. */ - ArgumentCaptor sessionListener = ArgumentCaptor.forClass(PackageInstaller.SessionCallback.class); - verifyStatic(InstallerUtils.class); - InstallerUtils.installPackage(any(InputStream.class), any(Context.class), sessionListener.capture()); - - /* Emulate session status. */ - sessionListener.getValue().onCreated(mMockSessionId); - sessionListener.getValue().onBadgingChanged(mMockSessionId); - - /* Verify that installer process was triggered in the Distribute. */ - sessionListener.getValue().onActiveChanged(mMockSessionId, true); - verify(mDistribute).notifyInstallProgress(eq(true)); - - /* Verity that progress dialog was updated. */ - sessionListener.getValue().onProgressChanged(mMockSessionId, 1); - - /* Verify that the handler was called and catch runnable. */ - ArgumentCaptor runnable = ArgumentCaptor.forClass(Runnable.class); - verifyStatic(HandlerUtils.class); - HandlerUtils.runOnUiThread(runnable.capture()); - runnable.getValue().run(); - - /* Verify that the progress dialog was updated. */ - verify(mMockProgressDialog).setProgress(anyInt()); - verify(mMockProgressDialog).setMax(anyInt()); - verify(mMockProgressDialog).setProgressPercentFormat(any(NumberFormat.class)); - verify(mMockProgressDialog).setProgressNumberFormat(anyString()); - verify(mMockProgressDialog, times(2)).setIndeterminate(anyBoolean()); - - /* Verify that progress dialog was closed after finish install process. */ - sessionListener.getValue().onFinished(mMockSessionId, true); - - /* Verify that the handler was called again. */ - verifyStatic(HandlerUtils.class, times(2)); - HandlerUtils.runOnUiThread(runnable.capture()); - runnable.getValue().run(); - - /* Verify that installer process was triggered in the Distribute again. */ - verify(mDistribute).notifyInstallProgress(eq(false)); - } - - @Test - public void releaseInstallerProcessWhenWithContext() { - - /* Start install process. */ - mReleaseInstallerListener.startInstall(); - - /* Verify that installPackage method was called. */ - ArgumentCaptor sessionListener = ArgumentCaptor.forClass(PackageInstaller.SessionCallback.class); - verifyStatic(InstallerUtils.class); - InstallerUtils.installPackage(any(InputStream.class), any(Context.class), sessionListener.capture()); - - /* Emulate session status. */ - sessionListener.getValue().onCreated(mMockSessionId); - sessionListener.getValue().onBadgingChanged(mMockSessionId); - - /* Verify that installer process was triggered in the Distribute. */ - sessionListener.getValue().onActiveChanged(mMockSessionId, true); - verify(mDistribute).notifyInstallProgress(eq(true)); - - /* Verity that progress dialog was updated. */ - sessionListener.getValue().onProgressChanged(mMockSessionId, 1); - - /* Verify that the handler was called and catch runnable. */ - ArgumentCaptor runnable = ArgumentCaptor.forClass(Runnable.class); - verifyStatic(HandlerUtils.class); - HandlerUtils.runOnUiThread(runnable.capture()); - runnable.getValue().run(); - - /* Verify that the progress dialog was updated. */ - verify(mMockProgressDialog).setProgress(anyInt()); - - /* Verify that progress dialog was closed after finish install process. */ - sessionListener.getValue().onFinished(mMockSessionId, true); - - /* Verify that the handler was called again. */ - verifyStatic(HandlerUtils.class, times(2)); - HandlerUtils.runOnUiThread(runnable.capture()); - runnable.getValue().run(); - - /* Verify that installer process was triggered in the Distribute again. */ - verify(mDistribute).notifyInstallProgress(eq(false)); - } - - @Test - public void startInstallWhenFileIsInvalid() throws FileNotFoundException { - - /* Mock file description. */ - ParcelFileDescriptor mockFileDescriptor = mock(ParcelFileDescriptor.class); - when(mockFileDescriptor.getStatSize()).thenReturn(0L); - when(mDownloadManager.openDownloadedFile(anyLong())).thenReturn(mockFileDescriptor); - - /* Set total size. */ - mReleaseInstallerListener.setTotalSize(1L); - - /* Start install process. */ - mReleaseInstallerListener.startInstall(); - - /* Verify that the install process never starts. */ - verifyStatic(InstallerUtils.class, never()); - InstallerUtils.installPackage(any(InputStream.class), any(Context.class), any(PackageInstaller.SessionCallback.class)); - } - - @Test - public void releaseInstallerHideDialogTwice() { - - /* Start install process. */ - mReleaseInstallerListener.startInstall(); - - /* Hide dialog. */ - mReleaseInstallerListener.hideInstallProgressDialog(); - - /* Try to hide dialog again. */ - mReleaseInstallerListener.hideInstallProgressDialog(); - - /* Verify that runnable was called once only. */ - verifyStatic(HandlerUtils.class); - HandlerUtils.runOnUiThread(any(Runnable.class)); - } -} diff --git a/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/download/manager/DownloadManagerDistributeDeadlockTest.java b/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/download/manager/DownloadManagerDistributeDeadlockTest.java index 39ccb4f1c..c50aacf7e 100644 --- a/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/download/manager/DownloadManagerDistributeDeadlockTest.java +++ b/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/download/manager/DownloadManagerDistributeDeadlockTest.java @@ -14,7 +14,6 @@ import com.microsoft.appcenter.AppCenterHandler; import com.microsoft.appcenter.channel.Channel; import com.microsoft.appcenter.distribute.Distribute; -import com.microsoft.appcenter.distribute.InstallerUtils; import com.microsoft.appcenter.distribute.R; import com.microsoft.appcenter.distribute.ReleaseDetails; import com.microsoft.appcenter.distribute.download.ReleaseDownloader; @@ -70,7 +69,6 @@ AppNameHelper.class, Distribute.class, HandlerUtils.class, - InstallerUtils.class, Toast.class, Uri.class }) @@ -191,7 +189,6 @@ public void setUp() throws Exception { /* Mock Install Intent. */ when(mInstallIntent.getData()).thenReturn(mUri); when(mInstallIntent.resolveActivity(eq(mPackageManager))).thenReturn(mock(ComponentName.class)); - mockStatic(InstallerUtils.class); /* Mock app name and other string resources. */ mockStatic(AppNameHelper.class); @@ -249,7 +246,7 @@ public void onDownloadCompleteTest() throws Exception { @Override public void run() { - mReleaseDownloader.onDownloadComplete(anyLong()); + mReleaseDownloader.onDownloadComplete(); } }); } diff --git a/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/download/manager/DownloadManagerReleaseDownloaderTest.java b/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/download/manager/DownloadManagerReleaseDownloaderTest.java index e9b7db422..4b917173a 100644 --- a/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/download/manager/DownloadManagerReleaseDownloaderTest.java +++ b/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/download/manager/DownloadManagerReleaseDownloaderTest.java @@ -301,10 +301,10 @@ public void completeDownload() { when(Uri.parse(anyString())).thenReturn(mock(Uri.class)); /* Complete download. */ - mReleaseDownloader.onDownloadComplete(1L); + mReleaseDownloader.onDownloadComplete(); /* Verify. */ - verify(mListener).onComplete(anyLong(), anyLong()); + verify(mListener).onComplete(any(Uri.class)); verify(mListener, never()).onError(anyString()); } @@ -315,10 +315,10 @@ public void completeDownloadFallbackOnOldDevices() { Whitebox.setInternalState(Build.VERSION.class, "SDK_INT", Build.VERSION_CODES.M); /* Complete download. */ - mReleaseDownloader.onDownloadComplete(1L); + mReleaseDownloader.onDownloadComplete(); /* Verify. */ - verify(mListener).onComplete(anyLong(), anyLong()); + verify(mListener).onComplete(any(Uri.class)); verify(mListener, never()).onError(anyString()); } @@ -329,10 +329,10 @@ public void completeDownloadDoesNothingAfterCancellation() { /* Complete download after cancelling. */ mReleaseDownloader.cancel(); - mReleaseDownloader.onDownloadComplete(1L); + mReleaseDownloader.onDownloadComplete(); /* Verify. */ - verify(mListener, never()).onComplete(anyLong(), anyLong()); + verify(mListener, never()).onComplete(any(Uri.class)); } @Test diff --git a/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/download/manager/DownloadManagerUpdateTaskTest.java b/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/download/manager/DownloadManagerUpdateTaskTest.java index 2063d9815..65636c0a6 100644 --- a/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/download/manager/DownloadManagerUpdateTaskTest.java +++ b/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/download/manager/DownloadManagerUpdateTaskTest.java @@ -143,7 +143,7 @@ public void completeDownloading() { mUpdateTask.doInBackground(); /* Verify. */ - verify(mDownloader).onDownloadComplete(anyLong()); + verify(mDownloader).onDownloadComplete(); verify(mDownloader, never()).onDownloadError(any(RuntimeException.class)); verify(mCursor).close(); } From 4a0bba3c300c46b4afc61bda3fb6a569124d4f69 Mon Sep 17 00:00:00 2001 From: Ivan Matkov Date: Thu, 12 May 2022 19:23:52 +0200 Subject: [PATCH 38/72] Fix tests (part 2, all tests passed) --- .../distribute/ReleaseDownloadListener.java | 2 +- .../DistributeDownloadReceiverTest.java | 4 ++ .../DistributeWarnAlertSystemWindowsTest.java | 66 +++++-------------- .../distribute/InstallerUtilsTest.java | 22 ++++++- .../DownloadManagerReleaseDownloaderTest.java | 31 ++++----- 5 files changed, 56 insertions(+), 69 deletions(-) diff --git a/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/ReleaseDownloadListener.java b/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/ReleaseDownloadListener.java index 2f5d5d87b..c3947d6a2 100644 --- a/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/ReleaseDownloadListener.java +++ b/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/ReleaseDownloadListener.java @@ -104,8 +104,8 @@ public void run() { /* Check if app should install now. */ if (!Distribute.getInstance().notifyDownload(mReleaseDetails, intent)) { AppCenterLog.info(LOG_TAG, "Release is downloaded. Starting to install it."); - Distribute.getInstance().showSystemSettingsDialogOrStartInstalling(localUri); Distribute.getInstance().setInstalling(mReleaseDetails); + Distribute.getInstance().showSystemSettingsDialogOrStartInstalling(localUri); } } }); diff --git a/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/DistributeDownloadReceiverTest.java b/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/DistributeDownloadReceiverTest.java index 1e0108f28..f883d916d 100644 --- a/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/DistributeDownloadReceiverTest.java +++ b/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/DistributeDownloadReceiverTest.java @@ -11,6 +11,7 @@ import com.microsoft.appcenter.utils.storage.SharedPreferencesManager; import org.junit.Test; +import org.powermock.core.classloader.annotations.PrepareForTest; import static android.app.DownloadManager.ACTION_NOTIFICATION_CLICKED; import static com.microsoft.appcenter.distribute.DistributeConstants.PREFERENCE_KEY_UPDATE_TOKEN; @@ -21,6 +22,9 @@ import static org.mockito.Mockito.when; import static org.powermock.api.mockito.PowerMockito.whenNew; +@PrepareForTest({ + DistributeUtils.class +}) public class DistributeDownloadReceiverTest extends AbstractDistributeTest { DownloadManagerReceiver mDownloadManagerReceiver = new DownloadManagerReceiver(); diff --git a/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/DistributeWarnAlertSystemWindowsTest.java b/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/DistributeWarnAlertSystemWindowsTest.java index eb23cc17c..01ab31561 100644 --- a/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/DistributeWarnAlertSystemWindowsTest.java +++ b/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/DistributeWarnAlertSystemWindowsTest.java @@ -11,11 +11,11 @@ import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.RETURNS_MOCKS; -import static org.mockito.Mockito.doCallRealMethod; import static org.mockito.Mockito.never; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.powermock.api.mockito.PowerMockito.doAnswer; +import static org.powermock.api.mockito.PowerMockito.doCallRealMethod; import static org.powermock.api.mockito.PowerMockito.mock; import static org.powermock.api.mockito.PowerMockito.mockStatic; import static org.powermock.api.mockito.PowerMockito.verifyStatic; @@ -41,7 +41,6 @@ import org.junit.Test; import org.mockito.ArgumentCaptor; import org.mockito.Mock; -import org.mockito.Mockito; import org.mockito.invocation.InvocationOnMock; import org.mockito.stubbing.Answer; import org.powermock.core.classloader.annotations.PrepareForTest; @@ -77,6 +76,9 @@ public void setUpDialog() throws Exception { mockStatic(Settings.class); mockStatic(AppCenterLog.class); + doCallRealMethod().when(InstallerUtils.class); + InstallerUtils.isSystemAlertWindowsEnabled(any(Context.class)); + /* Reset mock release listener methods. */ doCallRealMethod().when(mReleaseDownloaderListener).onComplete(any(Uri.class)); when(mReleaseInstallerListener.showInstallProgressDialog(any(Activity.class))).thenReturn(mock(Dialog.class)); @@ -99,12 +101,12 @@ public void setUpDialog() throws Exception { Distribute.getInstance().onActivityResumed(mFirstActivity); /* Mock system alert windows dialog. */ - Mockito.when(mDialogBuilder.create()).thenReturn(mAlertWindowsDialog); + when(mDialogBuilder.create()).thenReturn(mAlertWindowsDialog); doAnswer(new Answer() { @Override public Void answer(InvocationOnMock invocation) { - Mockito.when(mAlertWindowsDialog.isShowing()).thenReturn(true); + when(mAlertWindowsDialog.isShowing()).thenReturn(true); return null; } }).when(mAlertWindowsDialog).show(); @@ -112,7 +114,7 @@ public Void answer(InvocationOnMock invocation) { @Override public Void answer(InvocationOnMock invocation) { - Mockito.when(mAlertWindowsDialog.isShowing()).thenReturn(false); + when(mAlertWindowsDialog.isShowing()).thenReturn(false); return null; } }).when(mAlertWindowsDialog).hide(); @@ -260,37 +262,6 @@ public void showAndCancelAlertWindowsDialog() { InstallerUtils.installPackage(any(Uri.class), any(Context.class), any(ReleaseInstallerListener.class)); } - @Test - public void returningToDialogFromBackgroundWhenInstallerIsNull() { - - /* Mock permission state for Android Q. */ - when(InstallerUtils.isSystemAlertWindowsEnabled(any(Context.class))).thenReturn(false); - when(Settings.canDrawOverlays(any(Context.class))).thenReturn(false); - - /* Notify about complete download. */ - mReleaseDownloaderListener.onComplete(mock(Uri.class)); - - /* Emulate behavior that settings was enabled via dialog. */ - Distribute.getInstance().onActivityPaused(mActivity); - Distribute.getInstance().onApplicationEnterBackground(); - - /* Make release details NULL to make installer listener NULL too. */ - Mockito.when(DistributeUtils.loadCachedReleaseDetails()).thenReturn(null); - - /* Start distribute with app secret NULL to make sure updateReleaseDetails is called on startFromBackground. */ - Distribute.getInstance().onStarting(mAppCenterHandler); - Distribute.getInstance().onStarted(mContext, mChannel, null, null, true); - Distribute.getInstance().startFromBackground(mContext); - - /* Enter foreground */ - Distribute.getInstance().onApplicationEnterForeground(); - Distribute.getInstance().onActivityResumed(mActivity); - - /* Verify that installation process is trying to resume but installer is null because release details was not loaded. */ - verifyStatic(AppCenterLog.class); - AppCenterLog.debug(eq(LOG_TAG), eq("Installing couldn't start due to the release installer wasn't initialized.")); - } - @Test public void showSettingsDialogWhenActivityIsNullOnAndroidQ() throws Exception { @@ -302,13 +273,13 @@ public void showSettingsDialogWhenActivityIsNullOnAndroidQ() throws Exception { Distribute.getInstance().onActivityPaused(mActivity); /* Moke download state notified. */ - Mockito.when(DistributeUtils.getStoredDownloadState()).thenReturn(DOWNLOAD_STATE_NOTIFIED); + when(DistributeUtils.getStoredDownloadState()).thenReturn(DOWNLOAD_STATE_NOTIFIED); /* Mock notification manager to avoid NRE. */ - NotificationManager manager = Mockito.mock(NotificationManager.class); + NotificationManager manager = mock(NotificationManager.class); whenNew(Notification.Builder.class).withAnyArguments() - .thenReturn(Mockito.mock(Notification.Builder.class, RETURNS_MOCKS)); - Mockito.when(mContext.getSystemService(NOTIFICATION_SERVICE)).thenReturn(manager); + .thenReturn(mock(Notification.Builder.class, RETURNS_MOCKS)); + when(mContext.getSystemService(NOTIFICATION_SERVICE)).thenReturn(manager); /* Notify about complete download. */ mReleaseDownloaderListener.onComplete(mock(Uri.class)); @@ -328,14 +299,14 @@ public void needRefreshDialogWhenStartInstallationOnAndroidQ() throws Exception when(InstallerUtils.isSystemAlertWindowsEnabled(any(Context.class))).thenReturn(false); when(Settings.canDrawOverlays(any(Context.class))).thenReturn(false); - /* Moke download state notified */ - Mockito.when(DistributeUtils.getStoredDownloadState()).thenReturn(DOWNLOAD_STATE_NOTIFIED); + /* Mock download state. */ + when(DistributeUtils.getStoredDownloadState()).thenReturn(DOWNLOAD_STATE_NOTIFIED); /* Mock notification manager to avoid NRE. */ - NotificationManager manager = Mockito.mock(NotificationManager.class); + NotificationManager manager = mock(NotificationManager.class); whenNew(Notification.Builder.class).withAnyArguments() - .thenReturn(Mockito.mock(Notification.Builder.class, RETURNS_MOCKS)); - Mockito.when(mContext.getSystemService(NOTIFICATION_SERVICE)).thenReturn(manager); + .thenReturn(mock(Notification.Builder.class, RETURNS_MOCKS)); + when(mContext.getSystemService(NOTIFICATION_SERVICE)).thenReturn(manager); /* Notify about complete download. */ mReleaseDownloaderListener.onComplete(mock(Uri.class)); @@ -346,7 +317,7 @@ public void needRefreshDialogWhenStartInstallationOnAndroidQ() throws Exception Distribute.getInstance().startFromBackground(mContext); /* Release details should be mandatory to avoid completion installation workflow. */ - Mockito.when(mReleaseDetails.isMandatoryUpdate()).thenReturn(true); + when(mReleaseDetails.isMandatoryUpdate()).thenReturn(true); /* Start installation */ mReleaseDownloaderListener.onComplete(mock(Uri.class)); @@ -358,5 +329,4 @@ public void needRefreshDialogWhenStartInstallationOnAndroidQ() throws Exception verifyStatic(AppCenterLog.class, never()); AppCenterLog.warn(eq(LOG_TAG), eq("Show new system alerts windows dialog.")); } -} - +} \ No newline at end of file diff --git a/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/InstallerUtilsTest.java b/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/InstallerUtilsTest.java index 080629979..81de0dabc 100644 --- a/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/InstallerUtilsTest.java +++ b/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/InstallerUtilsTest.java @@ -18,8 +18,10 @@ import static org.powermock.api.mockito.PowerMockito.mock; import static org.powermock.api.mockito.PowerMockito.mockStatic; import static org.powermock.api.mockito.PowerMockito.when; +import static org.powermock.api.mockito.PowerMockito.whenNew; import android.app.PendingIntent; +import android.content.ContentResolver; import android.content.Context; import android.content.Intent; import android.content.IntentSender; @@ -27,6 +29,7 @@ import android.content.pm.PackageManager; import android.net.Uri; import android.os.Build; +import android.os.ParcelFileDescriptor; import android.provider.Settings; import org.junit.Assert; @@ -39,13 +42,14 @@ import org.powermock.core.classloader.annotations.PrepareForTest; import org.powermock.modules.junit4.rule.PowerMockRule; +import java.io.FileInputStream; import java.io.IOException; -import java.io.InputStream; import java.io.OutputStream; import java.lang.reflect.Field; import java.lang.reflect.Modifier; @PrepareForTest({ + InstallerUtils.class, PendingIntent.class, Settings.class }) @@ -60,6 +64,9 @@ public class InstallerUtilsTest { @Mock private PackageInstaller mMockPackageInstaller; + @Mock + private FileInputStream mInputStream; + @Mock private OutputStream mOutputStream; @@ -67,13 +74,19 @@ public class InstallerUtilsTest { private PackageInstaller.Session mSession; @Before - public void setUp() throws IOException { + public void setUp() throws Exception { /* Mock package installer. */ PackageManager mockPackageManager = mock(PackageManager.class); when(mockPackageManager.getPackageInstaller()).thenReturn(mMockPackageInstaller); when(mContext.getPackageManager()).thenReturn(mockPackageManager); + ContentResolver contentResolver = mock(ContentResolver.class); + when(mContext.getContentResolver()).thenReturn(contentResolver); + ParcelFileDescriptor fileDescriptor = mock(ParcelFileDescriptor.class); + when(contentResolver.openFileDescriptor(any(Uri.class), eq("r"))).thenReturn(fileDescriptor); + whenNew(FileInputStream.class).withAnyArguments().thenReturn(mInputStream); + /* Mock session. */ when(mMockPackageInstaller.openSession(anyInt())).thenReturn(mSession); when(mSession.openWrite(anyString(), anyLong(), anyLong())).thenReturn(mOutputStream); @@ -91,11 +104,15 @@ public void installPackage() throws IOException { /* Mock session callback. */ PackageInstaller.SessionCallback mockSessionCallback = mock(PackageInstaller.SessionCallback.class); + /* Mock data. */ + when(mInputStream.read(any())).thenReturn(10).thenReturn(-1); + /* Call install. */ InstallerUtils.installPackage(mock(Uri.class), mContext, mockSessionCallback); /* Verify. */ verify(mMockPackageInstaller).registerSessionCallback(eq(mockSessionCallback)); + verify(mInputStream).close(); verify(mOutputStream).close(); verify(mSession).commit(any(IntentSender.class)); verify(mSession, never()).abandon(); @@ -112,6 +129,7 @@ public void throwIOExceptionWhenTryToOpenWriteSession() throws IOException { InstallerUtils.installPackage(mock(Uri.class), mContext, null); /* Verify. */ + verify(mInputStream, never()).close(); verify(mSession).abandon(); } diff --git a/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/download/manager/DownloadManagerReleaseDownloaderTest.java b/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/download/manager/DownloadManagerReleaseDownloaderTest.java index 4b917173a..d9d4ba977 100644 --- a/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/download/manager/DownloadManagerReleaseDownloaderTest.java +++ b/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/download/manager/DownloadManagerReleaseDownloaderTest.java @@ -30,6 +30,7 @@ import android.net.Uri; import android.os.Build; import android.os.Handler; +import android.os.ParcelFileDescriptor; import com.microsoft.appcenter.distribute.ReleaseDetails; import com.microsoft.appcenter.distribute.download.ReleaseDownloader; @@ -45,7 +46,8 @@ import org.mockito.stubbing.Answer; import org.powermock.core.classloader.annotations.PrepareForTest; import org.powermock.modules.junit4.PowerMockRunner; -import org.powermock.reflect.Whitebox; + +import java.io.FileNotFoundException; @PrepareForTest({ AsyncTaskUtils.class, @@ -60,6 +62,8 @@ public class DownloadManagerReleaseDownloaderTest { private static final long DOWNLOAD_ID = 42; + private static final long PACKAGE_SIZE = 42 * 1024; + @Mock private Context mContext; @@ -84,6 +88,7 @@ public class DownloadManagerReleaseDownloaderTest { public void setUp() { mockStatic(SharedPreferencesManager.class); mockStatic(HandlerUtils.class); + when(mReleaseDetails.getSize()).thenReturn(PACKAGE_SIZE); /* Mock AsyncTaskUtils. */ mockStatic(AsyncTaskUtils.class); @@ -296,23 +301,13 @@ public void doNotOnDownloadProgressAfterCancellation() { } @Test - public void completeDownload() { - mockStatic(Uri.class); - when(Uri.parse(anyString())).thenReturn(mock(Uri.class)); - - /* Complete download. */ - mReleaseDownloader.onDownloadComplete(); - - /* Verify. */ - verify(mListener).onComplete(any(Uri.class)); - verify(mListener, never()).onError(anyString()); - } - - @Test - public void completeDownloadFallbackOnOldDevices() { - mockStatic(Uri.class); - when(Uri.parse(anyString())).thenReturn(mock(Uri.class)); - Whitebox.setInternalState(Build.VERSION.class, "SDK_INT", Build.VERSION_CODES.M); + public void completeDownload() throws FileNotFoundException { + DownloadManager downloadManager = mock(DownloadManager.class); + when(mContext.getSystemService(DOWNLOAD_SERVICE)).thenReturn(downloadManager); + when(downloadManager.getUriForDownloadedFile(anyLong())).thenReturn(mock(Uri.class)); + ParcelFileDescriptor fileDescriptor = mock(ParcelFileDescriptor.class); + when(fileDescriptor.getStatSize()).thenReturn(PACKAGE_SIZE); + when(downloadManager.openDownloadedFile(anyLong())).thenReturn(fileDescriptor); /* Complete download. */ mReleaseDownloader.onDownloadComplete(); From 88483fabbafe34843aa1703be1d204cef5ba99cc Mon Sep 17 00:00:00 2001 From: Ivan Matkov Date: Thu, 12 May 2022 21:41:55 +0200 Subject: [PATCH 39/72] Fix tests (part 3, coverage) --- .../appcenter/distribute/Distribute.java | 7 +- .../DownloadManagerReleaseDownloader.java | 32 +- .../DistributeWarnAlertSystemWindowsTest.java | 34 ++- .../distribute/InstallerUtilsTest.java | 1 + .../ReleaseInstallerListenerTest.java | 280 ++++++++++++++++++ .../DownloadManagerReleaseDownloaderTest.java | 85 +++++- 6 files changed, 413 insertions(+), 26 deletions(-) create mode 100644 sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/ReleaseInstallerListenerTest.java diff --git a/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/Distribute.java b/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/Distribute.java index eec418dab..a2f3427d9 100644 --- a/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/Distribute.java +++ b/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/Distribute.java @@ -251,6 +251,9 @@ public class Distribute extends AbstractAppCenterService { */ private AppCenterPackageInstallerReceiver mAppCenterPackageInstallerReceiver; + /** + * Uri of downloaded package file. + */ private Uri mDownloadedPackageFileUri; /** @@ -1902,10 +1905,6 @@ private synchronized void installUpdate() { AppCenterLog.debug(LOG_TAG, "Installing couldn't start due to the release installer wasn't initialized."); return; } - if (mDownloadedPackageFileUri == null) { - AppCenterLog.debug(LOG_TAG, "Installing couldn't start because the package file is missing."); - return; - } post(new Runnable() { @Override diff --git a/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/download/manager/DownloadManagerReleaseDownloader.java b/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/download/manager/DownloadManagerReleaseDownloader.java index 7107aabee..9ccd4281b 100644 --- a/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/download/manager/DownloadManagerReleaseDownloader.java +++ b/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/download/manager/DownloadManagerReleaseDownloader.java @@ -188,22 +188,16 @@ synchronized void onDownloadComplete() { if (isCancelled()) { return; } - DownloadManager downloadManager = getDownloadManager(); - try (ParcelFileDescriptor fileDescriptor = downloadManager.openDownloadedFile(mDownloadId)) { - if (fileDescriptor.getStatSize() != mReleaseDetails.getSize()) { - mListener.onError("The file is invalid"); - return; - } - } catch (IOException e) { - mListener.onError("Cannot check file size: " + e.getMessage()); + if (!isDownloadedFileValid()) { + mListener.onError("Downloaded package file is invalid."); return; } AppCenterLog.debug(LOG_TAG, "Download was successful for id=" + mDownloadId); - Uri localUri = downloadManager.getUriForDownloadedFile(mDownloadId); + Uri localUri = getDownloadManager().getUriForDownloadedFile(mDownloadId); if (localUri != null) { mListener.onComplete(localUri); } else { - mListener.onError("Downloaded file not found"); + mListener.onError("Downloaded file not found."); } } @@ -215,4 +209,22 @@ synchronized void onDownloadError(RuntimeException e) { AppCenterLog.error(LOG_TAG, "Failed to download update id=" + mDownloadId, e); mListener.onError(e.getMessage()); } + + private boolean isDownloadedFileValid() { + ParcelFileDescriptor fileDescriptor = null; + try { + fileDescriptor = getDownloadManager().openDownloadedFile(mDownloadId); + return fileDescriptor.getStatSize() == mReleaseDetails.getSize(); + } catch (IOException e) { + AppCenterLog.error(LOG_TAG, "Cannot open downloaded file for id=" + mDownloadId, e); + return false; + } finally { + try { + if (fileDescriptor != null) { + fileDescriptor.close(); + } + } catch (IOException ignore) { + } + } + } } diff --git a/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/DistributeWarnAlertSystemWindowsTest.java b/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/DistributeWarnAlertSystemWindowsTest.java index 01ab31561..37250e644 100644 --- a/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/DistributeWarnAlertSystemWindowsTest.java +++ b/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/DistributeWarnAlertSystemWindowsTest.java @@ -76,9 +76,6 @@ public void setUpDialog() throws Exception { mockStatic(Settings.class); mockStatic(AppCenterLog.class); - doCallRealMethod().when(InstallerUtils.class); - InstallerUtils.isSystemAlertWindowsEnabled(any(Context.class)); - /* Reset mock release listener methods. */ doCallRealMethod().when(mReleaseDownloaderListener).onComplete(any(Uri.class)); when(mReleaseInstallerListener.showInstallProgressDialog(any(Activity.class))).thenReturn(mock(Dialog.class)); @@ -262,6 +259,37 @@ public void showAndCancelAlertWindowsDialog() { InstallerUtils.installPackage(any(Uri.class), any(Context.class), any(ReleaseInstallerListener.class)); } + @Test + public void returningToDialogFromBackgroundWhenInstallerIsNull() { + + /* Mock permission state for Android Q. */ + when(InstallerUtils.isSystemAlertWindowsEnabled(any(Context.class))).thenReturn(false); + when(Settings.canDrawOverlays(any(Context.class))).thenReturn(false); + + /* Notify about complete download. */ + mReleaseDownloaderListener.onComplete(mock(Uri.class)); + + /* Emulate behavior that settings was enabled via dialog. */ + Distribute.getInstance().onActivityPaused(mActivity); + Distribute.getInstance().onApplicationEnterBackground(); + + /* Make release details NULL to make installer listener NULL too. */ + when(DistributeUtils.loadCachedReleaseDetails()).thenReturn(null); + + /* Start distribute with app secret NULL to make sure updateReleaseDetails is called on startFromBackground. */ + Distribute.getInstance().onStarting(mAppCenterHandler); + Distribute.getInstance().onStarted(mContext, mChannel, null, null, true); + Distribute.getInstance().startFromBackground(mContext); + + /* Enter foreground */ + Distribute.getInstance().onApplicationEnterForeground(); + Distribute.getInstance().onActivityResumed(mActivity); + + /* Verify that installation process is trying to resume but installer is null because release details was not loaded. */ + verifyStatic(AppCenterLog.class); + AppCenterLog.debug(eq(LOG_TAG), eq("Installing couldn't start due to the release installer wasn't initialized.")); + } + @Test public void showSettingsDialogWhenActivityIsNullOnAndroidQ() throws Exception { diff --git a/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/InstallerUtilsTest.java b/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/InstallerUtilsTest.java index 81de0dabc..fbf688059 100644 --- a/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/InstallerUtilsTest.java +++ b/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/InstallerUtilsTest.java @@ -81,6 +81,7 @@ public void setUp() throws Exception { when(mockPackageManager.getPackageInstaller()).thenReturn(mMockPackageInstaller); when(mContext.getPackageManager()).thenReturn(mockPackageManager); + /* Mock input file. */ ContentResolver contentResolver = mock(ContentResolver.class); when(mContext.getContentResolver()).thenReturn(contentResolver); ParcelFileDescriptor fileDescriptor = mock(ParcelFileDescriptor.class); diff --git a/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/ReleaseInstallerListenerTest.java b/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/ReleaseInstallerListenerTest.java new file mode 100644 index 000000000..45bdef4ed --- /dev/null +++ b/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/ReleaseInstallerListenerTest.java @@ -0,0 +1,280 @@ +/* + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. + */ + +package com.microsoft.appcenter.distribute; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyBoolean; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.anyLong; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.ArgumentMatchers.isNull; +import static org.mockito.Mockito.doNothing; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; +import static org.powermock.api.mockito.PowerMockito.mockStatic; +import static org.powermock.api.mockito.PowerMockito.verifyStatic; +import static org.powermock.api.mockito.PowerMockito.whenNew; + +import android.app.Activity; +import android.app.DownloadManager; +import android.app.ProgressDialog; +import android.content.Context; +import android.os.ParcelFileDescriptor; +import android.widget.Toast; + +import com.microsoft.appcenter.utils.AppCenterLog; +import com.microsoft.appcenter.utils.HandlerUtils; + +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.mockito.ArgumentCaptor; +import org.mockito.Mock; +import org.powermock.core.classloader.annotations.PrepareForTest; +import org.powermock.modules.junit4.rule.PowerMockRule; + +import java.text.NumberFormat; + +@PrepareForTest({ + AppCenterLog.class, + Distribute.class, + HandlerUtils.class, + ReleaseInstallerListener.class, + Toast.class +}) +public class ReleaseInstallerListenerTest { + + private static final int SESSION_ID = 42; + + @Rule + public PowerMockRule mPowerMockRule = new PowerMockRule(); + + @Mock + private Context mContext; + + @Mock + private Distribute mDistribute; + + @Mock + private Toast mToast; + + @Mock + private DownloadManager mDownloadManager; + + @Mock + private android.app.ProgressDialog mProgressDialog; + + private ReleaseInstallerListener mReleaseInstallerListener; + + @Before + public void setUp() throws Exception { + + /* Mock static classes. */ + mockStatic(AppCenterLog.class); + mockStatic(Distribute.class); + mockStatic(HandlerUtils.class); + mockStatic(Toast.class); + + /* Mock progress dialog. */ + whenNew(android.app.ProgressDialog.class).withAnyArguments().thenReturn(mProgressDialog); + when(mProgressDialog.isIndeterminate()).thenReturn(false); + + /* Mock toast. */ + when(mContext.getString(anyInt())).thenReturn("localized_message"); + when(Toast.makeText(any(Context.class), anyString(), anyInt())).thenReturn(mToast); + + /* Mock Distribute. */ + when(Distribute.getInstance()).thenReturn(mDistribute); + doNothing().when(mDistribute).notifyInstallProgress(anyBoolean()); + + /* Mock download manager. */ + when(mDownloadManager.openDownloadedFile(anyLong())).thenReturn(mock(ParcelFileDescriptor.class)); + when(mContext.getSystemService(anyString())).thenReturn(mDownloadManager); + + /* Create installer listener. */ + mReleaseInstallerListener = new ReleaseInstallerListener(mContext); + + /* Init install progress dialog. */ + mReleaseInstallerListener.showInstallProgressDialog(mock(Activity.class)); + + /* Verify call methods. */ + verify(mProgressDialog).setProgressPercentFormat(isNull()); + verify(mProgressDialog).setProgressNumberFormat(isNull()); + verify(mProgressDialog).setIndeterminate(anyBoolean()); + } + + @Test + public void releaseInstallProcessOnFinishFailureWithContext() { + + /* Mock progress dialog. */ + when(mProgressDialog.isIndeterminate()).thenReturn(true); + + /* Emulate session status. */ + mReleaseInstallerListener.onCreated(SESSION_ID); + + /* Verify that installer process was triggered in the Distribute. */ + mReleaseInstallerListener.onActiveChanged(SESSION_ID, true); + verify(mDistribute).notifyInstallProgress(eq(true)); + + /* Verity that progress dialog was updated. */ + mReleaseInstallerListener.onProgressChanged(SESSION_ID, 1); + + /* Verify that the handler was called and catch runnable. */ + verifyStatic(HandlerUtils.class); + HandlerUtils.runOnUiThread(any(Runnable.class)); + + /* Verify that progress dialog was closed after finish install process. */ + mReleaseInstallerListener.onFinished(SESSION_ID, false); + + /* Verify that the handler was called again. */ + ArgumentCaptor runnable = ArgumentCaptor.forClass(Runnable.class); + verifyStatic(HandlerUtils.class, times(2)); + HandlerUtils.runOnUiThread(runnable.capture()); + runnable.getValue().run(); + + /* Verify that installer process was triggered in the Distribute again. */ + verify(mToast).show(); + } + + @Test + public void releaseInstallerProcessWhenProgressDialogNull() { + + /* Emulate session status. */ + mReleaseInstallerListener.onCreated(SESSION_ID); + mReleaseInstallerListener.onBadgingChanged(SESSION_ID); + + /* Verify that installer process was triggered in the Distribute. */ + mReleaseInstallerListener.onActiveChanged(SESSION_ID, true); + verify(mDistribute).notifyInstallProgress(eq(true)); + + /* Hide dialog. */ + mReleaseInstallerListener.hideInstallProgressDialog(); + + /* Verify that runnable was called. */ + ArgumentCaptor runnable = ArgumentCaptor.forClass(Runnable.class); + verifyStatic(HandlerUtils.class); + HandlerUtils.runOnUiThread(runnable.capture()); + runnable.getValue().run(); + + /* Verity that progress dialog was updated. */ + mReleaseInstallerListener.onProgressChanged(SESSION_ID, 1); + + /* Verify that the handler was called and catch runnable. */ + runnable = ArgumentCaptor.forClass(Runnable.class); + verifyStatic(HandlerUtils.class, times(2)); + HandlerUtils.runOnUiThread(runnable.capture()); + runnable.getValue().run(); + + /* Verify that the progress dialog was updated. */ + verify(mProgressDialog, never()).setProgress(anyInt()); + + /* Verify that progress dialog was closed after finish install process. */ + mReleaseInstallerListener.onFinished(SESSION_ID, true); + + /* Verify that the handler was called again. */ + verifyStatic(HandlerUtils.class, times(3)); + HandlerUtils.runOnUiThread(runnable.capture()); + runnable.getValue().run(); + + /* Verify that installer process was triggered in the Distribute again. */ + verify(mDistribute).notifyInstallProgress(eq(false)); + } + + @Test + public void releaseInstallerProcessWhenDialogIsIndeterminate() { + + /* Mock progress dialog. */ + when(mProgressDialog.isIndeterminate()).thenReturn(true); + + /* Emulate session status. */ + mReleaseInstallerListener.onCreated(SESSION_ID); + mReleaseInstallerListener.onBadgingChanged(SESSION_ID); + + /* Verify that installer process was triggered in the Distribute. */ + mReleaseInstallerListener.onActiveChanged(SESSION_ID, true); + verify(mDistribute).notifyInstallProgress(eq(true)); + + /* Verity that progress dialog was updated. */ + mReleaseInstallerListener.onProgressChanged(SESSION_ID, 1); + + /* Verify that the handler was called and catch runnable. */ + ArgumentCaptor runnable = ArgumentCaptor.forClass(Runnable.class); + verifyStatic(HandlerUtils.class); + HandlerUtils.runOnUiThread(runnable.capture()); + runnable.getValue().run(); + + /* Verify that the progress dialog was updated. */ + verify(mProgressDialog).setProgress(anyInt()); + verify(mProgressDialog).setMax(anyInt()); + verify(mProgressDialog).setProgressPercentFormat(any(NumberFormat.class)); + verify(mProgressDialog).setProgressNumberFormat(anyString()); + verify(mProgressDialog, times(2)).setIndeterminate(anyBoolean()); + + /* Verify that progress dialog was closed after finish install process. */ + mReleaseInstallerListener.onFinished(SESSION_ID, true); + + /* Verify that the handler was called again. */ + verifyStatic(HandlerUtils.class, times(2)); + HandlerUtils.runOnUiThread(runnable.capture()); + runnable.getValue().run(); + + /* Verify that installer process was triggered in the Distribute again. */ + verify(mDistribute).notifyInstallProgress(eq(false)); + } + + @Test + public void releaseInstallerProcessWhenWithContext() { + + /* Emulate session status. */ + mReleaseInstallerListener.onCreated(SESSION_ID); + mReleaseInstallerListener.onBadgingChanged(SESSION_ID); + + /* Verify that installer process was triggered in the Distribute. */ + mReleaseInstallerListener.onActiveChanged(SESSION_ID, true); + verify(mDistribute).notifyInstallProgress(eq(true)); + + /* Verity that progress dialog was updated. */ + mReleaseInstallerListener.onProgressChanged(SESSION_ID, 1); + + /* Verify that the handler was called and catch runnable. */ + ArgumentCaptor runnable = ArgumentCaptor.forClass(Runnable.class); + verifyStatic(HandlerUtils.class); + HandlerUtils.runOnUiThread(runnable.capture()); + runnable.getValue().run(); + + /* Verify that the progress dialog was updated. */ + verify(mProgressDialog).setProgress(anyInt()); + + /* Verify that progress dialog was closed after finish install process. */ + mReleaseInstallerListener.onFinished(SESSION_ID, true); + + /* Verify that the handler was called again. */ + verifyStatic(HandlerUtils.class, times(2)); + HandlerUtils.runOnUiThread(runnable.capture()); + runnable.getValue().run(); + + /* Verify that installer process was triggered in the Distribute again. */ + verify(mDistribute).notifyInstallProgress(eq(false)); + } + + @Test + public void releaseInstallerHideDialogTwice() { + + /* Hide dialog. */ + mReleaseInstallerListener.hideInstallProgressDialog(); + + /* Try to hide dialog again. */ + mReleaseInstallerListener.hideInstallProgressDialog(); + + /* Verify that runnable was called once only. */ + verifyStatic(HandlerUtils.class); + HandlerUtils.runOnUiThread(any(Runnable.class)); + } +} diff --git a/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/download/manager/DownloadManagerReleaseDownloaderTest.java b/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/download/manager/DownloadManagerReleaseDownloaderTest.java index d9d4ba977..fda09673e 100644 --- a/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/download/manager/DownloadManagerReleaseDownloaderTest.java +++ b/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/download/manager/DownloadManagerReleaseDownloaderTest.java @@ -16,6 +16,7 @@ import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.ArgumentMatchers.isA; +import static org.mockito.Mockito.doThrow; import static org.mockito.Mockito.never; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; @@ -48,6 +49,7 @@ import org.powermock.modules.junit4.PowerMockRunner; import java.io.FileNotFoundException; +import java.io.IOException; @PrepareForTest({ AsyncTaskUtils.class, @@ -82,13 +84,18 @@ public class DownloadManagerReleaseDownloaderTest { @Mock private DownloadManagerRemoveTask mRemoveTask; + @Mock + private DownloadManager mDownloadManager; + + @Mock + private ParcelFileDescriptor mFileDescriptor; + private DownloadManagerReleaseDownloader mReleaseDownloader; @Before - public void setUp() { + public void setUp() throws Exception { mockStatic(SharedPreferencesManager.class); mockStatic(HandlerUtils.class); - when(mReleaseDetails.getSize()).thenReturn(PACKAGE_SIZE); /* Mock AsyncTaskUtils. */ mockStatic(AsyncTaskUtils.class); @@ -108,6 +115,15 @@ public Object answer(InvocationOnMock invocation) { } }); + /* Mock download manager. */ + when(mContext.getSystemService(DOWNLOAD_SERVICE)).thenReturn(mDownloadManager); + when(mDownloadManager.getUriForDownloadedFile(anyLong())).thenReturn(mock(Uri.class)); + + /* Mock package size. */ + when(mFileDescriptor.getStatSize()).thenReturn(PACKAGE_SIZE); + when(mDownloadManager.openDownloadedFile(anyLong())).thenReturn(mFileDescriptor); + when(mReleaseDetails.getSize()).thenReturn(PACKAGE_SIZE); + /* Create release downloader. */ mReleaseDownloader = new DownloadManagerReleaseDownloader(mContext, mReleaseDetails, mListener); } @@ -301,18 +317,13 @@ public void doNotOnDownloadProgressAfterCancellation() { } @Test - public void completeDownload() throws FileNotFoundException { - DownloadManager downloadManager = mock(DownloadManager.class); - when(mContext.getSystemService(DOWNLOAD_SERVICE)).thenReturn(downloadManager); - when(downloadManager.getUriForDownloadedFile(anyLong())).thenReturn(mock(Uri.class)); - ParcelFileDescriptor fileDescriptor = mock(ParcelFileDescriptor.class); - when(fileDescriptor.getStatSize()).thenReturn(PACKAGE_SIZE); - when(downloadManager.openDownloadedFile(anyLong())).thenReturn(fileDescriptor); + public void completeDownload() throws IOException { /* Complete download. */ mReleaseDownloader.onDownloadComplete(); /* Verify. */ + verify(mFileDescriptor).close(); verify(mListener).onComplete(any(Uri.class)); verify(mListener, never()).onError(anyString()); } @@ -338,6 +349,62 @@ public void errorDownload() { verify(mListener).onError(anyString()); } + @Test + public void errorOnOpenDownloadedFile() throws IOException { + + /* Throw exception. */ + when(mDownloadManager.openDownloadedFile(anyLong())).thenThrow(new FileNotFoundException()); + + /* Complete download. */ + mReleaseDownloader.onDownloadComplete(); + + /* Verify. */ + verify(mListener).onError(anyString()); + } + + @Test + public void errorOnInvalidFile() throws IOException { + + /* If size is different. */ + when(mReleaseDetails.getSize()).thenReturn(142 * 1024L); + + /* Complete download. */ + mReleaseDownloader.onDownloadComplete(); + + /* Verify. */ + verify(mFileDescriptor).close(); + verify(mListener).onError(anyString()); + } + + @Test + public void errorDownloadFileNotFound() throws IOException { + + /* DownloadManager returns null. */ + when(mDownloadManager.getUriForDownloadedFile(anyLong())).thenReturn(null); + + /* Complete download. */ + mReleaseDownloader.onDownloadComplete(); + + /* Verify. */ + verify(mFileDescriptor).close(); + verify(mListener).onError(anyString()); + } + + @Test + public void exceptionOnClosingFileDescriptor() throws IOException { + + /* Throw exception in invalid size callback. */ + doThrow(new IOException()).when(mFileDescriptor).close(); + + /* Complete download. */ + mReleaseDownloader.onDownloadComplete(); + + /* Verify. */ + verify(mFileDescriptor).close(); + verify(mListener).onComplete(any(Uri.class)); + verify(mListener, never()).onError(anyString()); + } + @Test public void errorDownloadDoesNothingAfterCancellation() { mReleaseDownloader.cancel(); From 09463de45224fd96593af709b3ca0bf7e3b5e51f Mon Sep 17 00:00:00 2001 From: Ivan Matkov Date: Mon, 16 May 2022 11:51:08 +0200 Subject: [PATCH 40/72] Update changelog --- CHANGELOG.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e8a9c6b69..dd3309c75 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,7 +5,8 @@ ### App Center Distribute * **[Fix]** Fix possible crash on resume event before initialization. -* **[Fix]** Fix clicks on the download notification. +* **[Fix]** Fix clicks on the download completion notification. +* **[Fix]** Fix ANR on installing large packages. ___ From a6a9fafaeb1ff313be64ec7ca6144c1c584d9153 Mon Sep 17 00:00:00 2001 From: Ivan Matkov Date: Tue, 17 May 2022 14:07:23 +0200 Subject: [PATCH 41/72] Fix threading issue in onActiveChanged --- .../distribute/ReleaseInstallerListener.java | 14 ++- .../ReleaseInstallerListenerTest.java | 93 ++++++++----------- 2 files changed, 54 insertions(+), 53 deletions(-) diff --git a/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/ReleaseInstallerListener.java b/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/ReleaseInstallerListener.java index 59bffeba0..0318a7c56 100644 --- a/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/ReleaseInstallerListener.java +++ b/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/ReleaseInstallerListener.java @@ -17,6 +17,7 @@ import android.widget.Toast; import androidx.annotation.UiThread; +import androidx.annotation.WorkerThread; import com.microsoft.appcenter.utils.AppCenterLog; import com.microsoft.appcenter.utils.HandlerUtils; @@ -55,20 +56,30 @@ public ReleaseInstallerListener(Context context) { mContext = context; } + @WorkerThread @Override public void onCreated(int sessionId) { AppCenterLog.debug(LOG_TAG, "The install session was created."); } + @WorkerThread @Override public void onBadgingChanged(int sessionId) { } + @WorkerThread @Override public void onActiveChanged(int sessionId, boolean active) { - Distribute.getInstance().notifyInstallProgress(true); + HandlerUtils.runOnUiThread(new Runnable() { + + @Override + public void run() { + Distribute.getInstance().notifyInstallProgress(true); + } + }); } + @WorkerThread @Override public void onProgressChanged(int sessionId, float progress) { final int downloadProgress = (int)(progress * 100); @@ -82,6 +93,7 @@ public void run() { }); } + @WorkerThread @Override public void onFinished(int sessionId, final boolean success) { AppCenterLog.debug(LOG_TAG, String.format(Locale.ENGLISH,"The installation of the new version is completed with the result: %s.", success ? "successful" : "failure")); diff --git a/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/ReleaseInstallerListenerTest.java b/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/ReleaseInstallerListenerTest.java index 45bdef4ed..a15794012 100644 --- a/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/ReleaseInstallerListenerTest.java +++ b/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/ReleaseInstallerListenerTest.java @@ -18,6 +18,7 @@ import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; +import static org.powermock.api.mockito.PowerMockito.doAnswer; import static org.powermock.api.mockito.PowerMockito.mockStatic; import static org.powermock.api.mockito.PowerMockito.verifyStatic; import static org.powermock.api.mockito.PowerMockito.whenNew; @@ -37,6 +38,8 @@ import org.junit.Test; import org.mockito.ArgumentCaptor; import org.mockito.Mock; +import org.mockito.invocation.InvocationOnMock; +import org.mockito.stubbing.Answer; import org.powermock.core.classloader.annotations.PrepareForTest; import org.powermock.modules.junit4.rule.PowerMockRule; @@ -79,14 +82,25 @@ public void setUp() throws Exception { /* Mock static classes. */ mockStatic(AppCenterLog.class); mockStatic(Distribute.class); - mockStatic(HandlerUtils.class); - mockStatic(Toast.class); /* Mock progress dialog. */ whenNew(android.app.ProgressDialog.class).withAnyArguments().thenReturn(mProgressDialog); when(mProgressDialog.isIndeterminate()).thenReturn(false); + /* Mock HandlerUtils. */ + mockStatic(HandlerUtils.class); + doAnswer(new Answer() { + + @Override + public Void answer(InvocationOnMock invocation) { + invocation.getArgument(0).run(); + return null; + } + }).when(HandlerUtils.class); + HandlerUtils.runOnUiThread(any(Runnable.class)); + /* Mock toast. */ + mockStatic(Toast.class); when(mContext.getString(anyInt())).thenReturn("localized_message"); when(Toast.makeText(any(Context.class), anyString(), anyInt())).thenReturn(mToast); @@ -121,23 +135,19 @@ public void releaseInstallProcessOnFinishFailureWithContext() { /* Verify that installer process was triggered in the Distribute. */ mReleaseInstallerListener.onActiveChanged(SESSION_ID, true); + verifyStatic(HandlerUtils.class); + HandlerUtils.runOnUiThread(any(Runnable.class)); verify(mDistribute).notifyInstallProgress(eq(true)); /* Verity that progress dialog was updated. */ mReleaseInstallerListener.onProgressChanged(SESSION_ID, 1); - - /* Verify that the handler was called and catch runnable. */ - verifyStatic(HandlerUtils.class); + verifyStatic(HandlerUtils.class, times(2)); HandlerUtils.runOnUiThread(any(Runnable.class)); /* Verify that progress dialog was closed after finish install process. */ mReleaseInstallerListener.onFinished(SESSION_ID, false); - - /* Verify that the handler was called again. */ - ArgumentCaptor runnable = ArgumentCaptor.forClass(Runnable.class); - verifyStatic(HandlerUtils.class, times(2)); - HandlerUtils.runOnUiThread(runnable.capture()); - runnable.getValue().run(); + verifyStatic(HandlerUtils.class, times(3)); + HandlerUtils.runOnUiThread(any(Runnable.class)); /* Verify that installer process was triggered in the Distribute again. */ verify(mToast).show(); @@ -152,38 +162,27 @@ public void releaseInstallerProcessWhenProgressDialogNull() { /* Verify that installer process was triggered in the Distribute. */ mReleaseInstallerListener.onActiveChanged(SESSION_ID, true); + verifyStatic(HandlerUtils.class); + HandlerUtils.runOnUiThread(any(Runnable.class)); verify(mDistribute).notifyInstallProgress(eq(true)); /* Hide dialog. */ mReleaseInstallerListener.hideInstallProgressDialog(); - - /* Verify that runnable was called. */ - ArgumentCaptor runnable = ArgumentCaptor.forClass(Runnable.class); - verifyStatic(HandlerUtils.class); - HandlerUtils.runOnUiThread(runnable.capture()); - runnable.getValue().run(); + verifyStatic(HandlerUtils.class, times(2)); + HandlerUtils.runOnUiThread(any(Runnable.class)); /* Verity that progress dialog was updated. */ mReleaseInstallerListener.onProgressChanged(SESSION_ID, 1); - - /* Verify that the handler was called and catch runnable. */ - runnable = ArgumentCaptor.forClass(Runnable.class); - verifyStatic(HandlerUtils.class, times(2)); - HandlerUtils.runOnUiThread(runnable.capture()); - runnable.getValue().run(); + verifyStatic(HandlerUtils.class, times(3)); + HandlerUtils.runOnUiThread(any(Runnable.class)); /* Verify that the progress dialog was updated. */ verify(mProgressDialog, never()).setProgress(anyInt()); /* Verify that progress dialog was closed after finish install process. */ mReleaseInstallerListener.onFinished(SESSION_ID, true); - - /* Verify that the handler was called again. */ - verifyStatic(HandlerUtils.class, times(3)); - HandlerUtils.runOnUiThread(runnable.capture()); - runnable.getValue().run(); - - /* Verify that installer process was triggered in the Distribute again. */ + verifyStatic(HandlerUtils.class, times(4)); + HandlerUtils.runOnUiThread(any(Runnable.class)); verify(mDistribute).notifyInstallProgress(eq(false)); } @@ -199,16 +198,14 @@ public void releaseInstallerProcessWhenDialogIsIndeterminate() { /* Verify that installer process was triggered in the Distribute. */ mReleaseInstallerListener.onActiveChanged(SESSION_ID, true); + verifyStatic(HandlerUtils.class); + HandlerUtils.runOnUiThread(any(Runnable.class)); verify(mDistribute).notifyInstallProgress(eq(true)); /* Verity that progress dialog was updated. */ mReleaseInstallerListener.onProgressChanged(SESSION_ID, 1); - - /* Verify that the handler was called and catch runnable. */ - ArgumentCaptor runnable = ArgumentCaptor.forClass(Runnable.class); - verifyStatic(HandlerUtils.class); - HandlerUtils.runOnUiThread(runnable.capture()); - runnable.getValue().run(); + verifyStatic(HandlerUtils.class, times(2)); + HandlerUtils.runOnUiThread(any(Runnable.class)); /* Verify that the progress dialog was updated. */ verify(mProgressDialog).setProgress(anyInt()); @@ -219,11 +216,8 @@ public void releaseInstallerProcessWhenDialogIsIndeterminate() { /* Verify that progress dialog was closed after finish install process. */ mReleaseInstallerListener.onFinished(SESSION_ID, true); - - /* Verify that the handler was called again. */ - verifyStatic(HandlerUtils.class, times(2)); - HandlerUtils.runOnUiThread(runnable.capture()); - runnable.getValue().run(); + verifyStatic(HandlerUtils.class, times(3)); + HandlerUtils.runOnUiThread(any(Runnable.class)); /* Verify that installer process was triggered in the Distribute again. */ verify(mDistribute).notifyInstallProgress(eq(false)); @@ -238,27 +232,22 @@ public void releaseInstallerProcessWhenWithContext() { /* Verify that installer process was triggered in the Distribute. */ mReleaseInstallerListener.onActiveChanged(SESSION_ID, true); + verifyStatic(HandlerUtils.class); + HandlerUtils.runOnUiThread(any(Runnable.class)); verify(mDistribute).notifyInstallProgress(eq(true)); /* Verity that progress dialog was updated. */ mReleaseInstallerListener.onProgressChanged(SESSION_ID, 1); - - /* Verify that the handler was called and catch runnable. */ - ArgumentCaptor runnable = ArgumentCaptor.forClass(Runnable.class); - verifyStatic(HandlerUtils.class); - HandlerUtils.runOnUiThread(runnable.capture()); - runnable.getValue().run(); + verifyStatic(HandlerUtils.class, times(2)); + HandlerUtils.runOnUiThread(any(Runnable.class)); /* Verify that the progress dialog was updated. */ verify(mProgressDialog).setProgress(anyInt()); /* Verify that progress dialog was closed after finish install process. */ mReleaseInstallerListener.onFinished(SESSION_ID, true); - - /* Verify that the handler was called again. */ - verifyStatic(HandlerUtils.class, times(2)); - HandlerUtils.runOnUiThread(runnable.capture()); - runnable.getValue().run(); + verifyStatic(HandlerUtils.class, times(3)); + HandlerUtils.runOnUiThread(any(Runnable.class)); /* Verify that installer process was triggered in the Distribute again. */ verify(mDistribute).notifyInstallProgress(eq(false)); From eded9ff3b31103a072560f25a9cb0fb3d4361bac Mon Sep 17 00:00:00 2001 From: "microsoft-github-policy-service[bot]" <77245923+microsoft-github-policy-service[bot]@users.noreply.github.com> Date: Tue, 17 May 2022 12:52:58 +0000 Subject: [PATCH 42/72] Microsoft mandatory file --- SECURITY.md | 41 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) create mode 100644 SECURITY.md diff --git a/SECURITY.md b/SECURITY.md new file mode 100644 index 000000000..766e6f887 --- /dev/null +++ b/SECURITY.md @@ -0,0 +1,41 @@ + + +## Security + +Microsoft takes the security of our software products and services seriously, which includes all source code repositories managed through our GitHub organizations, which include [Microsoft](https://github.com/Microsoft), [Azure](https://github.com/Azure), [DotNet](https://github.com/dotnet), [AspNet](https://github.com/aspnet), [Xamarin](https://github.com/xamarin), and [our GitHub organizations](https://opensource.microsoft.com/). + +If you believe you have found a security vulnerability in any Microsoft-owned repository that meets [Microsoft's definition of a security vulnerability](https://docs.microsoft.com/previous-versions/tn-archive/cc751383(v=technet.10)), please report it to us as described below. + +## Reporting Security Issues + +**Please do not report security vulnerabilities through public GitHub issues.** + +Instead, please report them to the Microsoft Security Response Center (MSRC) at [https://msrc.microsoft.com/create-report](https://msrc.microsoft.com/create-report). + +If you prefer to submit without logging in, send email to [secure@microsoft.com](mailto:secure@microsoft.com). If possible, encrypt your message with our PGP key; please download it from the [Microsoft Security Response Center PGP Key page](https://www.microsoft.com/msrc/pgp-key-msrc). + +You should receive a response within 24 hours. If for some reason you do not, please follow up via email to ensure we received your original message. Additional information can be found at [microsoft.com/msrc](https://www.microsoft.com/msrc). + +Please include the requested information listed below (as much as you can provide) to help us better understand the nature and scope of the possible issue: + + * Type of issue (e.g. buffer overflow, SQL injection, cross-site scripting, etc.) + * Full paths of source file(s) related to the manifestation of the issue + * The location of the affected source code (tag/branch/commit or direct URL) + * Any special configuration required to reproduce the issue + * Step-by-step instructions to reproduce the issue + * Proof-of-concept or exploit code (if possible) + * Impact of the issue, including how an attacker might exploit the issue + +This information will help us triage your report more quickly. + +If you are reporting for a bug bounty, more complete reports can contribute to a higher bounty award. Please visit our [Microsoft Bug Bounty Program](https://microsoft.com/msrc/bounty) page for more details about our active programs. + +## Preferred Languages + +We prefer all communications to be in English. + +## Policy + +Microsoft follows the principle of [Coordinated Vulnerability Disclosure](https://www.microsoft.com/msrc/cvd). + + From 9079ff989322a98f9b08807dcfada3462c458fdc Mon Sep 17 00:00:00 2001 From: Ivan Matkov Date: Wed, 13 Apr 2022 10:16:13 +0200 Subject: [PATCH 43/72] Fix resource leak and strict mode violation --- .../DownloadManagerCancelPendingTask.java | 51 +++++++++++++++ .../DownloadManagerReleaseDownloader.java | 62 ++++++++++++++----- .../manager/DownloadManagerRequestTask.java | 40 +++--------- 3 files changed, 106 insertions(+), 47 deletions(-) create mode 100644 sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/download/manager/DownloadManagerCancelPendingTask.java diff --git a/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/download/manager/DownloadManagerCancelPendingTask.java b/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/download/manager/DownloadManagerCancelPendingTask.java new file mode 100644 index 000000000..f2edf957d --- /dev/null +++ b/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/download/manager/DownloadManagerCancelPendingTask.java @@ -0,0 +1,51 @@ +/* + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. + */ + +package com.microsoft.appcenter.distribute.download.manager; + +import android.app.DownloadManager; +import android.database.Cursor; +import android.os.AsyncTask; + +/** + * Cancel download if it's still in pending state. + */ +class DownloadManagerCancelPendingTask extends AsyncTask { + + private final DownloadManagerReleaseDownloader mDownloader; + + /** + * Download identifier to check. + */ + private final long mDownloadId; + + DownloadManagerCancelPendingTask(DownloadManagerReleaseDownloader downloader, long downloadId) { + mDownloader = downloader; + mDownloadId = downloadId; + } + + @Override + protected Void doInBackground(Void... params) { + if (isPending()) { + mDownloader.clearDownloadId(mDownloadId); + mDownloader.onDownloadError(new IllegalStateException("Failed to start downloading file due to timeout exception.")); + } + return null; + } + + /** + * Checks if download is in pending state. + * + * @return true download is in pending state, false otherwise. + */ + private boolean isPending() { + DownloadManager.Query query = new DownloadManager.Query() + .setFilterById(mDownloadId) + .setFilterByStatus(DownloadManager.STATUS_PENDING); + try (Cursor cursor = mDownloader.getDownloadManager().query(query)) { + return cursor != null && cursor.moveToFirst(); + } + } +} diff --git a/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/download/manager/DownloadManagerReleaseDownloader.java b/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/download/manager/DownloadManagerReleaseDownloader.java index 9ccd4281b..2b8fbd7a1 100644 --- a/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/download/manager/DownloadManagerReleaseDownloader.java +++ b/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/download/manager/DownloadManagerReleaseDownloader.java @@ -5,14 +5,19 @@ package com.microsoft.appcenter.distribute.download.manager; +import static android.content.Context.DOWNLOAD_SERVICE; +import static com.microsoft.appcenter.distribute.DistributeConstants.HANDLER_TOKEN_CHECK_PROGRESS; +import static com.microsoft.appcenter.distribute.DistributeConstants.INVALID_DOWNLOAD_IDENTIFIER; +import static com.microsoft.appcenter.distribute.DistributeConstants.LOG_TAG; +import static com.microsoft.appcenter.distribute.DistributeConstants.PREFERENCE_KEY_DOWNLOAD_ID; +import static com.microsoft.appcenter.distribute.DistributeConstants.UPDATE_PROGRESS_TIME_THRESHOLD; + import android.app.DownloadManager; import android.content.Context; import android.database.Cursor; import android.net.Uri; -import android.os.Build; import android.os.ParcelFileDescriptor; import android.os.SystemClock; -import android.widget.Toast; import androidx.annotation.AnyThread; import androidx.annotation.NonNull; @@ -26,22 +31,18 @@ import com.microsoft.appcenter.utils.HandlerUtils; import com.microsoft.appcenter.utils.storage.SharedPreferencesManager; -import static android.content.Context.DOWNLOAD_SERVICE; -import static com.microsoft.appcenter.distribute.DistributeConstants.HANDLER_TOKEN_CHECK_PROGRESS; -import static com.microsoft.appcenter.distribute.DistributeConstants.INVALID_DOWNLOAD_IDENTIFIER; -import static com.microsoft.appcenter.distribute.DistributeConstants.LOG_TAG; -import static com.microsoft.appcenter.distribute.DistributeConstants.PREFERENCE_KEY_DOWNLOAD_ID; -import static com.microsoft.appcenter.distribute.DistributeConstants.UPDATE_PROGRESS_TIME_THRESHOLD; - -import java.io.FileNotFoundException; import java.io.IOException; public class DownloadManagerReleaseDownloader extends AbstractReleaseDownloader { - public DownloadManagerReleaseDownloader(@NonNull Context context, @NonNull ReleaseDetails releaseDetails, @NonNull Listener listener) { - super(context, releaseDetails, listener); - } + /** + * Timeout to start download the package, in milliseconds. + */ + private final static int PENDING_TIMEOUT = 10 * 1000; + /** + * Current download identifier. + */ private long mDownloadId = INVALID_DOWNLOAD_IDENTIFIER; /** @@ -54,6 +55,10 @@ public DownloadManagerReleaseDownloader(@NonNull Context context, @NonNull Relea */ private DownloadManagerRequestTask mRequestTask; + public DownloadManagerReleaseDownloader(@NonNull Context context, @NonNull ReleaseDetails releaseDetails, @NonNull Listener listener) { + super(context, releaseDetails, listener); + } + DownloadManager getDownloadManager() { return (DownloadManager) mContext.getSystemService(DOWNLOAD_SERVICE); } @@ -113,6 +118,16 @@ public synchronized void cancel() { } } + /** + * Clears download id if it's active. + */ + synchronized void clearDownloadId(long downloadId) { + if (downloadId == getDownloadId()) { + remove(downloadId); + setDownloadId(INVALID_DOWNLOAD_IDENTIFIER); + } + } + /** * Start new download. */ @@ -142,13 +157,23 @@ private void remove(long downloadId) { AsyncTaskUtils.execute(LOG_TAG, new DownloadManagerRemoveTask(mContext, downloadId)); } + /** + * Cancels download if it's still in pending state. + */ + private void cancelPendingDownload(long downloadId) { + if (isCancelled()) { + return; + } + AsyncTaskUtils.execute(LOG_TAG, new DownloadManagerCancelPendingTask(this, downloadId)); + } + @WorkerThread synchronized void onStart() { request(); } @WorkerThread - synchronized void onDownloadStarted(long downloadId, long enqueueTime) { + synchronized void onDownloadStarted(final long downloadId, long enqueueTime) { if (isCancelled()) { return; } @@ -161,6 +186,15 @@ synchronized void onDownloadStarted(long downloadId, long enqueueTime) { if (mReleaseDetails.isMandatoryUpdate()) { update(); } + + /* Handle pending timeout. */ + HandlerUtils.getMainHandler().postDelayed(new Runnable() { + + @Override + public void run() { + cancelPendingDownload(downloadId); + } + }, PENDING_TIMEOUT); } @WorkerThread diff --git a/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/download/manager/DownloadManagerRequestTask.java b/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/download/manager/DownloadManagerRequestTask.java index fc690fd10..5408178a1 100644 --- a/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/download/manager/DownloadManagerRequestTask.java +++ b/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/download/manager/DownloadManagerRequestTask.java @@ -5,37 +5,32 @@ package com.microsoft.appcenter.distribute.download.manager; +import static com.microsoft.appcenter.distribute.DistributeConstants.LOG_TAG; + import android.app.DownloadManager; -import android.database.Cursor; import android.net.Uri; import android.os.AsyncTask; -import android.os.Handler; -import android.os.Looper; import androidx.annotation.VisibleForTesting; import com.microsoft.appcenter.distribute.ReleaseDetails; import com.microsoft.appcenter.utils.AppCenterLog; -import static com.microsoft.appcenter.distribute.DistributeConstants.LOG_TAG; - -import java.util.concurrent.TimeoutException; - /** * The download manager API triggers strict mode exception in UI thread. */ class DownloadManagerRequestTask extends AsyncTask { - - private final int TIMEOUT_LIMIT = 10000; + private final DownloadManagerReleaseDownloader mDownloader; + + /** + * The title of this download, to be displayed in notifications. + */ private final String mTitle; - private Handler mHandler; - private Runnable handlerCallback; DownloadManagerRequestTask(DownloadManagerReleaseDownloader downloader, String title) { mDownloader = downloader; mTitle = title; - mHandler = new Handler(Looper.getMainLooper()); } @Override @@ -59,24 +54,6 @@ protected Void doInBackground(Void... params) { final long downloadId = downloadManager.enqueue(request); if (!isCancelled()) { mDownloader.onDownloadStarted(downloadId, enqueueTime); - handlerCallback = new Runnable() { - - @Override - public void run() { - DownloadManager.Query query = new DownloadManager.Query(); - query.setFilterByStatus(DownloadManager.STATUS_PENDING); - // FIXME: android.os.strictmode.LeakedClosableViolation: A resource was acquired at attached stack trace but never released - // FIXME: StrictMode policy violation; ~duration=6 ms: android.os.strictmode.DiskReadViolation - Cursor c = downloadManager.query(query); - if (c.moveToFirst()) { - downloadManager.remove(downloadId); - mDownloader.onDownloadError(new IllegalStateException("Failed to start downloading file due to timeout exception.")); - } - } - }; - - /* Check that the file started to download. */ - mHandler.postDelayed(handlerCallback, TIMEOUT_LIMIT); } } catch (IllegalArgumentException e) { @@ -91,9 +68,6 @@ public void run() { @VisibleForTesting DownloadManager.Request createRequest(Uri Uri) { - if (handlerCallback != null) { - mHandler.removeCallbacks(handlerCallback); - } return new DownloadManager.Request(Uri); } } From 0361e096e4848f2c18e784ca34341651e79116d7 Mon Sep 17 00:00:00 2001 From: dorofeevs Date: Wed, 18 May 2022 20:29:57 +0400 Subject: [PATCH 44/72] Add tests for DownloadManagerCancelPendingTask --- .../DownloadManagerCancelPendingTaskTest.java | 109 ++++++++++++++++++ .../DownloadManagerReleaseDownloaderTest.java | 82 ++++++++++++- .../DownloadManagerRequestTaskTest.java | 68 ----------- 3 files changed, 188 insertions(+), 71 deletions(-) create mode 100644 sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/download/manager/DownloadManagerCancelPendingTaskTest.java diff --git a/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/download/manager/DownloadManagerCancelPendingTaskTest.java b/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/download/manager/DownloadManagerCancelPendingTaskTest.java new file mode 100644 index 000000000..f1c6499c8 --- /dev/null +++ b/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/download/manager/DownloadManagerCancelPendingTaskTest.java @@ -0,0 +1,109 @@ +/* + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. + */ + +package com.microsoft.appcenter.distribute.download.manager; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.anyLong; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoMoreInteractions; +import static org.mockito.Mockito.when; +import static org.powermock.api.mockito.PowerMockito.whenNew; + +import android.app.DownloadManager; +import android.database.Cursor; + +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.mockito.Mock; +import org.powermock.core.classloader.annotations.PrepareForTest; +import org.powermock.modules.junit4.rule.PowerMockRule; + +@PrepareForTest({ + DownloadManagerCancelPendingTask.class +}) +public class DownloadManagerCancelPendingTaskTest { + + private static final long DOWNLOAD_ID = 42; + + @Rule + public PowerMockRule mRule = new PowerMockRule(); + + @Mock + private DownloadManagerReleaseDownloader mDownloader; + + @Mock + private DownloadManager mDownloadManager; + + @Mock + private Cursor mCursor; + + private DownloadManagerCancelPendingTask mCancelPendingTask; + + @Before + public void setUp() throws Exception { + + /* Mock DownloadManager. */ + when(mDownloader.getDownloadManager()).thenReturn(mDownloadManager); + + /* Mock query. */ + DownloadManager.Query query = mock(DownloadManager.Query.class); + whenNew(DownloadManager.Query.class).withAnyArguments().thenReturn(query); + when(query.setFilterById(anyLong())).thenReturn(query); + when(query.setFilterByStatus(anyInt())).thenReturn(query); + + /* Create DownloadManagerCancelPendingTask. */ + mCancelPendingTask = new DownloadManagerCancelPendingTask(mDownloader, DOWNLOAD_ID); + } + + @Test + public void ifTaskIsPending() { + when(mDownloadManager.query(any(DownloadManager.Query.class))).thenReturn(mCursor); + when(mCursor.moveToFirst()).thenReturn(true); + + /* Perform background task. */ + mCancelPendingTask.doInBackground(); + + /* Verify. */ + verify(mCursor).moveToFirst(); + verify(mCursor).close(); + verifyNoMoreInteractions(mCursor); + verify(mDownloader).clearDownloadId(DOWNLOAD_ID); + verify(mDownloader).onDownloadError(any(RuntimeException.class)); + } + + @Test + public void ifTaskIsNotPending() { + when(mDownloadManager.query(any(DownloadManager.Query.class))).thenReturn(mCursor); + when(mCursor.moveToFirst()).thenReturn(false); + + /* Perform background task. */ + mCancelPendingTask.doInBackground(); + + /* Verify. */ + verify(mCursor).moveToFirst(); + verify(mCursor).close(); + verifyNoMoreInteractions(mCursor); + verify(mDownloader, never()).clearDownloadId(DOWNLOAD_ID); + verify(mDownloader, never()).onDownloadError(any(RuntimeException.class)); + } + + @Test + public void ifCursorIsNull() { + when(mDownloadManager.query(any(DownloadManager.Query.class))).thenReturn(null); + + /* Perform background task. */ + mCancelPendingTask.doInBackground(); + + /* Verify. */ + verifyNoMoreInteractions(mCursor); + verify(mDownloader, never()).clearDownloadId(DOWNLOAD_ID); + verify(mDownloader, never()).onDownloadError(any(RuntimeException.class)); + } +} diff --git a/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/download/manager/DownloadManagerReleaseDownloaderTest.java b/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/download/manager/DownloadManagerReleaseDownloaderTest.java index fda09673e..46e2c6035 100644 --- a/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/download/manager/DownloadManagerReleaseDownloaderTest.java +++ b/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/download/manager/DownloadManagerReleaseDownloaderTest.java @@ -42,6 +42,7 @@ import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; +import org.mockito.ArgumentCaptor; import org.mockito.Mock; import org.mockito.invocation.InvocationOnMock; import org.mockito.stubbing.Answer; @@ -84,6 +85,9 @@ public class DownloadManagerReleaseDownloaderTest { @Mock private DownloadManagerRemoveTask mRemoveTask; + @Mock + private Handler mMainHandler; + @Mock private DownloadManager mDownloadManager; @@ -104,9 +108,8 @@ public void setUp() throws Exception { when(AsyncTaskUtils.execute(anyString(), isA(DownloadManagerRemoveTask.class))).thenReturn(mRemoveTask); /* Run handler immediately. */ - Handler handler = mock(Handler.class); - when(HandlerUtils.getMainHandler()).thenReturn(handler); - when(handler.postAtTime(any(Runnable.class), anyString(), anyLong())).thenAnswer(new Answer() { + when(HandlerUtils.getMainHandler()).thenReturn(mMainHandler); + when(mMainHandler.postAtTime(any(Runnable.class), anyString(), anyLong())).thenAnswer(new Answer() { @Override public Object answer(InvocationOnMock invocation) { @@ -191,6 +194,79 @@ public void cancelClearsEverything() { SharedPreferencesManager.remove(eq(PREFERENCE_KEY_DOWNLOAD_ID)); } + @Test + public void clearDownloadId() { + when(SharedPreferencesManager.getLong(eq(PREFERENCE_KEY_DOWNLOAD_ID), eq(INVALID_DOWNLOAD_IDENTIFIER))) + .thenReturn(DOWNLOAD_ID); + assertEquals(DOWNLOAD_ID, mReleaseDownloader.getDownloadId()); + + /* Clear with valid id. */ + mReleaseDownloader.clearDownloadId(DOWNLOAD_ID); + + /* Verify. */ + verifyStatic(AsyncTaskUtils.class); + AsyncTaskUtils.execute(anyString(), isA(DownloadManagerRemoveTask.class)); + verifyStatic(SharedPreferencesManager.class); + SharedPreferencesManager.remove(eq(PREFERENCE_KEY_DOWNLOAD_ID)); + } + + @Test + public void clearInvalidDownloadId() { + when(SharedPreferencesManager.getLong(eq(PREFERENCE_KEY_DOWNLOAD_ID), eq(INVALID_DOWNLOAD_IDENTIFIER))) + .thenReturn(DOWNLOAD_ID); + assertEquals(DOWNLOAD_ID, mReleaseDownloader.getDownloadId()); + + /* Clear with invalid id. */ + mReleaseDownloader.clearDownloadId(43); + + /* Verify. */ + verifyStatic(AsyncTaskUtils.class, never()); + AsyncTaskUtils.execute(anyString(), isA(DownloadManagerRemoveTask.class)); + verifyStatic(SharedPreferencesManager.class, never()); + SharedPreferencesManager.remove(eq(PREFERENCE_KEY_DOWNLOAD_ID)); + } + + @Test + public void cancelPendingDownloadIfWasCancelled() { + when(SharedPreferencesManager.getLong(eq(PREFERENCE_KEY_DOWNLOAD_ID), eq(INVALID_DOWNLOAD_IDENTIFIER))) + .thenReturn(DOWNLOAD_ID); + + /* Start download. */ + mReleaseDownloader.onDownloadStarted(DOWNLOAD_ID, 0); + + /* Capture timeout callback. */ + ArgumentCaptor timeoutCallback = ArgumentCaptor.forClass(Runnable.class); + verify(mMainHandler).postDelayed(timeoutCallback.capture(), anyLong()); + + /* Cancel before timeout. */ + mReleaseDownloader.cancel(); + timeoutCallback.getValue().run(); + + /* Verify. */ + verifyStatic(AsyncTaskUtils.class, never()); + AsyncTaskUtils.execute(anyString(), isA(DownloadManagerCancelPendingTask.class)); + } + + @Test + public void cancelPendingDownloadIfWasNotCancelled() { + when(SharedPreferencesManager.getLong(eq(PREFERENCE_KEY_DOWNLOAD_ID), eq(INVALID_DOWNLOAD_IDENTIFIER))) + .thenReturn(DOWNLOAD_ID); + + /* Start download. */ + mReleaseDownloader.onDownloadStarted(DOWNLOAD_ID, 0); + + /* Capture timeout callback. */ + ArgumentCaptor timeoutCallback = ArgumentCaptor.forClass(Runnable.class); + verify(mMainHandler).postDelayed(timeoutCallback.capture(), anyLong()); + + /* Call timeout when download is in progress. */ + timeoutCallback.getValue().run(); + + /* Verify. */ + verifyStatic(AsyncTaskUtils.class); + AsyncTaskUtils.execute(anyString(), isA(DownloadManagerCancelPendingTask.class)); + } + @Test public void doNotTryToRemoveInvalidDownloadId() { when(SharedPreferencesManager.getLong(eq(PREFERENCE_KEY_DOWNLOAD_ID), eq(INVALID_DOWNLOAD_IDENTIFIER))) diff --git a/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/download/manager/DownloadManagerRequestTaskTest.java b/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/download/manager/DownloadManagerRequestTaskTest.java index 1532e11a7..ca233e5dd 100644 --- a/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/download/manager/DownloadManagerRequestTaskTest.java +++ b/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/download/manager/DownloadManagerRequestTaskTest.java @@ -6,7 +6,6 @@ package com.microsoft.appcenter.distribute.download.manager; import android.app.DownloadManager; -import android.database.Cursor; import android.net.Uri; import android.os.AsyncTask; import android.os.Handler; @@ -18,9 +17,7 @@ import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; -import org.mockito.invocation.InvocationOnMock; import org.mockito.junit.MockitoJUnitRunner; -import org.mockito.stubbing.Answer; import org.powermock.core.classloader.annotations.PrepareForTest; import org.powermock.modules.junit4.rule.PowerMockRule; @@ -63,9 +60,6 @@ public class DownloadManagerRequestTaskTest { @Mock private Handler mHandler; - @Mock - private Cursor mCursor; - private DownloadManagerRequestTask mRequestTask; @Before @@ -145,66 +139,4 @@ public void enqueueTaskIllegalStateExceptionHandled() { verify(mDownloader).onDownloadError(any(IllegalStateException.class)); verify(mDownloader, never()).onDownloadStarted(anyLong(), anyLong()); } - - @Test - public void noExceptionsWhenDownloadFinishedAfterTimeout() { - when(mDownloadManager.query(any(DownloadManager.Query.class))).thenReturn(mCursor); - when(mCursor.moveToFirst()).thenReturn(false); - when(mRequestTask.isCancelled()).thenReturn(false); - - /* Run Callback immediately. */ - when(mHandler.postDelayed(any(Runnable.class), anyLong())).thenAnswer(new Answer() { - - @Override - public Object answer(InvocationOnMock invocation) { - invocation.getArgument(0).run(); - return true; - } - }); - - /* Perform background task. */ - mRequestTask.doInBackground(); - - /* Verify. */ - verify(mDownloader, never()).onDownloadError(any(IllegalStateException.class)); - } - - - @Test - public void throwExceptionWhenDownloadStillNotFinishedAfterTimeout() { - when(mDownloadManager.query(any(DownloadManager.Query.class))).thenReturn(mCursor); - when(mCursor.moveToFirst()).thenReturn(true); - when(mRequestTask.isCancelled()).thenReturn(false); - - /* Run Callback immediately. */ - when(mHandler.postDelayed(any(Runnable.class), anyLong())).thenAnswer(new Answer() { - - @Override - public Object answer(InvocationOnMock invocation) { - invocation.getArgument(0).run(); - return true; - } - }); - - /* Perform background task. */ - mRequestTask.doInBackground(); - - /* Verify that exception was thrown. */ - verify(mDownloader).onDownloadError(any(IllegalStateException.class)); - } - - @Test - public void oldCallbacksRemovedBeforeCreatingNewDownload() { - when(mRequestTask.isCancelled()).thenReturn(false); - when(mRequestTask.createRequest(any(Uri.class))).thenCallRealMethod(); - - /* Perform background task. Emulates creating callback, which would not be used */ - mRequestTask.doInBackground(); - - /* Perform background task. Must delete old callbacks */ - mRequestTask.doInBackground(); - - /* Verify that callback was removed. */ - verify(mHandler).removeCallbacks(any(Runnable.class)); - } } From 264dd524b5153483c33b4900d700a4ada2cc0fe8 Mon Sep 17 00:00:00 2001 From: Ivan Matkov Date: Fri, 20 May 2022 13:53:29 +0200 Subject: [PATCH 45/72] Update changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index dd3309c75..272426ae9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,7 @@ * **[Fix]** Fix possible crash on resume event before initialization. * **[Fix]** Fix clicks on the download completion notification. * **[Fix]** Fix ANR on installing large packages. +* **[Fix]** Fix strict mode issues. ___ From 9e6c054b220d6b02a9de8af0edd68bdc7ab894c2 Mon Sep 17 00:00:00 2001 From: Ivan Matkov Date: Thu, 12 May 2022 12:13:05 +0200 Subject: [PATCH 46/72] Refactor AppCenterPackageInstallerReceiver --- .../src/main/AndroidManifest.xml | 2 +- .../AppCenterPackageInstallerReceiver.java | 118 ++++++++++++------ .../appcenter/distribute/Distribute.java | 8 +- .../appcenter/distribute/InstallerUtils.java | 27 +--- 4 files changed, 88 insertions(+), 67 deletions(-) diff --git a/sdk/appcenter-distribute/src/main/AndroidManifest.xml b/sdk/appcenter-distribute/src/main/AndroidManifest.xml index cab3fe952..505741e66 100644 --- a/sdk/appcenter-distribute/src/main/AndroidManifest.xml +++ b/sdk/appcenter-distribute/src/main/AndroidManifest.xml @@ -46,7 +46,7 @@ android:exported="false"> - + diff --git a/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/AppCenterPackageInstallerReceiver.java b/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/AppCenterPackageInstallerReceiver.java index 004a0b793..e7fd65732 100644 --- a/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/AppCenterPackageInstallerReceiver.java +++ b/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/AppCenterPackageInstallerReceiver.java @@ -7,13 +7,15 @@ import static com.microsoft.appcenter.distribute.DistributeConstants.LOG_TAG; +import android.app.PendingIntent; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; +import android.content.IntentSender; import android.content.pm.PackageInstaller; +import android.os.Build; import android.os.Bundle; -import android.widget.Toast; import androidx.annotation.VisibleForTesting; @@ -27,57 +29,93 @@ public class AppCenterPackageInstallerReceiver extends BroadcastReceiver { @VisibleForTesting - static final String START_ACTION = "com.microsoft.appcenter.action.START"; + static final String INSTALL_STATUS_ACTION = "com.microsoft.appcenter.action.INSTALL_STATUS"; @VisibleForTesting static final String MY_PACKAGE_REPLACED_ACTION = "android.intent.action.MY_PACKAGE_REPLACED"; - public IntentFilter getInstallerReceiverFilter() { + static IntentFilter getInstallerReceiverFilter() { IntentFilter installerReceiverFilter = new IntentFilter(); - installerReceiverFilter.addAction(START_ACTION); + installerReceiverFilter.addAction(INSTALL_STATUS_ACTION); installerReceiverFilter.addAction(MY_PACKAGE_REPLACED_ACTION); return installerReceiverFilter; } + /** + * Return IntentSender with the receiver that listens to the package installer session status. + * + * @param context any context. + * @param sessionId install sessionId. + * @return IntentSender with receiver. + */ + static IntentSender getInstallStatusIntentSender(Context context, int sessionId) { + int broadcastFlags = 0; + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { + broadcastFlags = PendingIntent.FLAG_MUTABLE; + } + PendingIntent pendingIntent = PendingIntent.getBroadcast( + context, + sessionId, + new Intent(INSTALL_STATUS_ACTION), + broadcastFlags); + return pendingIntent.getIntentSender(); + } + @Override public void onReceive(Context context, Intent intent) { - if (MY_PACKAGE_REPLACED_ACTION.equals(intent.getAction())) { - AppCenterLog.debug(LOG_TAG, "Restart application after installing a new release."); - Intent launchIntent = context.getPackageManager().getLaunchIntentForPackage(context.getPackageName()); - launchIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP); - context.startActivity(launchIntent); - } else if (START_ACTION.equals(intent.getAction())) { - Bundle extras = intent.getExtras(); - int status = extras.getInt(PackageInstaller.EXTRA_STATUS); - String message = extras.getString(PackageInstaller.EXTRA_STATUS_MESSAGE); - switch (status) { - case PackageInstaller.STATUS_PENDING_USER_ACTION: - AppCenterLog.debug(LOG_TAG, "Ask confirmation to install a new release."); - Intent confirmIntent = (Intent) extras.get(Intent.EXTRA_INTENT); - confirmIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); - context.startActivity(confirmIntent); - break; - case PackageInstaller.STATUS_SUCCESS: - AppCenterLog.debug(LOG_TAG, "Application was successfully updated."); - break; - case PackageInstaller.STATUS_FAILURE: - case PackageInstaller.STATUS_FAILURE_ABORTED: - case PackageInstaller.STATUS_FAILURE_BLOCKED: - case PackageInstaller.STATUS_FAILURE_CONFLICT: - case PackageInstaller.STATUS_FAILURE_INCOMPATIBLE: - case PackageInstaller.STATUS_FAILURE_INVALID: - case PackageInstaller.STATUS_FAILURE_STORAGE: - AppCenterLog.debug(LOG_TAG, String.format(Locale.ENGLISH, "Failed to install a new release with status: %s. Error message: %s.", status, message)); - // FIXME: StrictMode policy violation: android.os.strictmode.IncorrectContextUseViolation - Toast.makeText(context, context.getString(R.string.appcenter_distribute_something_went_wrong_during_installing_new_release), Toast.LENGTH_SHORT).show(); - break; - default: - AppCenterLog.debug(LOG_TAG, String.format(Locale.ENGLISH, "Unrecognized status received from installer: %s", status)); - // FIXME: StrictMode policy violation: android.os.strictmode.IncorrectContextUseViolation - Toast.makeText(context, context.getString(R.string.appcenter_distribute_something_went_wrong_during_installing_new_release), Toast.LENGTH_SHORT).show(); - } + String action = intent.getAction(); + if (MY_PACKAGE_REPLACED_ACTION.equals(action)) { + onPackageReplaced(context); + } else if (INSTALL_STATUS_ACTION.equals(action)) { + onInstallStatus(context, intent); } else { - AppCenterLog.debug(LOG_TAG, String.format(Locale.ENGLISH, "Unrecognized action %s - do nothing.", intent.getAction())); + AppCenterLog.debug(LOG_TAG, String.format(Locale.ENGLISH, "Unrecognized action %s - do nothing.", action)); + } + } + + private void onPackageReplaced(Context context) { + AppCenterLog.debug(LOG_TAG, "Restart application after installing a new release."); + Distribute.getInstance().resumeApp(context); + } + + private void onInstallStatus(Context context, Intent intent) { + Bundle extras = intent.getExtras(); + int status = extras.getInt(PackageInstaller.EXTRA_STATUS); + switch (status) { + case PackageInstaller.STATUS_PENDING_USER_ACTION: + Intent confirmIntent = (Intent) extras.get(Intent.EXTRA_INTENT); + onInstallStatusPendingUserAction(context, confirmIntent); + break; + case PackageInstaller.STATUS_SUCCESS: + onInstallStatusSuccess(); + break; + case PackageInstaller.STATUS_FAILURE: + case PackageInstaller.STATUS_FAILURE_BLOCKED: + case PackageInstaller.STATUS_FAILURE_ABORTED: + case PackageInstaller.STATUS_FAILURE_INVALID: + case PackageInstaller.STATUS_FAILURE_CONFLICT: + case PackageInstaller.STATUS_FAILURE_STORAGE: + case PackageInstaller.STATUS_FAILURE_INCOMPATIBLE: + String message = extras.getString(PackageInstaller.EXTRA_STATUS_MESSAGE); + onInstallStatusFailure(status, message); + break; + default: + AppCenterLog.debug(LOG_TAG, String.format(Locale.ENGLISH, "Unrecognized status received from installer: %s", status)); } } + + private void onInstallStatusPendingUserAction(Context context, Intent confirmIntent) { + AppCenterLog.debug(LOG_TAG, "Ask confirmation to install a new release."); + confirmIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + context.startActivity(confirmIntent); + } + + private void onInstallStatusSuccess() { + AppCenterLog.debug(LOG_TAG, "Application was successfully updated."); + } + + private void onInstallStatusFailure(int status, String message) { + AppCenterLog.debug(LOG_TAG, String.format(Locale.ENGLISH, "Failed to install a new release with status: %s. Error message: %s.", status, message)); + Distribute.getInstance().showInstallingErrorToast(); + } } diff --git a/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/Distribute.java b/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/Distribute.java index a2f3427d9..0e7b9f327 100644 --- a/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/Distribute.java +++ b/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/Distribute.java @@ -599,7 +599,7 @@ protected synchronized void applyEnabledState(boolean enabled) { private void registerReceiver() { mAppCenterPackageInstallerReceiver = new AppCenterPackageInstallerReceiver(); mContext.registerReceiver(mAppCenterPackageInstallerReceiver, - mAppCenterPackageInstallerReceiver.getInstallerReceiverFilter()); + AppCenterPackageInstallerReceiver.getInstallerReceiverFilter()); AppCenterLog.debug(LOG_TAG, "The receiver for installing a new release was registered."); } @@ -1854,6 +1854,12 @@ private void showDisabledToast() { Toast.makeText(mContext, R.string.appcenter_distribute_dialog_actioned_on_disabled_toast, Toast.LENGTH_SHORT).show(); } + void showInstallingErrorToast() { + // Use Activity context if possible to avoid StrictMode policy violation: android.os.strictmode.IncorrectContextUseViolation + Context context = mForegroundActivity != null ? mForegroundActivity : mContext; + Toast.makeText(context, R.string.appcenter_distribute_something_went_wrong_during_installing_new_release, Toast.LENGTH_SHORT).show(); + } + /** * Bring app to foreground if in background. * diff --git a/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/InstallerUtils.java b/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/InstallerUtils.java index 357d827df..9aaddcbf0 100644 --- a/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/InstallerUtils.java +++ b/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/InstallerUtils.java @@ -5,18 +5,15 @@ package com.microsoft.appcenter.distribute; -import static android.app.PendingIntent.FLAG_MUTABLE; import static com.microsoft.appcenter.distribute.DistributeConstants.LOG_TAG; -import android.app.PendingIntent; import android.content.Context; -import android.content.Intent; -import android.content.IntentSender; import android.content.pm.PackageInstaller; import android.net.Uri; import android.os.Build; import android.os.ParcelFileDescriptor; import android.provider.Settings; + import androidx.annotation.NonNull; import androidx.annotation.VisibleForTesting; import androidx.annotation.WorkerThread; @@ -170,7 +167,7 @@ public static void installPackage(@NonNull Uri localUri, Context context, Packag } /* Start to install a new release. */ - session.commit(createIntentSender(context, sessionId)); + session.commit(AppCenterPackageInstallerReceiver.getInstallStatusIntentSender(context, sessionId)); session.close(); } catch (IOException e) { if (session != null) { @@ -180,26 +177,6 @@ public static void installPackage(@NonNull Uri localUri, Context context, Packag } } - /** - * Return IntentSender with the receiver that listens to the package installer session status. - * - * @param context any context. - * @param sessionId install sessionId. - * @return IntentSender with receiver. - */ - public static IntentSender createIntentSender(Context context, int sessionId) { - int broadcastFlags = 0; - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { - broadcastFlags |= FLAG_MUTABLE; - } - PendingIntent pendingIntent = PendingIntent.getBroadcast( - context, - sessionId, - new Intent(AppCenterPackageInstallerReceiver.START_ACTION), - broadcastFlags); - return pendingIntent.getIntentSender(); - } - @WorkerThread private static void addFileToInstallSession(ParcelFileDescriptor fileDescriptor, PackageInstaller.Session session) throws IOException { From 9d09bdd24f06d8983f109bfd4f874c4a39089baf Mon Sep 17 00:00:00 2001 From: Ivan Matkov Date: Tue, 17 May 2022 15:10:23 +0200 Subject: [PATCH 47/72] Remove alert window permission --- .../src/main/AndroidManifest.xml | 1 - .../appcenter/distribute/Distribute.java | 102 +----------------- .../appcenter/distribute/InstallerUtils.java | 19 ---- .../distribute/ReleaseDownloadListener.java | 2 +- .../main/res/values/appcenter_distribute.xml | 1 - 5 files changed, 3 insertions(+), 122 deletions(-) diff --git a/sdk/appcenter-distribute/src/main/AndroidManifest.xml b/sdk/appcenter-distribute/src/main/AndroidManifest.xml index 505741e66..a9f2b4b16 100644 --- a/sdk/appcenter-distribute/src/main/AndroidManifest.xml +++ b/sdk/appcenter-distribute/src/main/AndroidManifest.xml @@ -6,7 +6,6 @@ - diff --git a/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/Distribute.java b/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/Distribute.java index 0e7b9f327..3259becf4 100644 --- a/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/Distribute.java +++ b/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/Distribute.java @@ -210,11 +210,6 @@ public class Distribute extends AbstractAppCenterService { */ private Dialog mUnknownSourcesDialog; - /** - * Alert system dialog that was shown. - */ - private Dialog mAlertSystemWindowsDialog; - /** * Mandatory download completed in app notification. */ @@ -251,11 +246,6 @@ public class Distribute extends AbstractAppCenterService { */ private AppCenterPackageInstallerReceiver mAppCenterPackageInstallerReceiver; - /** - * Uri of downloaded package file. - */ - private Uri mDownloadedPackageFileUri; - /** * Remember if we checked download since our own process restarted. */ @@ -759,7 +749,6 @@ private synchronized void cancelPreviousTasks() { mCheckReleaseCallId = null; mUpdateDialog = null; mUnknownSourcesDialog = null; - mAlertSystemWindowsDialog = null; mCompletedDownloadDialog = null; mUpdateSetupFailedDialog = null; mLastActivityWithDialog.clear(); @@ -797,14 +786,6 @@ private synchronized void resumeDistributeWorkflow() { return; } - /* Continue installing a new release if the dialog was shown before resumeDistributeWorkflow. */ - if (mAlertSystemWindowsDialog != null) { - mAlertSystemWindowsDialog.dismiss(); - mAlertSystemWindowsDialog = null; - installUpdate(); - return; - } - /* Do nothing during installing a new release. */ if (mInstallInProgress) { AppCenterLog.info(LOG_TAG, "Installing in progress..."); @@ -1056,7 +1037,6 @@ synchronized void completeWorkflow() { mUpdateDialog = null; mUpdateSetupFailedDialog = null; mUnknownSourcesDialog = null; - mAlertSystemWindowsDialog = null; mLastActivityWithDialog.clear(); mReleaseDetails = null; if (mReleaseDownloaderListener != null) { @@ -1601,65 +1581,6 @@ private synchronized void handleUpdateFailedDialogReinstallAction(DialogInterfac } } - - /** - * Show system alerts windows setting dialog. - */ - @RequiresApi(api = Build.VERSION_CODES.Q) - private synchronized void showSystemAlertsWindowsSettingsDialog() { - - /* Do not attempt to show dialog if application is in the background. */ - if (mForegroundActivity == null) { - AppCenterLog.warn(LOG_TAG, "The application is in background mode, the system alerts windows won't be displayed."); - return; - } - - /* Check if we need to replace dialog. */ - if (!shouldRefreshDialog(mAlertSystemWindowsDialog)) { - return; - } - AppCenterLog.debug(LOG_TAG, "Show new system alerts windows dialog."); - - /* Build confirmation dialog on enabled system alerts windows permission. */ - AlertDialog.Builder dialogBuilder = new AlertDialog.Builder(mForegroundActivity); - dialogBuilder.setMessage(R.string.appcenter_distribute_alert_system_dialog_message); - dialogBuilder.setNegativeButton(android.R.string.cancel, new DialogInterface.OnClickListener() { - - @Override - public void onClick(DialogInterface dialog, int which) { - mAlertSystemWindowsDialog = null; - AppCenterLog.debug(LOG_TAG, "Permission request on alert system windows denied. Continue installing..."); - - /* It is optional and installing can be continued if customer reject permission request. */ - installUpdate(); - } - }); - dialogBuilder.setOnCancelListener(new DialogInterface.OnCancelListener() { - - @Override - public void onCancel(DialogInterface dialog) { - mAlertSystemWindowsDialog = null; - AppCenterLog.debug(LOG_TAG, "Permission request on alert system windows denied. Continue installing..."); - - /* It is optional and installing can be continued if customer reject permission request. */ - installUpdate(); - } - }); - dialogBuilder.setPositiveButton(R.string.appcenter_distribute_unknown_sources_dialog_settings, new DialogInterface.OnClickListener() { - - @Override - public void onClick(DialogInterface dialog, int which) { - - /* Open system alerts windows settings activity. */ - Intent intent = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION, - Uri.parse("package:" + mForegroundActivity.getPackageName())); - mForegroundActivity.startActivity(intent); - } - }); - mAlertSystemWindowsDialog = dialogBuilder.create(); - showAndRememberDialogActivity(mAlertSystemWindowsDialog); - } - /** * Show unknown sources dialog. This can be called multiple times if clicking on HOME and app resumed * (it could be resumed in another activity covering the previous one). @@ -1906,7 +1827,7 @@ synchronized void notifyInstallProgress(boolean isInProgress) { * Start to install a new update. */ @UiThread - private synchronized void installUpdate() { + synchronized void installUpdate(@NonNull Uri localUri) { if (mReleaseInstallerListener == null) { AppCenterLog.debug(LOG_TAG, "Installing couldn't start due to the release installer wasn't initialized."); return; @@ -1916,30 +1837,11 @@ private synchronized void installUpdate() { @Override public void run() { AppCenterLog.debug(AppCenterLog.LOG_TAG, "Start installing new release..."); - InstallerUtils.installPackage(mDownloadedPackageFileUri, mContext, mReleaseInstallerListener); + InstallerUtils.installPackage(localUri, mContext, mReleaseInstallerListener); } }); } - /** - * Ask permission on start application after update or start to install a new update. - */ - @UiThread - synchronized void showSystemSettingsDialogOrStartInstalling(@NonNull Uri localUri) { - if (mReleaseInstallerListener == null) { - AppCenterLog.debug(LOG_TAG, "Installing couldn't start due to the release installer wasn't initialized."); - return; - } - mDownloadedPackageFileUri = localUri; - - /* Check permission on start application after update. */ - if (InstallerUtils.isSystemAlertWindowsEnabled(mContext)) { - installUpdate(); - } else { - showSystemAlertsWindowsSettingsDialog(); - } - } - /** * Post notification about a completed download if we are in background when download completes. * If this method is called on app process restart or if application is in foreground diff --git a/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/InstallerUtils.java b/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/InstallerUtils.java index 9aaddcbf0..105710964 100644 --- a/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/InstallerUtils.java +++ b/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/InstallerUtils.java @@ -125,25 +125,6 @@ public static boolean isUnknownSourcesEnabled(@NonNull Context context) { } } - /** - * Check whether user enabled start activity from the background. - * - * @param context any context. - * @return true if start activity from the background is enabled, false otherwise. - */ - public static boolean isSystemAlertWindowsEnabled(@NonNull Context context) { - - /* - * From Android 10 (29 API level) or higher we have to - * use this permission for restarting activity after update. - * See more about restrictions on starting activities from the background: - * - https://developer.android.com/guide/components/activities/background-starts - * - https://developer.android.com/about/versions/10/behavior-changes-all#sysalert-go - */ - return Build.VERSION.SDK_INT < Build.VERSION_CODES.Q || - Settings.canDrawOverlays(context); - } - /** * Install a new release. */ diff --git a/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/ReleaseDownloadListener.java b/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/ReleaseDownloadListener.java index c3947d6a2..9cb272772 100644 --- a/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/ReleaseDownloadListener.java +++ b/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/ReleaseDownloadListener.java @@ -104,8 +104,8 @@ public void run() { /* Check if app should install now. */ if (!Distribute.getInstance().notifyDownload(mReleaseDetails, intent)) { AppCenterLog.info(LOG_TAG, "Release is downloaded. Starting to install it."); + Distribute.getInstance().installUpdate(localUri); Distribute.getInstance().setInstalling(mReleaseDetails); - Distribute.getInstance().showSystemSettingsDialogOrStartInstalling(localUri); } } }); diff --git a/sdk/appcenter-distribute/src/main/res/values/appcenter_distribute.xml b/sdk/appcenter-distribute/src/main/res/values/appcenter_distribute.xml index b2a6784fd..ab411a2da 100644 --- a/sdk/appcenter-distribute/src/main/res/values/appcenter_distribute.xml +++ b/sdk/appcenter-distribute/src/main/res/values/appcenter_distribute.xml @@ -17,7 +17,6 @@ Downloading update - For security, your device is set to block start application after update. Downloading version %1$s (%2$d) Failed to download app update %1$d MB of %2$d MB From b741d3fc6bca9ddea55d116a47481bca008226be Mon Sep 17 00:00:00 2001 From: Ivan Matkov Date: Tue, 17 May 2022 16:34:09 +0200 Subject: [PATCH 48/72] Post notification instead try to restart --- .../AppCenterPackageInstallerReceiver.java | 22 ++++++++++-- .../appcenter/distribute/Distribute.java | 11 +++--- .../appcenter/distribute/DistributeUtils.java | 8 ++--- .../main/res/values/appcenter_distribute.xml | 2 ++ .../appcenter/utils/DeviceInfoHelper.java | 34 ++++++++++++------- 5 files changed, 51 insertions(+), 26 deletions(-) diff --git a/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/AppCenterPackageInstallerReceiver.java b/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/AppCenterPackageInstallerReceiver.java index e7fd65732..b597dd993 100644 --- a/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/AppCenterPackageInstallerReceiver.java +++ b/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/AppCenterPackageInstallerReceiver.java @@ -13,6 +13,7 @@ import android.content.Intent; import android.content.IntentFilter; import android.content.IntentSender; +import android.content.pm.PackageInfo; import android.content.pm.PackageInstaller; import android.os.Build; import android.os.Bundle; @@ -20,6 +21,8 @@ import androidx.annotation.VisibleForTesting; import com.microsoft.appcenter.utils.AppCenterLog; +import com.microsoft.appcenter.utils.AppNameHelper; +import com.microsoft.appcenter.utils.DeviceInfoHelper; import java.util.Locale; @@ -74,8 +77,23 @@ public void onReceive(Context context, Intent intent) { } private void onPackageReplaced(Context context) { - AppCenterLog.debug(LOG_TAG, "Restart application after installing a new release."); - Distribute.getInstance().resumeApp(context); + AppCenterLog.debug(LOG_TAG, "Post a notification as the installation finished in background."); + String title = context.getString(R.string.appcenter_distribute_install_completed_title); + Intent intent = DistributeUtils.getResumeAppIntent(context); + DistributeUtils.postNotification(context, title, getInstallCompletedMessage(context), intent); + } + + private static String getInstallCompletedMessage(Context context) { + String versionName = ""; + int versionCode = 0; + PackageInfo packageInfo = DeviceInfoHelper.getPackageInfo(context); + if (packageInfo != null) { + versionName = packageInfo.versionName; + versionCode = DeviceInfoHelper.getVersionCode(packageInfo); + } + String format = context.getString(R.string.appcenter_distribute_install_completed_message); + String appName = AppNameHelper.getAppName(context); + return String.format(format, appName, versionName, versionCode); } private void onInstallStatus(Context context, Intent intent) { diff --git a/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/Distribute.java b/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/Distribute.java index 3259becf4..0c2b7bcd3 100644 --- a/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/Distribute.java +++ b/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/Distribute.java @@ -476,11 +476,7 @@ public Map getLogFactories() { public synchronized void onStarted(@NonNull Context context, @NonNull Channel channel, String appSecret, String transmissionTargetToken, boolean startedFromApp) { mContext = context; mAppSecret = appSecret; - try { - mPackageInfo = mContext.getPackageManager().getPackageInfo(mContext.getPackageName(), 0); - } catch (PackageManager.NameNotFoundException e) { - AppCenterLog.error(LOG_TAG, "Could not get self package info.", e); - } + mPackageInfo = DeviceInfoHelper.getPackageInfo(context); /* * Apply enabled state is called by this method, we need fields to be initialized before. @@ -1021,7 +1017,7 @@ synchronized void completeWorkflow(ReleaseDetails releaseDetails) { private synchronized void cancelNotification() { if (DistributeUtils.getStoredDownloadState() == DOWNLOAD_STATE_NOTIFIED) { AppCenterLog.debug(LOG_TAG, "Cancel download notification."); - DistributeUtils.cancelDownloadNotification(mContext); + DistributeUtils.cancelNotification(mContext); } } @@ -1872,7 +1868,8 @@ synchronized boolean notifyDownload(ReleaseDetails releaseDetails, Intent intent /* Post notification. */ AppCenterLog.debug(LOG_TAG, "Post a notification as the download finished in background."); - DistributeUtils.postDownloadNotification(mContext, getInstallReadyMessage(), intent); + String title = mContext.getString(R.string.appcenter_distribute_install_ready_title); + DistributeUtils.postNotification(mContext, title, getInstallReadyMessage(), intent); SharedPreferencesManager.putInt(PREFERENCE_KEY_DOWNLOAD_STATE, DOWNLOAD_STATE_NOTIFIED); /* Reset check download flag to show install U.I. on resume if notification ignored. */ diff --git a/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/DistributeUtils.java b/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/DistributeUtils.java index 764629178..e5a1b6040 100644 --- a/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/DistributeUtils.java +++ b/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/DistributeUtils.java @@ -70,7 +70,7 @@ static int getNotificationId() { return Distribute.class.getName().hashCode(); } - static void postDownloadNotification(@NonNull Context context, String message, Intent intent) { + static void postNotification(@NonNull Context context, String title, String message, Intent intent) { NotificationManager notificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE); Notification.Builder builder; if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { @@ -89,8 +89,8 @@ static void postDownloadNotification(@NonNull Context context, String message, I pendingIntentFlag = PendingIntent.FLAG_IMMUTABLE; } PendingIntent pendingIntent = PendingIntent.getActivity(context, 0, intent, pendingIntentFlag); - builder.setTicker(context.getString(R.string.appcenter_distribute_install_ready_title)) - .setContentTitle(context.getString(R.string.appcenter_distribute_install_ready_title)) + builder.setTicker(title) + .setContentTitle(title) .setContentText(message) .setSmallIcon(context.getApplicationInfo().icon) .setStyle(new Notification.BigTextStyle().bigText(message)) @@ -100,7 +100,7 @@ static void postDownloadNotification(@NonNull Context context, String message, I notificationManager.notify(DistributeUtils.getNotificationId(), notification); } - static void cancelDownloadNotification(@NonNull Context context) { + static void cancelNotification(@NonNull Context context) { NotificationManager notificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE); notificationManager.cancel(DistributeUtils.getNotificationId()); } diff --git a/sdk/appcenter-distribute/src/main/res/values/appcenter_distribute.xml b/sdk/appcenter-distribute/src/main/res/values/appcenter_distribute.xml index ab411a2da..f91398679 100644 --- a/sdk/appcenter-distribute/src/main/res/values/appcenter_distribute.xml +++ b/sdk/appcenter-distribute/src/main/res/values/appcenter_distribute.xml @@ -25,6 +25,8 @@ Installing... App update downloaded %1$s %2$s (%3$d) has been downloaded and is ready to be installed. + App update installed + %1$s %2$s (%3$d) has been installed. Install App updates In-app updates disabled diff --git a/sdk/appcenter/src/main/java/com/microsoft/appcenter/utils/DeviceInfoHelper.java b/sdk/appcenter/src/main/java/com/microsoft/appcenter/utils/DeviceInfoHelper.java index d58c57ff3..c3772fabe 100644 --- a/sdk/appcenter/src/main/java/com/microsoft/appcenter/utils/DeviceInfoHelper.java +++ b/sdk/appcenter/src/main/java/com/microsoft/appcenter/utils/DeviceInfoHelper.java @@ -5,6 +5,8 @@ package com.microsoft.appcenter.utils; +import static com.microsoft.appcenter.AppCenter.LOG_TAG; + import android.annotation.SuppressLint; import android.content.Context; import android.content.pm.PackageInfo; @@ -45,6 +47,16 @@ public class DeviceInfoHelper { */ private static String mCountryCode; + public static PackageInfo getPackageInfo(Context context) { + try { + PackageManager packageManager = context.getPackageManager(); + return packageManager.getPackageInfo(context.getPackageName(), 0); + } catch (Exception e) { + AppCenterLog.error(LOG_TAG, "Cannot retrieve package info", e); + return null; + } + } + /** * Gets device information. * @@ -56,16 +68,12 @@ public static synchronized Device getDeviceInfo(Context context) throws DeviceIn Device device = new Device(); /* Application version. */ - PackageInfo packageInfo; - try { - PackageManager packageManager = context.getPackageManager(); - packageInfo = packageManager.getPackageInfo(context.getPackageName(), 0); - device.setAppVersion(packageInfo.versionName); - device.setAppBuild(String.valueOf(getVersionCode(packageInfo))); - } catch (Exception e) { - AppCenterLog.error(AppCenter.LOG_TAG, "Cannot retrieve package info", e); - throw new DeviceInfoException("Cannot retrieve package info", e); + PackageInfo packageInfo = getPackageInfo(context); + if (packageInfo == null) { + throw new DeviceInfoException("Cannot retrieve package info"); } + device.setAppVersion(packageInfo.versionName); + device.setAppBuild(String.valueOf(getVersionCode(packageInfo))); /* Application namespace. */ device.setAppNamespace(context.getPackageName()); @@ -82,7 +90,7 @@ public static synchronized Device getDeviceInfo(Context context) throws DeviceIn device.setCarrierName(networkOperatorName); } } catch (Exception e) { - AppCenterLog.error(AppCenter.LOG_TAG, "Cannot retrieve carrier info", e); + AppCenterLog.error(LOG_TAG, "Cannot retrieve carrier info", e); } /* Set country code. */ @@ -107,7 +115,7 @@ public static synchronized Device getDeviceInfo(Context context) throws DeviceIn try { device.setScreenSize(getScreenSize(context)); } catch (Exception e) { - AppCenterLog.error(AppCenter.LOG_TAG, "Cannot retrieve screen size", e); + AppCenterLog.error(LOG_TAG, "Cannot retrieve screen size", e); } /* Set SDK name and version. Don't add the BuildConfig import or it will trigger a Javadoc warning... */ @@ -209,8 +217,8 @@ public static void setCountryCode(String countryCode) { public static class DeviceInfoException extends Exception { @SuppressWarnings("SameParameterValue") - public DeviceInfoException(String detailMessage, Throwable throwable) { - super(detailMessage, throwable); + public DeviceInfoException(String detailMessage) { + super(detailMessage); } } } \ No newline at end of file From 7e9819c13e4fd8ce8bc3dd293fe541b81bf9870c Mon Sep 17 00:00:00 2001 From: Ivan Matkov Date: Tue, 17 May 2022 21:25:39 +0200 Subject: [PATCH 49/72] Update tests --- .../WrapperSdkExceptionManagerTest.java | 3 +- .../crashes/utils/ErrorLogHelperTest.java | 10 +- .../AppCenterPackageInstallerReceiver.java | 8 +- .../distribute/AbstractDistributeTest.java | 27 +- ...AppCenterPackageInstallerReceiverTest.java | 151 +++----- .../DistributeBeforeApiSuccessTest.java | 55 ++- .../DistributeCustomizationTest.java | 9 +- .../appcenter/distribute/DistributeTest.java | 61 --- .../distribute/DistributeUpdateTrackTest.java | 8 +- .../DistributeWarnAlertSystemWindowsTest.java | 360 ------------------ .../distribute/InstallerUtilsTest.java | 107 ------ .../ReleaseDownloadListenerTest.java | 4 +- .../appcenter/channel/DefaultChannelTest.java | 8 +- 13 files changed, 138 insertions(+), 673 deletions(-) delete mode 100644 sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/DistributeWarnAlertSystemWindowsTest.java diff --git a/sdk/appcenter-crashes/src/test/java/com/microsoft/appcenter/crashes/WrapperSdkExceptionManagerTest.java b/sdk/appcenter-crashes/src/test/java/com/microsoft/appcenter/crashes/WrapperSdkExceptionManagerTest.java index c3b7103f8..a3882d054 100644 --- a/sdk/appcenter-crashes/src/test/java/com/microsoft/appcenter/crashes/WrapperSdkExceptionManagerTest.java +++ b/sdk/appcenter-crashes/src/test/java/com/microsoft/appcenter/crashes/WrapperSdkExceptionManagerTest.java @@ -346,7 +346,8 @@ public void handledErrorReportFailedToGetDeviceInfo() throws DeviceInfoHelper.De /* If device info fails. */ mockStatic(DeviceInfoHelper.class); - when(DeviceInfoHelper.getDeviceInfo(any(Context.class))).thenThrow(new DeviceInfoHelper.DeviceInfoException("mock", new java.lang.Exception())); + when(DeviceInfoHelper.getDeviceInfo(any(Context.class))) + .thenThrow(new DeviceInfoHelper.DeviceInfoException("mock")); /* When we build an handled error report. */ String errorReportId = UUID.randomUUID().toString(); diff --git a/sdk/appcenter-crashes/src/test/java/com/microsoft/appcenter/crashes/utils/ErrorLogHelperTest.java b/sdk/appcenter-crashes/src/test/java/com/microsoft/appcenter/crashes/utils/ErrorLogHelperTest.java index f433bc3be..e24fa5233 100644 --- a/sdk/appcenter-crashes/src/test/java/com/microsoft/appcenter/crashes/utils/ErrorLogHelperTest.java +++ b/sdk/appcenter-crashes/src/test/java/com/microsoft/appcenter/crashes/utils/ErrorLogHelperTest.java @@ -28,7 +28,6 @@ import android.app.ActivityManager; import android.app.ActivityManager.RunningAppProcessInfo; import android.content.Context; -import android.content.pm.PackageManager; import android.os.Build; import android.os.Process; import android.text.TextUtils; @@ -223,7 +222,8 @@ public Date answer(InvocationOnMock invocation) { }); /* Mock device. */ - when(DeviceInfoHelper.getDeviceInfo(any(Context.class))).thenThrow(new DeviceInfoHelper.DeviceInfoException("mock", new PackageManager.NameNotFoundException())); + when(DeviceInfoHelper.getDeviceInfo(any(Context.class))) + .thenThrow(new DeviceInfoHelper.DeviceInfoException("mock")); /* Mock architecture. */ TestUtils.setInternalState(Build.VERSION.class, "SDK_INT", 15); @@ -264,7 +264,8 @@ public Date answer(InvocationOnMock invocation) { }); /* Mock device. */ - when(DeviceInfoHelper.getDeviceInfo(any(Context.class))).thenThrow(new DeviceInfoHelper.DeviceInfoException("mock", new PackageManager.NameNotFoundException())); + when(DeviceInfoHelper.getDeviceInfo(any(Context.class))) + .thenThrow(new DeviceInfoHelper.DeviceInfoException("mock")); /* Mock activity manager to return null active processes. */ ActivityManager activityManager = mock(ActivityManager.class); @@ -482,7 +483,8 @@ public void throwDeviceInfoExceptionWhenGetMinidumpSubfolderWithDeviceInfo() thr /* Prepare data. */ mockStatic(DeviceInfoHelper.class); - when(DeviceInfoHelper.getDeviceInfo(any(Context.class))).thenThrow(new DeviceInfoHelper.DeviceInfoException("crash", new java.lang.Exception())); + when(DeviceInfoHelper.getDeviceInfo(any(Context.class))) + .thenThrow(new DeviceInfoHelper.DeviceInfoException("crash")); Context mockContext = mock(Context.class); File mockFile = mock(File.class); whenNew(File.class).withAnyArguments().thenReturn(mockFile); diff --git a/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/AppCenterPackageInstallerReceiver.java b/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/AppCenterPackageInstallerReceiver.java index b597dd993..eb5dc52da 100644 --- a/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/AppCenterPackageInstallerReceiver.java +++ b/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/AppCenterPackageInstallerReceiver.java @@ -72,7 +72,7 @@ public void onReceive(Context context, Intent intent) { } else if (INSTALL_STATUS_ACTION.equals(action)) { onInstallStatus(context, intent); } else { - AppCenterLog.debug(LOG_TAG, String.format(Locale.ENGLISH, "Unrecognized action %s - do nothing.", action)); + AppCenterLog.warn(LOG_TAG, String.format(Locale.ENGLISH, "Unrecognized action %s - do nothing.", action)); } } @@ -118,7 +118,7 @@ private void onInstallStatus(Context context, Intent intent) { onInstallStatusFailure(status, message); break; default: - AppCenterLog.debug(LOG_TAG, String.format(Locale.ENGLISH, "Unrecognized status received from installer: %s", status)); + AppCenterLog.warn(LOG_TAG, String.format(Locale.ENGLISH, "Unrecognized status received from installer: %s", status)); } } @@ -129,11 +129,11 @@ private void onInstallStatusPendingUserAction(Context context, Intent confirmInt } private void onInstallStatusSuccess() { - AppCenterLog.debug(LOG_TAG, "Application was successfully updated."); + AppCenterLog.info(LOG_TAG, "Application was successfully updated."); } private void onInstallStatusFailure(int status, String message) { - AppCenterLog.debug(LOG_TAG, String.format(Locale.ENGLISH, "Failed to install a new release with status: %s. Error message: %s.", status, message)); + AppCenterLog.error(LOG_TAG, String.format(Locale.ENGLISH, "Failed to install a new release with status: %s. Error message: %s.", status, message)); Distribute.getInstance().showInstallingErrorToast(); } } diff --git a/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/AbstractDistributeTest.java b/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/AbstractDistributeTest.java index 0770aa0fa..b6c7d14d8 100644 --- a/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/AbstractDistributeTest.java +++ b/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/AbstractDistributeTest.java @@ -29,6 +29,7 @@ import com.microsoft.appcenter.http.HttpUtils; import com.microsoft.appcenter.utils.AppCenterLog; import com.microsoft.appcenter.utils.AppNameHelper; +import com.microsoft.appcenter.utils.DeviceInfoHelper; import com.microsoft.appcenter.utils.HandlerUtils; import com.microsoft.appcenter.utils.HashUtils; import com.microsoft.appcenter.utils.IdHelper; @@ -73,6 +74,7 @@ AppNameHelper.class, BrowserUtils.class, CryptoUtils.class, + DeviceInfoHelper.class, Distribute.class, HandlerUtils.class, HttpUtils.class, @@ -210,11 +212,12 @@ public Void answer(InvocationOnMock invocation) { when(mActivity.getPackageName()).thenReturn("com.contoso"); when(mActivity.getApplicationContext()).thenReturn(mContext); when(mContext.getApplicationInfo()).thenReturn(mApplicationInfo); - when(mActivity.getApplicationInfo()).thenReturn(mApplicationInfo); when(mContext.getPackageManager()).thenReturn(mPackageManager); - when(mActivity.getPackageManager()).thenReturn(mPackageManager); + + mockStatic(DeviceInfoHelper.class); PackageInfo packageInfo = mock(PackageInfo.class); - when(mPackageManager.getPackageInfo("com.contoso", 0)).thenReturn(packageInfo); + when(DeviceInfoHelper.getPackageInfo(any(Context.class))).thenReturn(packageInfo); + when(DeviceInfoHelper.getVersionCode(eq(packageInfo))).thenReturn(6); Whitebox.setInternalState(packageInfo, "packageName", "com.contoso"); Whitebox.setInternalState(packageInfo, "versionName", "1.2.3"); Whitebox.setInternalState(packageInfo, "versionCode", 6); @@ -294,7 +297,7 @@ public Void answer(InvocationOnMock invocation) { mockStatic(Toast.class); when(Toast.makeText(any(Context.class), anyInt(), anyInt())).thenReturn(mToast); - /* Mock Handler .*/ + /* Mock Handler. */ mockStatic(HandlerUtils.class); doAnswer(new Answer() { @@ -342,6 +345,22 @@ void start() { Distribute.getInstance().onStarting(mAppCenterHandler); Distribute.getInstance().onStarted(mContext, mChannel, "a", null, true); } + + void withTesterApp() { + try { + when(mPackageManager.getPackageInfo(DistributeUtils.TESTER_APP_PACKAGE_NAME, 0)) + .thenReturn(mock(PackageInfo.class)); + } catch (PackageManager.NameNotFoundException ignored) { + } + } + + void withoutTesterApp() { + try { + when(mPackageManager.getPackageInfo(DistributeUtils.TESTER_APP_PACKAGE_NAME, 0)) + .thenThrow(new PackageManager.NameNotFoundException()); + } catch (PackageManager.NameNotFoundException ignored) { + } + } /* Shared code to mock a restart of an activity considered to be the launcher. */ void restartResumeLauncher(Activity activity) { diff --git a/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/AppCenterPackageInstallerReceiverTest.java b/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/AppCenterPackageInstallerReceiverTest.java index 235ab43ef..1b75eb1c0 100644 --- a/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/AppCenterPackageInstallerReceiverTest.java +++ b/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/AppCenterPackageInstallerReceiverTest.java @@ -5,203 +5,176 @@ package com.microsoft.appcenter.distribute; +import static com.microsoft.appcenter.distribute.AppCenterPackageInstallerReceiver.INSTALL_STATUS_ACTION; import static com.microsoft.appcenter.distribute.AppCenterPackageInstallerReceiver.MY_PACKAGE_REPLACED_ACTION; -import static com.microsoft.appcenter.distribute.AppCenterPackageInstallerReceiver.START_ACTION; import static com.microsoft.appcenter.distribute.DistributeConstants.LOG_TAG; import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.spy; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import static org.powermock.api.mockito.PowerMockito.mockStatic; import static org.powermock.api.mockito.PowerMockito.verifyStatic; -import android.app.Activity; import android.content.Context; import android.content.Intent; import android.content.pm.PackageInstaller; -import android.content.pm.PackageManager; import android.os.Bundle; -import android.widget.Toast; import com.microsoft.appcenter.utils.AppCenterLog; +import com.microsoft.appcenter.utils.AppNameHelper; -import org.junit.After; import org.junit.Before; -import org.junit.Rule; import org.junit.Test; +import org.junit.runner.RunWith; import org.mockito.Mock; import org.powermock.core.classloader.annotations.PrepareForTest; -import org.powermock.modules.junit4.rule.PowerMockRule; +import org.powermock.modules.junit4.PowerMockRunner; @PrepareForTest({ AppCenterLog.class, - AppCenterPackageInstallerReceiver.class, - Toast.class + AppNameHelper.class, + Distribute.class, + DistributeUtils.class }) +@RunWith(PowerMockRunner.class) public class AppCenterPackageInstallerReceiverTest { - @Rule - public PowerMockRule mPowerMockRule = new PowerMockRule(); - @Mock private Context mContext; - @Mock - private Activity mActivity; - @Mock private Intent mIntent; @Mock - private PackageManager mPackageManager; - - @Mock - private Bundle mBundle; - - @Mock - private Intent mConfirmIntent; - - @Mock - private Toast mToast; + private Bundle mExtra; private AppCenterPackageInstallerReceiver mAppCenterPackageInstallerReceiver; + private void installStatus(int status) { + when(mIntent.getAction()).thenReturn(INSTALL_STATUS_ACTION); + when(mExtra.getInt(eq(PackageInstaller.EXTRA_STATUS))).thenReturn(status); + } + @Before public void setUp() { + when(mIntent.getExtras()).thenReturn(mExtra); /* Mock static classes. */ mockStatic(AppCenterLog.class); - mockStatic(Toast.class); - - /* Mock methods. */ - when(mContext.getPackageManager()).thenReturn(mPackageManager); - when(mContext.getPackageName()).thenReturn("com.contoso"); - when(mContext.getString(anyInt())).thenReturn("localized_message"); - when(mActivity.getApplicationContext()).thenReturn(mContext); - - /* Mock toast. */ - when(Toast.makeText(any(Context.class), anyString(), anyInt())).thenReturn(mToast); + mockStatic(DistributeUtils.class); /* Init receiver. */ - mAppCenterPackageInstallerReceiver = spy(new AppCenterPackageInstallerReceiver()); - } - - @After - public void cleanUp() { - mAppCenterPackageInstallerReceiver = null; + mAppCenterPackageInstallerReceiver = new AppCenterPackageInstallerReceiver(); } @Test - public void onReceiveWithActionMyPackageReplace() { + public void receiveMyPackageReplaced() { + mockStatic(AppNameHelper.class); + when(AppNameHelper.getAppName(mContext)).thenReturn("Contoso"); + Intent intent = mock(Intent.class); + when(DistributeUtils.getResumeAppIntent(mContext)).thenReturn(intent); when(mIntent.getAction()).thenReturn(MY_PACKAGE_REPLACED_ACTION); - when(mPackageManager.getLaunchIntentForPackage(anyString())).thenReturn(mock(Intent.class)); + when(mContext.getString(R.string.appcenter_distribute_install_completed_title)) + .thenReturn("Title"); + when(mContext.getString(R.string.appcenter_distribute_install_completed_message)) + .thenReturn("%1$s %2$s (%3$d)"); /* Call method with MY_PACKAGE_REPLACED_ACTION action. */ mAppCenterPackageInstallerReceiver.onReceive(mContext, mIntent); - /* Verify that activity was started. */ - verify(mContext).startActivity(any()); + verifyStatic(DistributeUtils.class); + DistributeUtils.postNotification(eq(mContext), anyString(), anyString(), eq(intent)); } @Test - public void onReceiveWithStartIntentWithStatusPendingUserAction() { - when(mIntent.getAction()).thenReturn(START_ACTION); - when(mIntent.getExtras()).thenReturn(mBundle); - when(mBundle.getInt(eq(PackageInstaller.EXTRA_STATUS))).thenReturn(PackageInstaller.STATUS_PENDING_USER_ACTION); - when(mBundle.get(eq(Intent.EXTRA_INTENT))).thenReturn(mConfirmIntent); + public void receiveInstallStatusPendingUserAction() { + installStatus(PackageInstaller.STATUS_PENDING_USER_ACTION); + Intent confirmIntent = mock(Intent.class); + when(mExtra.get(eq(Intent.EXTRA_INTENT))).thenReturn(confirmIntent); /* Call method with STATUS_PENDING_USER_ACTION action. */ mAppCenterPackageInstallerReceiver.onReceive(mContext, mIntent); /* Verify that activity was started. */ - verify(mContext).startActivity(mConfirmIntent); + verify(mContext).startActivity(eq(confirmIntent)); } @Test - public void onReceiveWithStartIntentWithStatusSuccess() { - when(mIntent.getAction()).thenReturn(START_ACTION); - when(mIntent.getExtras()).thenReturn(mBundle); - when(mBundle.getInt(eq(PackageInstaller.EXTRA_STATUS))).thenReturn(PackageInstaller.STATUS_SUCCESS); - when(mBundle.get(eq(Intent.EXTRA_INTENT))).thenReturn(mConfirmIntent); + public void receiveInstallStatusSuccess() { + installStatus(PackageInstaller.STATUS_SUCCESS); /* Call method with STATUS_SUCCESS action. */ mAppCenterPackageInstallerReceiver.onReceive(mContext, mIntent); /* Verify. */ verifyStatic(AppCenterLog.class); - AppCenterLog.debug(eq(LOG_TAG), eq("Application was successfully updated.")); + AppCenterLog.info(eq(LOG_TAG), eq("Application was successfully updated.")); } @Test - public void onReceiveWithStartIntentWithStatusFailure() { - onReceiveWithStartFailureAction(PackageInstaller.STATUS_FAILURE); + public void receiveInstallStatusFailure() { + receiveInstallStatusFailure(PackageInstaller.STATUS_FAILURE); } @Test - public void onReceiveWithStartIntentWithStatusFailureAborted() { - onReceiveWithStartFailureAction(PackageInstaller.STATUS_FAILURE_ABORTED); + public void receiveInstallStatusFailureAborted() { + receiveInstallStatusFailure(PackageInstaller.STATUS_FAILURE_ABORTED); } @Test - public void onReceiveWithStartIntentWithStatusFailureBlocked() { - onReceiveWithStartFailureAction(PackageInstaller.STATUS_FAILURE_BLOCKED); + public void receiveInstallStatusFailureBlocked() { + receiveInstallStatusFailure(PackageInstaller.STATUS_FAILURE_BLOCKED); } @Test - public void onReceiveWithStartIntentWithStatusFailureConflict() { - onReceiveWithStartFailureAction(PackageInstaller.STATUS_FAILURE_CONFLICT); + public void receiveInstallStatusFailureConflict() { + receiveInstallStatusFailure(PackageInstaller.STATUS_FAILURE_CONFLICT); } @Test - public void onReceiveWithStartIntentWithStatusFailureIncompatible() { - onReceiveWithStartFailureAction(PackageInstaller.STATUS_FAILURE_INCOMPATIBLE); + public void receiveInstallStatusFailureIncompatible() { + receiveInstallStatusFailure(PackageInstaller.STATUS_FAILURE_INCOMPATIBLE); } @Test - public void onReceiveWithStartIntentWithStatusFailureInvalid() { - onReceiveWithStartFailureAction(PackageInstaller.STATUS_FAILURE_INVALID); + public void receiveInstallStatusFailureInvalid() { + receiveInstallStatusFailure(PackageInstaller.STATUS_FAILURE_INVALID); } @Test - public void onReceiveWithStartIntentWithStatusFailureStorage() { - onReceiveWithStartFailureAction(PackageInstaller.STATUS_FAILURE_STORAGE); + public void receiveInstallStatusFailureStorage() { + receiveInstallStatusFailure(PackageInstaller.STATUS_FAILURE_STORAGE); } - private void onReceiveWithStartFailureAction(int status) { - when(mIntent.getAction()).thenReturn(START_ACTION); - when(mIntent.getExtras()).thenReturn(mBundle); - when(mBundle.getInt(eq(PackageInstaller.EXTRA_STATUS))).thenReturn(status); - when(mBundle.get(eq(Intent.EXTRA_INTENT))).thenReturn(mConfirmIntent); + private void receiveInstallStatusFailure(int status) { + installStatus(status); + + mockStatic(Distribute.class); + Distribute distribute = mock(Distribute.class); + when(Distribute.getInstance()).thenReturn(distribute); /* Call method with failure action. */ mAppCenterPackageInstallerReceiver.onReceive(mContext, mIntent); /* Verify that log was called. */ + verify(distribute).showInstallingErrorToast(); verifyStatic(AppCenterLog.class); - AppCenterLog.debug(eq(LOG_TAG), eq("Failed to install a new release with status: " + status + ". Error message: null.")); + AppCenterLog.error(eq(LOG_TAG), eq("Failed to install a new release with status: " + status + ". Error message: null.")); } @Test public void onReceiveWithStartIntentWithUnrecognizedStatus() { - - /* Create unrecognized status */ - int status = 10; - when(mIntent.getAction()).thenReturn(START_ACTION); - when(mIntent.getExtras()).thenReturn(mBundle); - when(mBundle.getInt(eq(PackageInstaller.EXTRA_STATUS))).thenReturn(status); - when(mBundle.get(eq(Intent.EXTRA_INTENT))).thenReturn(mConfirmIntent); - when(Toast.makeText(any(Context.class), eq("Failed during installing new release."), anyInt())).thenReturn(mToast); + final int UNKNOWN_STATUS = 42; + installStatus(UNKNOWN_STATUS); /* Call method with wrong action. */ mAppCenterPackageInstallerReceiver.onReceive(mContext, mIntent); /* Verify that log was called. */ verifyStatic(AppCenterLog.class); - AppCenterLog.debug(eq(LOG_TAG), eq("Unrecognized status received from installer: " + status)); + AppCenterLog.warn(eq(LOG_TAG), eq("Unrecognized status received from installer: " + UNKNOWN_STATUS)); } @Test @@ -213,7 +186,7 @@ public void onReceiverWithUnknownAction() { /* Verify that log was called. */ verifyStatic(AppCenterLog.class); - AppCenterLog.debug(eq(LOG_TAG), eq("Unrecognized action UnknownAction - do nothing.")); + AppCenterLog.warn(eq(LOG_TAG), eq("Unrecognized action UnknownAction - do nothing.")); } } diff --git a/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/DistributeBeforeApiSuccessTest.java b/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/DistributeBeforeApiSuccessTest.java index bbe83a833..d2bb94a70 100644 --- a/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/DistributeBeforeApiSuccessTest.java +++ b/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/DistributeBeforeApiSuccessTest.java @@ -65,6 +65,7 @@ import com.microsoft.appcenter.http.HttpResponse; import com.microsoft.appcenter.http.ServiceCall; import com.microsoft.appcenter.http.ServiceCallback; +import com.microsoft.appcenter.utils.DeviceInfoHelper; import com.microsoft.appcenter.utils.HandlerUtils; import com.microsoft.appcenter.utils.async.AppCenterConsumer; import com.microsoft.appcenter.utils.context.SessionContext; @@ -103,10 +104,10 @@ }) public class DistributeBeforeApiSuccessTest extends AbstractDistributeTest { - private void testDistributeInactiveOnPrivateTrack() throws PackageManager.NameNotFoundException { + private void testDistributeInactiveOnPrivateTrack() { /* Check browser not opened. */ - when(mPackageManager.getPackageInfo(DistributeUtils.TESTER_APP_PACKAGE_NAME, 0)).thenThrow(new PackageManager.NameNotFoundException()); + withoutTesterApp(); Distribute.setUpdateTrack(UpdateTrack.PRIVATE); start(); Distribute.getInstance().onActivityResumed(mActivity); @@ -146,13 +147,13 @@ private void showUpdateSetupFailedDialog() { } @Test - public void doNothingIfEnabledForDebuggableBuildNotSet() throws PackageManager.NameNotFoundException { + public void doNothingIfEnabledForDebuggableBuildNotSet() { Whitebox.setInternalState(mApplicationInfo, "flags", ApplicationInfo.FLAG_DEBUGGABLE); testDistributeInactiveOnPrivateTrack(); } @Test - public void doNothingWhenEnabledForDebuggableBuildSetToFalse() throws PackageManager.NameNotFoundException { + public void doNothingWhenEnabledForDebuggableBuildSetToFalse() { Whitebox.setInternalState(mApplicationInfo, "flags", ApplicationInfo.FLAG_DEBUGGABLE); Distribute.setEnabledForDebuggableBuild(false); testDistributeInactiveOnPrivateTrack(); @@ -170,7 +171,7 @@ public void continueWhenEnabledForDebuggableBuildSetToTrue() throws Exception { mockStatic(UUID.class); when(UUID.randomUUID()).thenReturn(requestId); when(SharedPreferencesManager.getString(PREFERENCE_KEY_REQUEST_ID)).thenReturn(requestId.toString()); - when(mPackageManager.getPackageInfo(DistributeUtils.TESTER_APP_PACKAGE_NAME, 0)).thenThrow(new PackageManager.NameNotFoundException()); + withoutTesterApp(); /* Start and resume: open browser. */ Distribute.setUpdateTrack(UpdateTrack.PRIVATE); @@ -198,19 +199,19 @@ public void continueWhenEnabledForDebuggableBuildSetToTrue() throws Exception { } @Test - public void doNothingIfInstallComesFromStore() throws PackageManager.NameNotFoundException { + public void doNothingIfInstallComesFromStore() { when(InstallerUtils.isInstalledFromAppStore(anyString(), any(Context.class))).thenReturn(true); testDistributeInactiveOnPrivateTrack(); } @Test - public void doNothingIfUpdateSetupFailedMessageExist() throws PackageManager.NameNotFoundException { + public void doNothingIfUpdateSetupFailedMessageExist() { when(SharedPreferencesManager.getString(PREFERENCE_KEY_UPDATE_SETUP_FAILED_MESSAGE_KEY)).thenReturn("failed_message_from_backend"); testDistributeInactiveOnPrivateTrack(); } @Test - public void doNothingIfReleaseHashEqualsToFailedPackageHash() throws PackageManager.NameNotFoundException { + public void doNothingIfReleaseHashEqualsToFailedPackageHash() { when(SharedPreferencesManager.getString(PREFERENCE_KEY_UPDATE_SETUP_FAILED_PACKAGE_HASH_KEY)).thenReturn("some_hash"); mockStatic(DistributeUtils.class); @@ -220,8 +221,8 @@ public void doNothingIfReleaseHashEqualsToFailedPackageHash() throws PackageMana } @Test - public void checkForUpdateIfIgnoredSideLoadingButSwitchedToPublicTrack() throws PackageManager.NameNotFoundException { - when(mPackageManager.getPackageInfo(DistributeUtils.TESTER_APP_PACKAGE_NAME, 0)).thenThrow(new PackageManager.NameNotFoundException()); + public void checkForUpdateIfIgnoredSideLoadingButSwitchedToPublicTrack() { + withoutTesterApp(); when(SharedPreferencesManager.getString(PREFERENCE_KEY_UPDATE_SETUP_FAILED_PACKAGE_HASH_KEY)).thenReturn("some_hash"); mockStatic(DistributeUtils.class); @@ -246,9 +247,10 @@ public void checkForUpdateIfIgnoredSideLoadingButSwitchedToPublicTrack() throws } @Test - public void continueIfReleaseHashNotEqualsToFailedPackageHash() throws PackageManager.NameNotFoundException { - when(mPackageManager.getPackageInfo(DistributeUtils.TESTER_APP_PACKAGE_NAME, 0)).thenThrow(new PackageManager.NameNotFoundException()); - when(SharedPreferencesManager.getString(PREFERENCE_KEY_UPDATE_SETUP_FAILED_PACKAGE_HASH_KEY)).thenReturn("some_hash"); + public void continueIfReleaseHashNotEqualsToFailedPackageHash() { + withoutTesterApp(); + when(SharedPreferencesManager.getString(PREFERENCE_KEY_UPDATE_SETUP_FAILED_PACKAGE_HASH_KEY)) + .thenReturn("some_hash"); mockStatic(DistributeUtils.class); /* Mock the computeReleaseHash to other_hash value. */ @@ -383,8 +385,8 @@ public void storePublicRedirectionBeforeStart() { } @Test - public void postponeBrowserIfNoNetwork() throws Exception { - when(mPackageManager.getPackageInfo(DistributeUtils.TESTER_APP_PACKAGE_NAME, 0)).thenThrow(new PackageManager.NameNotFoundException()); + public void postponeBrowserIfNoNetwork() { + withoutTesterApp(); /* Check browser not opened if no network. */ when(mNetworkStateHelper.isNetworkConnected()).thenReturn(false); @@ -547,7 +549,7 @@ public void testerAppNotInstalled() throws Exception { mockStatic(UUID.class); when(UUID.randomUUID()).thenReturn(requestId); when(SharedPreferencesManager.getString(PREFERENCE_KEY_REQUEST_ID)).thenReturn(requestId.toString()); - when(mPackageManager.getPackageInfo(DistributeUtils.TESTER_APP_PACKAGE_NAME, 0)).thenThrow(new PackageManager.NameNotFoundException()); + withoutTesterApp(); /* Start and resume: open browser. */ Distribute.setUpdateTrack(UpdateTrack.PRIVATE); @@ -576,8 +578,8 @@ public void useBrowserUpdateSetupIfAppIsTesterApp() throws Exception { mockStatic(UUID.class); when(UUID.randomUUID()).thenReturn(requestId); when(SharedPreferencesManager.getString(PREFERENCE_KEY_REQUEST_ID)).thenReturn(requestId.toString()); - when(mPackageManager.getPackageInfo(DistributeUtils.TESTER_APP_PACKAGE_NAME, 0)).thenReturn(mock(PackageInfo.class)); when(mContext.getPackageName()).thenReturn(DistributeUtils.TESTER_APP_PACKAGE_NAME); + withTesterApp(); /* Start and resume: open browser. */ Distribute.setUpdateTrack(UpdateTrack.PRIVATE); @@ -602,7 +604,7 @@ public void testerAppUpdateSetupFailed() throws Exception { url += "&" + PARAMETER_REQUEST_ID + "=" + requestId; url += "&" + PARAMETER_PLATFORM + "=" + PARAMETER_PLATFORM_VALUE; whenNew(Intent.class).withArguments(Intent.ACTION_VIEW, Uri.parse(url)).thenReturn(mock(Intent.class)); - when(mPackageManager.getPackageInfo(DistributeUtils.TESTER_APP_PACKAGE_NAME, 0)).thenReturn(mock(PackageInfo.class)); + withTesterApp(); /* Start and resume: open tester app. */ Distribute.setUpdateTrack(UpdateTrack.PRIVATE); @@ -651,7 +653,7 @@ public void happyPathUsingTesterAppUpdateSetup() throws Exception { url += "&" + PARAMETER_REQUEST_ID + "=" + requestId; url += "&" + PARAMETER_PLATFORM + "=" + PARAMETER_PLATFORM_VALUE; whenNew(Intent.class).withArguments(Intent.ACTION_VIEW, Uri.parse(url)).thenReturn(mock(Intent.class)); - when(mPackageManager.getPackageInfo(DistributeUtils.TESTER_APP_PACKAGE_NAME, 0)).thenReturn(mock(PackageInfo.class)); + withTesterApp(); /* * Start and resume: open tester app. @@ -742,7 +744,7 @@ public void happyPathUntilHangingCallWithToken() throws Exception { mockStatic(UUID.class); when(UUID.randomUUID()).thenReturn(requestId); when(SharedPreferencesManager.getString(PREFERENCE_KEY_REQUEST_ID)).thenReturn(requestId.toString()); - when(mPackageManager.getPackageInfo(DistributeUtils.TESTER_APP_PACKAGE_NAME, 0)).thenThrow(new PackageManager.NameNotFoundException()); + withoutTesterApp(); /* Start and resume: open browser. */ Distribute.setUpdateTrack(UpdateTrack.PRIVATE); @@ -870,7 +872,7 @@ public void setUrls() throws Exception { mockStatic(UUID.class); when(UUID.randomUUID()).thenReturn(requestId); when(SharedPreferencesManager.getString(PREFERENCE_KEY_REQUEST_ID)).thenReturn(requestId.toString()); - when(mPackageManager.getPackageInfo(DistributeUtils.TESTER_APP_PACKAGE_NAME, 0)).thenThrow(new PackageManager.NameNotFoundException()); + withoutTesterApp(); /* Start and resume: open browser. */ Distribute.setUpdateTrack(UpdateTrack.PRIVATE); @@ -898,18 +900,15 @@ public void setUrls() throws Exception { } @Test - public void computeHashFailsWhenOpeningBrowser() throws Exception { + public void computeHashFailsWhenOpeningBrowser() { - /* Mock package manager. */ - when(mPackageManager.getPackageInfo("com.contoso", 0)).thenThrow(new PackageManager.NameNotFoundException()); + /* Mock package info. */ + when(DeviceInfoHelper.getPackageInfo(any(Context.class))).thenReturn(null); /* Start and resume: open browser. */ start(); Distribute.getInstance().onActivityResumed(mActivity); - /* Verify only tried once. */ - verify(mPackageManager).getPackageInfo("com.contoso", 0); - /* And verify we didn't open browser. */ verifyStatic(BrowserUtils.class, never()); BrowserUtils.openBrowser(anyString(), any(Activity.class)); @@ -919,7 +918,7 @@ public void computeHashFailsWhenOpeningBrowser() throws Exception { @Test public void disableBeforeStoreToken() throws Exception { - when(mPackageManager.getPackageInfo(DistributeUtils.TESTER_APP_PACKAGE_NAME, 0)).thenThrow(new PackageManager.NameNotFoundException()); + withoutTesterApp(); /* Start and resume: open browser. */ UUID requestId = UUID.randomUUID(); diff --git a/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/DistributeCustomizationTest.java b/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/DistributeCustomizationTest.java index 09013af07..e04ca11d6 100644 --- a/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/DistributeCustomizationTest.java +++ b/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/DistributeCustomizationTest.java @@ -53,8 +53,10 @@ import static org.powermock.api.mockito.PowerMockito.verifyStatic; import static org.powermock.api.mockito.PowerMockito.when; -@SuppressWarnings("unused") -@PrepareForTest({ErrorDetails.class, DistributeUtils.class, DeviceInfoHelper.class}) +@PrepareForTest({ + DistributeUtils.class, + ErrorDetails.class +}) public class DistributeCustomizationTest extends AbstractDistributeTest { private void start(Distribute distribute) { @@ -70,7 +72,6 @@ public void distributeListenerVersionsEqual() throws Exception { when(DistributeUtils.computeReleaseHash(any(PackageInfo.class))).thenReturn("some_hash"); /* Set the package version equal to the portal's one. */ - mockStatic(DeviceInfoHelper.class); when(DeviceInfoHelper.getVersionCode(any(PackageInfo.class))).thenReturn(10); /* Mock http call. */ @@ -157,7 +158,6 @@ public void distributeListenerNoReleaseFound() throws Exception { public void distributeNotRecent() throws Exception { /* Set the package version higher than the portal's one. */ - mockStatic(DeviceInfoHelper.class); when(DeviceInfoHelper.getVersionCode(any(PackageInfo.class))).thenReturn(11); /* Mock http call. */ @@ -206,7 +206,6 @@ public void distributeNotRecentNoListenerNoActivity() throws Exception { private void distributeNotRecentCoverage(DistributeListener actualListener, DistributeListener verificationListener, boolean onPause) throws Exception { /* Set the package version higher than the portal's one. */ - mockStatic(DeviceInfoHelper.class); when(DeviceInfoHelper.getVersionCode(any(PackageInfo.class))).thenReturn(11); /* Mock http call. */ diff --git a/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/DistributeTest.java b/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/DistributeTest.java index 0aa08949d..5f9d6a0a4 100644 --- a/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/DistributeTest.java +++ b/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/DistributeTest.java @@ -833,7 +833,6 @@ public void checkUpdateReleaseAfterInterruptDownloading() { /* Prepare data. */ mockStatic(DistributeUtils.class); - mockStatic(DeviceInfoHelper.class); when(mReleaseDetails.getVersion()).thenReturn(1); when(mReleaseDetails.isMandatoryUpdate()).thenReturn(true); when(DeviceInfoHelper.getVersionCode(any(PackageInfo.class))).thenReturn(0); @@ -938,66 +937,6 @@ public void checkInstallProgressState() { verify(mReleaseInstallerListener, times(2)).hideInstallProgressDialog(); } - @Test - public void showSystemSettingsDialogWhenPackageInstallerNull() { - - /* Try to show dialog. */ - Distribute.getInstance().showSystemSettingsDialogOrStartInstalling(mock(Uri.class)); - - /* Verify that log was called. */ - verifyStatic(AppCenterLog.class); - AppCenterLog.debug(eq(LOG_TAG), eq("Installing couldn't start due to the release installer wasn't initialized.")); - } - - @Test - public void showUpdateDialogAfterShowingInstallReleaseDialogTest() { - - /* Mock App Center log. */ - mockStatic(AppCenterLog.class); - - /* Mock system alert settings. */ - mockStatic(InstallerUtils.class); - when(InstallerUtils.isSystemAlertWindowsEnabled(any(Context.class))).thenReturn(false); - - /* Mock distribute utils. */ - mockStatic(DistributeUtils.class); - when(DistributeUtils.getStoredDownloadState()).thenReturn(-1).thenReturn(1); - - /* Mock that download time is bigger than packageInfo.lastUpdateTime. */ - when(SharedPreferencesManager.getLong(eq(PREFERENCE_KEY_DOWNLOAD_TIME))).thenReturn(3L); - - /* mReleaseDetails is not null and it's a mandatory update. */ - when(DistributeUtils.loadCachedReleaseDetails()).thenReturn(mReleaseDetails); - when(mReleaseDetails.isMandatoryUpdate()).thenReturn(true); - when(mReleaseDownloader.isDownloading()).thenReturn(false); - - /* Prepare distribute listener. */ - Distribute.setListener(Mockito.mock(DistributeListener.class)); - - /* Show install settings dialog. */ - Distribute.getInstance().startFromBackground(mContext); - resumeWorkflow(mActivity); - Distribute.getInstance().showSystemSettingsDialogOrStartInstalling(mock(Uri.class)); - - /* Emulate that settings was applied. */ - Distribute.getInstance().onActivityPaused(mActivity); - Distribute.getInstance().onActivityStopped(mActivity); - resumeWorkflow(mActivity); - - /* Verify that install progress was started. */ - verifyStatic(InstallerUtils.class); - InstallerUtils.installPackage(any(Uri.class), any(Context.class), any(ReleaseInstallerListener.class)); - - /* Emulate system confirmation dialog about installing new release. */ - Distribute.getInstance().onActivityPaused(mActivity); - Distribute.getInstance().onActivityStopped(mActivity); - resumeWorkflow(mActivity); - - /* Verify that SDK wasn't crashed with NPE and showed dialog. */ - verifyStatic(AppCenterLog.class); - AppCenterLog.debug(eq(LOG_TAG), eq("Show default update dialog.")); - } - private void firstDownloadNotification(int apiLevel) throws Exception { TestUtils.setInternalState(Build.VERSION.class, "SDK_INT", apiLevel); spy(DistributeUtils.class); diff --git a/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/DistributeUpdateTrackTest.java b/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/DistributeUpdateTrackTest.java index 9c76f7438..d64afd8c5 100644 --- a/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/DistributeUpdateTrackTest.java +++ b/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/DistributeUpdateTrackTest.java @@ -64,10 +64,10 @@ public void setInvalidUpdateTrack() { } @Test - public void switchTrack() throws PackageManager.NameNotFoundException { + public void switchTrack() { /* Start (in public mode). */ - when(mPackageManager.getPackageInfo(DistributeUtils.TESTER_APP_PACKAGE_NAME, 0)).thenThrow(new PackageManager.NameNotFoundException()); + withoutTesterApp(); start(); Distribute.getInstance().onActivityResumed(mActivity); @@ -91,10 +91,10 @@ public void switchTrack() throws PackageManager.NameNotFoundException { } @Test - public void switchTrackBeforeForeground() throws PackageManager.NameNotFoundException { + public void switchTrackBeforeForeground() { /* Start (in public mode) in background. */ - when(mPackageManager.getPackageInfo(DistributeUtils.TESTER_APP_PACKAGE_NAME, 0)).thenThrow(new PackageManager.NameNotFoundException()); + withoutTesterApp(); start(); /* Switch to private. */ diff --git a/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/DistributeWarnAlertSystemWindowsTest.java b/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/DistributeWarnAlertSystemWindowsTest.java deleted file mode 100644 index 37250e644..000000000 --- a/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/DistributeWarnAlertSystemWindowsTest.java +++ /dev/null @@ -1,360 +0,0 @@ -/* - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. - */ - -package com.microsoft.appcenter.distribute; - -import static android.content.Context.NOTIFICATION_SERVICE; -import static com.microsoft.appcenter.distribute.DistributeConstants.DOWNLOAD_STATE_NOTIFIED; -import static com.microsoft.appcenter.distribute.DistributeConstants.LOG_TAG; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.Mockito.RETURNS_MOCKS; -import static org.mockito.Mockito.never; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.verify; -import static org.powermock.api.mockito.PowerMockito.doAnswer; -import static org.powermock.api.mockito.PowerMockito.doCallRealMethod; -import static org.powermock.api.mockito.PowerMockito.mock; -import static org.powermock.api.mockito.PowerMockito.mockStatic; -import static org.powermock.api.mockito.PowerMockito.verifyStatic; -import static org.powermock.api.mockito.PowerMockito.when; -import static org.powermock.api.mockito.PowerMockito.whenNew; - -import android.annotation.SuppressLint; -import android.app.Activity; -import android.app.AlertDialog; -import android.app.Dialog; -import android.app.Notification; -import android.app.NotificationManager; -import android.content.Context; -import android.content.DialogInterface; -import android.content.Intent; -import android.net.Uri; -import android.os.Build; -import android.provider.Settings; - -import com.microsoft.appcenter.utils.AppCenterLog; - -import org.junit.Before; -import org.junit.Test; -import org.mockito.ArgumentCaptor; -import org.mockito.Mock; -import org.mockito.invocation.InvocationOnMock; -import org.mockito.stubbing.Answer; -import org.powermock.core.classloader.annotations.PrepareForTest; - -@SuppressLint("InlinedApi") -@SuppressWarnings("CanBeFinal") -@PrepareForTest({ - Settings.class, - AlertDialog.class, - AlertDialog.Builder.class, - ReleaseInstallerListener.class, - AppCenterLog.class, - DistributeUtils.class, - Distribute.class, - AppCenterLog.class, - Build.class -}) -public class DistributeWarnAlertSystemWindowsTest extends AbstractDistributeTest { - - @Mock - private AlertDialog mAlertWindowsDialog; - - @Mock - private Activity mFirstActivity; - - @Mock - private ReleaseInstallerListener mReleaseInstallerListener; - - @Before - public void setUpDialog() throws Exception { - - /* Mock static classes. */ - mockStatic(Settings.class); - mockStatic(AppCenterLog.class); - - /* Reset mock release listener methods. */ - doCallRealMethod().when(mReleaseDownloaderListener).onComplete(any(Uri.class)); - when(mReleaseInstallerListener.showInstallProgressDialog(any(Activity.class))).thenReturn(mock(Dialog.class)); - - /* Mock release installer listener. */ - whenNew(ReleaseInstallerListener.class).withAnyArguments().thenReturn(mReleaseInstallerListener); - - /* Mock release details. */ - mockStatic(DistributeUtils.class); - when(DistributeUtils.loadCachedReleaseDetails()).thenReturn(mReleaseDetails); - - /* Start distribute from background. */ - Distribute.getInstance().startFromBackground(mContext); - - /* Start distribute module. */ - start(); - - /* Resume distribute. */ - when(mFirstActivity.getApplicationContext()).thenReturn(mContext); - Distribute.getInstance().onActivityResumed(mFirstActivity); - - /* Mock system alert windows dialog. */ - when(mDialogBuilder.create()).thenReturn(mAlertWindowsDialog); - doAnswer(new Answer() { - - @Override - public Void answer(InvocationOnMock invocation) { - when(mAlertWindowsDialog.isShowing()).thenReturn(true); - return null; - } - }).when(mAlertWindowsDialog).show(); - doAnswer(new Answer() { - - @Override - public Void answer(InvocationOnMock invocation) { - when(mAlertWindowsDialog.isShowing()).thenReturn(false); - return null; - } - }).when(mAlertWindowsDialog).hide(); - - /* Verify that dialog wasn't shown. */ - verify(mAlertWindowsDialog, never()).show(); - } - - @Test - public void showAndEnableAlertWindowsDialogQ() { - - /* Mock permission state for Android Q. */ - when(InstallerUtils.isSystemAlertWindowsEnabled(any(Context.class))).thenReturn(false); - when(Settings.canDrawOverlays(any(Context.class))).thenReturn(false); - - /* Notify about complete download. */ - mReleaseDownloaderListener.onComplete(mock(Uri.class)); - - /* Verify that dialog was shown after complete download. */ - verify(mAlertWindowsDialog).show(); - - /* Emulate click on positive button. */ - ArgumentCaptor clickListener = ArgumentCaptor.forClass(DialogInterface.OnClickListener.class); - verify(mDialogBuilder).setPositiveButton(eq(R.string.appcenter_distribute_unknown_sources_dialog_settings), clickListener.capture()); - clickListener.getValue().onClick(mDialog, DialogInterface.BUTTON_POSITIVE); - verify(mFirstActivity).startActivity(any(Intent.class)); - - /* Emulate behavior that settings was enabled via dialog. */ - Distribute.getInstance().onActivityPaused(mActivity); - Distribute.getInstance().onApplicationEnterBackground(); - - /* Verify that dialog was closed after call onActivityPaused. */ - verify(mReleaseInstallerListener).hideInstallProgressDialog(); - - /* Mock settings result and resume Distribute. */ - when(Settings.canDrawOverlays(any(Context.class))).thenReturn(true); - Distribute.getInstance().onApplicationEnterForeground(); - Distribute.getInstance().onActivityResumed(mActivity); - - /* Verify that after enabling permissions the install process was started. */ - verifyStatic(InstallerUtils.class); - InstallerUtils.installPackage(any(Uri.class), any(Context.class), any(ReleaseInstallerListener.class)); - - /* Emulate that install a new release was started. */ - Distribute.getInstance().notifyInstallProgress(true); - verify(mReleaseInstallerListener, times(2)).hideInstallProgressDialog(); - - /* Pause and resume Distribute again. */ - Distribute.getInstance().onActivityPaused(mActivity); - Distribute.getInstance().onApplicationEnterBackground(); - Distribute.getInstance().onApplicationEnterForeground(); - Distribute.getInstance().onActivityResumed(mActivity); - when(InstallerUtils.isSystemAlertWindowsEnabled(any(Context.class))).thenReturn(true); - - /* Verify that after resume do nothing. */ - verifyStatic(AppCenterLog.class); - AppCenterLog.info(eq(LOG_TAG), eq("Installing in progress...")); - } - - @Test - public void showAndEnableAlertWindowsDialogLowQWithEnabledSettings() { - showAndEnableAlertWindowsDialogLowQ(true); - } - - @Test - public void showAndEnableAlertWindowsDialogLowQWithDisabledSettings() { - showAndEnableAlertWindowsDialogLowQ(false); - } - - private void showAndEnableAlertWindowsDialogLowQ(boolean isEnabled) { - - /* Mock permission state for Android M. */ - when(InstallerUtils.isSystemAlertWindowsEnabled(any(Context.class))).thenReturn(true); - when(Settings.canDrawOverlays(any(Context.class))).thenReturn(isEnabled); - - /* Notify about complete download. */ - mReleaseDownloaderListener.onComplete(mock(Uri.class)); - - /* Verify that dialog was not shown. */ - verify(mAlertWindowsDialog, never()).show(); - - /* Emulate behavior that settings was enabled. */ - Distribute.getInstance().onActivityPaused(mActivity); - Distribute.getInstance().onApplicationEnterBackground(); - - /* Verify that dialog was closed. */ - verify(mReleaseInstallerListener).hideInstallProgressDialog(); - - /* Do nothing with settings and resume Distribute module. */ - Distribute.getInstance().onApplicationEnterForeground(); - Distribute.getInstance().onActivityResumed(mActivity); - - /* Verify that after enabling permissions the install process was started. */ - verifyStatic(InstallerUtils.class); - InstallerUtils.installPackage(any(Uri.class), any(Context.class), any(ReleaseInstallerListener.class)); - - /* Emulate that install a new release was started. */ - Distribute.getInstance().notifyInstallProgress(true); - verify(mReleaseInstallerListener, times(2)).hideInstallProgressDialog(); - } - - @Test - public void showAndDisableAlertWindowsDialogQ() { - - /* Mock permission state for Android Q. */ - when(InstallerUtils.isSystemAlertWindowsEnabled(any(Context.class))).thenReturn(false); - when(Settings.canDrawOverlays(any(Context.class))).thenReturn(false); - - /* Notify about complete download. */ - mReleaseDownloaderListener.onComplete(mock(Uri.class)); - - /* Verify that dialog was shown after complete download. */ - verify(mAlertWindowsDialog).show(); - - /* Emulate click on positive button. */ - ArgumentCaptor clickListener = ArgumentCaptor.forClass(DialogInterface.OnClickListener.class); - verify(mDialogBuilder).setNegativeButton(eq(android.R.string.cancel), clickListener.capture()); - clickListener.getValue().onClick(mDialog, DialogInterface.BUTTON_NEGATIVE); - - /* Verify that after enabling permissions the install process was started. */ - verifyStatic(InstallerUtils.class); - InstallerUtils.installPackage(any(Uri.class), any(Context.class), any(ReleaseInstallerListener.class)); - } - - @Test - public void showAndCancelAlertWindowsDialog() { - - /* Mock permission state for Android Q. */ - when(InstallerUtils.isSystemAlertWindowsEnabled(any(Context.class))).thenReturn(false); - when(Settings.canDrawOverlays(any(Context.class))).thenReturn(false); - - /* Notify about complete download. */ - mReleaseDownloaderListener.onComplete(mock(Uri.class)); - - /* Verify that dialog was shown after complete download. */ - verify(mAlertWindowsDialog).show(); - - /* Emulate click on positive button. */ - ArgumentCaptor cancelListener = ArgumentCaptor.forClass(DialogInterface.OnCancelListener.class); - verify(mDialogBuilder).setOnCancelListener(cancelListener.capture()); - cancelListener.getValue().onCancel(mAlertWindowsDialog); - - /* Verify that after enabling permissions the install process was started. */ - verifyStatic(InstallerUtils.class); - InstallerUtils.installPackage(any(Uri.class), any(Context.class), any(ReleaseInstallerListener.class)); - } - - @Test - public void returningToDialogFromBackgroundWhenInstallerIsNull() { - - /* Mock permission state for Android Q. */ - when(InstallerUtils.isSystemAlertWindowsEnabled(any(Context.class))).thenReturn(false); - when(Settings.canDrawOverlays(any(Context.class))).thenReturn(false); - - /* Notify about complete download. */ - mReleaseDownloaderListener.onComplete(mock(Uri.class)); - - /* Emulate behavior that settings was enabled via dialog. */ - Distribute.getInstance().onActivityPaused(mActivity); - Distribute.getInstance().onApplicationEnterBackground(); - - /* Make release details NULL to make installer listener NULL too. */ - when(DistributeUtils.loadCachedReleaseDetails()).thenReturn(null); - - /* Start distribute with app secret NULL to make sure updateReleaseDetails is called on startFromBackground. */ - Distribute.getInstance().onStarting(mAppCenterHandler); - Distribute.getInstance().onStarted(mContext, mChannel, null, null, true); - Distribute.getInstance().startFromBackground(mContext); - - /* Enter foreground */ - Distribute.getInstance().onApplicationEnterForeground(); - Distribute.getInstance().onActivityResumed(mActivity); - - /* Verify that installation process is trying to resume but installer is null because release details was not loaded. */ - verifyStatic(AppCenterLog.class); - AppCenterLog.debug(eq(LOG_TAG), eq("Installing couldn't start due to the release installer wasn't initialized.")); - } - - @Test - public void showSettingsDialogWhenActivityIsNullOnAndroidQ() throws Exception { - - /* Mock permission state for Android Q. */ - when(InstallerUtils.isSystemAlertWindowsEnabled(any(Context.class))).thenReturn(false); - when(Settings.canDrawOverlays(any(Context.class))).thenReturn(false); - - /* Set activity null in Distribute. */ - Distribute.getInstance().onActivityPaused(mActivity); - - /* Moke download state notified. */ - when(DistributeUtils.getStoredDownloadState()).thenReturn(DOWNLOAD_STATE_NOTIFIED); - - /* Mock notification manager to avoid NRE. */ - NotificationManager manager = mock(NotificationManager.class); - whenNew(Notification.Builder.class).withAnyArguments() - .thenReturn(mock(Notification.Builder.class, RETURNS_MOCKS)); - when(mContext.getSystemService(NOTIFICATION_SERVICE)).thenReturn(manager); - - /* Notify about complete download. */ - mReleaseDownloaderListener.onComplete(mock(Uri.class)); - - /* Verify that dialog was not shown. */ - verify(mAlertWindowsDialog, never()).show(); - - /* Verify system alert window is not trying to display if activity is null. */ - verifyStatic(AppCenterLog.class); - AppCenterLog.warn(eq(LOG_TAG), eq("The application is in background mode, the system alerts windows won't be displayed.")); - } - - @Test - public void needRefreshDialogWhenStartInstallationOnAndroidQ() throws Exception { - - /* Mock permission state for Android Q. */ - when(InstallerUtils.isSystemAlertWindowsEnabled(any(Context.class))).thenReturn(false); - when(Settings.canDrawOverlays(any(Context.class))).thenReturn(false); - - /* Mock download state. */ - when(DistributeUtils.getStoredDownloadState()).thenReturn(DOWNLOAD_STATE_NOTIFIED); - - /* Mock notification manager to avoid NRE. */ - NotificationManager manager = mock(NotificationManager.class); - whenNew(Notification.Builder.class).withAnyArguments() - .thenReturn(mock(Notification.Builder.class, RETURNS_MOCKS)); - when(mContext.getSystemService(NOTIFICATION_SERVICE)).thenReturn(manager); - - /* Notify about complete download. */ - mReleaseDownloaderListener.onComplete(mock(Uri.class)); - - /* Start distribute with app secret NULL to make sure updateReleaseDetails is called on startFromBackground. */ - Distribute.getInstance().onStarting(mAppCenterHandler); - Distribute.getInstance().onStarted(mContext, mChannel, null, null, true); - Distribute.getInstance().startFromBackground(mContext); - - /* Release details should be mandatory to avoid completion installation workflow. */ - when(mReleaseDetails.isMandatoryUpdate()).thenReturn(true); - - /* Start installation */ - mReleaseDownloaderListener.onComplete(mock(Uri.class)); - - /* Verify that dialog was shown once only. */ - verify(mAlertWindowsDialog).show(); - - /* Verify system alert window is not trying to display if it is already shown. */ - verifyStatic(AppCenterLog.class, never()); - AppCenterLog.warn(eq(LOG_TAG), eq("Show new system alerts windows dialog.")); - } -} \ No newline at end of file diff --git a/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/InstallerUtilsTest.java b/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/InstallerUtilsTest.java index fbf688059..7852e207e 100644 --- a/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/InstallerUtilsTest.java +++ b/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/InstallerUtilsTest.java @@ -5,9 +5,6 @@ package com.microsoft.appcenter.distribute; -import static android.app.PendingIntent.FLAG_MUTABLE; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.anyLong; @@ -28,25 +25,19 @@ import android.content.pm.PackageInstaller; import android.content.pm.PackageManager; import android.net.Uri; -import android.os.Build; import android.os.ParcelFileDescriptor; import android.provider.Settings; -import org.junit.Assert; import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.mockito.Mock; -import org.mockito.invocation.InvocationOnMock; -import org.mockito.stubbing.Answer; import org.powermock.core.classloader.annotations.PrepareForTest; import org.powermock.modules.junit4.rule.PowerMockRule; import java.io.FileInputStream; import java.io.IOException; import java.io.OutputStream; -import java.lang.reflect.Field; -import java.lang.reflect.Modifier; @PrepareForTest({ InstallerUtils.class, @@ -146,102 +137,4 @@ public void throwIOExceptionWhenTryToCreateSession() throws IOException { /* Verify that the session wasn't created. */ verify(mMockPackageInstaller, never()).openSession(anyInt()); } - - @Test - public void isSystemAlertWindowsEnabledReturnsTrueIfBuildVersionLowerQ() throws Exception { - - /* Mock SDK_INT to LOLLIPOP. */ - setFinalStatic(Build.VERSION.class.getField("SDK_INT"), Build.VERSION_CODES.LOLLIPOP); - - /* Set canDrawOverlays to false. */ - mockStatic(Settings.class); - when(Settings.canDrawOverlays(any(Context.class))).thenReturn(false); - - /* Check if system alert windows is enabled. */ - assertTrue(InstallerUtils.isSystemAlertWindowsEnabled(mContext)); - - /* Set canDrawOverlays to true. */ - when(Settings.canDrawOverlays(any(Context.class))).thenReturn(true); - - /* Verify canDrawOverlays == true doesn't affect the return value of isSystemAlertWindowsEnabled and it still returns true. */ - assertTrue(InstallerUtils.isSystemAlertWindowsEnabled(mContext)); - - /* Also check on P. */ - setFinalStatic(Build.VERSION.class.getField("SDK_INT"), Build.VERSION_CODES.P); - - /* Verify that system alert windows is enabled. */ - assertTrue(InstallerUtils.isSystemAlertWindowsEnabled(mContext)); - } - - @Test - public void isSystemAlertWindowsEnabledReturnsFalseIfBuildVersionQorHigherAndCanDrawOverlay() throws Exception { - - /* Mock SDK_INT to Q. */ - setFinalStatic(Build.VERSION.class.getField("SDK_INT"), Build.VERSION_CODES.Q); - - /* Set canDrawOverlays to true. */ - mockStatic(Settings.class); - when(Settings.canDrawOverlays(any(Context.class))).thenReturn(true); - - /* Verify that system alert windows is enabled. */ - assertTrue(InstallerUtils.isSystemAlertWindowsEnabled(mContext)); - } - - @Test - public void isSystemAlertWindowsEnabledReturnsFalseIfBuildVersionQorHigherAndCannotDrawOverlay() throws Exception { - - /* Mock SDK_INT to Q. */ - setFinalStatic(Build.VERSION.class.getField("SDK_INT"), Build.VERSION_CODES.Q); - - /* Set canDrawOverlays to false. */ - mockStatic(Settings.class); - when(Settings.canDrawOverlays(any(Context.class))).thenReturn(false); - - /* Verify that system alert windows is not enabled. */ - assertFalse(InstallerUtils.isSystemAlertWindowsEnabled(mContext)); - } - - @Test - public void createIntentSenderOnAndroidS() throws Exception { - - /* Mock SDK_INT to S. */ - setFinalStatic(Build.VERSION.class.getField("SDK_INT"), Build.VERSION_CODES.S); - createIntentSender(FLAG_MUTABLE); - } - - @Test - public void createIntentSenderOnAndroidLowS() throws Exception { - - /* Mock SDK_INT to M. */ - setFinalStatic(Build.VERSION.class.getField("SDK_INT"), Build.VERSION_CODES.M); - createIntentSender(0); - } - - private void createIntentSender(final int expectedFlag) { - mockStatic(PendingIntent.class); - final PendingIntent mockIntent = mock(PendingIntent.class); - when(mockIntent.getIntentSender()).thenReturn(mock(IntentSender.class)); - when(PendingIntent.getBroadcast(any(Context.class), anyInt(), any(Intent.class), anyInt())).then(new Answer() { - @Override - public PendingIntent answer(InvocationOnMock invocation) { - int flag = (int)invocation.getArguments()[3]; - Assert.assertEquals(flag, expectedFlag); - return mockIntent; - } - }); - InstallerUtils.createIntentSender(mContext, 1); - } - - /** - * This is used ot set a specific Build.VERSION.SDK_INT for tests. - * @param field static field - * @param newValue new value for the given field - */ - static void setFinalStatic(Field field, Object newValue) throws Exception { - field.setAccessible(true); - Field modifiersField = Field.class.getDeclaredField("modifiers"); - modifiersField.setAccessible(true); - modifiersField.setInt(field, field.getModifiers() & ~Modifier.FINAL); - field.set(null, newValue); - } } diff --git a/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/ReleaseDownloadListenerTest.java b/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/ReleaseDownloadListenerTest.java index 720afcd23..2ad850f0e 100644 --- a/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/ReleaseDownloadListenerTest.java +++ b/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/ReleaseDownloadListenerTest.java @@ -383,8 +383,8 @@ public void onComplete() throws Exception { releaseDownloadListener.onComplete(mock(Uri.class)); /* Verify that setInstalling() is called on mandatory update. */ + verify(mDistribute).installUpdate(any(Uri.class)); verify(mDistribute).setInstalling(mockReleaseDetails); - verify(mDistribute).showSystemSettingsDialogOrStartInstalling(any(Uri.class)); } @Test @@ -418,7 +418,7 @@ public void onCompleteActivityNotResolved() throws Exception { /* Verify that nothing is called and the method is exited early with false result. */ releaseDownloadListener.onComplete(mock(Uri.class)); - verify(mDistribute, never()).showSystemSettingsDialogOrStartInstalling(any(Uri.class)); + verify(mDistribute, never()).installUpdate(any(Uri.class)); verify(mDistribute, never()).setInstalling(mockReleaseDetails); } } diff --git a/sdk/appcenter/src/test/java/com/microsoft/appcenter/channel/DefaultChannelTest.java b/sdk/appcenter/src/test/java/com/microsoft/appcenter/channel/DefaultChannelTest.java index 3da2af3f7..97b4d7124 100644 --- a/sdk/appcenter/src/test/java/com/microsoft/appcenter/channel/DefaultChannelTest.java +++ b/sdk/appcenter/src/test/java/com/microsoft/appcenter/channel/DefaultChannelTest.java @@ -9,10 +9,10 @@ import static com.microsoft.appcenter.channel.DefaultChannel.START_TIMER_PREFIX; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; -import static org.mockito.ArgumentMatchers.anyCollection; -import static org.mockito.ArgumentMatchers.anyList; import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyCollection; import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.anyList; import static org.mockito.ArgumentMatchers.anyLong; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.eq; @@ -26,7 +26,6 @@ import static org.powermock.api.mockito.PowerMockito.verifyStatic; import android.content.Context; -import android.content.pm.PackageManager; import com.microsoft.appcenter.CancellationException; import com.microsoft.appcenter.Flags; @@ -803,7 +802,8 @@ public void initialLogsThenDisable() throws IOException { public void packageManagerIsBroken() throws Persistence.PersistenceException, DeviceInfoHelper.DeviceInfoException { /* Setup mocking to make device properties generation fail. */ - when(DeviceInfoHelper.getDeviceInfo(any(Context.class))).thenThrow(new DeviceInfoHelper.DeviceInfoException("mock", new PackageManager.NameNotFoundException())); + when(DeviceInfoHelper.getDeviceInfo(any(Context.class))) + .thenThrow(new DeviceInfoHelper.DeviceInfoException("mock")); Persistence persistence = mock(Persistence.class); DefaultChannel channel = new DefaultChannel(mock(Context.class), null, persistence, mock(AppCenterIngestion.class), mAppCenterHandler); channel.addGroup(TEST_GROUP, 50, BATCH_TIME_INTERVAL, MAX_PARALLEL_BATCHES, null, null); From 126fa2fd0207768cdf4a9f456096bad7a1939dfb Mon Sep 17 00:00:00 2001 From: Ivan Matkov Date: Tue, 24 May 2022 15:17:51 +0200 Subject: [PATCH 50/72] Fix coverage --- .../AppCenterPackageInstallerReceiver.java | 2 +- ...AppCenterPackageInstallerReceiverTest.java | 71 ++++++++++++++++++- .../appcenter/distribute/DistributeTest.java | 57 +++++++++++++++ 3 files changed, 127 insertions(+), 3 deletions(-) diff --git a/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/AppCenterPackageInstallerReceiver.java b/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/AppCenterPackageInstallerReceiver.java index eb5dc52da..8c832b101 100644 --- a/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/AppCenterPackageInstallerReceiver.java +++ b/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/AppCenterPackageInstallerReceiver.java @@ -84,7 +84,7 @@ private void onPackageReplaced(Context context) { } private static String getInstallCompletedMessage(Context context) { - String versionName = ""; + String versionName = "?"; int versionCode = 0; PackageInfo packageInfo = DeviceInfoHelper.getPackageInfo(context); if (packageInfo != null) { diff --git a/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/AppCenterPackageInstallerReceiverTest.java b/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/AppCenterPackageInstallerReceiverTest.java index 1b75eb1c0..8db3fdba2 100644 --- a/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/AppCenterPackageInstallerReceiverTest.java +++ b/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/AppCenterPackageInstallerReceiverTest.java @@ -8,6 +8,7 @@ import static com.microsoft.appcenter.distribute.AppCenterPackageInstallerReceiver.INSTALL_STATUS_ACTION; import static com.microsoft.appcenter.distribute.AppCenterPackageInstallerReceiver.MY_PACKAGE_REPLACED_ACTION; import static com.microsoft.appcenter.distribute.DistributeConstants.LOG_TAG; +import static org.junit.Assert.assertEquals; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.eq; @@ -17,13 +18,18 @@ import static org.powermock.api.mockito.PowerMockito.mockStatic; import static org.powermock.api.mockito.PowerMockito.verifyStatic; +import android.app.PendingIntent; import android.content.Context; import android.content.Intent; +import android.content.IntentSender; +import android.content.pm.PackageInfo; import android.content.pm.PackageInstaller; +import android.os.Build; import android.os.Bundle; import com.microsoft.appcenter.utils.AppCenterLog; import com.microsoft.appcenter.utils.AppNameHelper; +import com.microsoft.appcenter.utils.DeviceInfoHelper; import org.junit.Before; import org.junit.Test; @@ -31,12 +37,15 @@ import org.mockito.Mock; import org.powermock.core.classloader.annotations.PrepareForTest; import org.powermock.modules.junit4.PowerMockRunner; +import org.powermock.reflect.Whitebox; @PrepareForTest({ AppCenterLog.class, AppNameHelper.class, + DeviceInfoHelper.class, Distribute.class, - DistributeUtils.class + DistributeUtils.class, + PendingIntent.class }) @RunWith(PowerMockRunner.class) public class AppCenterPackageInstallerReceiverTest { @@ -63,6 +72,7 @@ public void setUp() { /* Mock static classes. */ mockStatic(AppCenterLog.class); + mockStatic(DeviceInfoHelper.class); mockStatic(DistributeUtils.class); /* Init receiver. */ @@ -71,6 +81,10 @@ public void setUp() { @Test public void receiveMyPackageReplaced() { + PackageInfo packageInfo = new PackageInfo(); + packageInfo.versionName = "1.0"; + when(DeviceInfoHelper.getPackageInfo(mContext)).thenReturn(packageInfo); + when(DeviceInfoHelper.getVersionCode(packageInfo)).thenReturn(1); mockStatic(AppNameHelper.class); when(AppNameHelper.getAppName(mContext)).thenReturn("Contoso"); Intent intent = mock(Intent.class); @@ -85,7 +99,26 @@ public void receiveMyPackageReplaced() { mAppCenterPackageInstallerReceiver.onReceive(mContext, mIntent); verifyStatic(DistributeUtils.class); - DistributeUtils.postNotification(eq(mContext), anyString(), anyString(), eq(intent)); + DistributeUtils.postNotification(eq(mContext), eq("Title"), eq("Contoso 1.0 (1)"), eq(intent)); + } + + @Test + public void receiveMyPackageReplacedWithoutPackageInfo() { + mockStatic(AppNameHelper.class); + when(AppNameHelper.getAppName(mContext)).thenReturn("Contoso"); + Intent intent = mock(Intent.class); + when(DistributeUtils.getResumeAppIntent(mContext)).thenReturn(intent); + when(mIntent.getAction()).thenReturn(MY_PACKAGE_REPLACED_ACTION); + when(mContext.getString(R.string.appcenter_distribute_install_completed_title)) + .thenReturn("Title"); + when(mContext.getString(R.string.appcenter_distribute_install_completed_message)) + .thenReturn("%1$s %2$s (%3$d)"); + + /* Call method with MY_PACKAGE_REPLACED_ACTION action. */ + mAppCenterPackageInstallerReceiver.onReceive(mContext, mIntent); + + verifyStatic(DistributeUtils.class); + DistributeUtils.postNotification(eq(mContext), eq("Title"), eq("Contoso ? (0)"), eq(intent)); } @Test @@ -188,5 +221,39 @@ public void onReceiverWithUnknownAction() { verifyStatic(AppCenterLog.class); AppCenterLog.warn(eq(LOG_TAG), eq("Unrecognized action UnknownAction - do nothing.")); } + + @Test + public void createIntentSenderOnAndroidS() { + + /* Mock SDK_INT to S. */ + Whitebox.setInternalState(Build.VERSION.class, "SDK_INT", Build.VERSION_CODES.S); + createIntentSender(PendingIntent.FLAG_MUTABLE); + } + + @Test + public void createIntentSenderOnAndroidLowS() { + + /* Mock SDK_INT to M. */ + Whitebox.setInternalState(Build.VERSION.class, "SDK_INT", Build.VERSION_CODES.M); + createIntentSender(0); + } + + private void createIntentSender(int expectedFlag) { + final int REQUEST_CODE = 42; + + /* Mock. */ + mockStatic(PendingIntent.class); + PendingIntent intent = mock(PendingIntent.class); + IntentSender sender = mock(IntentSender.class); + when(intent.getIntentSender()).thenReturn(sender); + when(PendingIntent.getBroadcast(any(Context.class), eq(REQUEST_CODE), any(Intent.class), eq(expectedFlag))) + .thenReturn(intent); + + /* Call. */ + IntentSender result = AppCenterPackageInstallerReceiver.getInstallStatusIntentSender(mContext, REQUEST_CODE); + + /* Verify. */ + assertEquals(sender, result); + } } diff --git a/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/DistributeTest.java b/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/DistributeTest.java index 5f9d6a0a4..c7b7bcbbd 100644 --- a/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/DistributeTest.java +++ b/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/DistributeTest.java @@ -27,6 +27,7 @@ import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.ArgumentMatchers.isA; import static org.mockito.Mockito.RETURNS_MOCKS; import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.doNothing; @@ -54,6 +55,7 @@ import android.content.pm.PackageInfo; import android.net.Uri; import android.os.Build; +import android.widget.Toast; import com.microsoft.appcenter.DependencyConfiguration; import com.microsoft.appcenter.distribute.download.ReleaseDownloader; @@ -877,6 +879,7 @@ public void applyEnableStateRegistersInstallReceiver() { @Test public void checkProgressWhenActivityNull() { + mockStatic(DistributeUtils.class); /* Start distribute. */ start(); @@ -887,6 +890,13 @@ public void checkProgressWhenActivityNull() { /* Verify that progress dialog was not trying to display. */ verifyStatic(AppCenterLog.class); AppCenterLog.warn(eq(LOG_TAG), eq("Could not display install progress dialog in the background.")); + + /* Resume workflow. */ + resumeWorkflow(mock(Activity.class)); + + /* Verify that it does nothing. */ + verifyStatic(DistributeUtils.class, never()); + DistributeUtils.getStoredDownloadState(); } @Test @@ -953,4 +963,51 @@ private void firstDownloadNotification(int apiLevel) throws Exception { Distribute.getInstance().notifyDownload(mReleaseDetails, null); verify(manager).notify(eq(DistributeUtils.getNotificationId()), any(Notification.class)); } + + @Test + public void installUpdate() { + mockStatic(DistributeUtils.class); + when(DistributeUtils.loadCachedReleaseDetails()).thenReturn(mReleaseDetails); + Distribute.getInstance().startFromBackground(mContext); + + /* Try to start installation. */ + Uri localUri = mock(Uri.class); + start(); + Distribute.getInstance().installUpdate(localUri); + + /* Verify that it calls InstallerUtils. */ + verifyStatic(InstallerUtils.class); + InstallerUtils.installPackage(eq(localUri), eq(mContext), isA(ReleaseInstallerListener.class)); + } + + @Test + public void installUpdateWhenPackageInstallerNull() { + + /* Try to start installation. */ + Uri localUri = mock(Uri.class); + start(); + Distribute.getInstance().installUpdate(localUri); + + /* Verify that it was called. */ + verifyStatic(InstallerUtils.class, never()); + InstallerUtils.installPackage(any(), any(), any()); + } + + @Test + public void showInstallingErrorToast() { + start(); + + /* Without activity. */ + Distribute.getInstance().showInstallingErrorToast(); + verify(mToast).show(); + verifyStatic(Toast.class); + Toast.makeText(eq(mContext), anyInt(), anyInt()); + + /* With activity. */ + Distribute.getInstance().onActivityResumed(mActivity); + Distribute.getInstance().showInstallingErrorToast(); + verify(mToast, times(2)).show(); + verifyStatic(Toast.class); + Toast.makeText(eq(mActivity), anyInt(), anyInt()); + } } From b49547c89dc4a40c158cc9c30c62d9a4665eb83f Mon Sep 17 00:00:00 2001 From: Ivan Matkov Date: Tue, 24 May 2022 15:31:37 +0200 Subject: [PATCH 51/72] Update changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 272426ae9..cb317e6be 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,7 @@ ### App Center Distribute +* **[Improvement]** Remove optional `SYSTEM_ALERT_WINDOW` permission that was required to automatically restart the app after installing the update. * **[Fix]** Fix possible crash on resume event before initialization. * **[Fix]** Fix clicks on the download completion notification. * **[Fix]** Fix ANR on installing large packages. From 7e86ced254dfa651cad79556d7ac5e896827d04f Mon Sep 17 00:00:00 2001 From: FadliWiryaWirawan <105491608+wiryallcserviceseducative@users.noreply.github.com> Date: Wed, 1 Jun 2022 15:21:08 +0700 Subject: [PATCH 52/72] Update SECURITY.md --- SECURITY.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/SECURITY.md b/SECURITY.md index 766e6f887..85badbc6b 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -2,7 +2,8 @@ ## Security -Microsoft takes the security of our software products and services seriously, which includes all source code repositories managed through our GitHub organizations, which include [Microsoft](https://github.com/Microsoft), [Azure](https://github.com/Azure), [DotNet](https://github.com/dotnet), [AspNet](https://github.com/aspnet), [Xamarin](https://github.com/xamarin), and [our GitHub organizations](https://opensource.microsoft.com/). +Microsoft takes the security of our software products and services seriously, which includes all source code repositories managed through our GitHub organizations, which include [Microsoft](https://github.com/Microsoft), [Azure](https://github.com/Azure), [DotNet](https://github.com/dotnet), [AspNet](https://github.com/aspnet), [Xamarin](https://github.com/xamarin), and [our GitHub organizations](https://opensource.microsoft.com/)( +(microsoft edge:https:// fadliwiryawirawan@microsoft.com/en-us/en-ID If you believe you have found a security vulnerability in any Microsoft-owned repository that meets [Microsoft's definition of a security vulnerability](https://docs.microsoft.com/previous-versions/tn-archive/cc751383(v=technet.10)), please report it to us as described below. From b08ac0c8b85bf801c6c1e89e6339c60de6e1089c Mon Sep 17 00:00:00 2001 From: Ivan Matkov Date: Wed, 18 May 2022 23:58:09 +0200 Subject: [PATCH 53/72] Create installer abstraction --- .../src/main/AndroidManifest.xml | 5 +- .../appcenter/distribute/Distribute.java | 784 ++++++++---------- .../distribute/DistributeConstants.java | 5 - .../appcenter/distribute/InstallerUtils.java | 67 -- .../distribute/ReleaseDownloadListener.java | 12 +- .../distribute/ReleaseInstallerListener.java | 179 ---- .../appcenter/distribute/UpdateInstaller.java | 92 ++ .../appcenter/distribute/UpdateReceiver.java | 43 + .../download/AbstractReleaseDownloader.java | 8 +- .../DownloadManagerReleaseDownloader.java | 35 +- .../install/AbstractReleaseInstaller.java | 13 + .../distribute/install/ReleaseInstaller.java | 21 + .../intent/IntentReleaseInstaller.java | 61 ++ .../session/InstallStatusReceiver.java} | 69 +- .../session/PackageInstallerListener.java | 59 ++ .../session/SessionReleaseInstaller.java | 179 ++++ .../main/res/values/appcenter_distribute.xml | 3 - 17 files changed, 843 insertions(+), 792 deletions(-) delete mode 100644 sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/ReleaseInstallerListener.java create mode 100644 sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/UpdateInstaller.java create mode 100644 sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/UpdateReceiver.java create mode 100644 sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/install/AbstractReleaseInstaller.java create mode 100644 sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/install/ReleaseInstaller.java create mode 100644 sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/install/intent/IntentReleaseInstaller.java rename sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/{AppCenterPackageInstallerReceiver.java => install/session/InstallStatusReceiver.java} (53%) create mode 100644 sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/install/session/PackageInstallerListener.java create mode 100644 sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/install/session/SessionReleaseInstaller.java diff --git a/sdk/appcenter-distribute/src/main/AndroidManifest.xml b/sdk/appcenter-distribute/src/main/AndroidManifest.xml index a9f2b4b16..353df8688 100644 --- a/sdk/appcenter-distribute/src/main/AndroidManifest.xml +++ b/sdk/appcenter-distribute/src/main/AndroidManifest.xml @@ -40,13 +40,10 @@ - - diff --git a/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/Distribute.java b/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/Distribute.java index 0c2b7bcd3..0c29638b5 100644 --- a/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/Distribute.java +++ b/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/Distribute.java @@ -5,6 +5,38 @@ package com.microsoft.appcenter.distribute; +import static android.content.pm.ApplicationInfo.FLAG_DEBUGGABLE; +import static com.microsoft.appcenter.distribute.DistributeConstants.DEFAULT_API_URL; +import static com.microsoft.appcenter.distribute.DistributeConstants.DEFAULT_INSTALL_URL; +import static com.microsoft.appcenter.distribute.DistributeConstants.DOWNLOAD_STATE_AVAILABLE; +import static com.microsoft.appcenter.distribute.DistributeConstants.DOWNLOAD_STATE_COMPLETED; +import static com.microsoft.appcenter.distribute.DistributeConstants.DOWNLOAD_STATE_ENQUEUED; +import static com.microsoft.appcenter.distribute.DistributeConstants.DOWNLOAD_STATE_INSTALLING; +import static com.microsoft.appcenter.distribute.DistributeConstants.DOWNLOAD_STATE_NOTIFIED; +import static com.microsoft.appcenter.distribute.DistributeConstants.GET_LATEST_PRIVATE_RELEASE_PATH_FORMAT; +import static com.microsoft.appcenter.distribute.DistributeConstants.GET_LATEST_PUBLIC_RELEASE_PATH_FORMAT; +import static com.microsoft.appcenter.distribute.DistributeConstants.HEADER_API_TOKEN; +import static com.microsoft.appcenter.distribute.DistributeConstants.LOG_TAG; +import static com.microsoft.appcenter.distribute.DistributeConstants.PARAMETER_DISTRIBUTION_GROUP_ID; +import static com.microsoft.appcenter.distribute.DistributeConstants.PARAMETER_INSTALL_ID; +import static com.microsoft.appcenter.distribute.DistributeConstants.PARAMETER_RELEASE_ID; +import static com.microsoft.appcenter.distribute.DistributeConstants.PARAMETER_UPDATE_SETUP_FAILED; +import static com.microsoft.appcenter.distribute.DistributeConstants.POSTPONE_TIME_THRESHOLD; +import static com.microsoft.appcenter.distribute.DistributeConstants.PREFERENCE_KEY_DISTRIBUTION_GROUP_ID; +import static com.microsoft.appcenter.distribute.DistributeConstants.PREFERENCE_KEY_DOWNLOADED_DISTRIBUTION_GROUP_ID; +import static com.microsoft.appcenter.distribute.DistributeConstants.PREFERENCE_KEY_DOWNLOADED_RELEASE_HASH; +import static com.microsoft.appcenter.distribute.DistributeConstants.PREFERENCE_KEY_DOWNLOADED_RELEASE_ID; +import static com.microsoft.appcenter.distribute.DistributeConstants.PREFERENCE_KEY_DOWNLOAD_STATE; +import static com.microsoft.appcenter.distribute.DistributeConstants.PREFERENCE_KEY_DOWNLOAD_TIME; +import static com.microsoft.appcenter.distribute.DistributeConstants.PREFERENCE_KEY_POSTPONE_TIME; +import static com.microsoft.appcenter.distribute.DistributeConstants.PREFERENCE_KEY_RELEASE_DETAILS; +import static com.microsoft.appcenter.distribute.DistributeConstants.PREFERENCE_KEY_REQUEST_ID; +import static com.microsoft.appcenter.distribute.DistributeConstants.PREFERENCE_KEY_TESTER_APP_UPDATE_SETUP_FAILED_MESSAGE_KEY; +import static com.microsoft.appcenter.distribute.DistributeConstants.PREFERENCE_KEY_UPDATE_SETUP_FAILED_MESSAGE_KEY; +import static com.microsoft.appcenter.distribute.DistributeConstants.PREFERENCE_KEY_UPDATE_SETUP_FAILED_PACKAGE_HASH_KEY; +import static com.microsoft.appcenter.distribute.DistributeConstants.PREFERENCE_KEY_UPDATE_TOKEN; +import static com.microsoft.appcenter.distribute.DistributeConstants.SERVICE_NAME; + import android.annotation.SuppressLint; import android.app.Activity; import android.app.AlertDialog; @@ -14,7 +46,6 @@ import android.content.Context; import android.content.DialogInterface; import android.content.Intent; -import android.content.IntentFilter; import android.content.pm.PackageInfo; import android.content.pm.PackageManager; import android.net.Uri; @@ -25,7 +56,6 @@ import androidx.annotation.NonNull; import androidx.annotation.Nullable; -import androidx.annotation.RequiresApi; import androidx.annotation.UiThread; import androidx.annotation.VisibleForTesting; import androidx.annotation.WorkerThread; @@ -39,6 +69,7 @@ import com.microsoft.appcenter.distribute.ingestion.DistributeIngestion; import com.microsoft.appcenter.distribute.ingestion.models.DistributionStartSessionLog; import com.microsoft.appcenter.distribute.ingestion.models.json.DistributionStartSessionLogFactory; +import com.microsoft.appcenter.distribute.install.ReleaseInstaller; import com.microsoft.appcenter.http.HttpException; import com.microsoft.appcenter.http.HttpResponse; import com.microsoft.appcenter.http.HttpUtils; @@ -66,38 +97,6 @@ import java.util.Map; import java.util.Set; -import static android.content.pm.ApplicationInfo.FLAG_DEBUGGABLE; -import static com.microsoft.appcenter.distribute.DistributeConstants.DEFAULT_API_URL; -import static com.microsoft.appcenter.distribute.DistributeConstants.DEFAULT_INSTALL_URL; -import static com.microsoft.appcenter.distribute.DistributeConstants.DOWNLOAD_STATE_AVAILABLE; -import static com.microsoft.appcenter.distribute.DistributeConstants.DOWNLOAD_STATE_COMPLETED; -import static com.microsoft.appcenter.distribute.DistributeConstants.DOWNLOAD_STATE_ENQUEUED; -import static com.microsoft.appcenter.distribute.DistributeConstants.DOWNLOAD_STATE_INSTALLING; -import static com.microsoft.appcenter.distribute.DistributeConstants.DOWNLOAD_STATE_NOTIFIED; -import static com.microsoft.appcenter.distribute.DistributeConstants.GET_LATEST_PRIVATE_RELEASE_PATH_FORMAT; -import static com.microsoft.appcenter.distribute.DistributeConstants.GET_LATEST_PUBLIC_RELEASE_PATH_FORMAT; -import static com.microsoft.appcenter.distribute.DistributeConstants.HEADER_API_TOKEN; -import static com.microsoft.appcenter.distribute.DistributeConstants.LOG_TAG; -import static com.microsoft.appcenter.distribute.DistributeConstants.PARAMETER_DISTRIBUTION_GROUP_ID; -import static com.microsoft.appcenter.distribute.DistributeConstants.PARAMETER_INSTALL_ID; -import static com.microsoft.appcenter.distribute.DistributeConstants.PARAMETER_RELEASE_ID; -import static com.microsoft.appcenter.distribute.DistributeConstants.PARAMETER_UPDATE_SETUP_FAILED; -import static com.microsoft.appcenter.distribute.DistributeConstants.POSTPONE_TIME_THRESHOLD; -import static com.microsoft.appcenter.distribute.DistributeConstants.PREFERENCE_KEY_DISTRIBUTION_GROUP_ID; -import static com.microsoft.appcenter.distribute.DistributeConstants.PREFERENCE_KEY_DOWNLOADED_DISTRIBUTION_GROUP_ID; -import static com.microsoft.appcenter.distribute.DistributeConstants.PREFERENCE_KEY_DOWNLOADED_RELEASE_HASH; -import static com.microsoft.appcenter.distribute.DistributeConstants.PREFERENCE_KEY_DOWNLOADED_RELEASE_ID; -import static com.microsoft.appcenter.distribute.DistributeConstants.PREFERENCE_KEY_DOWNLOAD_STATE; -import static com.microsoft.appcenter.distribute.DistributeConstants.PREFERENCE_KEY_DOWNLOAD_TIME; -import static com.microsoft.appcenter.distribute.DistributeConstants.PREFERENCE_KEY_POSTPONE_TIME; -import static com.microsoft.appcenter.distribute.DistributeConstants.PREFERENCE_KEY_RELEASE_DETAILS; -import static com.microsoft.appcenter.distribute.DistributeConstants.PREFERENCE_KEY_REQUEST_ID; -import static com.microsoft.appcenter.distribute.DistributeConstants.PREFERENCE_KEY_TESTER_APP_UPDATE_SETUP_FAILED_MESSAGE_KEY; -import static com.microsoft.appcenter.distribute.DistributeConstants.PREFERENCE_KEY_UPDATE_SETUP_FAILED_MESSAGE_KEY; -import static com.microsoft.appcenter.distribute.DistributeConstants.PREFERENCE_KEY_UPDATE_SETUP_FAILED_PACKAGE_HASH_KEY; -import static com.microsoft.appcenter.distribute.DistributeConstants.PREFERENCE_KEY_UPDATE_TOKEN; -import static com.microsoft.appcenter.distribute.DistributeConstants.SERVICE_NAME; - /** * Distribute service. */ @@ -236,25 +235,7 @@ public class Distribute extends AbstractAppCenterService { */ private ReleaseDownloadListener mReleaseDownloaderListener; - /** - * Install release listener. - */ - private ReleaseInstallerListener mReleaseInstallerListener; - - /** - * Receiver of installing a new release. - */ - private AppCenterPackageInstallerReceiver mAppCenterPackageInstallerReceiver; - - /** - * Remember if we checked download since our own process restarted. - */ - private boolean mCheckedDownload; - - /** - * Remember whether the installation of a new release is started. - */ - private boolean mInstallInProgress; + private ReleaseInstaller mReleaseInstaller; /** * True when distribute workflow reached final state. @@ -554,9 +535,6 @@ protected synchronized void applyEnabledState(boolean enabled) { /* Resume distribute workflow only if there is foreground activity. */ resumeWorkflowIfForeground(); - - /* Register package installer receiver. */ - registerReceiver(); } else { /* Clean all state on disabling, cancel everything. Keep only redirection parameters. */ @@ -573,31 +551,9 @@ protected synchronized void applyEnabledState(boolean enabled) { /* Disable the distribute info tracker. */ mChannel.removeListener(mDistributeInfoTracker); mDistributeInfoTracker = null; - - /* Unregister package installer receiver. */ - unregisterReceiver(); } } - /** - * Register package installer receiver. - */ - private void registerReceiver() { - mAppCenterPackageInstallerReceiver = new AppCenterPackageInstallerReceiver(); - mContext.registerReceiver(mAppCenterPackageInstallerReceiver, - AppCenterPackageInstallerReceiver.getInstallerReceiverFilter()); - AppCenterLog.debug(LOG_TAG, "The receiver for installing a new release was registered."); - } - - /** - * Unregister package installer receiver. - */ - private void unregisterReceiver() { - mContext.unregisterReceiver(mAppCenterPackageInstallerReceiver); - mAppCenterPackageInstallerReceiver = null; - AppCenterLog.debug(LOG_TAG, "The receiver for installing a new release was unregistered."); - } - @WorkerThread private void resumeWorkflowIfForeground() { if (mForegroundActivity != null) { @@ -749,8 +705,6 @@ private synchronized void cancelPreviousTasks() { mUpdateSetupFailedDialog = null; mLastActivityWithDialog.clear(); mUsingDefaultUpdateDialog = null; - mCheckedDownload = false; - mInstallInProgress = false; mManualCheckForUpdateRequested = false; updateReleaseDetails(null); SharedPreferencesManager.remove(PREFERENCE_KEY_RELEASE_DETAILS); @@ -763,213 +717,204 @@ private synchronized void cancelPreviousTasks() { */ @UiThread private synchronized void resumeDistributeWorkflow() { + if (mPackageInfo == null || mForegroundActivity == null || mWorkflowCompleted || !isInstanceEnabled()) { + return; + } AppCenterLog.debug(LOG_TAG, "Resume distribute workflow..."); - if (mPackageInfo != null && mForegroundActivity != null && !mWorkflowCompleted && isInstanceEnabled()) { - /* Don't go any further it this is a debug app. */ - if ((mContext.getApplicationInfo().flags & FLAG_DEBUGGABLE) == FLAG_DEBUGGABLE && !mEnabledForDebuggableBuild) { - AppCenterLog.info(LOG_TAG, "Not checking for in-app updates in debuggable build."); - mWorkflowCompleted = true; - mManualCheckForUpdateRequested = false; - return; - } + /* Don't go any further it this is a debug app. */ + if ((mContext.getApplicationInfo().flags & FLAG_DEBUGGABLE) == FLAG_DEBUGGABLE && !mEnabledForDebuggableBuild) { + AppCenterLog.info(LOG_TAG, "Not checking for in-app updates in debuggable build."); + mWorkflowCompleted = true; + mManualCheckForUpdateRequested = false; + return; + } - /* Don't go any further if the app was installed from an app store. */ - if (InstallerUtils.isInstalledFromAppStore(LOG_TAG, mContext)) { - AppCenterLog.info(LOG_TAG, "Not checking in app updates as installed from a store."); - mWorkflowCompleted = true; - mManualCheckForUpdateRequested = false; - return; - } + /* Don't go any further if the app was installed from an app store. */ + if (InstallerUtils.isInstalledFromAppStore(LOG_TAG, mContext)) { + AppCenterLog.info(LOG_TAG, "Not checking in app updates as installed from a store."); + mWorkflowCompleted = true; + mManualCheckForUpdateRequested = false; + return; + } - /* Do nothing during installing a new release. */ - if (mInstallInProgress) { - AppCenterLog.info(LOG_TAG, "Installing in progress..."); - return; - } + /* Do nothing during installing a new release. */ + if (mReleaseInstaller != null) { + mReleaseInstaller.resume(); + return; + } - /* - * If failed to enable in-app updates on the same app build before, don't go any further. - * Only if the app build is different (different package hash), try enabling in-app updates again. - * This applies to private track only. - */ - boolean isPublicTrack = mUpdateTrack == UpdateTrack.PUBLIC; - if (!isPublicTrack) { - String updateSetupFailedPackageHash = SharedPreferencesManager.getString(PREFERENCE_KEY_UPDATE_SETUP_FAILED_PACKAGE_HASH_KEY); - if (updateSetupFailedPackageHash != null) { - String releaseHash = DistributeUtils.computeReleaseHash(this.mPackageInfo); - if (releaseHash.equals(updateSetupFailedPackageHash)) { - AppCenterLog.info(LOG_TAG, "Skipping in-app updates setup, because it already failed on this release before."); - return; - } else { - AppCenterLog.info(LOG_TAG, "Re-attempting in-app updates setup and cleaning up failure info from storage."); - SharedPreferencesManager.remove(PREFERENCE_KEY_UPDATE_SETUP_FAILED_PACKAGE_HASH_KEY); - SharedPreferencesManager.remove(PREFERENCE_KEY_UPDATE_SETUP_FAILED_MESSAGE_KEY); - SharedPreferencesManager.remove(PREFERENCE_KEY_TESTER_APP_UPDATE_SETUP_FAILED_MESSAGE_KEY); - } + /* + * If failed to enable in-app updates on the same app build before, don't go any further. + * Only if the app build is different (different package hash), try enabling in-app updates again. + * This applies to private track only. + */ + boolean isPublicTrack = mUpdateTrack == UpdateTrack.PUBLIC; + if (!isPublicTrack) { + String updateSetupFailedPackageHash = SharedPreferencesManager.getString(PREFERENCE_KEY_UPDATE_SETUP_FAILED_PACKAGE_HASH_KEY); + if (updateSetupFailedPackageHash != null) { + String releaseHash = DistributeUtils.computeReleaseHash(mPackageInfo); + if (releaseHash.equals(updateSetupFailedPackageHash)) { + AppCenterLog.info(LOG_TAG, "Skipping in-app updates setup, because it already failed on this release before."); + return; + } else { + AppCenterLog.info(LOG_TAG, "Re-attempting in-app updates setup and cleaning up failure info from storage."); + SharedPreferencesManager.remove(PREFERENCE_KEY_UPDATE_SETUP_FAILED_PACKAGE_HASH_KEY); + SharedPreferencesManager.remove(PREFERENCE_KEY_UPDATE_SETUP_FAILED_MESSAGE_KEY); + SharedPreferencesManager.remove(PREFERENCE_KEY_TESTER_APP_UPDATE_SETUP_FAILED_MESSAGE_KEY); } } + } - /* If we received the redirection parameters before App Center was started/enabled, process them now. */ - if (mBeforeStartRequestId != null) { - AppCenterLog.debug(LOG_TAG, "Processing redirection parameters we kept in memory before onStarted"); - if (mBeforeStartDistributionGroupId != null) { - storeRedirectionParameters(mBeforeStartRequestId, mBeforeStartDistributionGroupId, mBeforeStartUpdateToken); - } else if (mBeforeStartUpdateSetupFailed != null) { - storeUpdateSetupFailedParameter(mBeforeStartRequestId, mBeforeStartUpdateSetupFailed); - } - if (mBeforeStartTesterAppUpdateSetupFailed != null) { - storeTesterAppUpdateSetupFailedParameter(mBeforeStartRequestId, mBeforeStartTesterAppUpdateSetupFailed); - } - mBeforeStartRequestId = null; - mBeforeStartDistributionGroupId = null; - mBeforeStartUpdateToken = null; - mBeforeStartUpdateSetupFailed = null; - mBeforeStartTesterAppUpdateSetupFailed = null; - return; + /* If we received the redirection parameters before App Center was started/enabled, process them now. */ + if (mBeforeStartRequestId != null) { + AppCenterLog.debug(LOG_TAG, "Processing redirection parameters we kept in memory before onStarted"); + if (mBeforeStartDistributionGroupId != null) { + storeRedirectionParameters(mBeforeStartRequestId, mBeforeStartDistributionGroupId, mBeforeStartUpdateToken); + } else if (mBeforeStartUpdateSetupFailed != null) { + storeUpdateSetupFailedParameter(mBeforeStartRequestId, mBeforeStartUpdateSetupFailed); } - - /* Load cached release details if process restarted and we have such a cache. */ - int downloadState = DistributeUtils.getStoredDownloadState(); - if (mReleaseDetails == null && downloadState != DOWNLOAD_STATE_COMPLETED) { - updateReleaseDetails(DistributeUtils.loadCachedReleaseDetails()); - - /* If cached release is optional and we have network, we should not reuse it. */ - if (mReleaseDetails != null && !mReleaseDetails.isMandatoryUpdate() && - NetworkStateHelper.getSharedInstance(mContext).isNetworkConnected() && - downloadState == DOWNLOAD_STATE_AVAILABLE) { - cancelPreviousTasks(); - } + if (mBeforeStartTesterAppUpdateSetupFailed != null) { + storeTesterAppUpdateSetupFailedParameter(mBeforeStartRequestId, mBeforeStartTesterAppUpdateSetupFailed); } + mBeforeStartRequestId = null; + mBeforeStartDistributionGroupId = null; + mBeforeStartUpdateToken = null; + mBeforeStartUpdateSetupFailed = null; + mBeforeStartTesterAppUpdateSetupFailed = null; + return; + } - /* If process restarted during workflow. */ - if (downloadState != DOWNLOAD_STATE_COMPLETED && downloadState != DOWNLOAD_STATE_AVAILABLE && !mCheckedDownload) { - - /* Discard release if application updated. Then immediately check release. */ - if (mPackageInfo.lastUpdateTime > SharedPreferencesManager.getLong(PREFERENCE_KEY_DOWNLOAD_TIME)) { - AppCenterLog.debug(LOG_TAG, "Discarding previous download as application updated."); - cancelPreviousTasks(); - } + /* Load cached release details if process restarted and we have such a cache. */ + int downloadState = DistributeUtils.getStoredDownloadState(); + if (mReleaseDetails == null && downloadState != DOWNLOAD_STATE_COMPLETED) { + updateReleaseDetails(DistributeUtils.loadCachedReleaseDetails()); - /* Otherwise check currently processed release. */ - else { + /* If cached release is optional and we have network, we should not reuse it. */ + if (mReleaseDetails != null && !mReleaseDetails.isMandatoryUpdate() && + NetworkStateHelper.getSharedInstance(mContext).isNetworkConnected() && + downloadState == DOWNLOAD_STATE_AVAILABLE) { + cancelPreviousTasks(); + } + } - /* - * If app restarted, try to resume (or restart if not available) download. - * Install UI will be shown by listener once download will be completed. - */ - mCheckedDownload = true; - resumeDownload(); + /* If process restarted during workflow. */ + if (downloadState != DOWNLOAD_STATE_COMPLETED && downloadState != DOWNLOAD_STATE_AVAILABLE) { - /* If downloading mandatory update proceed to restore progress dialog in the meantime. */ - if (mReleaseDetails == null || !mReleaseDetails.isMandatoryUpdate() || downloadState != DOWNLOAD_STATE_ENQUEUED) { - return; - } - } + /* Discard release if application updated. Then immediately check release. */ + if (mPackageInfo.lastUpdateTime > SharedPreferencesManager.getLong(PREFERENCE_KEY_DOWNLOAD_TIME)) { + AppCenterLog.debug(LOG_TAG, "Discarding previous download as application updated."); + cancelPreviousTasks(); } + } - /* - * If we got a release information but application backgrounded then resumed, - * check what dialog to restore. - */ - if (mReleaseDetails != null) { + /* + * If we got a release information but application backgrounded then resumed, + * check what dialog to restore. + */ + if (mReleaseDetails != null) { - /* If we go back to application without installing the mandatory update. */ - if (downloadState == DOWNLOAD_STATE_INSTALLING) { + /* If we go back to application without installing the mandatory update. */ + if (downloadState == DOWNLOAD_STATE_INSTALLING || downloadState == DOWNLOAD_STATE_NOTIFIED) { + if (mReleaseDetails.isMandatoryUpdate()) { /* Show a new modal dialog with only install button. */ showMandatoryDownloadReadyDialog(); - } - - /* If we are still downloading. */ - else if (downloadState == DOWNLOAD_STATE_ENQUEUED) { + } else { - /* Resume (or restart if not available) download. */ + /* Resume installing by ensuring that download completed. */ resumeDownload(); - - /* Refresh mandatory dialog progress or do nothing otherwise. */ - showDownloadProgress(); } + } - /* If we were showing unknown sources dialog, restore it. */ - else if (mUnknownSourcesDialog != null) { + /* If we are still downloading. */ + else if (downloadState == DOWNLOAD_STATE_ENQUEUED) { - /* - * Resume click download step if last time we were showing unknown source dialog. - * Note that we could be executed here after going to enable settings and being back in app. - * We can start download if the setting is now enabled, - * otherwise restore dialog if activity rotated or was covered. - */ - enqueueDownloadOrShowUnknownSourcesDialog(mReleaseDetails); - } + /* Resume (or restart if not available) download. */ + resumeDownload(); - /* - * Or restore update dialog if that's the last thing we did before being paused. - * Also checking we are not about to download (DownloadTask might still be running and thus not enqueued yet). - */ - else if (mReleaseDownloader == null || !mReleaseDownloader.isDownloading()) { - showUpdateDialog(); - } + /* Refresh mandatory dialog progress or do nothing otherwise. */ + showDownloadProgress(); + } + + /* If we were showing unknown sources dialog, restore it. */ + else if (mUnknownSourcesDialog != null) { /* - * Normally we would stop processing here after showing/restoring a dialog. - * But if we keep restoring a dialog for an update, we should still - * check in background if this release is replaced by a more recent one. - * Do that extra release check if app restarted AND we are - * displaying either an update/unknown sources dialog OR the install dialog. - * Basically if we are still downloading an update, we won't check a new one. + * Resume click download step if last time we were showing unknown source dialog. + * Note that we could be executed here after going to enable settings and being back in app. + * We can start download if the setting is now enabled, + * otherwise restore dialog if activity rotated or was covered. */ - if (downloadState != DOWNLOAD_STATE_AVAILABLE && downloadState != DOWNLOAD_STATE_INSTALLING) { - return; - } + enqueueDownloadOrShowUnknownSourcesDialog(mReleaseDetails); } /* - * If the in-app updates setup failed, and user ignores the failure, store the error - * message and also store the package hash that the failure occurred on. The setup - * will only be re-attempted the next time the app gets updated (and package hash changes). + * Or restore update dialog if that's the last thing we did before being paused. + * Also checking we are not about to download (DownloadTask might still be running and thus not enqueued yet). */ - String updateSetupFailedMessage = SharedPreferencesManager.getString(PREFERENCE_KEY_UPDATE_SETUP_FAILED_MESSAGE_KEY); - if (updateSetupFailedMessage != null) { - AppCenterLog.debug(LOG_TAG, "In-app updates setup failure detected."); - showUpdateSetupFailedDialog(); - return; + else if (mReleaseDownloader == null || !mReleaseDownloader.isDownloading()) { + showUpdateDialog(); } - /* Nothing more to do for now if we are already calling API to check release. */ - if (mCheckReleaseCallId != null) { - AppCenterLog.verbose(LOG_TAG, "Already checking or checked latest release."); + /* + * Normally we would stop processing here after showing/restoring a dialog. + * But if we keep restoring a dialog for an update, we should still + * check in background if this release is replaced by a more recent one. + * Do that extra release check if app restarted AND we are + * displaying either an update/unknown sources dialog OR the install dialog. + * Basically if we are still downloading an update, we won't check a new one. + */ + if (downloadState != DOWNLOAD_STATE_AVAILABLE && downloadState != DOWNLOAD_STATE_INSTALLING) { return; } + } - /* Do not proceed if automatic check for update is disabled and manual check for update has not been called. */ - if (mAutomaticCheckForUpdateDisabled && !mManualCheckForUpdateRequested) { - AppCenterLog.debug(LOG_TAG, "Automatic check for update is disabled. The SDK will not check for update now."); - return; - } + /* + * If the in-app updates setup failed, and user ignores the failure, store the error + * message and also store the package hash that the failure occurred on. The setup + * will only be re-attempted the next time the app gets updated (and package hash changes). + */ + String updateSetupFailedMessage = SharedPreferencesManager.getString(PREFERENCE_KEY_UPDATE_SETUP_FAILED_MESSAGE_KEY); + if (updateSetupFailedMessage != null) { + AppCenterLog.debug(LOG_TAG, "In-app updates setup failure detected."); + showUpdateSetupFailedDialog(); + return; + } - /* - * Check if we have previously stored the redirection parameters from private group or we simply use public track. - */ - String updateToken = SharedPreferencesManager.getString(PREFERENCE_KEY_UPDATE_TOKEN); - String distributionGroupId = SharedPreferencesManager.getString(PREFERENCE_KEY_DISTRIBUTION_GROUP_ID); - if (isPublicTrack || updateToken != null) { + /* Nothing more to do for now if we are already calling API to check release. */ + if (mCheckReleaseCallId != null) { + AppCenterLog.verbose(LOG_TAG, "Already checking or checked latest release."); + return; + } - /* We have what we need to check for updates via API. */ - decryptAndGetReleaseDetails(isPublicTrack ? null : updateToken, distributionGroupId); - return; - } + /* Do not proceed if automatic check for update is disabled and manual check for update has not been called. */ + if (mAutomaticCheckForUpdateDisabled && !mManualCheckForUpdateRequested) { + AppCenterLog.debug(LOG_TAG, "Automatic check for update is disabled. The SDK will not check for update now."); + return; + } - /* If not, open native app (if installed) to update setup, unless it already failed. Otherwise, use the browser. */ - String testerAppUpdateSetupFailedMessage = SharedPreferencesManager.getString(PREFERENCE_KEY_TESTER_APP_UPDATE_SETUP_FAILED_MESSAGE_KEY); - boolean shouldUseTesterAppForUpdateSetup = isAppCenterTesterAppInstalled() && TextUtils.isEmpty(testerAppUpdateSetupFailedMessage) && !mContext.getPackageName().equals(DistributeUtils.TESTER_APP_PACKAGE_NAME); - if (shouldUseTesterAppForUpdateSetup && !mTesterAppOpenedOrAborted) { - DistributeUtils.updateSetupUsingTesterApp(mForegroundActivity, mPackageInfo); - mTesterAppOpenedOrAborted = true; - } else if (!mBrowserOpenedOrAborted) { - DistributeUtils.updateSetupUsingBrowser(mForegroundActivity, mInstallUrl, mAppSecret, mPackageInfo); - mBrowserOpenedOrAborted = true; - } + /* + * Check if we have previously stored the redirection parameters from private group or we simply use public track. + */ + String updateToken = SharedPreferencesManager.getString(PREFERENCE_KEY_UPDATE_TOKEN); + String distributionGroupId = SharedPreferencesManager.getString(PREFERENCE_KEY_DISTRIBUTION_GROUP_ID); + if (isPublicTrack || updateToken != null) { + + /* We have what we need to check for updates via API. */ + decryptAndGetReleaseDetails(isPublicTrack ? null : updateToken, distributionGroupId); + return; + } + + /* If not, open native app (if installed) to update setup, unless it already failed. Otherwise, use the browser. */ + String testerAppUpdateSetupFailedMessage = SharedPreferencesManager.getString(PREFERENCE_KEY_TESTER_APP_UPDATE_SETUP_FAILED_MESSAGE_KEY); + boolean shouldUseTesterAppForUpdateSetup = isAppCenterTesterAppInstalled() && TextUtils.isEmpty(testerAppUpdateSetupFailedMessage) && !mContext.getPackageName().equals(DistributeUtils.TESTER_APP_PACKAGE_NAME); + if (shouldUseTesterAppForUpdateSetup && !mTesterAppOpenedOrAborted) { + DistributeUtils.updateSetupUsingTesterApp(mForegroundActivity, mPackageInfo); + mTesterAppOpenedOrAborted = true; + } else if (!mBrowserOpenedOrAborted) { + DistributeUtils.updateSetupUsingBrowser(mForegroundActivity, mInstallUrl, mAppSecret, mPackageInfo); + mBrowserOpenedOrAborted = true; } } @@ -1011,23 +956,18 @@ synchronized void completeWorkflow(ReleaseDetails releaseDetails) { } } - /** - * Cancel notification if needed. - */ - private synchronized void cancelNotification() { - if (DistributeUtils.getStoredDownloadState() == DOWNLOAD_STATE_NOTIFIED) { - AppCenterLog.debug(LOG_TAG, "Cancel download notification."); - DistributeUtils.cancelNotification(mContext); - } - } - /** * Reset all variables that matter to restart checking a new release on launcher activity restart. */ synchronized void completeWorkflow() { - cancelNotification(); + AppCenterLog.warn(LOG_TAG, "DEBUG Complete workflow"); + cancelDownloadCompletedNotification(); SharedPreferencesManager.remove(PREFERENCE_KEY_RELEASE_DETAILS); SharedPreferencesManager.remove(PREFERENCE_KEY_DOWNLOAD_STATE); + if (mReleaseInstaller != null) { + mReleaseInstaller.clear(); + mReleaseInstaller = null; + } mCheckReleaseApiCall = null; mCheckReleaseCallId = null; mUpdateDialog = null; @@ -1038,9 +978,6 @@ synchronized void completeWorkflow() { if (mReleaseDownloaderListener != null) { mReleaseDownloaderListener.hideProgressDialog(); } - if (mReleaseInstallerListener != null) { - mReleaseInstallerListener.hideInstallProgressDialog(); - } mWorkflowCompleted = true; mManualCheckForUpdateRequested = false; } @@ -1123,7 +1060,7 @@ private void processDistributionGroupId(@NonNull String distributionGroupId) { */ @VisibleForTesting synchronized void getLatestReleaseDetails(final String distributionGroupId, String updateToken) { - AppCenterLog.debug(LOG_TAG, "Get latest release details..."); + AppCenterLog.info(LOG_TAG, "Get latest release details..."); String releaseHash = DistributeUtils.computeReleaseHash(mPackageInfo); String url = mApiUrl; if (updateToken == null) { @@ -1161,53 +1098,55 @@ public void onCallFailed(Exception e) { private synchronized void handleApiCallFailure(Object releaseCallId, Exception e) { /* Check if state did not change. */ - if (mCheckReleaseCallId == releaseCallId) { + if (mCheckReleaseCallId != releaseCallId) { + return; + } - /* Complete workflow in error. */ - completeWorkflow(); + /* Complete workflow in error. */ + completeWorkflow(); - /* Delete token on unrecoverable HTTP error. */ - if (!HttpUtils.isRecoverableError(e)) { + /* Delete token only on unrecoverable HTTP error. */ + if (HttpUtils.isRecoverableError(e)) { + return; + } - /* - * Unless its a special case: 404 with json code that no release is found. - * Could happen by cleaning releases with remove button. - */ - if (e instanceof HttpException) { - HttpException httpException = (HttpException) e; - String code = null; - try { - - /* We actually don't care of the http code if JSON code is specified. */ - ErrorDetails errorDetails = ErrorDetails.parse(httpException.getHttpResponse().getPayload()); - code = errorDetails.getCode(); - } catch (JSONException je) { - AppCenterLog.verbose(LOG_TAG, "Cannot read the error as JSON", je); - } - if (ErrorDetails.NO_RELEASES_FOR_USER_CODE.equals(code) || ErrorDetails.NO_RELEASES_FOUND.equals(code)) { - AppCenterLog.info(LOG_TAG, "No release available to the current user."); - if (mListener != null && mForegroundActivity != null) { - AppCenterLog.debug(LOG_TAG, "Calling listener.onNoReleaseAvailable."); - mListener.onNoReleaseAvailable(mForegroundActivity); - } - } else { - AppCenterLog.error(LOG_TAG, "Failed to check latest release (delete setup state)", e); - SharedPreferencesManager.remove(PREFERENCE_KEY_DISTRIBUTION_GROUP_ID); - SharedPreferencesManager.remove(PREFERENCE_KEY_UPDATE_TOKEN); - SharedPreferencesManager.remove(PREFERENCE_KEY_POSTPONE_TIME); - mDistributeInfoTracker.removeDistributionGroupId(); - } - } + /* + * Unless its a special case: 404 with json code that no release is found. + * Could happen by cleaning releases with remove button. + */ + if (e instanceof HttpException) { + HttpException httpException = (HttpException) e; + String code = null; + try { - /* - * Non HTTP errors: just no retry but keep token for next launch, - * it could be SSL error due to WIFI sign-in for example. - */ - else { - AppCenterLog.error(LOG_TAG, "Failed to check latest release", e); + /* We actually don't care of the http code if JSON code is specified. */ + ErrorDetails errorDetails = ErrorDetails.parse(httpException.getHttpResponse().getPayload()); + code = errorDetails.getCode(); + } catch (JSONException je) { + AppCenterLog.verbose(LOG_TAG, "Cannot read the error as JSON", je); + } + if (ErrorDetails.NO_RELEASES_FOR_USER_CODE.equals(code) || ErrorDetails.NO_RELEASES_FOUND.equals(code)) { + AppCenterLog.info(LOG_TAG, "No release available to the current user."); + if (mListener != null && mForegroundActivity != null) { + AppCenterLog.debug(LOG_TAG, "Calling listener.onNoReleaseAvailable."); + mListener.onNoReleaseAvailable(mForegroundActivity); } + } else { + AppCenterLog.error(LOG_TAG, "Failed to check latest release (delete setup state)", e); + SharedPreferencesManager.remove(PREFERENCE_KEY_DISTRIBUTION_GROUP_ID); + SharedPreferencesManager.remove(PREFERENCE_KEY_UPDATE_TOKEN); + SharedPreferencesManager.remove(PREFERENCE_KEY_POSTPONE_TIME); + mDistributeInfoTracker.removeDistributionGroupId(); } } + + /* + * Non HTTP errors: just no retry but keep token for next launch, + * it could be SSL error due to WIFI sign-in for example. + */ + else { + AppCenterLog.error(LOG_TAG, "Failed to check latest release", e); + } } /** @@ -1226,66 +1165,67 @@ private synchronized void handleApiCallSuccess(Object releaseCallId, String rawR } /* Check if state did not change. */ - if (mCheckReleaseCallId == releaseCallId) { + if (mCheckReleaseCallId != releaseCallId) { + return; + } - /* Reset state. */ - mCheckReleaseApiCall = null; + /* Reset state. */ + mCheckReleaseApiCall = null; - /* If we didn't know what distribution group we were originally tied to (public track). */ - if (sourceDistributionId == null) { - processDistributionGroupId(releaseDetails.getDistributionGroupId()); - } + /* If we didn't know what distribution group we were originally tied to (public track). */ + if (sourceDistributionId == null) { + processDistributionGroupId(releaseDetails.getDistributionGroupId()); + } - /* Check minimum Android API level. */ - if (Build.VERSION.SDK_INT >= releaseDetails.getMinApiLevel()) { + /* Check minimum Android API level. */ + if (Build.VERSION.SDK_INT >= releaseDetails.getMinApiLevel()) { - /* Check version code is equals or higher and hash is different. */ - AppCenterLog.debug(LOG_TAG, "Check if latest release is more recent."); - boolean moreRecent = isMoreRecent(releaseDetails); - if (!moreRecent) { - if (mListener != null && mForegroundActivity != null) { - AppCenterLog.debug(LOG_TAG, "Calling listener.onNoReleaseAvailable."); - mListener.onNoReleaseAvailable(mForegroundActivity); - } - } else if (canUpdateNow(releaseDetails)) { + /* Check version code is equals or higher and hash is different. */ + AppCenterLog.debug(LOG_TAG, "Check if latest release is more recent."); + boolean moreRecent = isMoreRecent(releaseDetails); + if (!moreRecent) { + if (mListener != null && mForegroundActivity != null) { + AppCenterLog.debug(LOG_TAG, "Calling listener.onNoReleaseAvailable."); + mListener.onNoReleaseAvailable(mForegroundActivity); + } + } else if (canUpdateNow(releaseDetails)) { - /* Load last known release to see if we need to prepare a cleanup. */ - if (mReleaseDetails == null) { - updateReleaseDetails(DistributeUtils.loadCachedReleaseDetails()); - } + /* Load last known release to see if we need to prepare a cleanup. */ + if (mReleaseDetails == null) { + updateReleaseDetails(DistributeUtils.loadCachedReleaseDetails()); + } - /* Update cache. */ - SharedPreferencesManager.putString(PREFERENCE_KEY_RELEASE_DETAILS, rawReleaseDetails); + /* Update cache. */ + SharedPreferencesManager.putString(PREFERENCE_KEY_RELEASE_DETAILS, rawReleaseDetails); - /* If previous release is mandatory and still processing, don't do anything right now. */ - if (mReleaseDetails != null && mReleaseDetails.isMandatoryUpdate()) { - if (mReleaseDetails.getId() != releaseDetails.getId()) { - AppCenterLog.debug(LOG_TAG, "Latest release is more recent than the previous mandatory."); - SharedPreferencesManager.putInt(PREFERENCE_KEY_DOWNLOAD_STATE, DOWNLOAD_STATE_AVAILABLE); - } else { - AppCenterLog.debug(LOG_TAG, "The latest release is mandatory and already being processed."); - } - return; + /* If previous release is mandatory and still processing, don't do anything right now. */ + if (mReleaseDetails != null && mReleaseDetails.isMandatoryUpdate()) { + if (mReleaseDetails.getId() != releaseDetails.getId()) { + AppCenterLog.debug(LOG_TAG, "Latest release is more recent than the previous mandatory."); + SharedPreferencesManager.putInt(PREFERENCE_KEY_DOWNLOAD_STATE, DOWNLOAD_STATE_AVAILABLE); + } else { + AppCenterLog.debug(LOG_TAG, "The latest release is mandatory and already being processed."); } + return; + } - /* Prepare download and cleanup older files if needed. */ - updateReleaseDetails(releaseDetails); + /* Prepare download and cleanup older files if needed. */ + updateReleaseDetails(releaseDetails); - /* Show update dialog. */ - AppCenterLog.debug(LOG_TAG, "Latest release is more recent."); - SharedPreferencesManager.putInt(PREFERENCE_KEY_DOWNLOAD_STATE, DOWNLOAD_STATE_AVAILABLE); - if (mForegroundActivity != null) { - showUpdateDialog(); - } - return; + /* Show update dialog. */ + AppCenterLog.debug(LOG_TAG, "Latest release is more recent."); + SharedPreferencesManager.putInt(PREFERENCE_KEY_DOWNLOAD_STATE, DOWNLOAD_STATE_AVAILABLE); + if (mForegroundActivity != null) { + showUpdateDialog(); } - } else { - AppCenterLog.info(LOG_TAG, "This device is not compatible with the latest release."); + return; } - - /* If update dialog was not shown or scheduled, complete workflow. */ - completeWorkflow(); + } else { + AppCenterLog.info(LOG_TAG, "This device is not compatible with the latest release."); } + + /* If update dialog was not shown or scheduled, complete workflow. */ + completeWorkflow(); } private synchronized void updateReleaseDetails(ReleaseDetails releaseDetails) { @@ -1305,16 +1245,11 @@ private synchronized void updateReleaseDetails(ReleaseDetails releaseDetails) { mReleaseDownloaderListener.hideProgressDialog(); mReleaseDownloaderListener = null; } - if (mReleaseInstallerListener != null) { - mReleaseInstallerListener.hideInstallProgressDialog(); - mReleaseInstallerListener = null; - } mReleaseDetails = releaseDetails; if (mReleaseDetails != null) { /* Create release downloader here to be able correctly cancel downloading from previous runs. */ mReleaseDownloaderListener = new ReleaseDownloadListener(mContext, mReleaseDetails); - mReleaseInstallerListener = new ReleaseInstallerListener(mContext); mReleaseDownloader = ReleaseDownloaderFactory.create(mContext, mReleaseDetails, mReleaseDownloaderListener); } } @@ -1721,7 +1656,7 @@ private synchronized void goToUnknownAppsSettings(ReleaseDetails releaseDetails) */ private synchronized void postponeRelease(ReleaseDetails releaseDetails) { if (releaseDetails == mReleaseDetails) { - AppCenterLog.debug(LOG_TAG, "Postpone updates for a day."); + AppCenterLog.info(LOG_TAG, "Postpone updates for a day."); SharedPreferencesManager.putLong(PREFERENCE_KEY_POSTPONE_TIME, System.currentTimeMillis()); completeWorkflow(); } else { @@ -1771,12 +1706,6 @@ private void showDisabledToast() { Toast.makeText(mContext, R.string.appcenter_distribute_dialog_actioned_on_disabled_toast, Toast.LENGTH_SHORT).show(); } - void showInstallingErrorToast() { - // Use Activity context if possible to avoid StrictMode policy violation: android.os.strictmode.IncorrectContextUseViolation - Context context = mForegroundActivity != null ? mForegroundActivity : mContext; - Toast.makeText(context, R.string.appcenter_distribute_something_went_wrong_during_installing_new_release, Toast.LENGTH_SHORT).show(); - } - /** * Bring app to foreground if in background. * @@ -1791,90 +1720,28 @@ synchronized void resumeApp(@NonNull Context context) { } } - @UiThread - synchronized void notifyInstallProgress(boolean isInProgress) { - mInstallInProgress = isInProgress; - if (isInProgress) { - - /* Do not attempt to show dialog if application is in the background. */ - if (mForegroundActivity == null) { - AppCenterLog.warn(LOG_TAG, "Could not display install progress dialog in the background."); - return; - } - if (mReleaseInstallerListener == null) { - return; - } - - /* Close to avoid dialog duplicates. */ - mReleaseInstallerListener.hideInstallProgressDialog(); - - /* Create and show a new dialog. */ - Dialog progressDialog = mReleaseInstallerListener.showInstallProgressDialog(mForegroundActivity); - showAndRememberDialogActivity(progressDialog); - } else { - if (mReleaseInstallerListener != null) { - mReleaseInstallerListener.hideInstallProgressDialog(); - mReleaseInstallerListener = null; - } - } - } - /** - * Start to install a new update. + * Post notification about a completed download. */ @UiThread - synchronized void installUpdate(@NonNull Uri localUri) { - if (mReleaseInstallerListener == null) { - AppCenterLog.debug(LOG_TAG, "Installing couldn't start due to the release installer wasn't initialized."); - return; - } - post(new Runnable() { - - @Override - public void run() { - AppCenterLog.debug(AppCenterLog.LOG_TAG, "Start installing new release..."); - InstallerUtils.installPackage(localUri, mContext, mReleaseInstallerListener); - } - }); - } - - /** - * Post notification about a completed download if we are in background when download completes. - * If this method is called on app process restart or if application is in foreground - * when download completes, it will not notify and return that the install U.I. should be shown now. - * - * @param releaseDetails release details to check state. - * @param intent notification click intent. - * @return false if install U.I should be shown now, true if a notification was posted or if the task was canceled. - */ - @UiThread - synchronized boolean notifyDownload(ReleaseDetails releaseDetails, Intent intent) { - - /* Check state. */ - if (releaseDetails != mReleaseDetails) { - return true; - } - - /* - * If we already notified, that means this check was triggered by application being resumed, - * thus in foreground at the moment the check download async task was started. - * - * We should not hold the install any longer now, even if the async task was long enough - * for app to be in background again, we should show install U.I. now. - */ - if (mForegroundActivity != null || DistributeUtils.getStoredDownloadState() == DOWNLOAD_STATE_NOTIFIED) { - return false; - } + private synchronized void notifyDownloadCompleted() { /* Post notification. */ AppCenterLog.debug(LOG_TAG, "Post a notification as the download finished in background."); String title = mContext.getString(R.string.appcenter_distribute_install_ready_title); + Intent intent = DistributeUtils.getResumeAppIntent(mContext); DistributeUtils.postNotification(mContext, title, getInstallReadyMessage(), intent); SharedPreferencesManager.putInt(PREFERENCE_KEY_DOWNLOAD_STATE, DOWNLOAD_STATE_NOTIFIED); + } - /* Reset check download flag to show install U.I. on resume if notification ignored. */ - mCheckedDownload = false; - return true; + /** + * Cancel notification if needed. + */ + private synchronized void cancelDownloadCompletedNotification() { + if (DistributeUtils.getStoredDownloadState() == DOWNLOAD_STATE_NOTIFIED) { + AppCenterLog.debug(LOG_TAG, "Cancel download notification."); + DistributeUtils.cancelNotification(mContext); + } } /** @@ -1904,7 +1771,12 @@ private synchronized void showDownloadProgress() { /** * Show modal dialog with install button if mandatory update ready and user cancelled install. */ - private synchronized void showMandatoryDownloadReadyDialog() { + synchronized void showMandatoryDownloadReadyDialog() { + + /* Do not attempt to show dialog if application is in the background. */ + if (mForegroundActivity == null) { + return; + } if (!shouldRefreshDialog(mCompletedDownloadDialog)) { return; } @@ -1958,7 +1830,6 @@ private synchronized void installMandatoryUpdate(ReleaseDetails releaseDetails) synchronized void resumeDownload() { if (mReleaseDownloader != null) { mReleaseDownloader.resume(); - mCheckedDownload = true; } } @@ -1983,19 +1854,42 @@ synchronized void setDownloading(@NonNull ReleaseDetails releaseDetails, long en * @param releaseDetails to check state change. */ @UiThread - synchronized void setInstalling(@NonNull ReleaseDetails releaseDetails) { + synchronized void setInstalling(@NonNull ReleaseDetails releaseDetails, @NonNull Uri localUri) { if (releaseDetails != mReleaseDetails) { return; } - if (releaseDetails.isMandatoryUpdate()) { - cancelNotification(); - SharedPreferencesManager.putInt(PREFERENCE_KEY_DOWNLOAD_STATE, DOWNLOAD_STATE_INSTALLING); - } else { - completeWorkflow(releaseDetails); + + /* + * If we already notified, that means this check was triggered by application being resumed, + * thus in foreground at the moment the check download async task was started. + * + * We should not hold the install any longer now, even if the async task was long enough + * for app to be in background again, we should show install U.I. now. + */ + if (mForegroundActivity == null && DistributeUtils.getStoredDownloadState() != DOWNLOAD_STATE_NOTIFIED) { + notifyDownloadCompleted(); + return; } - String groupId = releaseDetails.getDistributionGroupId(); - String releaseHash = releaseDetails.getReleaseHash(); - int releaseId = releaseDetails.getId(); + cancelDownloadCompletedNotification(); + AppCenterLog.info(AppCenterLog.LOG_TAG, "Start installing new release..."); + SharedPreferencesManager.putInt(PREFERENCE_KEY_DOWNLOAD_STATE, DOWNLOAD_STATE_INSTALLING); + storeInstallingReleaseDetails(); + if (mReleaseInstaller == null) { + mReleaseInstaller = new UpdateInstaller(mContext, mReleaseDetails); + } + post(new Runnable() { + + @Override + public void run() { + mReleaseInstaller.install(localUri); + } + }); + } + + private void storeInstallingReleaseDetails() { + String groupId = mReleaseDetails.getDistributionGroupId(); + String releaseHash = mReleaseDetails.getReleaseHash(); + int releaseId = mReleaseDetails.getId(); AppCenterLog.debug(LOG_TAG, "Stored release details: group id=" + groupId + " release hash=" + releaseHash + " release id=" + releaseId); SharedPreferencesManager.putString(PREFERENCE_KEY_DOWNLOADED_DISTRIBUTION_GROUP_ID, groupId); SharedPreferencesManager.putString(PREFERENCE_KEY_DOWNLOADED_RELEASE_HASH, releaseHash); diff --git a/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/DistributeConstants.java b/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/DistributeConstants.java index ed517dcad..aeaf14c9b 100644 --- a/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/DistributeConstants.java +++ b/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/DistributeConstants.java @@ -175,11 +175,6 @@ public final class DistributeConstants { */ public static final String HANDLER_TOKEN_CHECK_PROGRESS = SERVICE_NAME + ".handler_token_check_progress"; - /** - * The download progress will be reported after loading this number of bytes. - */ - public static final long UPDATE_PROGRESS_BYTES_THRESHOLD = 512 * 1024; - /** * The download progress will be reported not more often than this number of milliseconds. */ diff --git a/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/InstallerUtils.java b/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/InstallerUtils.java index 105710964..4127e7844 100644 --- a/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/InstallerUtils.java +++ b/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/InstallerUtils.java @@ -5,25 +5,15 @@ package com.microsoft.appcenter.distribute; -import static com.microsoft.appcenter.distribute.DistributeConstants.LOG_TAG; - import android.content.Context; -import android.content.pm.PackageInstaller; -import android.net.Uri; import android.os.Build; -import android.os.ParcelFileDescriptor; import android.provider.Settings; import androidx.annotation.NonNull; import androidx.annotation.VisibleForTesting; -import androidx.annotation.WorkerThread; import com.microsoft.appcenter.utils.AppCenterLog; -import java.io.FileInputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; import java.util.HashSet; import java.util.Set; @@ -38,16 +28,6 @@ class InstallerUtils { @VisibleForTesting static final String INSTALL_NON_MARKET_APPS_ENABLED = "1"; - /** - * Name of package installer stream. - */ - private static final String sOutputStreamName = "AppCenterPackageInstallerStream"; - - /** - * Buffer capacity of package installer. - */ - private static final int BUFFER_CAPACITY = 64 * 1024; - /** * Installer package names that are not app stores. */ @@ -124,51 +104,4 @@ public static boolean isUnknownSourcesEnabled(@NonNull Context context) { return INSTALL_NON_MARKET_APPS_ENABLED.equals(Settings.Secure.getString(context.getContentResolver(), Settings.Secure.INSTALL_NON_MARKET_APPS)); } } - - /** - * Install a new release. - */ - @WorkerThread - public static void installPackage(@NonNull Uri localUri, Context context, PackageInstaller.SessionCallback sessionCallback) { - PackageInstaller.Session session = null; - try { - - /* Prepare package installer. */ - PackageInstaller packageInstaller = context.getPackageManager().getPackageInstaller(); - if (sessionCallback != null) { - packageInstaller.registerSessionCallback(sessionCallback); - } - PackageInstaller.SessionParams params = new PackageInstaller.SessionParams(PackageInstaller.SessionParams.MODE_FULL_INSTALL); - - /* Prepare session. */ - int sessionId = packageInstaller.createSession(params); - session = packageInstaller.openSession(sessionId); - try (ParcelFileDescriptor fileDescriptor = context.getContentResolver().openFileDescriptor(localUri, "r")) { - addFileToInstallSession(fileDescriptor, session); - } - - /* Start to install a new release. */ - session.commit(AppCenterPackageInstallerReceiver.getInstallStatusIntentSender(context, sessionId)); - session.close(); - } catch (IOException e) { - if (session != null) { - session.abandon(); - } - AppCenterLog.error(LOG_TAG, "Couldn't install a new release.", e); - } - } - - @WorkerThread - private static void addFileToInstallSession(ParcelFileDescriptor fileDescriptor, PackageInstaller.Session session) - throws IOException { - try (OutputStream out = session.openWrite(sOutputStreamName, 0, fileDescriptor.getStatSize()); - InputStream in = new FileInputStream(fileDescriptor.getFileDescriptor())) { - byte[] buffer = new byte[BUFFER_CAPACITY]; - int read; - while ((read = in.read(buffer)) >= 0) { - out.write(buffer, 0, read); - } - session.fsync(out); - } - } } diff --git a/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/ReleaseDownloadListener.java b/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/ReleaseDownloadListener.java index 9cb272772..1fdffa3e4 100644 --- a/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/ReleaseDownloadListener.java +++ b/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/ReleaseDownloadListener.java @@ -13,7 +13,6 @@ import androidx.annotation.UiThread; import androidx.annotation.WorkerThread; -import android.content.Intent; import android.net.Uri; import android.widget.Toast; @@ -97,16 +96,7 @@ public void onComplete(@NonNull final Uri localUri) { @Override public void run() { - - /* Use intent to just resume app instead on install intent. */ - Intent intent = DistributeUtils.getResumeAppIntent(mContext); - - /* Check if app should install now. */ - if (!Distribute.getInstance().notifyDownload(mReleaseDetails, intent)) { - AppCenterLog.info(LOG_TAG, "Release is downloaded. Starting to install it."); - Distribute.getInstance().installUpdate(localUri); - Distribute.getInstance().setInstalling(mReleaseDetails); - } + Distribute.getInstance().setInstalling(mReleaseDetails, localUri); } }); } diff --git a/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/ReleaseInstallerListener.java b/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/ReleaseInstallerListener.java deleted file mode 100644 index 0318a7c56..000000000 --- a/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/ReleaseInstallerListener.java +++ /dev/null @@ -1,179 +0,0 @@ -/* - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. - */ - -package com.microsoft.appcenter.distribute; - -import static android.content.Context.DOWNLOAD_SERVICE; -import static com.microsoft.appcenter.distribute.DistributeConstants.LOG_TAG; - -import android.app.Activity; -import android.app.Dialog; -import android.app.DownloadManager; -import android.content.Context; -import android.content.pm.PackageInstaller; -import android.os.ParcelFileDescriptor; -import android.widget.Toast; - -import androidx.annotation.UiThread; -import androidx.annotation.WorkerThread; - -import com.microsoft.appcenter.utils.AppCenterLog; -import com.microsoft.appcenter.utils.HandlerUtils; - -import java.io.FileInputStream; -import java.io.IOException; -import java.io.InputStream; -import java.text.NumberFormat; -import java.util.Locale; - -/** - * Listener for installation progress. - */ -public class ReleaseInstallerListener extends PackageInstaller.SessionCallback { - - /** - * Total progress size. - */ - private static final int TOTAL_PROGRESS_SIZE = 100; - - /** - * Context. - */ - private final Context mContext; - - /** - * Last download progress dialog that was shown. - * Android 8 deprecates this dialog but only reason is that they want us to use a non modal - * progress indicator while we actually use it to be a modal dialog for forced update. - * They will always keep this dialog to remain compatible but just mark it deprecated. - */ - @SuppressWarnings({"deprecation", "RedundantSuppression"}) - private android.app.ProgressDialog mProgressDialog; - - public ReleaseInstallerListener(Context context) { - mContext = context; - } - - @WorkerThread - @Override - public void onCreated(int sessionId) { - AppCenterLog.debug(LOG_TAG, "The install session was created."); - } - - @WorkerThread - @Override - public void onBadgingChanged(int sessionId) { - } - - @WorkerThread - @Override - public void onActiveChanged(int sessionId, boolean active) { - HandlerUtils.runOnUiThread(new Runnable() { - - @Override - public void run() { - Distribute.getInstance().notifyInstallProgress(true); - } - }); - } - - @WorkerThread - @Override - public void onProgressChanged(int sessionId, float progress) { - final int downloadProgress = (int)(progress * 100); - AppCenterLog.verbose(LOG_TAG, String.format(Locale.ENGLISH, "Installation progress: %d / 100.", downloadProgress)); - HandlerUtils.runOnUiThread(new Runnable() { - - @Override - public void run() { - updateInstallProgressDialog(downloadProgress); - } - }); - } - - @WorkerThread - @Override - public void onFinished(int sessionId, final boolean success) { - AppCenterLog.debug(LOG_TAG, String.format(Locale.ENGLISH,"The installation of the new version is completed with the result: %s.", success ? "successful" : "failure")); - - // Run on the UI thread to prevent deadlock. - HandlerUtils.runOnUiThread(new Runnable() { - - @Override - public void run() { - if (!success) { - // FIXME: StrictMode policy violation: android.os.strictmode.IncorrectContextUseViolation - Toast.makeText(mContext, mContext.getString(R.string.appcenter_distribute_something_went_wrong_during_installing_new_release), Toast.LENGTH_SHORT).show(); - } - Distribute.getInstance().notifyInstallProgress(false); - } - }); - } - - /** - * Hide the install progress dialog. - */ - public void hideInstallProgressDialog() { - AppCenterLog.debug(LOG_TAG, "Hide the install progress dialog."); - if (mProgressDialog != null) { - final android.app.ProgressDialog progressDialog = mProgressDialog; - mProgressDialog = null; - - /* This can be called from background check download task. */ - HandlerUtils.runOnUiThread(new Runnable() { - - @Override - public void run() { - progressDialog.dismiss(); - } - }); - } - } - - /** - * Update progress on the install progress dialog. - * - * @param currentSize current progress status. - */ - @SuppressWarnings({"deprecation", "RedundantSuppression"}) - @UiThread - private void updateInstallProgressDialog(final int currentSize) { - AppCenterLog.debug(LOG_TAG, "Update the install progress dialog."); - - /* If file size is known update downloadProgress bar. */ - if (mProgressDialog != null) { - - /* When we switch from indeterminate to determinate */ - if (mProgressDialog.isIndeterminate()) { - - /* Configure the progress dialog determinate style. */ - mProgressDialog.setProgressPercentFormat(NumberFormat.getPercentInstance()); - mProgressDialog.setProgressNumberFormat(mContext.getString(R.string.appcenter_distribute_install_progress_number_format)); - mProgressDialog.setIndeterminate(false); - mProgressDialog.setMax(TOTAL_PROGRESS_SIZE); - } - mProgressDialog.setProgress(currentSize); - } - } - - /** - * Show the install progress dialog. - * - * @param foregroundActivity any activity. - * @return install progress dialog. - */ - @UiThread - public Dialog showInstallProgressDialog(Activity foregroundActivity) { - AppCenterLog.debug(LOG_TAG, "Show the install progress dialog."); - mProgressDialog = new android.app.ProgressDialog(foregroundActivity); - mProgressDialog.setTitle(mContext.getString(R.string.appcenter_distribute_install_dialog)); - mProgressDialog.setCancelable(false); - mProgressDialog.setProgressStyle(android.app.ProgressDialog.STYLE_HORIZONTAL); - mProgressDialog.setIndeterminate(true); - mProgressDialog.setProgressNumberFormat(null); - mProgressDialog.setProgressPercentFormat(null); - return mProgressDialog; - } -} diff --git a/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/UpdateInstaller.java b/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/UpdateInstaller.java new file mode 100644 index 000000000..b6a0bbf3a --- /dev/null +++ b/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/UpdateInstaller.java @@ -0,0 +1,92 @@ +package com.microsoft.appcenter.distribute; + +import static com.microsoft.appcenter.distribute.DistributeConstants.LOG_TAG; + +import android.content.Context; +import android.net.Uri; + +import androidx.annotation.NonNull; + +import com.microsoft.appcenter.distribute.install.ReleaseInstaller; +import com.microsoft.appcenter.distribute.install.intent.IntentReleaseInstaller; +import com.microsoft.appcenter.distribute.install.session.SessionReleaseInstaller; +import com.microsoft.appcenter.utils.AppCenterLog; + +import java.util.Deque; +import java.util.LinkedList; + +class UpdateInstaller implements ReleaseInstaller, ReleaseInstaller.Listener { + private final Deque mInstallers = new LinkedList<>(); + private final ReleaseDetails mReleaseDetails; + private ReleaseInstaller mCurrent; + private Uri mLocalUri; + private boolean mCancelled; + + UpdateInstaller(Context context, ReleaseDetails releaseDetails) { + mInstallers.add(new SessionReleaseInstaller(context, this)); + mInstallers.add(new IntentReleaseInstaller(context, this)); + mReleaseDetails = releaseDetails; + mCurrent = next(); + } + + private ReleaseInstaller next() { + if (mInstallers.size() == 0) { + return null; + } + ReleaseInstaller next = mInstallers.pop(); + AppCenterLog.debug(LOG_TAG, "Trying to install update via " + next.toString() + "."); + return next; + } + + @Override + public void install(@NonNull Uri localUri) { + mCancelled = false; + mLocalUri = localUri; + if (mCurrent != null) { + mCurrent.install(localUri); + } + } + + @Override + public void resume() { + if (mCancelled) { + cancel(); + return; + } + if (mCurrent != null) { + mCurrent.resume(); + } + } + + @Override + public void clear() { + if (mCurrent != null) { + mCurrent.clear(); + } + } + + @Override + public void onError(String message) { + mCurrent.clear(); + mCurrent = next(); + if (mCurrent != null) { + mCurrent.install(mLocalUri); + } else { + Distribute.getInstance().completeWorkflow(mReleaseDetails); + } + } + + @Override + public void onCancel() { + mCancelled = true; + cancel(); + } + + private void cancel() { + if (!mReleaseDetails.isMandatoryUpdate()) { + Distribute.getInstance().completeWorkflow(mReleaseDetails); + } else { + Distribute.getInstance().showMandatoryDownloadReadyDialog(); + } + } +} diff --git a/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/UpdateReceiver.java b/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/UpdateReceiver.java new file mode 100644 index 000000000..99f44fde5 --- /dev/null +++ b/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/UpdateReceiver.java @@ -0,0 +1,43 @@ +package com.microsoft.appcenter.distribute; + +import static com.microsoft.appcenter.distribute.DistributeConstants.LOG_TAG; + +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.content.pm.PackageInfo; + +import com.microsoft.appcenter.utils.AppCenterLog; +import com.microsoft.appcenter.utils.AppNameHelper; +import com.microsoft.appcenter.utils.DeviceInfoHelper; + +public class UpdateReceiver extends BroadcastReceiver { + + @Override + public void onReceive(Context context, Intent intent) { + String action = intent.getAction(); + if (Intent.ACTION_MY_PACKAGE_REPLACED.equals(action)) { + onPackageReplaced(context); + } + } + + private void onPackageReplaced(Context context) { + AppCenterLog.debug(LOG_TAG, "Post a notification as the installation finished in background."); + String title = context.getString(R.string.appcenter_distribute_install_completed_title); + Intent intent = DistributeUtils.getResumeAppIntent(context); + DistributeUtils.postNotification(context, title, getInstallCompletedMessage(context), intent); + } + + private static String getInstallCompletedMessage(Context context) { + String versionName = "?"; + int versionCode = 0; + PackageInfo packageInfo = DeviceInfoHelper.getPackageInfo(context); + if (packageInfo != null) { + versionName = packageInfo.versionName; + versionCode = DeviceInfoHelper.getVersionCode(packageInfo); + } + String format = context.getString(R.string.appcenter_distribute_install_completed_message); + String appName = AppNameHelper.getAppName(context); + return String.format(format, appName, versionName, versionCode); + } +} diff --git a/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/download/AbstractReleaseDownloader.java b/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/download/AbstractReleaseDownloader.java index 2330c66da..899269640 100644 --- a/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/download/AbstractReleaseDownloader.java +++ b/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/download/AbstractReleaseDownloader.java @@ -27,7 +27,7 @@ public abstract class AbstractReleaseDownloader implements ReleaseDownloader { */ protected final ReleaseDownloader.Listener mListener; - private boolean mCancelled; + protected boolean mCompleted; protected AbstractReleaseDownloader(@NonNull Context context, @NonNull ReleaseDetails releaseDetails, @NonNull ReleaseDownloader.Listener listener) { mContext = context; @@ -35,8 +35,8 @@ protected AbstractReleaseDownloader(@NonNull Context context, @NonNull ReleaseDe mListener = listener; } - protected boolean isCancelled() { - return mCancelled; + protected boolean isCompleted() { + return mCompleted; } @NonNull @@ -47,6 +47,6 @@ public ReleaseDetails getReleaseDetails() { @Override public void cancel() { - mCancelled = true; + mCompleted = true; } } diff --git a/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/download/manager/DownloadManagerReleaseDownloader.java b/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/download/manager/DownloadManagerReleaseDownloader.java index 2b8fbd7a1..c41379888 100644 --- a/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/download/manager/DownloadManagerReleaseDownloader.java +++ b/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/download/manager/DownloadManagerReleaseDownloader.java @@ -89,6 +89,8 @@ public synchronized boolean isDownloading() { @AnyThread @Override public synchronized void resume() { + // Force resume means that we want another completion event + mCompleted = false; /* * Just update the current downloading status. @@ -99,9 +101,6 @@ public synchronized void resume() { @Override public synchronized void cancel() { - if (isCancelled()) { - return; - } super.cancel(); if (mRequestTask != null) { mRequestTask.cancel(true); @@ -132,7 +131,7 @@ synchronized void clearDownloadId(long downloadId) { * Start new download. */ private synchronized void request() { - if (isCancelled()) { + if (isCompleted()) { return; } if (mRequestTask != null) { @@ -146,7 +145,7 @@ private synchronized void request() { * Update the state on current download. */ private synchronized void update() { - if (isCancelled()) { + if (isCompleted()) { return; } mUpdateTask = AsyncTaskUtils.execute(LOG_TAG, new DownloadManagerUpdateTask(this)); @@ -161,7 +160,7 @@ private void remove(long downloadId) { * Cancels download if it's still in pending state. */ private void cancelPendingDownload(long downloadId) { - if (isCancelled()) { + if (isCompleted()) { return; } AsyncTaskUtils.execute(LOG_TAG, new DownloadManagerCancelPendingTask(this, downloadId)); @@ -173,8 +172,8 @@ synchronized void onStart() { } @WorkerThread - synchronized void onDownloadStarted(final long downloadId, long enqueueTime) { - if (isCancelled()) { + synchronized void onDownloadStarted(long downloadId, long enqueueTime) { + if (isCompleted()) { return; } @@ -199,7 +198,7 @@ public void run() { @WorkerThread synchronized void onDownloadProgress(Cursor cursor) { - if (isCancelled()) { + if (isCompleted()) { return; } long totalSize = cursor.getLong(cursor.getColumnIndexOrThrow(DownloadManager.COLUMN_TOTAL_SIZE_BYTES)); @@ -219,9 +218,11 @@ public void run() { @WorkerThread synchronized void onDownloadComplete() { - if (isCancelled()) { + if (isCompleted()) { return; } + // Mark completed + mCompleted = true; if (!isDownloadedFileValid()) { mListener.onError("Downloaded package file is invalid."); return; @@ -237,28 +238,20 @@ synchronized void onDownloadComplete() { @WorkerThread synchronized void onDownloadError(RuntimeException e) { - if (isCancelled()) { + if (isCompleted()) { return; } + mCompleted = true; AppCenterLog.error(LOG_TAG, "Failed to download update id=" + mDownloadId, e); mListener.onError(e.getMessage()); } private boolean isDownloadedFileValid() { - ParcelFileDescriptor fileDescriptor = null; - try { - fileDescriptor = getDownloadManager().openDownloadedFile(mDownloadId); + try (ParcelFileDescriptor fileDescriptor = getDownloadManager().openDownloadedFile(mDownloadId)) { return fileDescriptor.getStatSize() == mReleaseDetails.getSize(); } catch (IOException e) { AppCenterLog.error(LOG_TAG, "Cannot open downloaded file for id=" + mDownloadId, e); return false; - } finally { - try { - if (fileDescriptor != null) { - fileDescriptor.close(); - } - } catch (IOException ignore) { - } } } } diff --git a/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/install/AbstractReleaseInstaller.java b/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/install/AbstractReleaseInstaller.java new file mode 100644 index 000000000..eadfcd672 --- /dev/null +++ b/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/install/AbstractReleaseInstaller.java @@ -0,0 +1,13 @@ +package com.microsoft.appcenter.distribute.install; + +import android.content.Context; + +public abstract class AbstractReleaseInstaller implements ReleaseInstaller { + protected final Context mContext; + protected final Listener mListener; + + protected AbstractReleaseInstaller(Context context, Listener listener) { + mContext = context; + mListener = listener; + } +} diff --git a/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/install/ReleaseInstaller.java b/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/install/ReleaseInstaller.java new file mode 100644 index 000000000..7f2dd33b6 --- /dev/null +++ b/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/install/ReleaseInstaller.java @@ -0,0 +1,21 @@ +package com.microsoft.appcenter.distribute.install; + +import android.net.Uri; + +import androidx.annotation.NonNull; +import androidx.annotation.WorkerThread; + +public interface ReleaseInstaller { + + @WorkerThread + void install(@NonNull Uri localUri); + + void resume(); + + void clear(); + + interface Listener { + void onError(String message); + void onCancel(); + } +} diff --git a/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/install/intent/IntentReleaseInstaller.java b/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/install/intent/IntentReleaseInstaller.java new file mode 100644 index 000000000..178f8daee --- /dev/null +++ b/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/install/intent/IntentReleaseInstaller.java @@ -0,0 +1,61 @@ +package com.microsoft.appcenter.distribute.install.intent; + +import android.content.Context; +import android.content.Intent; +import android.net.Uri; + +import androidx.annotation.NonNull; +import androidx.annotation.WorkerThread; + +import com.microsoft.appcenter.distribute.install.AbstractReleaseInstaller; + +public class IntentReleaseInstaller extends AbstractReleaseInstaller { + + public IntentReleaseInstaller(Context context, Listener listener) { + super(context, listener); + } + + @WorkerThread + @Override + public void install(@NonNull Uri localUri) { + final Intent intent = getInstallIntent(localUri); + if (intent.resolveActivity(mContext.getPackageManager()) == null) { + mListener.onError("Cannot resolve install intent for " + localUri); + return; + } + mContext.startActivity(intent); + } + + @Override + public void resume() { + // If we're still here then it was cancelled. + mListener.onCancel(); + } + + @Override + public void clear() { + // Nothing to clear. + } + + @NonNull + @Override + public String toString() { + return "ACTION_INSTALL_PACKAGE"; + } + + /** + * Get the intent used to open installation UI. + * + * @param fileUri downloaded file URI from the download manager. + * @return intent to open installation UI. + */ + @NonNull + protected static Intent getInstallIntent(Uri fileUri) { + Intent intent = new Intent(Intent.ACTION_INSTALL_PACKAGE); + intent.setData(fileUri); + intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); + intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP); + return intent; + } +} diff --git a/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/AppCenterPackageInstallerReceiver.java b/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/install/session/InstallStatusReceiver.java similarity index 53% rename from sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/AppCenterPackageInstallerReceiver.java rename to sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/install/session/InstallStatusReceiver.java index 8c832b101..08fa792eb 100644 --- a/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/AppCenterPackageInstallerReceiver.java +++ b/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/install/session/InstallStatusReceiver.java @@ -3,7 +3,7 @@ * Licensed under the MIT License. */ -package com.microsoft.appcenter.distribute; +package com.microsoft.appcenter.distribute.install.session; import static com.microsoft.appcenter.distribute.DistributeConstants.LOG_TAG; @@ -13,34 +13,28 @@ import android.content.Intent; import android.content.IntentFilter; import android.content.IntentSender; -import android.content.pm.PackageInfo; import android.content.pm.PackageInstaller; import android.os.Build; import android.os.Bundle; +import androidx.annotation.NonNull; import androidx.annotation.VisibleForTesting; import com.microsoft.appcenter.utils.AppCenterLog; -import com.microsoft.appcenter.utils.AppNameHelper; -import com.microsoft.appcenter.utils.DeviceInfoHelper; import java.util.Locale; /** * Process install manager callbacks. */ -public class AppCenterPackageInstallerReceiver extends BroadcastReceiver { +class InstallStatusReceiver extends BroadcastReceiver { @VisibleForTesting static final String INSTALL_STATUS_ACTION = "com.microsoft.appcenter.action.INSTALL_STATUS"; - @VisibleForTesting - static final String MY_PACKAGE_REPLACED_ACTION = "android.intent.action.MY_PACKAGE_REPLACED"; - static IntentFilter getInstallerReceiverFilter() { IntentFilter installerReceiverFilter = new IntentFilter(); installerReceiverFilter.addAction(INSTALL_STATUS_ACTION); - installerReceiverFilter.addAction(MY_PACKAGE_REPLACED_ACTION); return installerReceiverFilter; } @@ -64,36 +58,18 @@ static IntentSender getInstallStatusIntentSender(Context context, int sessionId) return pendingIntent.getIntentSender(); } + private final SessionReleaseInstaller mInstaller; + + InstallStatusReceiver(@NonNull SessionReleaseInstaller installer) { + mInstaller = installer; + } + @Override public void onReceive(Context context, Intent intent) { String action = intent.getAction(); - if (MY_PACKAGE_REPLACED_ACTION.equals(action)) { - onPackageReplaced(context); - } else if (INSTALL_STATUS_ACTION.equals(action)) { + if (INSTALL_STATUS_ACTION.equals(action)) { onInstallStatus(context, intent); - } else { - AppCenterLog.warn(LOG_TAG, String.format(Locale.ENGLISH, "Unrecognized action %s - do nothing.", action)); - } - } - - private void onPackageReplaced(Context context) { - AppCenterLog.debug(LOG_TAG, "Post a notification as the installation finished in background."); - String title = context.getString(R.string.appcenter_distribute_install_completed_title); - Intent intent = DistributeUtils.getResumeAppIntent(context); - DistributeUtils.postNotification(context, title, getInstallCompletedMessage(context), intent); - } - - private static String getInstallCompletedMessage(Context context) { - String versionName = "?"; - int versionCode = 0; - PackageInfo packageInfo = DeviceInfoHelper.getPackageInfo(context); - if (packageInfo != null) { - versionName = packageInfo.versionName; - versionCode = DeviceInfoHelper.getVersionCode(packageInfo); } - String format = context.getString(R.string.appcenter_distribute_install_completed_message); - String appName = AppNameHelper.getAppName(context); - return String.format(format, appName, versionName, versionCode); } private void onInstallStatus(Context context, Intent intent) { @@ -102,38 +78,25 @@ private void onInstallStatus(Context context, Intent intent) { switch (status) { case PackageInstaller.STATUS_PENDING_USER_ACTION: Intent confirmIntent = (Intent) extras.get(Intent.EXTRA_INTENT); - onInstallStatusPendingUserAction(context, confirmIntent); + mInstaller.onInstallConfirmation(confirmIntent); break; case PackageInstaller.STATUS_SUCCESS: - onInstallStatusSuccess(); + AppCenterLog.info(LOG_TAG, "Application was successfully updated."); + break; + case PackageInstaller.STATUS_FAILURE_ABORTED: + mInstaller.onInstallCancel(); break; case PackageInstaller.STATUS_FAILURE: case PackageInstaller.STATUS_FAILURE_BLOCKED: - case PackageInstaller.STATUS_FAILURE_ABORTED: case PackageInstaller.STATUS_FAILURE_INVALID: case PackageInstaller.STATUS_FAILURE_CONFLICT: case PackageInstaller.STATUS_FAILURE_STORAGE: case PackageInstaller.STATUS_FAILURE_INCOMPATIBLE: String message = extras.getString(PackageInstaller.EXTRA_STATUS_MESSAGE); - onInstallStatusFailure(status, message); + mInstaller.onInstallError(message); break; default: AppCenterLog.warn(LOG_TAG, String.format(Locale.ENGLISH, "Unrecognized status received from installer: %s", status)); } } - - private void onInstallStatusPendingUserAction(Context context, Intent confirmIntent) { - AppCenterLog.debug(LOG_TAG, "Ask confirmation to install a new release."); - confirmIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); - context.startActivity(confirmIntent); - } - - private void onInstallStatusSuccess() { - AppCenterLog.info(LOG_TAG, "Application was successfully updated."); - } - - private void onInstallStatusFailure(int status, String message) { - AppCenterLog.error(LOG_TAG, String.format(Locale.ENGLISH, "Failed to install a new release with status: %s. Error message: %s.", status, message)); - Distribute.getInstance().showInstallingErrorToast(); - } } diff --git a/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/install/session/PackageInstallerListener.java b/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/install/session/PackageInstallerListener.java new file mode 100644 index 000000000..737504102 --- /dev/null +++ b/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/install/session/PackageInstallerListener.java @@ -0,0 +1,59 @@ +/* + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. + */ + +package com.microsoft.appcenter.distribute.install.session; + +import static com.microsoft.appcenter.distribute.DistributeConstants.LOG_TAG; + +import android.content.pm.PackageInstaller; + +import androidx.annotation.NonNull; +import androidx.annotation.WorkerThread; + +import com.microsoft.appcenter.utils.AppCenterLog; + +import java.util.Locale; + +/** + * Listener for installation progress. + */ +public class PackageInstallerListener extends PackageInstaller.SessionCallback { + + private final SessionReleaseInstaller mInstaller; + + PackageInstallerListener(@NonNull SessionReleaseInstaller installer) { + mInstaller = installer; + } + + @WorkerThread + @Override + public void onCreated(int sessionId) { + AppCenterLog.debug(LOG_TAG, "The install session was created."); + } + + @WorkerThread + @Override + public void onBadgingChanged(int sessionId) { + } + + @WorkerThread + @Override + public void onActiveChanged(int sessionId, boolean active) { + } + + @WorkerThread + @Override + public void onProgressChanged(int sessionId, float progress) { + final int downloadProgress = (int)(progress * 100); + AppCenterLog.verbose(LOG_TAG, String.format(Locale.ENGLISH, "Installation progress: %d / 100.", downloadProgress)); + mInstaller.onInstallProgress(); + } + + @WorkerThread + @Override + public void onFinished(int sessionId, boolean success) { + AppCenterLog.debug(LOG_TAG, "The installation has been finished. success=" + success); + } +} diff --git a/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/install/session/SessionReleaseInstaller.java b/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/install/session/SessionReleaseInstaller.java new file mode 100644 index 000000000..3ae21d089 --- /dev/null +++ b/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/install/session/SessionReleaseInstaller.java @@ -0,0 +1,179 @@ +package com.microsoft.appcenter.distribute.install.session; + +import static android.content.pm.PackageInstaller.SessionParams.MODE_FULL_INSTALL; +import static com.microsoft.appcenter.distribute.DistributeConstants.LOG_TAG; + +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.content.IntentSender; +import android.content.pm.PackageInstaller; +import android.net.Uri; +import android.os.ParcelFileDescriptor; + +import androidx.annotation.NonNull; +import androidx.annotation.WorkerThread; + +import com.microsoft.appcenter.distribute.install.AbstractReleaseInstaller; +import com.microsoft.appcenter.utils.AppCenterLog; + +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; + +public class SessionReleaseInstaller extends AbstractReleaseInstaller { + + /** + * Name of package installer stream. + */ + private static final String sOutputStreamName = "AppCenterPackageInstallerStream"; + + /** + * Buffer capacity of package installer. + */ + private static final int BUFFER_CAPACITY = 64 * 1024; + + private static final int INVALID_SESSION_ID = -1; + + private BroadcastReceiver mInstallStatusReceiver; + + private PackageInstaller.SessionCallback mSessionCallback; + + private boolean mUserConfirmationRequested; + + private int mSessionId = INVALID_SESSION_ID; + + public SessionReleaseInstaller(Context context, Listener listener) { + super(context, listener); + } + + private PackageInstaller getPackageInstaller() { + return mContext.getPackageManager().getPackageInstaller(); + } + + @WorkerThread + @Override + public void install(@NonNull Uri localUri) { + registerListeners(); + PackageInstaller.Session session = null; + try (ParcelFileDescriptor fileDescriptor = mContext.getContentResolver().openFileDescriptor(localUri, "r")) { + + /* Prepare session. */ + session = createSession(fileDescriptor); + addFileToInstallSession(fileDescriptor, session); + + /* Start to install a new release. */ + IntentSender statusReceiver = InstallStatusReceiver.getInstallStatusIntentSender(mContext, mSessionId); + session.commit(statusReceiver); + session.close(); + + // IllegalStateException - Too many active sessions + } catch (IOException | IllegalStateException e) { + if (session != null) { + session.abandon(); + } + mListener.onError("Couldn't install a new release: " + e); + } + } + + @Override + public synchronized void resume() { + if (mUserConfirmationRequested) { + onInstallCancel(); + } + + // Sometimes progress event comes a bit late, in this case second resume means cancellation. + mUserConfirmationRequested = true; + } + + @Override + public synchronized void clear() { + unregisterListeners(); + abandonSession(); + } + + @NonNull + @Override + public String toString() { + return "PackageInstaller"; + } + + synchronized void onInstallProgress() { + mUserConfirmationRequested = false; + } + + synchronized void onInstallConfirmation(Intent intent) { + AppCenterLog.info(LOG_TAG, "Ask confirmation to install a new release."); + intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + mContext.startActivity(intent); + mUserConfirmationRequested = true; + } + + synchronized void onInstallError(String message) { + AppCenterLog.error(LOG_TAG, "Failed to install a new release: " + message); + mListener.onError(message); + } + + synchronized void onInstallCancel() { + AppCenterLog.debug(LOG_TAG, "Installation cancelled."); + mListener.onCancel(); + } + + @WorkerThread + private static void addFileToInstallSession(ParcelFileDescriptor fileDescriptor, PackageInstaller.Session session) + throws IOException { + try (OutputStream out = session.openWrite(sOutputStreamName, 0, fileDescriptor.getStatSize()); + InputStream in = new FileInputStream(fileDescriptor.getFileDescriptor())) { + byte[] buffer = new byte[BUFFER_CAPACITY]; + int read; + while ((read = in.read(buffer)) >= 0) { + out.write(buffer, 0, read); + } + session.fsync(out); + } + } + + private synchronized void registerListeners() { + if (mInstallStatusReceiver == null) { + AppCenterLog.debug(LOG_TAG, "Register receiver for installing a new release."); + mInstallStatusReceiver = new InstallStatusReceiver(this); + mContext.registerReceiver(mInstallStatusReceiver, InstallStatusReceiver.getInstallerReceiverFilter()); + } + if (mSessionCallback == null) { + PackageInstaller packageInstaller = getPackageInstaller(); + mSessionCallback = new PackageInstallerListener(this); + packageInstaller.registerSessionCallback(mSessionCallback); + } + } + + private synchronized void unregisterListeners() { + if (mInstallStatusReceiver != null) { + AppCenterLog.debug(LOG_TAG, "Unregister receiver for installing a new release."); + mContext.unregisterReceiver(mInstallStatusReceiver); + mInstallStatusReceiver = null; + } + if (mSessionCallback != null) { + PackageInstaller packageInstaller = getPackageInstaller(); + packageInstaller.unregisterSessionCallback(mSessionCallback); + mSessionCallback = null; + } + } + + private PackageInstaller.Session createSession(ParcelFileDescriptor fileDescriptor) throws IOException { + PackageInstaller packageInstaller = getPackageInstaller(); + PackageInstaller.SessionParams params = new PackageInstaller.SessionParams(MODE_FULL_INSTALL); + params.setSize(fileDescriptor.getStatSize()); + params.setAppPackageName(mContext.getPackageName()); + mSessionId = packageInstaller.createSession(params); + return packageInstaller.openSession(mSessionId); + } + + private void abandonSession() { + if (mSessionId != INVALID_SESSION_ID) { + PackageInstaller packageInstaller = getPackageInstaller(); + packageInstaller.abandonSession(mSessionId); + mSessionId = INVALID_SESSION_ID; + } + } +} diff --git a/sdk/appcenter-distribute/src/main/res/values/appcenter_distribute.xml b/sdk/appcenter-distribute/src/main/res/values/appcenter_distribute.xml index f91398679..7a65386c8 100644 --- a/sdk/appcenter-distribute/src/main/res/values/appcenter_distribute.xml +++ b/sdk/appcenter-distribute/src/main/res/values/appcenter_distribute.xml @@ -20,9 +20,6 @@ Downloading version %1$s (%2$d) Failed to download app update %1$d MB of %2$d MB - %1$d of %2$d - Something went wrong. Update was not installed. - Installing... App update downloaded %1$s %2$s (%3$d) has been downloaded and is ready to be installed. App update installed From daf8c313c6b6fb8f9d1ed823504884032176a802 Mon Sep 17 00:00:00 2001 From: Ivan Matkov Date: Thu, 19 May 2022 10:51:22 +0200 Subject: [PATCH 54/72] Fix threading --- .../appcenter/distribute/Distribute.java | 13 ++- .../appcenter/distribute/UpdateInstaller.java | 9 ++- .../install/AbstractReleaseInstaller.java | 29 ++++++- .../distribute/install/ReleaseInstaller.java | 4 +- .../intent/IntentReleaseInstaller.java | 27 ++++--- .../session/InstallStatusReceiver.java | 4 +- .../session/SessionReleaseInstaller.java | 81 +++++++++++++------ 7 files changed, 115 insertions(+), 52 deletions(-) diff --git a/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/Distribute.java b/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/Distribute.java index 0c29638b5..612500496 100644 --- a/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/Distribute.java +++ b/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/Distribute.java @@ -805,6 +805,9 @@ private synchronized void resumeDistributeWorkflow() { if (mPackageInfo.lastUpdateTime > SharedPreferencesManager.getLong(PREFERENCE_KEY_DOWNLOAD_TIME)) { AppCenterLog.debug(LOG_TAG, "Discarding previous download as application updated."); cancelPreviousTasks(); + + // Close notification about success update. + DistributeUtils.cancelNotification(mContext); } } @@ -960,7 +963,6 @@ synchronized void completeWorkflow(ReleaseDetails releaseDetails) { * Reset all variables that matter to restart checking a new release on launcher activity restart. */ synchronized void completeWorkflow() { - AppCenterLog.warn(LOG_TAG, "DEBUG Complete workflow"); cancelDownloadCompletedNotification(); SharedPreferencesManager.remove(PREFERENCE_KEY_RELEASE_DETAILS); SharedPreferencesManager.remove(PREFERENCE_KEY_DOWNLOAD_STATE); @@ -1636,6 +1638,7 @@ private synchronized void goToUnknownAppsSettings(ReleaseDetails releaseDetails) * And a no U.I. activity of our own must finish in onCreate, * so it cannot receive a result. */ + // FIXME: StrictMode policy violation; mForegroundActivity.startActivity(intent); } catch (ActivityNotFoundException e) { @@ -1877,13 +1880,7 @@ synchronized void setInstalling(@NonNull ReleaseDetails releaseDetails, @NonNull if (mReleaseInstaller == null) { mReleaseInstaller = new UpdateInstaller(mContext, mReleaseDetails); } - post(new Runnable() { - - @Override - public void run() { - mReleaseInstaller.install(localUri); - } - }); + mReleaseInstaller.install(localUri); } private void storeInstallingReleaseDetails() { diff --git a/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/UpdateInstaller.java b/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/UpdateInstaller.java index b6a0bbf3a..76504b7e6 100644 --- a/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/UpdateInstaller.java +++ b/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/UpdateInstaller.java @@ -4,6 +4,8 @@ import android.content.Context; import android.net.Uri; +import android.os.Handler; +import android.os.HandlerThread; import androidx.annotation.NonNull; @@ -23,8 +25,11 @@ class UpdateInstaller implements ReleaseInstaller, ReleaseInstaller.Listener { private boolean mCancelled; UpdateInstaller(Context context, ReleaseDetails releaseDetails) { - mInstallers.add(new SessionReleaseInstaller(context, this)); - mInstallers.add(new IntentReleaseInstaller(context, this)); + HandlerThread thread = new HandlerThread("AppCenter.Installer"); + thread.start(); + Handler handler = new Handler(thread.getLooper()); + mInstallers.add(new SessionReleaseInstaller(context, handler, this)); + mInstallers.add(new IntentReleaseInstaller(context, handler, this)); mReleaseDetails = releaseDetails; mCurrent = next(); } diff --git a/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/install/AbstractReleaseInstaller.java b/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/install/AbstractReleaseInstaller.java index eadfcd672..eaab0ede8 100644 --- a/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/install/AbstractReleaseInstaller.java +++ b/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/install/AbstractReleaseInstaller.java @@ -1,13 +1,38 @@ package com.microsoft.appcenter.distribute.install; +import static com.microsoft.appcenter.distribute.DistributeConstants.LOG_TAG; + import android.content.Context; +import android.os.Handler; + +import com.microsoft.appcenter.utils.AppCenterLog; public abstract class AbstractReleaseInstaller implements ReleaseInstaller { protected final Context mContext; - protected final Listener mListener; + private final Handler mInstallerHandler; + private final Listener mListener; - protected AbstractReleaseInstaller(Context context, Listener listener) { + protected AbstractReleaseInstaller(Context context, Handler installerHandler, Listener listener) { mContext = context; + mInstallerHandler = installerHandler; mListener = listener; } + + protected void post(Runnable runnable) { + mInstallerHandler.post(runnable); + } + + protected void postDelayed(Runnable runnable, long delayMillis) { + mInstallerHandler.postDelayed(runnable, delayMillis); + } + + protected void onError(String message) { + AppCenterLog.error(LOG_TAG, "Failed to install a new release: " + message); + mListener.onError(message); + } + + protected void onCancel() { + AppCenterLog.debug(LOG_TAG, "Installation cancelled."); + mListener.onCancel(); + } } diff --git a/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/install/ReleaseInstaller.java b/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/install/ReleaseInstaller.java index 7f2dd33b6..aed16ecd7 100644 --- a/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/install/ReleaseInstaller.java +++ b/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/install/ReleaseInstaller.java @@ -2,12 +2,12 @@ import android.net.Uri; +import androidx.annotation.AnyThread; import androidx.annotation.NonNull; -import androidx.annotation.WorkerThread; public interface ReleaseInstaller { - @WorkerThread + @AnyThread void install(@NonNull Uri localUri); void resume(); diff --git a/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/install/intent/IntentReleaseInstaller.java b/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/install/intent/IntentReleaseInstaller.java index 178f8daee..7cc633c10 100644 --- a/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/install/intent/IntentReleaseInstaller.java +++ b/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/install/intent/IntentReleaseInstaller.java @@ -3,33 +3,40 @@ import android.content.Context; import android.content.Intent; import android.net.Uri; +import android.os.Handler; +import androidx.annotation.AnyThread; import androidx.annotation.NonNull; -import androidx.annotation.WorkerThread; import com.microsoft.appcenter.distribute.install.AbstractReleaseInstaller; public class IntentReleaseInstaller extends AbstractReleaseInstaller { - public IntentReleaseInstaller(Context context, Listener listener) { - super(context, listener); + public IntentReleaseInstaller(Context context, Handler installerHandler, Listener listener) { + super(context, installerHandler, listener); } - @WorkerThread + @AnyThread @Override public void install(@NonNull Uri localUri) { final Intent intent = getInstallIntent(localUri); - if (intent.resolveActivity(mContext.getPackageManager()) == null) { - mListener.onError("Cannot resolve install intent for " + localUri); - return; - } - mContext.startActivity(intent); + post(new Runnable() { + + @Override + public void run() { + if (intent.resolveActivity(mContext.getPackageManager()) == null) { + onError("Cannot resolve install intent for " + localUri); + return; + } + mContext.startActivity(intent); + } + }); } @Override public void resume() { // If we're still here then it was cancelled. - mListener.onCancel(); + onCancel(); } @Override diff --git a/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/install/session/InstallStatusReceiver.java b/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/install/session/InstallStatusReceiver.java index 08fa792eb..e8d81d22a 100644 --- a/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/install/session/InstallStatusReceiver.java +++ b/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/install/session/InstallStatusReceiver.java @@ -68,11 +68,11 @@ static IntentSender getInstallStatusIntentSender(Context context, int sessionId) public void onReceive(Context context, Intent intent) { String action = intent.getAction(); if (INSTALL_STATUS_ACTION.equals(action)) { - onInstallStatus(context, intent); + onInstallStatus(intent); } } - private void onInstallStatus(Context context, Intent intent) { + private void onInstallStatus(Intent intent) { Bundle extras = intent.getExtras(); int status = extras.getInt(PackageInstaller.EXTRA_STATUS); switch (status) { diff --git a/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/install/session/SessionReleaseInstaller.java b/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/install/session/SessionReleaseInstaller.java index 3ae21d089..303ca803e 100644 --- a/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/install/session/SessionReleaseInstaller.java +++ b/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/install/session/SessionReleaseInstaller.java @@ -9,8 +9,10 @@ import android.content.IntentSender; import android.content.pm.PackageInstaller; import android.net.Uri; +import android.os.Handler; import android.os.ParcelFileDescriptor; +import androidx.annotation.AnyThread; import androidx.annotation.NonNull; import androidx.annotation.WorkerThread; @@ -44,43 +46,42 @@ public class SessionReleaseInstaller extends AbstractReleaseInstaller { private int mSessionId = INVALID_SESSION_ID; - public SessionReleaseInstaller(Context context, Listener listener) { - super(context, listener); + public SessionReleaseInstaller(Context context, Handler installerHandler, Listener listener) { + super(context, installerHandler, listener); } + private PackageInstaller getPackageInstaller() { return mContext.getPackageManager().getPackageInstaller(); } - @WorkerThread + @AnyThread @Override public void install(@NonNull Uri localUri) { registerListeners(); - PackageInstaller.Session session = null; - try (ParcelFileDescriptor fileDescriptor = mContext.getContentResolver().openFileDescriptor(localUri, "r")) { + post(new Runnable() { - /* Prepare session. */ - session = createSession(fileDescriptor); - addFileToInstallSession(fileDescriptor, session); + @Override + public void run() { + startInstallSession(localUri); + } + }); + } - /* Start to install a new release. */ - IntentSender statusReceiver = InstallStatusReceiver.getInstallStatusIntentSender(mContext, mSessionId); - session.commit(statusReceiver); - session.close(); + @Override + public void resume() { + postDelayed(new Runnable() { - // IllegalStateException - Too many active sessions - } catch (IOException | IllegalStateException e) { - if (session != null) { - session.abandon(); + @Override + public void run() { + delayedResume(); } - mListener.onError("Couldn't install a new release: " + e); - } + }, 500); } - @Override - public synchronized void resume() { + private synchronized void delayedResume() { if (mUserConfirmationRequested) { - onInstallCancel(); + onCancel(); } // Sometimes progress event comes a bit late, in this case second resume means cancellation. @@ -106,18 +107,46 @@ synchronized void onInstallProgress() { synchronized void onInstallConfirmation(Intent intent) { AppCenterLog.info(LOG_TAG, "Ask confirmation to install a new release."); intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); - mContext.startActivity(intent); mUserConfirmationRequested = true; + post(new Runnable() { + @Override + public void run() { + mContext.startActivity(intent); + } + }); } synchronized void onInstallError(String message) { - AppCenterLog.error(LOG_TAG, "Failed to install a new release: " + message); - mListener.onError(message); + mSessionId = INVALID_SESSION_ID; + onError(message); } synchronized void onInstallCancel() { - AppCenterLog.debug(LOG_TAG, "Installation cancelled."); - mListener.onCancel(); + mSessionId = INVALID_SESSION_ID; + onCancel(); + } + + @WorkerThread + private synchronized void startInstallSession(@NonNull Uri localUri) { + PackageInstaller.Session session = null; + try (ParcelFileDescriptor fileDescriptor = mContext.getContentResolver().openFileDescriptor(localUri, "r")) { + + /* Prepare session. */ + session = createSession(fileDescriptor); + addFileToInstallSession(fileDescriptor, session); + + /* Start to install a new release. */ + IntentSender statusReceiver = InstallStatusReceiver.getInstallStatusIntentSender(mContext, mSessionId); + session.commit(statusReceiver); + session.close(); + + // IllegalStateException - Too many active sessions + } catch (IOException | IllegalStateException e) { + if (session != null) { + session.abandon(); + } + onError("Couldn't install a new release: " + e); + } } @WorkerThread From ba5e15a1f4b981ef753026fe95140f3425cee7c1 Mon Sep 17 00:00:00 2001 From: Ivan Matkov Date: Fri, 20 May 2022 02:00:33 +0200 Subject: [PATCH 55/72] Fix tracking cancel installing --- .../src/main/AndroidManifest.xml | 16 +-- .../appcenter/distribute/Distribute.java | 5 +- .../distribute/DownloadManagerReceiver.java | 2 +- .../appcenter/distribute/UpdateInstaller.java | 46 ++++---- .../appcenter/distribute/UpdateReceiver.java | 1 + .../install/AbstractReleaseInstaller.java | 13 +++ .../install/ReleaseInstallerActivity.java | 85 +++++++++++++++ .../intent/IntentReleaseInstaller.java | 47 ++++---- .../session/InstallStatusReceiver.java | 19 ++-- .../session/PackageInstallerListener.java | 10 +- .../session/SessionReleaseInstaller.java | 102 +++++++++++------- 11 files changed, 246 insertions(+), 100 deletions(-) create mode 100644 sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/install/ReleaseInstallerActivity.java diff --git a/sdk/appcenter-distribute/src/main/AndroidManifest.xml b/sdk/appcenter-distribute/src/main/AndroidManifest.xml index 353df8688..c621034b9 100644 --- a/sdk/appcenter-distribute/src/main/AndroidManifest.xml +++ b/sdk/appcenter-distribute/src/main/AndroidManifest.xml @@ -13,19 +13,23 @@ Android 11 brings in a lot of changes regarding privacy. By default, list of installed apps is now hidden. Manifest block below make browsers on user devices acessible in order to let SDK check for updates in private update track. See more about managing package visibility: https://developer.android.com/training/basics/intents/package-visibility - --> + --> - + + + android:exported="true" + android:theme="@android:style/Theme.NoDisplay"> @@ -47,7 +51,7 @@ - @@ -56,4 +60,4 @@ - + \ No newline at end of file diff --git a/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/Distribute.java b/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/Distribute.java index 612500496..416212ba7 100644 --- a/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/Distribute.java +++ b/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/Distribute.java @@ -720,7 +720,6 @@ private synchronized void resumeDistributeWorkflow() { if (mPackageInfo == null || mForegroundActivity == null || mWorkflowCompleted || !isInstanceEnabled()) { return; } - AppCenterLog.debug(LOG_TAG, "Resume distribute workflow..."); /* Don't go any further it this is a debug app. */ if ((mContext.getApplicationInfo().flags & FLAG_DEBUGGABLE) == FLAG_DEBUGGABLE && !mEnabledForDebuggableBuild) { @@ -743,6 +742,7 @@ private synchronized void resumeDistributeWorkflow() { mReleaseInstaller.resume(); return; } + AppCenterLog.debug(LOG_TAG, "Resume distribute workflow..."); /* * If failed to enable in-app updates on the same app build before, don't go any further. @@ -963,6 +963,7 @@ synchronized void completeWorkflow(ReleaseDetails releaseDetails) { * Reset all variables that matter to restart checking a new release on launcher activity restart. */ synchronized void completeWorkflow() { + AppCenterLog.debug(LOG_TAG, "Complete current updating process."); cancelDownloadCompletedNotification(); SharedPreferencesManager.remove(PREFERENCE_KEY_RELEASE_DETAILS); SharedPreferencesManager.remove(PREFERENCE_KEY_DOWNLOAD_STATE); @@ -1874,7 +1875,7 @@ synchronized void setInstalling(@NonNull ReleaseDetails releaseDetails, @NonNull return; } cancelDownloadCompletedNotification(); - AppCenterLog.info(AppCenterLog.LOG_TAG, "Start installing new release..."); + AppCenterLog.info(LOG_TAG, "Start installing new release..."); SharedPreferencesManager.putInt(PREFERENCE_KEY_DOWNLOAD_STATE, DOWNLOAD_STATE_INSTALLING); storeInstallingReleaseDetails(); if (mReleaseInstaller == null) { diff --git a/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/DownloadManagerReceiver.java b/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/DownloadManagerReceiver.java index f7c86985a..1498ab686 100644 --- a/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/DownloadManagerReceiver.java +++ b/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/DownloadManagerReceiver.java @@ -30,7 +30,7 @@ public void onReceive(Context context, Intent intent) { * Another option would be to open download list. */ String action = intent.getAction(); - AppCenterLog.debug(LOG_TAG, "Receive broadcast action: " + action); + AppCenterLog.verbose(LOG_TAG, "Receive broadcast action: " + action); if (DownloadManager.ACTION_NOTIFICATION_CLICKED.equals(action)) { Distribute.getInstance().resumeApp(context); } diff --git a/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/UpdateInstaller.java b/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/UpdateInstaller.java index 76504b7e6..9bece6fe9 100644 --- a/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/UpdateInstaller.java +++ b/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/UpdateInstaller.java @@ -44,7 +44,7 @@ private ReleaseInstaller next() { } @Override - public void install(@NonNull Uri localUri) { + public synchronized void install(@NonNull Uri localUri) { mCancelled = false; mLocalUri = localUri; if (mCurrent != null) { @@ -53,9 +53,10 @@ public void install(@NonNull Uri localUri) { } @Override - public void resume() { - if (mCancelled) { - cancel(); + public synchronized void resume() { + // Show mandatory dialog if cancelled in background. + if (mCancelled && mReleaseDetails.isMandatoryUpdate()) { + Distribute.getInstance().showMandatoryDownloadReadyDialog(); return; } if (mCurrent != null) { @@ -64,34 +65,39 @@ public void resume() { } @Override - public void clear() { + public synchronized void clear() { if (mCurrent != null) { mCurrent.clear(); } } @Override - public void onError(String message) { - mCurrent.clear(); - mCurrent = next(); - if (mCurrent != null) { - mCurrent.install(mLocalUri); - } else { - Distribute.getInstance().completeWorkflow(mReleaseDetails); + public synchronized void onError(String message) { + if (isRecoverableError(message)) { + mCurrent.clear(); + mCurrent = next(); + if (mCurrent != null) { + mCurrent.install(mLocalUri); + return; + } } + Distribute.getInstance().completeWorkflow(mReleaseDetails); } @Override - public void onCancel() { + public synchronized void onCancel() { mCancelled = true; - cancel(); - } - - private void cancel() { - if (!mReleaseDetails.isMandatoryUpdate()) { - Distribute.getInstance().completeWorkflow(mReleaseDetails); - } else { + if (mReleaseDetails.isMandatoryUpdate()) { Distribute.getInstance().showMandatoryDownloadReadyDialog(); + } else { + Distribute.getInstance().completeWorkflow(mReleaseDetails); } } + + // MIUI fallback + private static final String RECOVERABLE_ERROR = "INSTALL_FAILED_INTERNAL_ERROR: Permission Denied"; + + private static boolean isRecoverableError(String message) { + return RECOVERABLE_ERROR.equals(message); + } } diff --git a/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/UpdateReceiver.java b/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/UpdateReceiver.java index 99f44fde5..2939839dc 100644 --- a/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/UpdateReceiver.java +++ b/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/UpdateReceiver.java @@ -16,6 +16,7 @@ public class UpdateReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { String action = intent.getAction(); + AppCenterLog.verbose(LOG_TAG, "Receive broadcast action: " + action); if (Intent.ACTION_MY_PACKAGE_REPLACED.equals(action)) { onPackageReplaced(context); } diff --git a/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/install/AbstractReleaseInstaller.java b/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/install/AbstractReleaseInstaller.java index eaab0ede8..f5d83dd50 100644 --- a/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/install/AbstractReleaseInstaller.java +++ b/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/install/AbstractReleaseInstaller.java @@ -31,8 +31,21 @@ protected void onError(String message) { mListener.onError(message); } + protected void onError(String message, Throwable throwable) { + AppCenterLog.error(LOG_TAG, "Failed to install a new release: " + message, throwable); + mListener.onError(message); + } + protected void onCancel() { AppCenterLog.debug(LOG_TAG, "Installation cancelled."); mListener.onCancel(); } + + @Override + public void resume() { + } + + @Override + public void clear() { + } } diff --git a/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/install/ReleaseInstallerActivity.java b/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/install/ReleaseInstallerActivity.java new file mode 100644 index 000000000..3c8f46a5a --- /dev/null +++ b/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/install/ReleaseInstallerActivity.java @@ -0,0 +1,85 @@ +package com.microsoft.appcenter.distribute.install; + +import static com.microsoft.appcenter.distribute.DistributeConstants.LOG_TAG; + +import android.app.Activity; +import android.content.Context; +import android.content.Intent; +import android.os.Bundle; + +import com.microsoft.appcenter.utils.AppCenterLog; +import com.microsoft.appcenter.utils.async.AppCenterFuture; +import com.microsoft.appcenter.utils.async.DefaultAppCenterFuture; + +public class ReleaseInstallerActivity extends Activity { + + public static class Result { + public final int code; + public final String message; + + public Result(int code, String message) { + this.code = code; + this.message = message; + } + } + + private static DefaultAppCenterFuture sResultFuture; + + public static AppCenterFuture startActivityForResult(Context context, Intent trackedIntent) { + if (sResultFuture != null) { + AppCenterLog.error(LOG_TAG, "ALREADY IN PROGRESS"); + return null; + } + sResultFuture = new DefaultAppCenterFuture<>(); + Intent intent = new Intent(context, ReleaseInstallerActivity.class); + intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP); + intent.addFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION); + intent.putExtra(Intent.EXTRA_INTENT, trackedIntent); + + // FIXME: StrictMode policy violation; ~duration=13 ms: android.os.strictmode.DiskReadViolation + context.startActivity(intent); + return sResultFuture; + } + + private static void complete(Result result) { + if (sResultFuture != null) { + sResultFuture.complete(result); + sResultFuture = null; + } + } + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + final Intent intent = getIntent().getParcelableExtra(Intent.EXTRA_INTENT); + if (intent == null) { + AppCenterLog.warn(LOG_TAG, "MISSING EXTRA INTENT"); + finish(); + return; + } + try { + startActivityForResult(intent, 0); + } catch (SecurityException e) { + complete(new Result(RESULT_FIRST_USER, e.getMessage())); + } + } + + @Override + protected void onActivityResult(int requestCode, int resultCode, Intent data) { + AppCenterLog.warn(LOG_TAG, "onActivityResult: " + resultCode); + if (data != null && data.getExtras() != null) { + for (String key : data.getExtras().keySet()) { + AppCenterLog.warn(LOG_TAG, key + ": " + data.getExtras().get(key)); + } + } + complete(new Result(resultCode, null)); + finish(); + } + + @Override + public void finish() { + super.finish(); + overridePendingTransition(0, 0); + } +} \ No newline at end of file diff --git a/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/install/intent/IntentReleaseInstaller.java b/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/install/intent/IntentReleaseInstaller.java index 7cc633c10..1959d76cc 100644 --- a/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/install/intent/IntentReleaseInstaller.java +++ b/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/install/intent/IntentReleaseInstaller.java @@ -1,5 +1,8 @@ package com.microsoft.appcenter.distribute.install.intent; +import static android.app.Activity.RESULT_CANCELED; +import static android.app.Activity.RESULT_FIRST_USER; + import android.content.Context; import android.content.Intent; import android.net.Uri; @@ -9,6 +12,9 @@ import androidx.annotation.NonNull; import com.microsoft.appcenter.distribute.install.AbstractReleaseInstaller; +import com.microsoft.appcenter.distribute.install.ReleaseInstallerActivity; +import com.microsoft.appcenter.utils.async.AppCenterConsumer; +import com.microsoft.appcenter.utils.async.AppCenterFuture; public class IntentReleaseInstaller extends AbstractReleaseInstaller { @@ -19,29 +25,25 @@ public IntentReleaseInstaller(Context context, Handler installerHandler, Listene @AnyThread @Override public void install(@NonNull Uri localUri) { - final Intent intent = getInstallIntent(localUri); - post(new Runnable() { + final Intent installIntent = getInstallIntent(localUri); + if (installIntent.resolveActivity(mContext.getPackageManager()) == null) { + onError("Cannot resolve install intent for " + localUri); + return; + } + AppCenterFuture confirmFuture = ReleaseInstallerActivity.startActivityForResult(mContext, installIntent); + if (confirmFuture != null) { + confirmFuture.thenAccept(new AppCenterConsumer() { - @Override - public void run() { - if (intent.resolveActivity(mContext.getPackageManager()) == null) { - onError("Cannot resolve install intent for " + localUri); - return; + @Override + public void accept(ReleaseInstallerActivity.Result result) { + if (result.code == RESULT_FIRST_USER) { + onError("Install failed"); + } else if (result.code == RESULT_CANCELED) { + onCancel(); + } } - mContext.startActivity(intent); - } - }); - } - - @Override - public void resume() { - // If we're still here then it was cancelled. - onCancel(); - } - - @Override - public void clear() { - // Nothing to clear. + }); + } } @NonNull @@ -61,8 +63,7 @@ protected static Intent getInstallIntent(Uri fileUri) { Intent intent = new Intent(Intent.ACTION_INSTALL_PACKAGE); intent.setData(fileUri); intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); - intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); - intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP); + intent.putExtra(Intent.EXTRA_RETURN_RESULT, true); return intent; } } diff --git a/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/install/session/InstallStatusReceiver.java b/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/install/session/InstallStatusReceiver.java index e8d81d22a..a91aad3ab 100644 --- a/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/install/session/InstallStatusReceiver.java +++ b/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/install/session/InstallStatusReceiver.java @@ -42,17 +42,17 @@ static IntentFilter getInstallerReceiverFilter() { * Return IntentSender with the receiver that listens to the package installer session status. * * @param context any context. - * @param sessionId install sessionId. + * @param requestCode request code for the sender. * @return IntentSender with receiver. */ - static IntentSender getInstallStatusIntentSender(Context context, int sessionId) { + static IntentSender getInstallStatusIntentSender(Context context, int requestCode) { int broadcastFlags = 0; if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { broadcastFlags = PendingIntent.FLAG_MUTABLE; } PendingIntent pendingIntent = PendingIntent.getBroadcast( context, - sessionId, + requestCode, new Intent(INSTALL_STATUS_ACTION), broadcastFlags); return pendingIntent.getIntentSender(); @@ -67,6 +67,7 @@ static IntentSender getInstallStatusIntentSender(Context context, int sessionId) @Override public void onReceive(Context context, Intent intent) { String action = intent.getAction(); + AppCenterLog.verbose(LOG_TAG, "Receive broadcast action: " + action); if (INSTALL_STATUS_ACTION.equals(action)) { onInstallStatus(intent); } @@ -74,17 +75,21 @@ public void onReceive(Context context, Intent intent) { private void onInstallStatus(Intent intent) { Bundle extras = intent.getExtras(); + for (String key : extras.keySet()) { + AppCenterLog.verbose(LOG_TAG, "\t" + key + ": " + extras.get(key)); + } int status = extras.getInt(PackageInstaller.EXTRA_STATUS); + int sessionId = extras.getInt(PackageInstaller.EXTRA_SESSION_ID); switch (status) { case PackageInstaller.STATUS_PENDING_USER_ACTION: - Intent confirmIntent = (Intent) extras.get(Intent.EXTRA_INTENT); - mInstaller.onInstallConfirmation(confirmIntent); + Intent confirmIntent = extras.getParcelable(Intent.EXTRA_INTENT); + mInstaller.onInstallConfirmation(sessionId, confirmIntent); break; case PackageInstaller.STATUS_SUCCESS: AppCenterLog.info(LOG_TAG, "Application was successfully updated."); break; case PackageInstaller.STATUS_FAILURE_ABORTED: - mInstaller.onInstallCancel(); + mInstaller.onInstallCancel(sessionId); break; case PackageInstaller.STATUS_FAILURE: case PackageInstaller.STATUS_FAILURE_BLOCKED: @@ -93,7 +98,7 @@ private void onInstallStatus(Intent intent) { case PackageInstaller.STATUS_FAILURE_STORAGE: case PackageInstaller.STATUS_FAILURE_INCOMPATIBLE: String message = extras.getString(PackageInstaller.EXTRA_STATUS_MESSAGE); - mInstaller.onInstallError(message); + mInstaller.onInstallError(sessionId, message); break; default: AppCenterLog.warn(LOG_TAG, String.format(Locale.ENGLISH, "Unrecognized status received from installer: %s", status)); diff --git a/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/install/session/PackageInstallerListener.java b/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/install/session/PackageInstallerListener.java index 737504102..df922c3a9 100644 --- a/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/install/session/PackageInstallerListener.java +++ b/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/install/session/PackageInstallerListener.java @@ -30,7 +30,7 @@ public class PackageInstallerListener extends PackageInstaller.SessionCallback { @WorkerThread @Override public void onCreated(int sessionId) { - AppCenterLog.debug(LOG_TAG, "The install session was created."); + AppCenterLog.verbose(LOG_TAG, "The install session was created. sessionId=" + sessionId); } @WorkerThread @@ -46,14 +46,14 @@ public void onActiveChanged(int sessionId, boolean active) { @WorkerThread @Override public void onProgressChanged(int sessionId, float progress) { - final int downloadProgress = (int)(progress * 100); - AppCenterLog.verbose(LOG_TAG, String.format(Locale.ENGLISH, "Installation progress: %d / 100.", downloadProgress)); - mInstaller.onInstallProgress(); + final int downloadProgress = (int) (progress * 100); + AppCenterLog.verbose(LOG_TAG, "Installation progress: " + downloadProgress + "%. sessionId=" + sessionId); + mInstaller.onInstallProgress(sessionId); } @WorkerThread @Override public void onFinished(int sessionId, boolean success) { - AppCenterLog.debug(LOG_TAG, "The installation has been finished. success=" + success); + AppCenterLog.verbose(LOG_TAG, "The installation has been finished. sessionId=" + sessionId + ", success=" + success); } } diff --git a/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/install/session/SessionReleaseInstaller.java b/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/install/session/SessionReleaseInstaller.java index 303ca803e..0aa35b234 100644 --- a/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/install/session/SessionReleaseInstaller.java +++ b/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/install/session/SessionReleaseInstaller.java @@ -17,7 +17,10 @@ import androidx.annotation.WorkerThread; import com.microsoft.appcenter.distribute.install.AbstractReleaseInstaller; +import com.microsoft.appcenter.distribute.install.ReleaseInstallerActivity; import com.microsoft.appcenter.utils.AppCenterLog; +import com.microsoft.appcenter.utils.async.AppCenterConsumer; +import com.microsoft.appcenter.utils.async.AppCenterFuture; import java.io.FileInputStream; import java.io.IOException; @@ -38,6 +41,8 @@ public class SessionReleaseInstaller extends AbstractReleaseInstaller { private static final int INVALID_SESSION_ID = -1; + private static final long CANCEL_TIMEOUT = 1000; + private BroadcastReceiver mInstallStatusReceiver; private PackageInstaller.SessionCallback mSessionCallback; @@ -63,31 +68,12 @@ public void install(@NonNull Uri localUri) { @Override public void run() { + abandonSession(); startInstallSession(localUri); } }); } - @Override - public void resume() { - postDelayed(new Runnable() { - - @Override - public void run() { - delayedResume(); - } - }, 500); - } - - private synchronized void delayedResume() { - if (mUserConfirmationRequested) { - onCancel(); - } - - // Sometimes progress event comes a bit late, in this case second resume means cancellation. - mUserConfirmationRequested = true; - } - @Override public synchronized void clear() { unregisterListeners(); @@ -100,32 +86,59 @@ public String toString() { return "PackageInstaller"; } - synchronized void onInstallProgress() { + synchronized void onInstallProgress(int sessionId) { + if (mSessionId != sessionId) { + return; + } mUserConfirmationRequested = false; } - synchronized void onInstallConfirmation(Intent intent) { + synchronized void onInstallConfirmation(int sessionId, Intent confirmIntent) { + if (mSessionId != sessionId) { + return; + } AppCenterLog.info(LOG_TAG, "Ask confirmation to install a new release."); - intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); mUserConfirmationRequested = true; - post(new Runnable() { - @Override - public void run() { - mContext.startActivity(intent); - } - }); + AppCenterFuture confirmFuture = ReleaseInstallerActivity.startActivityForResult(mContext, confirmIntent); + if (confirmFuture != null) { + confirmFuture.thenAccept(new AppCenterConsumer() { + + @Override + public void accept(ReleaseInstallerActivity.Result result) { + cancelIfNoProgress(); + } + }); + } } - synchronized void onInstallError(String message) { + synchronized void onInstallError(int sessionId, String message) { + if (mSessionId != sessionId) { + return; + } mSessionId = INVALID_SESSION_ID; onError(message); } - synchronized void onInstallCancel() { + synchronized void onInstallCancel(int sessionId) { + if (mSessionId != sessionId) { + return; + } mSessionId = INVALID_SESSION_ID; onCancel(); } + private void cancelIfNoProgress() { + postDelayed(new Runnable() { + + @Override + public void run() { + if (mUserConfirmationRequested) { + onCancel(); + } + } + }, CANCEL_TIMEOUT); + } + @WorkerThread private synchronized void startInstallSession(@NonNull Uri localUri) { PackageInstaller.Session session = null; @@ -139,13 +152,11 @@ private synchronized void startInstallSession(@NonNull Uri localUri) { IntentSender statusReceiver = InstallStatusReceiver.getInstallStatusIntentSender(mContext, mSessionId); session.commit(statusReceiver); session.close(); - - // IllegalStateException - Too many active sessions - } catch (IOException | IllegalStateException e) { + } catch (IOException | RuntimeException e) { if (session != null) { session.abandon(); } - onError("Couldn't install a new release: " + e); + onError("Cannot initiate PackageInstaller.Session", e); } } @@ -195,14 +206,33 @@ private PackageInstaller.Session createSession(ParcelFileDescriptor fileDescript params.setSize(fileDescriptor.getStatSize()); params.setAppPackageName(mContext.getPackageName()); mSessionId = packageInstaller.createSession(params); - return packageInstaller.openSession(mSessionId); + try { + return packageInstaller.openSession(mSessionId); + + // IllegalStateException - Too many active sessions + } catch (IllegalStateException e) { + AppCenterLog.warn(LOG_TAG, "Cannot open session, trying to cleanup previous ones.", e); + + // Leaked sessions can prevent opening new ones. + cleanPreviousSessions(); + return packageInstaller.openSession(mSessionId); + } } private void abandonSession() { if (mSessionId != INVALID_SESSION_ID) { + AppCenterLog.debug(LOG_TAG, "Abandon PackageInstaller session."); PackageInstaller packageInstaller = getPackageInstaller(); packageInstaller.abandonSession(mSessionId); mSessionId = INVALID_SESSION_ID; } } + + private void cleanPreviousSessions() { + PackageInstaller packageInstaller = getPackageInstaller(); + for (PackageInstaller.SessionInfo session: getPackageInstaller().getMySessions()) { + AppCenterLog.warn(LOG_TAG, "Abandon leaked session: " + session.getSessionId()); + packageInstaller.abandonSession(session.getSessionId()); + } + } } From 614edccdb40f091568a096c1481474ae525511ce Mon Sep 17 00:00:00 2001 From: Ivan Matkov Date: Wed, 25 May 2022 10:36:58 +0200 Subject: [PATCH 56/72] Fix tests (part 1, build errors only) --- .../distribute/AbstractDistributeTest.java | 41 ++- ...AppCenterPackageInstallerReceiverTest.java | 259 ----------------- .../appcenter/distribute/DistributeTest.java | 227 +-------------- .../distribute/InstallerUtilsTest.java | 140 --------- .../ReleaseDownloadListenerTest.java | 94 ++---- .../ReleaseInstallerListenerTest.java | 269 ------------------ 6 files changed, 41 insertions(+), 989 deletions(-) delete mode 100644 sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/AppCenterPackageInstallerReceiverTest.java delete mode 100644 sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/InstallerUtilsTest.java delete mode 100644 sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/ReleaseInstallerListenerTest.java diff --git a/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/AbstractDistributeTest.java b/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/AbstractDistributeTest.java index b6c7d14d8..956fa622d 100644 --- a/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/AbstractDistributeTest.java +++ b/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/AbstractDistributeTest.java @@ -5,6 +5,23 @@ package com.microsoft.appcenter.distribute; +import static com.microsoft.appcenter.distribute.DistributeConstants.INVALID_DOWNLOAD_IDENTIFIER; +import static com.microsoft.appcenter.distribute.DistributeConstants.PREFERENCE_KEY_DOWNLOAD_ID; +import static com.microsoft.appcenter.utils.PrefStorageConstants.ALLOWED_NETWORK_REQUEST; +import static com.microsoft.appcenter.utils.PrefStorageConstants.KEY_ENABLED; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyBoolean; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; +import static org.powermock.api.mockito.PowerMockito.doAnswer; +import static org.powermock.api.mockito.PowerMockito.doCallRealMethod; +import static org.powermock.api.mockito.PowerMockito.mockStatic; +import static org.powermock.api.mockito.PowerMockito.spy; +import static org.powermock.api.mockito.PowerMockito.whenNew; + import android.annotation.SuppressLint; import android.app.Activity; import android.app.AlertDialog; @@ -50,23 +67,6 @@ import java.util.UUID; -import static com.microsoft.appcenter.distribute.DistributeConstants.INVALID_DOWNLOAD_IDENTIFIER; -import static com.microsoft.appcenter.distribute.DistributeConstants.PREFERENCE_KEY_DOWNLOAD_ID; -import static com.microsoft.appcenter.utils.PrefStorageConstants.ALLOWED_NETWORK_REQUEST; -import static com.microsoft.appcenter.utils.PrefStorageConstants.KEY_ENABLED; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.anyBoolean; -import static org.mockito.ArgumentMatchers.anyInt; -import static org.mockito.ArgumentMatchers.anyString; -import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; -import static org.powermock.api.mockito.PowerMockito.doAnswer; -import static org.powermock.api.mockito.PowerMockito.doCallRealMethod; -import static org.powermock.api.mockito.PowerMockito.mockStatic; -import static org.powermock.api.mockito.PowerMockito.spy; -import static org.powermock.api.mockito.PowerMockito.whenNew; - @SuppressWarnings("CanBeFinal") @PrepareForTest({ AppCenter.class, @@ -148,9 +148,6 @@ public class AbstractDistributeTest { @Mock ReleaseDownloadListener mReleaseDownloaderListener; - @Mock - ReleaseInstallerListener mReleaseInstallerListener; - @Mock ReleaseDetails mReleaseDetails; @@ -322,10 +319,6 @@ public Void answer(InvocationOnMock invocation) { mReleaseDownloaderListener = spy(new ReleaseDownloadListener(mContext, mReleaseDetails)); whenNew(ReleaseDownloadListener.class).withArguments(any(Context.class), any(ReleaseDetails.class)).thenReturn(mReleaseDownloaderListener); - /* Mock Release Installer Listener. */ - mReleaseInstallerListener = mock(ReleaseInstallerListener.class); - whenNew(ReleaseInstallerListener.class).withArguments(any(Context.class)).thenReturn(mReleaseInstallerListener); - /* Mock Uri. */ when(mUri.toString()).thenReturn(LOCAL_FILENAME_PATH_MOCK); when(mUri.getPath()).thenReturn(LOCAL_FILENAME_PATH_MOCK); diff --git a/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/AppCenterPackageInstallerReceiverTest.java b/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/AppCenterPackageInstallerReceiverTest.java deleted file mode 100644 index 8db3fdba2..000000000 --- a/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/AppCenterPackageInstallerReceiverTest.java +++ /dev/null @@ -1,259 +0,0 @@ -/* - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. - */ - -package com.microsoft.appcenter.distribute; - -import static com.microsoft.appcenter.distribute.AppCenterPackageInstallerReceiver.INSTALL_STATUS_ACTION; -import static com.microsoft.appcenter.distribute.AppCenterPackageInstallerReceiver.MY_PACKAGE_REPLACED_ACTION; -import static com.microsoft.appcenter.distribute.DistributeConstants.LOG_TAG; -import static org.junit.Assert.assertEquals; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.anyString; -import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; -import static org.powermock.api.mockito.PowerMockito.mockStatic; -import static org.powermock.api.mockito.PowerMockito.verifyStatic; - -import android.app.PendingIntent; -import android.content.Context; -import android.content.Intent; -import android.content.IntentSender; -import android.content.pm.PackageInfo; -import android.content.pm.PackageInstaller; -import android.os.Build; -import android.os.Bundle; - -import com.microsoft.appcenter.utils.AppCenterLog; -import com.microsoft.appcenter.utils.AppNameHelper; -import com.microsoft.appcenter.utils.DeviceInfoHelper; - -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.Mock; -import org.powermock.core.classloader.annotations.PrepareForTest; -import org.powermock.modules.junit4.PowerMockRunner; -import org.powermock.reflect.Whitebox; - -@PrepareForTest({ - AppCenterLog.class, - AppNameHelper.class, - DeviceInfoHelper.class, - Distribute.class, - DistributeUtils.class, - PendingIntent.class -}) -@RunWith(PowerMockRunner.class) -public class AppCenterPackageInstallerReceiverTest { - - @Mock - private Context mContext; - - @Mock - private Intent mIntent; - - @Mock - private Bundle mExtra; - - private AppCenterPackageInstallerReceiver mAppCenterPackageInstallerReceiver; - - private void installStatus(int status) { - when(mIntent.getAction()).thenReturn(INSTALL_STATUS_ACTION); - when(mExtra.getInt(eq(PackageInstaller.EXTRA_STATUS))).thenReturn(status); - } - - @Before - public void setUp() { - when(mIntent.getExtras()).thenReturn(mExtra); - - /* Mock static classes. */ - mockStatic(AppCenterLog.class); - mockStatic(DeviceInfoHelper.class); - mockStatic(DistributeUtils.class); - - /* Init receiver. */ - mAppCenterPackageInstallerReceiver = new AppCenterPackageInstallerReceiver(); - } - - @Test - public void receiveMyPackageReplaced() { - PackageInfo packageInfo = new PackageInfo(); - packageInfo.versionName = "1.0"; - when(DeviceInfoHelper.getPackageInfo(mContext)).thenReturn(packageInfo); - when(DeviceInfoHelper.getVersionCode(packageInfo)).thenReturn(1); - mockStatic(AppNameHelper.class); - when(AppNameHelper.getAppName(mContext)).thenReturn("Contoso"); - Intent intent = mock(Intent.class); - when(DistributeUtils.getResumeAppIntent(mContext)).thenReturn(intent); - when(mIntent.getAction()).thenReturn(MY_PACKAGE_REPLACED_ACTION); - when(mContext.getString(R.string.appcenter_distribute_install_completed_title)) - .thenReturn("Title"); - when(mContext.getString(R.string.appcenter_distribute_install_completed_message)) - .thenReturn("%1$s %2$s (%3$d)"); - - /* Call method with MY_PACKAGE_REPLACED_ACTION action. */ - mAppCenterPackageInstallerReceiver.onReceive(mContext, mIntent); - - verifyStatic(DistributeUtils.class); - DistributeUtils.postNotification(eq(mContext), eq("Title"), eq("Contoso 1.0 (1)"), eq(intent)); - } - - @Test - public void receiveMyPackageReplacedWithoutPackageInfo() { - mockStatic(AppNameHelper.class); - when(AppNameHelper.getAppName(mContext)).thenReturn("Contoso"); - Intent intent = mock(Intent.class); - when(DistributeUtils.getResumeAppIntent(mContext)).thenReturn(intent); - when(mIntent.getAction()).thenReturn(MY_PACKAGE_REPLACED_ACTION); - when(mContext.getString(R.string.appcenter_distribute_install_completed_title)) - .thenReturn("Title"); - when(mContext.getString(R.string.appcenter_distribute_install_completed_message)) - .thenReturn("%1$s %2$s (%3$d)"); - - /* Call method with MY_PACKAGE_REPLACED_ACTION action. */ - mAppCenterPackageInstallerReceiver.onReceive(mContext, mIntent); - - verifyStatic(DistributeUtils.class); - DistributeUtils.postNotification(eq(mContext), eq("Title"), eq("Contoso ? (0)"), eq(intent)); - } - - @Test - public void receiveInstallStatusPendingUserAction() { - installStatus(PackageInstaller.STATUS_PENDING_USER_ACTION); - Intent confirmIntent = mock(Intent.class); - when(mExtra.get(eq(Intent.EXTRA_INTENT))).thenReturn(confirmIntent); - - /* Call method with STATUS_PENDING_USER_ACTION action. */ - mAppCenterPackageInstallerReceiver.onReceive(mContext, mIntent); - - /* Verify that activity was started. */ - verify(mContext).startActivity(eq(confirmIntent)); - } - - @Test - public void receiveInstallStatusSuccess() { - installStatus(PackageInstaller.STATUS_SUCCESS); - - /* Call method with STATUS_SUCCESS action. */ - mAppCenterPackageInstallerReceiver.onReceive(mContext, mIntent); - - /* Verify. */ - verifyStatic(AppCenterLog.class); - AppCenterLog.info(eq(LOG_TAG), eq("Application was successfully updated.")); - } - - @Test - public void receiveInstallStatusFailure() { - receiveInstallStatusFailure(PackageInstaller.STATUS_FAILURE); - } - - @Test - public void receiveInstallStatusFailureAborted() { - receiveInstallStatusFailure(PackageInstaller.STATUS_FAILURE_ABORTED); - } - - @Test - public void receiveInstallStatusFailureBlocked() { - receiveInstallStatusFailure(PackageInstaller.STATUS_FAILURE_BLOCKED); - } - - @Test - public void receiveInstallStatusFailureConflict() { - receiveInstallStatusFailure(PackageInstaller.STATUS_FAILURE_CONFLICT); - } - - @Test - public void receiveInstallStatusFailureIncompatible() { - receiveInstallStatusFailure(PackageInstaller.STATUS_FAILURE_INCOMPATIBLE); - } - - @Test - public void receiveInstallStatusFailureInvalid() { - receiveInstallStatusFailure(PackageInstaller.STATUS_FAILURE_INVALID); - } - - @Test - public void receiveInstallStatusFailureStorage() { - receiveInstallStatusFailure(PackageInstaller.STATUS_FAILURE_STORAGE); - } - - private void receiveInstallStatusFailure(int status) { - installStatus(status); - - mockStatic(Distribute.class); - Distribute distribute = mock(Distribute.class); - when(Distribute.getInstance()).thenReturn(distribute); - - /* Call method with failure action. */ - mAppCenterPackageInstallerReceiver.onReceive(mContext, mIntent); - - /* Verify that log was called. */ - verify(distribute).showInstallingErrorToast(); - verifyStatic(AppCenterLog.class); - AppCenterLog.error(eq(LOG_TAG), eq("Failed to install a new release with status: " + status + ". Error message: null.")); - } - - @Test - public void onReceiveWithStartIntentWithUnrecognizedStatus() { - final int UNKNOWN_STATUS = 42; - installStatus(UNKNOWN_STATUS); - - /* Call method with wrong action. */ - mAppCenterPackageInstallerReceiver.onReceive(mContext, mIntent); - - /* Verify that log was called. */ - verifyStatic(AppCenterLog.class); - AppCenterLog.warn(eq(LOG_TAG), eq("Unrecognized status received from installer: " + UNKNOWN_STATUS)); - } - - @Test - public void onReceiverWithUnknownAction() { - when(mIntent.getAction()).thenReturn("UnknownAction"); - - /* Call method with wrong action. */ - mAppCenterPackageInstallerReceiver.onReceive(mContext, mIntent); - - /* Verify that log was called. */ - verifyStatic(AppCenterLog.class); - AppCenterLog.warn(eq(LOG_TAG), eq("Unrecognized action UnknownAction - do nothing.")); - } - - @Test - public void createIntentSenderOnAndroidS() { - - /* Mock SDK_INT to S. */ - Whitebox.setInternalState(Build.VERSION.class, "SDK_INT", Build.VERSION_CODES.S); - createIntentSender(PendingIntent.FLAG_MUTABLE); - } - - @Test - public void createIntentSenderOnAndroidLowS() { - - /* Mock SDK_INT to M. */ - Whitebox.setInternalState(Build.VERSION.class, "SDK_INT", Build.VERSION_CODES.M); - createIntentSender(0); - } - - private void createIntentSender(int expectedFlag) { - final int REQUEST_CODE = 42; - - /* Mock. */ - mockStatic(PendingIntent.class); - PendingIntent intent = mock(PendingIntent.class); - IntentSender sender = mock(IntentSender.class); - when(intent.getIntentSender()).thenReturn(sender); - when(PendingIntent.getBroadcast(any(Context.class), eq(REQUEST_CODE), any(Intent.class), eq(expectedFlag))) - .thenReturn(intent); - - /* Call. */ - IntentSender result = AppCenterPackageInstallerReceiver.getInstallStatusIntentSender(mContext, REQUEST_CODE); - - /* Verify. */ - assertEquals(sender, result); - } -} - diff --git a/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/DistributeTest.java b/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/DistributeTest.java index c7b7bcbbd..5c6b3f519 100644 --- a/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/DistributeTest.java +++ b/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/DistributeTest.java @@ -11,7 +11,6 @@ import static com.microsoft.appcenter.distribute.DistributeConstants.DOWNLOAD_STATE_ENQUEUED; import static com.microsoft.appcenter.distribute.DistributeConstants.DOWNLOAD_STATE_INSTALLING; import static com.microsoft.appcenter.distribute.DistributeConstants.DOWNLOAD_STATE_NOTIFIED; -import static com.microsoft.appcenter.distribute.DistributeConstants.LOG_TAG; import static com.microsoft.appcenter.distribute.DistributeConstants.PREFERENCE_KEY_DOWNLOADED_DISTRIBUTION_GROUP_ID; import static com.microsoft.appcenter.distribute.DistributeConstants.PREFERENCE_KEY_DOWNLOADED_RELEASE_HASH; import static com.microsoft.appcenter.distribute.DistributeConstants.PREFERENCE_KEY_DOWNLOADED_RELEASE_ID; @@ -19,7 +18,6 @@ import static com.microsoft.appcenter.distribute.DistributeConstants.PREFERENCE_KEY_DOWNLOAD_TIME; import static com.microsoft.appcenter.distribute.DistributeConstants.PREFERENCE_KEY_RELEASE_DETAILS; import static com.microsoft.appcenter.distribute.DistributeConstants.PREFERENCE_KEY_UPDATE_SETUP_FAILED_MESSAGE_KEY; -import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertSame; import static org.junit.Assert.assertTrue; @@ -27,8 +25,6 @@ import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.ArgumentMatchers.isA; -import static org.mockito.Mockito.RETURNS_MOCKS; import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.doNothing; import static org.mockito.Mockito.doReturn; @@ -43,7 +39,6 @@ import static org.powermock.api.mockito.PowerMockito.whenNew; import android.app.Activity; -import android.app.Notification; import android.app.NotificationManager; import android.app.ProgressDialog; import android.content.BroadcastReceiver; @@ -53,9 +48,7 @@ import android.content.Intent; import android.content.IntentFilter; import android.content.pm.PackageInfo; -import android.net.Uri; import android.os.Build; -import android.widget.Toast; import com.microsoft.appcenter.DependencyConfiguration; import com.microsoft.appcenter.distribute.download.ReleaseDownloader; @@ -69,14 +62,12 @@ import com.microsoft.appcenter.http.ServiceCallback; import com.microsoft.appcenter.ingestion.models.json.LogFactory; import com.microsoft.appcenter.test.TestUtils; -import com.microsoft.appcenter.utils.AppCenterLog; import com.microsoft.appcenter.utils.DeviceInfoHelper; import com.microsoft.appcenter.utils.storage.SharedPreferencesManager; import org.junit.After; import org.junit.Test; import org.mockito.ArgumentCaptor; -import org.mockito.Mockito; import org.mockito.invocation.InvocationOnMock; import org.mockito.stubbing.Answer; import org.powermock.api.mockito.PowerMockito; @@ -266,7 +257,7 @@ public void setDownloadingReleaseDetailsNotEqualTest() { @Test public void setInstallingReleaseDetailsNotEqualTest() { - Distribute.getInstance().setInstalling(mReleaseDetails); + Distribute.getInstance().setInstalling(mReleaseDetails, mUri); verifyStatic(SharedPreferencesManager.class, never()); SharedPreferencesManager.getInt(eq(PREFERENCE_KEY_DOWNLOAD_STATE), eq(DOWNLOAD_STATE_COMPLETED)); verifyStatic(SharedPreferencesManager.class, never()); @@ -279,7 +270,7 @@ public void setInstallingTest() { /* Mock release details and startFromBackground to apply it. */ mockReleaseDetails(false); Distribute.getInstance().startFromBackground(mContext); - Distribute.getInstance().setInstalling(mReleaseDetails); + Distribute.getInstance().setInstalling(mReleaseDetails, mUri); /* Verify. */ verifyStatic(SharedPreferencesManager.class); @@ -297,7 +288,7 @@ public void setInstallingMandatoryReleaseDetailsTest() { /* Mock release details and startFromBackground to apply it. */ mockReleaseDetails(true); Distribute.getInstance().startFromBackground(mContext); - Distribute.getInstance().setInstalling(mReleaseDetails); + Distribute.getInstance().setInstalling(mReleaseDetails, mUri); /* Verify. */ verifyStatic(DistributeUtils.class); @@ -339,84 +330,6 @@ public void cancelingNotification() { verify(manager).cancel(any(Integer.class)); } - @Test - public void firstDownloadNotificationApi26() throws Exception { - firstDownloadNotification(Build.VERSION_CODES.O); - } - - @Test - public void firstDownloadNotificationApi21() throws Exception { - firstDownloadNotification(Build.VERSION_CODES.LOLLIPOP); - } - - @Test - public void notifyDownloadNoReleaseDetails() throws Exception { - mockStatic(DistributeUtils.class); - whenNew(Notification.Builder.class).withAnyArguments() - .thenReturn(mock(Notification.Builder.class, RETURNS_MOCKS)); - when(DistributeUtils.loadCachedReleaseDetails()).thenReturn(mReleaseDetails); - when(mContext.getSystemService(NOTIFICATION_SERVICE)).thenReturn(mNotificationManager); - Distribute.getInstance().startFromBackground(mContext); - - /* Mock another ReleaseDetails. */ - ReleaseDetails mockReleaseDetails = mock(ReleaseDetails.class); - - /* Call notify download. */ - boolean notifyDownloadResult = Distribute.getInstance().notifyDownload(mockReleaseDetails, null); - - /* Verify. */ - assertTrue(notifyDownloadResult); - verify(mNotificationManager, never()).notify(eq(DistributeUtils.getNotificationId()), any(Notification.class)); - } - - @Test - public void notifyDownloadStateNotified() throws Exception { - mockStatic(DistributeUtils.class); - whenNew(Notification.Builder.class).withAnyArguments() - .thenReturn(mock(Notification.Builder.class, RETURNS_MOCKS)); - when(DistributeUtils.loadCachedReleaseDetails()).thenReturn(mReleaseDetails); - when(mContext.getSystemService(NOTIFICATION_SERVICE)).thenReturn(mNotificationManager); - Distribute.getInstance().startFromBackground(mContext); - - /* Mock notified state. */ - when(DistributeUtils.getStoredDownloadState()).thenReturn(DOWNLOAD_STATE_NOTIFIED); - - /* Call notify download. */ - boolean notifyDownloadResult = Distribute.getInstance().notifyDownload(mReleaseDetails, null); - - /* Verify. */ - assertFalse(notifyDownloadResult); - verify(mNotificationManager, never()).notify(eq(DistributeUtils.getNotificationId()), any(Notification.class)); - } - - @Test - public void notifyDownloadForegroundActivity() throws Exception { - mockStatic(DistributeUtils.class); - whenNew(Notification.Builder.class).withAnyArguments() - .thenReturn(mock(Notification.Builder.class, RETURNS_MOCKS)); - when(DistributeUtils.loadCachedReleaseDetails()).thenReturn(mReleaseDetails); - when(mContext.getSystemService(NOTIFICATION_SERVICE)).thenReturn(mNotificationManager); - Distribute.getInstance().startFromBackground(mContext); - - /* Mock foreground activity. */ - Distribute.getInstance().onActivityResumed(mock(Activity.class)); - - /* Call notify download. */ - boolean notifyDownloadResult = Distribute.getInstance().notifyDownload(mReleaseDetails, null); - - /* Verify. */ - assertFalse(notifyDownloadResult); - verify(mNotificationManager, never()).notify(eq(DistributeUtils.getNotificationId()), any(Notification.class)); - } - - @Test - public void checkNotificationState() { - mockStatic(DistributeUtils.class); - when(DistributeUtils.loadCachedReleaseDetails()).thenReturn(mReleaseDetails); - Distribute.getInstance().startFromBackground(mContext); - assertTrue(Distribute.getInstance().notifyDownload(mock(ReleaseDetails.class), null)); - } - @Test public void updateReleaseDetailsFromBackground() { mockStatic(DistributeUtils.class); @@ -876,138 +789,4 @@ public void applyEnableStateRegistersInstallReceiver() { /* Check that receiver was unregistered. */ verify(mContext).unregisterReceiver(any(BroadcastReceiver.class)); } - - @Test - public void checkProgressWhenActivityNull() { - mockStatic(DistributeUtils.class); - - /* Start distribute. */ - start(); - - /* Notify install progress. */ - Distribute.getInstance().notifyInstallProgress(true); - - /* Verify that progress dialog was not trying to display. */ - verifyStatic(AppCenterLog.class); - AppCenterLog.warn(eq(LOG_TAG), eq("Could not display install progress dialog in the background.")); - - /* Resume workflow. */ - resumeWorkflow(mock(Activity.class)); - - /* Verify that it does nothing. */ - verifyStatic(DistributeUtils.class, never()); - DistributeUtils.getStoredDownloadState(); - } - - @Test - public void checkProgressWhenReleaseInstallerIsNull() { - - /* Start distribute. */ - start(); - - /* Initialize mReleaseInstallerListener. */ - Distribute.getInstance().startFromBackground(mContext); - - /* Call resume with the activity reference so that mForegroundActivity not null. */ - Distribute.getInstance().onActivityResumed(mActivity); - - /* Remove listener if it is not null. */ - Distribute.getInstance().notifyInstallProgress(true); - verify(mReleaseInstallerListener, never()).showInstallProgressDialog(any(Activity.class)); - verify(mReleaseInstallerListener, never()).hideInstallProgressDialog(); - - /* Verify hide install progress was not invoked. */ - Distribute.getInstance().notifyInstallProgress(false); - verify(mReleaseInstallerListener, never()).hideInstallProgressDialog(); - } - - @Test - public void checkInstallProgressState() { - - /* Mock Distribute Utils. */ - mockStatic(DistributeUtils.class); - when(DistributeUtils.loadCachedReleaseDetails()).thenReturn(mReleaseDetails); - - /* Start distribute. */ - Distribute.getInstance().onStarting(mAppCenterHandler); - Distribute.getInstance().onStarted(mContext, mChannel, null, null, true); - when(mReleaseInstallerListener.showInstallProgressDialog(any(Activity.class))).thenReturn(mDialog); - - /* Resume activity. */ - Distribute.getInstance().onActivityResumed(mActivity); - - /* Verify that release listener was initialized. */ - Distribute.getInstance().startFromBackground(mContext); - Distribute.getInstance().notifyInstallProgress(true); - verify(mReleaseInstallerListener).hideInstallProgressDialog(); - verify(mReleaseInstallerListener).showInstallProgressDialog(any(Activity.class)); - - /* Stop installing and verify thad dialog was hide. */ - Distribute.getInstance().notifyInstallProgress(false); - verify(mReleaseInstallerListener, times(2)).hideInstallProgressDialog(); - } - - private void firstDownloadNotification(int apiLevel) throws Exception { - TestUtils.setInternalState(Build.VERSION.class, "SDK_INT", apiLevel); - spy(DistributeUtils.class); - NotificationManager manager = mock(NotificationManager.class); - whenNew(Notification.Builder.class).withAnyArguments() - .thenReturn(mock(Notification.Builder.class, RETURNS_MOCKS)); - PowerMockito.doReturn(mReleaseDetails).when(DistributeUtils.class); - DistributeUtils.loadCachedReleaseDetails(); - PowerMockito.doReturn(0).when(DistributeUtils.class); - DistributeUtils.getNotificationId(); - when(mContext.getSystemService(NOTIFICATION_SERVICE)).thenReturn(manager); - Distribute.getInstance().startFromBackground(mContext); - Distribute.getInstance().onStarted(mContext, mChannel, "0", "test", false); - Distribute.getInstance().notifyDownload(mReleaseDetails, null); - verify(manager).notify(eq(DistributeUtils.getNotificationId()), any(Notification.class)); - } - - @Test - public void installUpdate() { - mockStatic(DistributeUtils.class); - when(DistributeUtils.loadCachedReleaseDetails()).thenReturn(mReleaseDetails); - Distribute.getInstance().startFromBackground(mContext); - - /* Try to start installation. */ - Uri localUri = mock(Uri.class); - start(); - Distribute.getInstance().installUpdate(localUri); - - /* Verify that it calls InstallerUtils. */ - verifyStatic(InstallerUtils.class); - InstallerUtils.installPackage(eq(localUri), eq(mContext), isA(ReleaseInstallerListener.class)); - } - - @Test - public void installUpdateWhenPackageInstallerNull() { - - /* Try to start installation. */ - Uri localUri = mock(Uri.class); - start(); - Distribute.getInstance().installUpdate(localUri); - - /* Verify that it was called. */ - verifyStatic(InstallerUtils.class, never()); - InstallerUtils.installPackage(any(), any(), any()); - } - - @Test - public void showInstallingErrorToast() { - start(); - - /* Without activity. */ - Distribute.getInstance().showInstallingErrorToast(); - verify(mToast).show(); - verifyStatic(Toast.class); - Toast.makeText(eq(mContext), anyInt(), anyInt()); - - /* With activity. */ - Distribute.getInstance().onActivityResumed(mActivity); - Distribute.getInstance().showInstallingErrorToast(); - verify(mToast, times(2)).show(); - verifyStatic(Toast.class); - Toast.makeText(eq(mActivity), anyInt(), anyInt()); - } } diff --git a/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/InstallerUtilsTest.java b/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/InstallerUtilsTest.java deleted file mode 100644 index 7852e207e..000000000 --- a/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/InstallerUtilsTest.java +++ /dev/null @@ -1,140 +0,0 @@ -/* - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. - */ - -package com.microsoft.appcenter.distribute; - -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.anyInt; -import static org.mockito.ArgumentMatchers.anyLong; -import static org.mockito.ArgumentMatchers.anyString; -import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.Mockito.never; -import static org.mockito.Mockito.verify; -import static org.powermock.api.mockito.PowerMockito.mock; -import static org.powermock.api.mockito.PowerMockito.mockStatic; -import static org.powermock.api.mockito.PowerMockito.when; -import static org.powermock.api.mockito.PowerMockito.whenNew; - -import android.app.PendingIntent; -import android.content.ContentResolver; -import android.content.Context; -import android.content.Intent; -import android.content.IntentSender; -import android.content.pm.PackageInstaller; -import android.content.pm.PackageManager; -import android.net.Uri; -import android.os.ParcelFileDescriptor; -import android.provider.Settings; - -import org.junit.Before; -import org.junit.Rule; -import org.junit.Test; -import org.mockito.Mock; -import org.powermock.core.classloader.annotations.PrepareForTest; -import org.powermock.modules.junit4.rule.PowerMockRule; - -import java.io.FileInputStream; -import java.io.IOException; -import java.io.OutputStream; - -@PrepareForTest({ - InstallerUtils.class, - PendingIntent.class, - Settings.class -}) -public class InstallerUtilsTest { - - @Rule - public PowerMockRule mPowerMockRule = new PowerMockRule(); - - @Mock - private Context mContext; - - @Mock - private PackageInstaller mMockPackageInstaller; - - @Mock - private FileInputStream mInputStream; - - @Mock - private OutputStream mOutputStream; - - @Mock - private PackageInstaller.Session mSession; - - @Before - public void setUp() throws Exception { - - /* Mock package installer. */ - PackageManager mockPackageManager = mock(PackageManager.class); - when(mockPackageManager.getPackageInstaller()).thenReturn(mMockPackageInstaller); - when(mContext.getPackageManager()).thenReturn(mockPackageManager); - - /* Mock input file. */ - ContentResolver contentResolver = mock(ContentResolver.class); - when(mContext.getContentResolver()).thenReturn(contentResolver); - ParcelFileDescriptor fileDescriptor = mock(ParcelFileDescriptor.class); - when(contentResolver.openFileDescriptor(any(Uri.class), eq("r"))).thenReturn(fileDescriptor); - whenNew(FileInputStream.class).withAnyArguments().thenReturn(mInputStream); - - /* Mock session. */ - when(mMockPackageInstaller.openSession(anyInt())).thenReturn(mSession); - when(mSession.openWrite(anyString(), anyLong(), anyLong())).thenReturn(mOutputStream); - } - - @Test - public void installPackage() throws IOException { - - /* Mock intent. */ - mockStatic(PendingIntent.class); - PendingIntent mockIntent = mock(PendingIntent.class); - when(mockIntent.getIntentSender()).thenReturn(mock(IntentSender.class)); - when(PendingIntent.getBroadcast(any(Context.class), anyInt(), any(Intent.class), anyInt())).thenReturn(mockIntent); - - /* Mock session callback. */ - PackageInstaller.SessionCallback mockSessionCallback = mock(PackageInstaller.SessionCallback.class); - - /* Mock data. */ - when(mInputStream.read(any())).thenReturn(10).thenReturn(-1); - - /* Call install. */ - InstallerUtils.installPackage(mock(Uri.class), mContext, mockSessionCallback); - - /* Verify. */ - verify(mMockPackageInstaller).registerSessionCallback(eq(mockSessionCallback)); - verify(mInputStream).close(); - verify(mOutputStream).close(); - verify(mSession).commit(any(IntentSender.class)); - verify(mSession, never()).abandon(); - verify(mSession).close(); - } - - @Test - public void throwIOExceptionWhenTryToOpenWriteSession() throws IOException { - - /* Throw error when try to open write to session. */ - when(mSession.openWrite(anyString(), anyLong(), anyLong())).thenThrow(new IOException()); - - /* Call install method. */ - InstallerUtils.installPackage(mock(Uri.class), mContext, null); - - /* Verify. */ - verify(mInputStream, never()).close(); - verify(mSession).abandon(); - } - - @Test - public void throwIOExceptionWhenTryToCreateSession() throws IOException { - - /* Throw error when try to create session. */ - when(mMockPackageInstaller.createSession(any(PackageInstaller.SessionParams.class))).thenThrow(new IOException()); - - /* Call install method. */ - InstallerUtils.installPackage(mock(Uri.class), mContext, null); - - /* Verify that the session wasn't created. */ - verify(mMockPackageInstaller, never()).openSession(anyInt()); - } -} diff --git a/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/ReleaseDownloadListenerTest.java b/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/ReleaseDownloadListenerTest.java index 2ad850f0e..860fb78cf 100644 --- a/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/ReleaseDownloadListenerTest.java +++ b/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/ReleaseDownloadListenerTest.java @@ -5,6 +5,27 @@ package com.microsoft.appcenter.distribute; +import static com.microsoft.appcenter.distribute.DistributeConstants.DOWNLOAD_STATE_COMPLETED; +import static com.microsoft.appcenter.distribute.DistributeConstants.MEBIBYTE_IN_BYTES; +import static com.microsoft.appcenter.distribute.DistributeConstants.PREFERENCE_KEY_DISTRIBUTION_GROUP_ID; +import static com.microsoft.appcenter.distribute.DistributeConstants.PREFERENCE_KEY_DOWNLOAD_STATE; +import static com.microsoft.appcenter.distribute.DistributeConstants.PREFERENCE_KEY_RELEASE_DETAILS; +import static com.microsoft.appcenter.distribute.DistributeConstants.PREFERENCE_KEY_UPDATE_TOKEN; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.powermock.api.mockito.PowerMockito.doAnswer; +import static org.powermock.api.mockito.PowerMockito.mockStatic; +import static org.powermock.api.mockito.PowerMockito.when; +import static org.powermock.api.mockito.PowerMockito.whenNew; + import android.app.Activity; import android.app.ProgressDialog; import android.content.ComponentName; @@ -31,28 +52,6 @@ import org.powermock.core.classloader.annotations.PrepareForTest; import org.powermock.modules.junit4.rule.PowerMockRule; -import static com.microsoft.appcenter.distribute.DistributeConstants.DOWNLOAD_STATE_COMPLETED; -import static com.microsoft.appcenter.distribute.DistributeConstants.MEBIBYTE_IN_BYTES; -import static com.microsoft.appcenter.distribute.DistributeConstants.PREFERENCE_KEY_DISTRIBUTION_GROUP_ID; -import static com.microsoft.appcenter.distribute.DistributeConstants.PREFERENCE_KEY_DOWNLOAD_STATE; -import static com.microsoft.appcenter.distribute.DistributeConstants.PREFERENCE_KEY_RELEASE_DETAILS; -import static com.microsoft.appcenter.distribute.DistributeConstants.PREFERENCE_KEY_UPDATE_TOKEN; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.anyInt; -import static org.mockito.ArgumentMatchers.anyLong; -import static org.mockito.ArgumentMatchers.anyString; -import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.never; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.verify; -import static org.powermock.api.mockito.PowerMockito.doAnswer; -import static org.powermock.api.mockito.PowerMockito.mockStatic; -import static org.powermock.api.mockito.PowerMockito.when; -import static org.powermock.api.mockito.PowerMockito.whenNew; - @PrepareForTest({ AppCenter.class, AppCenterLog.class, @@ -370,55 +369,4 @@ public void onErrorNullMessage() throws Exception { /* Verify that completeWorkflow() is called on error. */ verify(mDistribute).completeWorkflow(mockReleaseDetails); } - - @Test - public void onComplete() throws Exception { - mockStatic(DistributeUtils.class); - when(DistributeUtils.getResumeAppIntent(eq(mContext))).thenReturn(mock(Intent.class)); - ReleaseDetails mockReleaseDetails = mockReleaseDetails(true); - - /* Do not notify the download. */ - when(mDistribute.notifyDownload(eq(mockReleaseDetails), any(Intent.class))).thenReturn(false); - ReleaseDownloadListener releaseDownloadListener = new ReleaseDownloadListener(mContext, mockReleaseDetails); - releaseDownloadListener.onComplete(mock(Uri.class)); - - /* Verify that setInstalling() is called on mandatory update. */ - verify(mDistribute).installUpdate(any(Uri.class)); - verify(mDistribute).setInstalling(mockReleaseDetails); - } - - @Test - public void onCompleteNotify() throws Exception { - mockStatic(DistributeUtils.class); - when(DistributeUtils.getResumeAppIntent(eq(mContext))).thenReturn(mock(Intent.class)); - ReleaseDetails mockReleaseDetails = mockReleaseDetails(false); - - /* Notify the download. */ - when(mDistribute.notifyDownload(eq(mockReleaseDetails), any(Intent.class))).thenReturn(true); - ReleaseDownloadListener releaseDownloadListener = new ReleaseDownloadListener(mContext, mockReleaseDetails); - releaseDownloadListener.onComplete(mock(Uri.class)); - - /* Verify that startActivity() and setInstalling() are not called here. */ - verify(mContext, never()).startActivity(any(Intent.class)); - verify(mDistribute, never()).setInstalling(mockReleaseDetails); - } - - @Test - public void onCompleteActivityNotResolved() throws Exception { - mockStatic(DistributeUtils.class); - when(DistributeUtils.getResumeAppIntent(eq(mContext))).thenReturn(mock(Intent.class)); - - /* Mock notify download result. */ - when(mDistribute.notifyDownload(any(ReleaseDetails.class), any(Intent.class))).thenReturn(true); - - /* Mock resolving to null activity. */ - when(mInstallIntent.resolveActivity(any(PackageManager.class))).thenReturn(null); - ReleaseDetails mockReleaseDetails = mockReleaseDetails(false); - ReleaseDownloadListener releaseDownloadListener = new ReleaseDownloadListener(mContext, mockReleaseDetails); - - /* Verify that nothing is called and the method is exited early with false result. */ - releaseDownloadListener.onComplete(mock(Uri.class)); - verify(mDistribute, never()).installUpdate(any(Uri.class)); - verify(mDistribute, never()).setInstalling(mockReleaseDetails); - } } diff --git a/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/ReleaseInstallerListenerTest.java b/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/ReleaseInstallerListenerTest.java deleted file mode 100644 index a15794012..000000000 --- a/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/ReleaseInstallerListenerTest.java +++ /dev/null @@ -1,269 +0,0 @@ -/* - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. - */ - -package com.microsoft.appcenter.distribute; - -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.anyBoolean; -import static org.mockito.ArgumentMatchers.anyInt; -import static org.mockito.ArgumentMatchers.anyLong; -import static org.mockito.ArgumentMatchers.anyString; -import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.ArgumentMatchers.isNull; -import static org.mockito.Mockito.doNothing; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.never; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; -import static org.powermock.api.mockito.PowerMockito.doAnswer; -import static org.powermock.api.mockito.PowerMockito.mockStatic; -import static org.powermock.api.mockito.PowerMockito.verifyStatic; -import static org.powermock.api.mockito.PowerMockito.whenNew; - -import android.app.Activity; -import android.app.DownloadManager; -import android.app.ProgressDialog; -import android.content.Context; -import android.os.ParcelFileDescriptor; -import android.widget.Toast; - -import com.microsoft.appcenter.utils.AppCenterLog; -import com.microsoft.appcenter.utils.HandlerUtils; - -import org.junit.Before; -import org.junit.Rule; -import org.junit.Test; -import org.mockito.ArgumentCaptor; -import org.mockito.Mock; -import org.mockito.invocation.InvocationOnMock; -import org.mockito.stubbing.Answer; -import org.powermock.core.classloader.annotations.PrepareForTest; -import org.powermock.modules.junit4.rule.PowerMockRule; - -import java.text.NumberFormat; - -@PrepareForTest({ - AppCenterLog.class, - Distribute.class, - HandlerUtils.class, - ReleaseInstallerListener.class, - Toast.class -}) -public class ReleaseInstallerListenerTest { - - private static final int SESSION_ID = 42; - - @Rule - public PowerMockRule mPowerMockRule = new PowerMockRule(); - - @Mock - private Context mContext; - - @Mock - private Distribute mDistribute; - - @Mock - private Toast mToast; - - @Mock - private DownloadManager mDownloadManager; - - @Mock - private android.app.ProgressDialog mProgressDialog; - - private ReleaseInstallerListener mReleaseInstallerListener; - - @Before - public void setUp() throws Exception { - - /* Mock static classes. */ - mockStatic(AppCenterLog.class); - mockStatic(Distribute.class); - - /* Mock progress dialog. */ - whenNew(android.app.ProgressDialog.class).withAnyArguments().thenReturn(mProgressDialog); - when(mProgressDialog.isIndeterminate()).thenReturn(false); - - /* Mock HandlerUtils. */ - mockStatic(HandlerUtils.class); - doAnswer(new Answer() { - - @Override - public Void answer(InvocationOnMock invocation) { - invocation.getArgument(0).run(); - return null; - } - }).when(HandlerUtils.class); - HandlerUtils.runOnUiThread(any(Runnable.class)); - - /* Mock toast. */ - mockStatic(Toast.class); - when(mContext.getString(anyInt())).thenReturn("localized_message"); - when(Toast.makeText(any(Context.class), anyString(), anyInt())).thenReturn(mToast); - - /* Mock Distribute. */ - when(Distribute.getInstance()).thenReturn(mDistribute); - doNothing().when(mDistribute).notifyInstallProgress(anyBoolean()); - - /* Mock download manager. */ - when(mDownloadManager.openDownloadedFile(anyLong())).thenReturn(mock(ParcelFileDescriptor.class)); - when(mContext.getSystemService(anyString())).thenReturn(mDownloadManager); - - /* Create installer listener. */ - mReleaseInstallerListener = new ReleaseInstallerListener(mContext); - - /* Init install progress dialog. */ - mReleaseInstallerListener.showInstallProgressDialog(mock(Activity.class)); - - /* Verify call methods. */ - verify(mProgressDialog).setProgressPercentFormat(isNull()); - verify(mProgressDialog).setProgressNumberFormat(isNull()); - verify(mProgressDialog).setIndeterminate(anyBoolean()); - } - - @Test - public void releaseInstallProcessOnFinishFailureWithContext() { - - /* Mock progress dialog. */ - when(mProgressDialog.isIndeterminate()).thenReturn(true); - - /* Emulate session status. */ - mReleaseInstallerListener.onCreated(SESSION_ID); - - /* Verify that installer process was triggered in the Distribute. */ - mReleaseInstallerListener.onActiveChanged(SESSION_ID, true); - verifyStatic(HandlerUtils.class); - HandlerUtils.runOnUiThread(any(Runnable.class)); - verify(mDistribute).notifyInstallProgress(eq(true)); - - /* Verity that progress dialog was updated. */ - mReleaseInstallerListener.onProgressChanged(SESSION_ID, 1); - verifyStatic(HandlerUtils.class, times(2)); - HandlerUtils.runOnUiThread(any(Runnable.class)); - - /* Verify that progress dialog was closed after finish install process. */ - mReleaseInstallerListener.onFinished(SESSION_ID, false); - verifyStatic(HandlerUtils.class, times(3)); - HandlerUtils.runOnUiThread(any(Runnable.class)); - - /* Verify that installer process was triggered in the Distribute again. */ - verify(mToast).show(); - } - - @Test - public void releaseInstallerProcessWhenProgressDialogNull() { - - /* Emulate session status. */ - mReleaseInstallerListener.onCreated(SESSION_ID); - mReleaseInstallerListener.onBadgingChanged(SESSION_ID); - - /* Verify that installer process was triggered in the Distribute. */ - mReleaseInstallerListener.onActiveChanged(SESSION_ID, true); - verifyStatic(HandlerUtils.class); - HandlerUtils.runOnUiThread(any(Runnable.class)); - verify(mDistribute).notifyInstallProgress(eq(true)); - - /* Hide dialog. */ - mReleaseInstallerListener.hideInstallProgressDialog(); - verifyStatic(HandlerUtils.class, times(2)); - HandlerUtils.runOnUiThread(any(Runnable.class)); - - /* Verity that progress dialog was updated. */ - mReleaseInstallerListener.onProgressChanged(SESSION_ID, 1); - verifyStatic(HandlerUtils.class, times(3)); - HandlerUtils.runOnUiThread(any(Runnable.class)); - - /* Verify that the progress dialog was updated. */ - verify(mProgressDialog, never()).setProgress(anyInt()); - - /* Verify that progress dialog was closed after finish install process. */ - mReleaseInstallerListener.onFinished(SESSION_ID, true); - verifyStatic(HandlerUtils.class, times(4)); - HandlerUtils.runOnUiThread(any(Runnable.class)); - verify(mDistribute).notifyInstallProgress(eq(false)); - } - - @Test - public void releaseInstallerProcessWhenDialogIsIndeterminate() { - - /* Mock progress dialog. */ - when(mProgressDialog.isIndeterminate()).thenReturn(true); - - /* Emulate session status. */ - mReleaseInstallerListener.onCreated(SESSION_ID); - mReleaseInstallerListener.onBadgingChanged(SESSION_ID); - - /* Verify that installer process was triggered in the Distribute. */ - mReleaseInstallerListener.onActiveChanged(SESSION_ID, true); - verifyStatic(HandlerUtils.class); - HandlerUtils.runOnUiThread(any(Runnable.class)); - verify(mDistribute).notifyInstallProgress(eq(true)); - - /* Verity that progress dialog was updated. */ - mReleaseInstallerListener.onProgressChanged(SESSION_ID, 1); - verifyStatic(HandlerUtils.class, times(2)); - HandlerUtils.runOnUiThread(any(Runnable.class)); - - /* Verify that the progress dialog was updated. */ - verify(mProgressDialog).setProgress(anyInt()); - verify(mProgressDialog).setMax(anyInt()); - verify(mProgressDialog).setProgressPercentFormat(any(NumberFormat.class)); - verify(mProgressDialog).setProgressNumberFormat(anyString()); - verify(mProgressDialog, times(2)).setIndeterminate(anyBoolean()); - - /* Verify that progress dialog was closed after finish install process. */ - mReleaseInstallerListener.onFinished(SESSION_ID, true); - verifyStatic(HandlerUtils.class, times(3)); - HandlerUtils.runOnUiThread(any(Runnable.class)); - - /* Verify that installer process was triggered in the Distribute again. */ - verify(mDistribute).notifyInstallProgress(eq(false)); - } - - @Test - public void releaseInstallerProcessWhenWithContext() { - - /* Emulate session status. */ - mReleaseInstallerListener.onCreated(SESSION_ID); - mReleaseInstallerListener.onBadgingChanged(SESSION_ID); - - /* Verify that installer process was triggered in the Distribute. */ - mReleaseInstallerListener.onActiveChanged(SESSION_ID, true); - verifyStatic(HandlerUtils.class); - HandlerUtils.runOnUiThread(any(Runnable.class)); - verify(mDistribute).notifyInstallProgress(eq(true)); - - /* Verity that progress dialog was updated. */ - mReleaseInstallerListener.onProgressChanged(SESSION_ID, 1); - verifyStatic(HandlerUtils.class, times(2)); - HandlerUtils.runOnUiThread(any(Runnable.class)); - - /* Verify that the progress dialog was updated. */ - verify(mProgressDialog).setProgress(anyInt()); - - /* Verify that progress dialog was closed after finish install process. */ - mReleaseInstallerListener.onFinished(SESSION_ID, true); - verifyStatic(HandlerUtils.class, times(3)); - HandlerUtils.runOnUiThread(any(Runnable.class)); - - /* Verify that installer process was triggered in the Distribute again. */ - verify(mDistribute).notifyInstallProgress(eq(false)); - } - - @Test - public void releaseInstallerHideDialogTwice() { - - /* Hide dialog. */ - mReleaseInstallerListener.hideInstallProgressDialog(); - - /* Try to hide dialog again. */ - mReleaseInstallerListener.hideInstallProgressDialog(); - - /* Verify that runnable was called once only. */ - verifyStatic(HandlerUtils.class); - HandlerUtils.runOnUiThread(any(Runnable.class)); - } -} From a35f770c4a883a5091f4911a57a980e9a642109d Mon Sep 17 00:00:00 2001 From: Ivan Matkov Date: Wed, 25 May 2022 13:48:19 +0200 Subject: [PATCH 57/72] Fix tests (part 2, all tests passed) --- .../distribute/AbstractDistributeTest.java | 3 - .../appcenter/distribute/DistributeTest.java | 191 ++++++------------ .../ReleaseDownloadListenerTest.java | 27 +-- .../DownloadManagerReleaseDownloaderTest.java | 19 +- sdk/build.gradle | 2 +- 5 files changed, 71 insertions(+), 171 deletions(-) diff --git a/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/AbstractDistributeTest.java b/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/AbstractDistributeTest.java index 956fa622d..19002a931 100644 --- a/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/AbstractDistributeTest.java +++ b/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/AbstractDistributeTest.java @@ -200,9 +200,6 @@ public Void answer(InvocationOnMock invocation) { }).when(SharedPreferencesManager.class); SharedPreferencesManager.putBoolean(eq(DISTRIBUTE_ENABLED_KEY), anyBoolean()); - /* Default download id when not found. */ - when(SharedPreferencesManager.getLong(PREFERENCE_KEY_DOWNLOAD_ID, INVALID_DOWNLOAD_IDENTIFIER)).thenReturn(INVALID_DOWNLOAD_IDENTIFIER); - /* Mock package manager. */ when(mContext.getApplicationContext()).thenReturn(mContext); when(mContext.getPackageName()).thenReturn("com.contoso"); diff --git a/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/DistributeTest.java b/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/DistributeTest.java index 5c6b3f519..40865c569 100644 --- a/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/DistributeTest.java +++ b/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/DistributeTest.java @@ -5,8 +5,8 @@ package com.microsoft.appcenter.distribute; -import static android.content.Context.NOTIFICATION_SERVICE; import static android.content.pm.ApplicationInfo.FLAG_DEBUGGABLE; +import static com.microsoft.appcenter.distribute.DistributeConstants.DOWNLOAD_STATE_AVAILABLE; import static com.microsoft.appcenter.distribute.DistributeConstants.DOWNLOAD_STATE_COMPLETED; import static com.microsoft.appcenter.distribute.DistributeConstants.DOWNLOAD_STATE_ENQUEUED; import static com.microsoft.appcenter.distribute.DistributeConstants.DOWNLOAD_STATE_INSTALLING; @@ -32,21 +32,17 @@ import static org.mockito.Mockito.never; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoMoreInteractions; import static org.mockito.Mockito.when; import static org.powermock.api.mockito.PowerMockito.mockStatic; -import static org.powermock.api.mockito.PowerMockito.spy; import static org.powermock.api.mockito.PowerMockito.verifyStatic; import static org.powermock.api.mockito.PowerMockito.whenNew; import android.app.Activity; -import android.app.NotificationManager; -import android.app.ProgressDialog; -import android.content.BroadcastReceiver; import android.content.ComponentName; import android.content.Context; import android.content.DialogInterface; import android.content.Intent; -import android.content.IntentFilter; import android.content.pm.PackageInfo; import android.os.Build; @@ -66,11 +62,12 @@ import com.microsoft.appcenter.utils.storage.SharedPreferencesManager; import org.junit.After; +import org.junit.Before; import org.junit.Test; import org.mockito.ArgumentCaptor; +import org.mockito.Mock; import org.mockito.invocation.InvocationOnMock; import org.mockito.stubbing.Answer; -import org.powermock.api.mockito.PowerMockito; import org.powermock.core.classloader.annotations.PrepareForTest; import org.powermock.reflect.Whitebox; @@ -89,6 +86,17 @@ public class DistributeTest extends AbstractDistributeTest { private static final int RELEASE_ID = 123; + @Mock + private UpdateInstaller mReleaseInstaller; + + @Before + public void setUp() throws Exception { + super.setUp(); + mockStatic(DistributeUtils.class); + when(DistributeUtils.getStoredDownloadState()).thenReturn(DOWNLOAD_STATE_COMPLETED); + whenNew(UpdateInstaller.class).withAnyArguments().thenReturn(mReleaseInstaller); + } + @After public void tearDown() throws Exception { TestUtils.setInternalState(Build.VERSION.class, "SDK_INT", 0); @@ -266,36 +274,21 @@ public void setInstallingReleaseDetailsNotEqualTest() { @Test public void setInstallingTest() { + when(DistributeUtils.getStoredDownloadState()).thenReturn(DOWNLOAD_STATE_ENQUEUED); - /* Mock release details and startFromBackground to apply it. */ + /* Mock that download time is bigger than packageInfo.lastUpdateTime. */ + when(SharedPreferencesManager.getLong(eq(PREFERENCE_KEY_DOWNLOAD_TIME))).thenReturn(3L); + + /* Mock release details and resumeWorkflow to apply it. */ mockReleaseDetails(false); - Distribute.getInstance().startFromBackground(mContext); + resumeWorkflow(mActivity); Distribute.getInstance().setInstalling(mReleaseDetails, mUri); /* Verify. */ - verifyStatic(SharedPreferencesManager.class); - SharedPreferencesManager.remove(eq(PREFERENCE_KEY_RELEASE_DETAILS)); - verifyStatic(SharedPreferencesManager.class); - SharedPreferencesManager.remove(eq(PREFERENCE_KEY_DOWNLOAD_STATE)); - verifyStatic(SharedPreferencesManager.class, never()); - SharedPreferencesManager.putInt(eq(PREFERENCE_KEY_DOWNLOAD_STATE), eq(DOWNLOAD_STATE_INSTALLING)); verifyReleaseDetailsAreStored(); - } - - @Test - public void setInstallingMandatoryReleaseDetailsTest() { - - /* Mock release details and startFromBackground to apply it. */ - mockReleaseDetails(true); - Distribute.getInstance().startFromBackground(mContext); - Distribute.getInstance().setInstalling(mReleaseDetails, mUri); - - /* Verify. */ - verifyStatic(DistributeUtils.class); - DistributeUtils.getStoredDownloadState(); verifyStatic(SharedPreferencesManager.class); SharedPreferencesManager.putInt(eq(PREFERENCE_KEY_DOWNLOAD_STATE), eq(DOWNLOAD_STATE_INSTALLING)); - verifyReleaseDetailsAreStored(); + verify(mReleaseInstaller).install(mUri); } private void mockReleaseDetails(boolean mandatoryUpdate) { @@ -303,7 +296,6 @@ private void mockReleaseDetails(boolean mandatoryUpdate) { when(mReleaseDetails.getDistributionGroupId()).thenReturn(DISTRIBUTION_GROUP_ID); when(mReleaseDetails.getReleaseHash()).thenReturn(RELEASE_HASH); when(mReleaseDetails.getId()).thenReturn(RELEASE_ID); - mockStatic(DistributeUtils.class); when(DistributeUtils.loadCachedReleaseDetails()).thenReturn(mReleaseDetails); } @@ -318,22 +310,16 @@ private void verifyReleaseDetailsAreStored() { @Test public void cancelingNotification() { - spy(DistributeUtils.class); - PowerMockito.doReturn(DOWNLOAD_STATE_NOTIFIED).when(DistributeUtils.class); - DistributeUtils.getStoredDownloadState(); - PowerMockito.doReturn(0).when(DistributeUtils.class); - DistributeUtils.getNotificationId(); - NotificationManager manager = mock(NotificationManager.class); - when(mContext.getSystemService(NOTIFICATION_SERVICE)).thenReturn(manager); + when(DistributeUtils.getStoredDownloadState()).thenReturn(DOWNLOAD_STATE_NOTIFIED); Distribute.getInstance().onStarted(mContext, mChannel, "a", null, true); Distribute.getInstance().completeWorkflow(); - verify(manager).cancel(any(Integer.class)); + verifyStatic(DistributeUtils.class); + DistributeUtils.cancelNotification(mContext); } @Test public void updateReleaseDetailsFromBackground() { - mockStatic(DistributeUtils.class); - when(DistributeUtils.loadCachedReleaseDetails()).thenReturn(mReleaseDetails); + mockReleaseDetails(false); /* mReleaseDownloader is null and is created. */ Distribute.getInstance().startFromBackground(mContext); @@ -351,13 +337,12 @@ public void updateReleaseDetailsFromBackground() { @Test public void discardDownloadAsAppUpdateTest() { - mockStatic(DistributeUtils.class); - when(DistributeUtils.getStoredDownloadState()).thenReturn(-1); + when(DistributeUtils.getStoredDownloadState()).thenReturn(DOWNLOAD_STATE_INSTALLING); /* Mock that download time is smaller than packageInfo.lastUpdateTime. */ when(SharedPreferencesManager.getLong(eq(PREFERENCE_KEY_DOWNLOAD_TIME))).thenReturn(1L); - resumeWorkflow(mock(Activity.class)); + resumeWorkflow(mActivity); /* Verify that previous tasks are cancelled. */ verifyStatic(SharedPreferencesManager.class); @@ -370,65 +355,52 @@ public void discardDownloadAsAppUpdateTest() { @Test public void restartDownloadNotEnqueued() { - mockStatic(DistributeUtils.class); - when(DistributeUtils.getStoredDownloadState()).thenReturn(-1); /* Mock that download time is bigger than packageInfo.lastUpdateTime. */ when(SharedPreferencesManager.getLong(eq(PREFERENCE_KEY_DOWNLOAD_TIME))).thenReturn(3L); /* mReleaseDetails is not null and it's not mandatory update. */ - when(DistributeUtils.loadCachedReleaseDetails()).thenReturn(mReleaseDetails); - when(mReleaseDetails.isMandatoryUpdate()).thenReturn(false); - - resumeWorkflow(mock(Activity.class)); + mockReleaseDetails(false); + resumeWorkflow(mActivity); verify(mDialog, never()).show(); } @Test public void showDownloadProgressTest() { - mockStatic(DistributeUtils.class); when(DistributeUtils.getStoredDownloadState()).thenReturn(DOWNLOAD_STATE_ENQUEUED); /* Mock that download time is bigger than packageInfo.lastUpdateTime. */ when(SharedPreferencesManager.getLong(eq(PREFERENCE_KEY_DOWNLOAD_TIME))).thenReturn(3L); /* mReleaseDetails is not null and it's a mandatory update. */ - when(DistributeUtils.loadCachedReleaseDetails()).thenReturn(mReleaseDetails); - when(mReleaseDetails.isMandatoryUpdate()).thenReturn(true); - + mockReleaseDetails(true); resumeWorkflow(mActivity); verify(mReleaseDownloaderListener).showDownloadProgress(mActivity); } @Test public void doNotShowDownloadProgressTestInBackground() { - mockStatic(DistributeUtils.class); when(DistributeUtils.getStoredDownloadState()).thenReturn(DOWNLOAD_STATE_ENQUEUED); /* Mock that download time is bigger than packageInfo.lastUpdateTime. */ when(SharedPreferencesManager.getLong(eq(PREFERENCE_KEY_DOWNLOAD_TIME))).thenReturn(3L); /* mReleaseDetails is not null and it's a mandatory update. */ - when(DistributeUtils.loadCachedReleaseDetails()).thenReturn(mReleaseDetails); - when(mReleaseDetails.isMandatoryUpdate()).thenReturn(true); - + mockReleaseDetails(true); resumeWorkflowWithNoOnResume(); verify(mReleaseDownloaderListener, never()).showDownloadProgress(mActivity); } @Test public void showDownloadProgressTestInForeground() { - mockStatic(DistributeUtils.class); when(DistributeUtils.getStoredDownloadState()).thenReturn(DOWNLOAD_STATE_ENQUEUED); /* Mock that download time is bigger than packageInfo.lastUpdateTime. */ when(SharedPreferencesManager.getLong(eq(PREFERENCE_KEY_DOWNLOAD_TIME))).thenReturn(3L); /* mReleaseDetails is not null and it's a mandatory update. */ - when(DistributeUtils.loadCachedReleaseDetails()).thenReturn(mReleaseDetails); - when(mReleaseDetails.isMandatoryUpdate()).thenReturn(true); - + mockReleaseDetails(true); resumeWorkflow(mActivity); verify(mReleaseDownloaderListener).showDownloadProgress(mActivity); } @@ -462,17 +434,16 @@ public void showUpdateSetupFailedDialogInForeground() { @Test public void showDownloadProgressAndActivityTest() { - mockStatic(DistributeUtils.class); when(DistributeUtils.getStoredDownloadState()).thenReturn(DOWNLOAD_STATE_ENQUEUED); /* Mock that download time is bigger than packageInfo.lastUpdateTime. */ when(SharedPreferencesManager.getLong(eq(PREFERENCE_KEY_DOWNLOAD_TIME))).thenReturn(3L); /* mReleaseDetails is not null and it's a mandatory update. */ - when(DistributeUtils.loadCachedReleaseDetails()).thenReturn(mReleaseDetails); - when(mReleaseDetails.isMandatoryUpdate()).thenReturn(true); + mockReleaseDetails(true); - ProgressDialog progressDialog = mock(ProgressDialog.class); + @SuppressWarnings({"deprecation", "RedundantSuppression"}) + android.app.ProgressDialog progressDialog = mock(android.app.ProgressDialog.class); doReturn(progressDialog).when(mReleaseDownloaderListener).showDownloadProgress(mActivity); resumeWorkflow(mActivity); @@ -482,16 +453,16 @@ public void showDownloadProgressAndActivityTest() { @Test public void showDownloadProgressNullDownloaderListenerTest() throws Exception { - whenNew(ReleaseDownloadListener.class).withArguments(any(Context.class), any(ReleaseDetails.class)).thenReturn(null); - mockStatic(DistributeUtils.class); + whenNew(ReleaseDownloadListener.class) + .withArguments(any(Context.class), any(ReleaseDetails.class)) + .thenReturn(null); when(DistributeUtils.getStoredDownloadState()).thenReturn(DOWNLOAD_STATE_ENQUEUED); /* Mock that download time is bigger than packageInfo.lastUpdateTime. */ when(SharedPreferencesManager.getLong(eq(PREFERENCE_KEY_DOWNLOAD_TIME))).thenReturn(3L); /* mReleaseDetails is not null and it's a mandatory update. */ - when(DistributeUtils.loadCachedReleaseDetails()).thenReturn(mReleaseDetails); - when(mReleaseDetails.isMandatoryUpdate()).thenReturn(true); + mockReleaseDetails(true); resumeWorkflow(mock(Activity.class)); verify(mReleaseDownloaderListener, never()).showDownloadProgress(mActivity); @@ -499,30 +470,26 @@ public void showDownloadProgressNullDownloaderListenerTest() throws Exception { @Test public void restartedDuringMandatoryUpdate() { - mockStatic(DistributeUtils.class); - when(DistributeUtils.getStoredDownloadState()).thenReturn(-1); + when(DistributeUtils.getStoredDownloadState()).thenReturn(DOWNLOAD_STATE_ENQUEUED); /* Mock that download time is bigger than packageInfo.lastUpdateTime. */ when(SharedPreferencesManager.getLong(eq(PREFERENCE_KEY_DOWNLOAD_TIME))).thenReturn(3L); /* mReleaseDetails is not null and it's a mandatory update. */ - when(DistributeUtils.loadCachedReleaseDetails()).thenReturn(mReleaseDetails); - when(mReleaseDetails.isMandatoryUpdate()).thenReturn(true); - - resumeWorkflow(mock(Activity.class)); + mockReleaseDetails(true); + resumeWorkflow(mActivity); verify(mDialog, never()).show(); } @Test public void downloadAlreadyCheckedTest() { - mockStatic(DistributeUtils.class); - when(DistributeUtils.getStoredDownloadState()).thenReturn(-1); + when(DistributeUtils.getStoredDownloadState()).thenReturn(DOWNLOAD_STATE_ENQUEUED); /* Mock that download time is bigger than packageInfo.lastUpdateTime. */ when(SharedPreferencesManager.getLong(eq(PREFERENCE_KEY_DOWNLOAD_TIME))).thenReturn(3L); - resumeWorkflow(mock(Activity.class)); + resumeWorkflow(mActivity); /* Call resume twice when download already checked. */ Distribute.getInstance().onActivityResumed(mock(Activity.class)); @@ -530,27 +497,17 @@ public void downloadAlreadyCheckedTest() { @Test public void showUpdateDialogReleaseDownloaderNullTest() { + when(DistributeUtils.getStoredDownloadState()).thenReturn(DOWNLOAD_STATE_AVAILABLE); /* Mock mReleaseDownloader null. */ when(ReleaseDownloaderFactory.create(any(Context.class), any(ReleaseDetails.class), any(ReleaseDownloadListener.class))).thenReturn(null); - mockStatic(DistributeUtils.class); - when(DistributeUtils.getStoredDownloadState()).thenReturn(-1); - /* Mock that download time is bigger than packageInfo.lastUpdateTime. */ when(SharedPreferencesManager.getLong(eq(PREFERENCE_KEY_DOWNLOAD_TIME))).thenReturn(3L); /* mReleaseDetails is not null and it's a mandatory update. */ - when(DistributeUtils.loadCachedReleaseDetails()).thenReturn(mReleaseDetails); - when(mReleaseDetails.isMandatoryUpdate()).thenReturn(true); - - resumeWorkflow(mock(Activity.class)); - - /* - * Call resume twice when download already checked, mReleaseDetails are not null, package is installing. - * mReleaseDownloader is null. - */ - Distribute.getInstance().onActivityResumed(mock(Activity.class)); + mockReleaseDetails(true); + resumeWorkflow(mActivity); verify(mDialogBuilder).create(); } @@ -560,17 +517,12 @@ public void showUpdateDialogReleaseDownloaderDownloadingTest() { /* Mock mReleaseDownloader is downloading. */ when(mReleaseDownloader.isDownloading()).thenReturn(true); - mockStatic(DistributeUtils.class); - when(DistributeUtils.getStoredDownloadState()).thenReturn(-1); - /* Mock that download time is bigger than packageInfo.lastUpdateTime. */ when(SharedPreferencesManager.getLong(eq(PREFERENCE_KEY_DOWNLOAD_TIME))).thenReturn(3L); /* mReleaseDetails is not null and it's a mandatory update. */ - when(DistributeUtils.loadCachedReleaseDetails()).thenReturn(mReleaseDetails); - when(mReleaseDetails.isMandatoryUpdate()).thenReturn(true); - - resumeWorkflow(mock(Activity.class)); + mockReleaseDetails(true); + resumeWorkflow(mActivity); /* * Call resume twice when download already checked, mReleaseDetails are not null, package is installing. @@ -599,7 +551,6 @@ public void getLastReleaseDetailsWithDifferentHttpClients() { /* Prepare data. */ HttpClient mockHttpClient = mock(HttpClient.class); DependencyConfiguration.setHttpClient(mockHttpClient); - mockStatic(DistributeUtils.class); when(DistributeUtils.computeReleaseHash(any(PackageInfo.class))).thenReturn("mock-hash"); Distribute.getInstance().startFromBackground(mContext); @@ -623,16 +574,15 @@ public void getLastReleaseDetailsWithDifferentHttpClients() { @Test public void showMandatoryDownloadReadyDialogTest() { - mockStatic(DistributeUtils.class); - when(DistributeUtils.getStoredDownloadState()).thenReturn(-1).thenReturn(DOWNLOAD_STATE_INSTALLING); + when(DistributeUtils.getStoredDownloadState()) + .thenReturn(DOWNLOAD_STATE_COMPLETED) + .thenReturn(DOWNLOAD_STATE_INSTALLING); /* Mock that download time is bigger than packageInfo.lastUpdateTime. */ when(SharedPreferencesManager.getLong(eq(PREFERENCE_KEY_DOWNLOAD_TIME))).thenReturn(3L); /* mReleaseDetails is not null and it's a mandatory update. */ - when(DistributeUtils.loadCachedReleaseDetails()).thenReturn(mReleaseDetails); - when(mReleaseDetails.isMandatoryUpdate()).thenReturn(true); - + mockReleaseDetails(true); resumeWorkflow(mock(Activity.class)); /* @@ -663,6 +613,7 @@ public void showMandatoryDownloadReadyDialogTest() { /* Click. */ clickListener.getValue().onClick(mDialog, DialogInterface.BUTTON_POSITIVE); + verify(mReleaseDownloader).resume(); /* Complete workflow and unset mReleaseDetails. */ Distribute.getInstance().completeWorkflow(); @@ -670,17 +621,11 @@ public void showMandatoryDownloadReadyDialogTest() { /* Click. */ clickListener.getValue().onClick(mDialog, DialogInterface.BUTTON_POSITIVE); - - /* Verify that download is resumed after click Install but only twice (on third time Distribute is disabled). */ - verify(mReleaseDownloader, times(2)).resume(); + verifyNoMoreInteractions(mReleaseDownloader); } @Test public void tryResetWorkflowWhenApplicationEnterForegroundWhenChannelNull() { - - /* Mock download state. */ - mockStatic(DistributeUtils.class); - when(DistributeUtils.getStoredDownloadState()).thenReturn(DOWNLOAD_STATE_COMPLETED); final ServiceCall call = mock(ServiceCall.class); doAnswer(new Answer() { @@ -704,10 +649,6 @@ public ServiceCall answer(InvocationOnMock invocationOnMock) { @Test public void tryResetWorkflowWhenApplicationEnterForegroundWhenChannelNotNull() { - - /* Prepare data. */ - mockStatic(DistributeUtils.class); - when(DistributeUtils.getStoredDownloadState()).thenReturn(DOWNLOAD_STATE_COMPLETED); final ServiceCall call = mock(ServiceCall.class); doAnswer(new Answer() { @@ -745,13 +686,9 @@ public ServiceCall answer(InvocationOnMock invocationOnMock) { @Test public void checkUpdateReleaseAfterInterruptDownloading() { - - /* Prepare data. */ - mockStatic(DistributeUtils.class); + mockReleaseDetails(true); when(mReleaseDetails.getVersion()).thenReturn(1); - when(mReleaseDetails.isMandatoryUpdate()).thenReturn(true); when(DeviceInfoHelper.getVersionCode(any(PackageInfo.class))).thenReturn(0); - when(DistributeUtils.loadCachedReleaseDetails()).thenReturn(mReleaseDetails); /* Start distribute. */ start(); @@ -775,18 +712,4 @@ public void checkUpdateReleaseAfterInterruptDownloading() { /* Verify that check release for update was called again. */ verify(mHttpClient, times(2)).callAsync(anyString(), anyString(), eq(Collections.emptyMap()), any(HttpClient.CallTemplate.class), httpCallback.capture()); } - - @Test - public void applyEnableStateRegistersInstallReceiver() { - start(); - - /* Verify that register receiver was called after activity is resumed. */ - verify(mContext).registerReceiver(any(BroadcastReceiver.class), any(IntentFilter.class)); - - /* Disable Distribute. */ - Distribute.setEnabled(false); - - /* Check that receiver was unregistered. */ - verify(mContext).unregisterReceiver(any(BroadcastReceiver.class)); - } } diff --git a/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/ReleaseDownloadListenerTest.java b/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/ReleaseDownloadListenerTest.java index 860fb78cf..13cbbd5ae 100644 --- a/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/ReleaseDownloadListenerTest.java +++ b/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/ReleaseDownloadListenerTest.java @@ -27,11 +27,7 @@ import static org.powermock.api.mockito.PowerMockito.whenNew; import android.app.Activity; -import android.app.ProgressDialog; -import android.content.ComponentName; import android.content.Context; -import android.content.Intent; -import android.content.pm.PackageManager; import android.net.Uri; import android.os.Handler; import android.os.SystemClock; @@ -52,6 +48,7 @@ import org.powermock.core.classloader.annotations.PrepareForTest; import org.powermock.modules.junit4.rule.PowerMockRule; +@SuppressWarnings({"deprecation", "RedundantSuppression"}) @PrepareForTest({ AppCenter.class, AppCenterLog.class, @@ -59,7 +56,7 @@ DistributeUtils.class, HandlerUtils.class, InstallerUtils.class, - ProgressDialog.class, + android.app.ProgressDialog.class, ReleaseDetails.class, ReleaseDownloadListener.class, SharedPreferencesManager.class, @@ -78,30 +75,23 @@ public class ReleaseDownloadListenerTest { public PowerMockRule mPowerMockRule = new PowerMockRule(); @Mock - Context mContext; + private Context mContext; @Mock - Activity mActivity; + private Activity mActivity; @Mock private Handler mHandler; @Mock - @SuppressWarnings({"deprecation", "RedundantSuppression"}) private android.app.ProgressDialog mProgressDialog; @Mock - Uri mUri; + private Uri mUri; @Mock private Distribute mDistribute; - @Mock - private Intent mInstallIntent; - - @Mock - private PackageManager mPackageManager; - @Mock private Toast mToast; @@ -110,7 +100,6 @@ public void setUp() throws Exception { mockHandler(); mockDialog(); mockStorage(); - mockInstallIntent(); mockDistribute(); mockToast(); } @@ -125,12 +114,6 @@ private void mockDistribute() { when(Distribute.getInstance()).thenReturn(mDistribute); } - private void mockInstallIntent() throws Exception { - whenNew(Intent.class).withArguments(Intent.ACTION_INSTALL_PACKAGE).thenReturn(mInstallIntent); - when(mInstallIntent.resolveActivity(any(PackageManager.class))).thenReturn(mock(ComponentName.class)); - when(mContext.getPackageManager()).thenReturn(mPackageManager); - } - private ReleaseDetails mockReleaseDetails(boolean isMandatory) throws Exception { mockStatic(ReleaseDetails.class); ReleaseDetails releaseDetails = mock(ReleaseDetails.class); diff --git a/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/download/manager/DownloadManagerReleaseDownloaderTest.java b/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/download/manager/DownloadManagerReleaseDownloaderTest.java index 46e2c6035..bb04679dc 100644 --- a/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/download/manager/DownloadManagerReleaseDownloaderTest.java +++ b/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/download/manager/DownloadManagerReleaseDownloaderTest.java @@ -99,7 +99,8 @@ public class DownloadManagerReleaseDownloaderTest { @Before public void setUp() throws Exception { mockStatic(SharedPreferencesManager.class); - mockStatic(HandlerUtils.class); + when(SharedPreferencesManager.getLong(eq(PREFERENCE_KEY_DOWNLOAD_ID), eq(INVALID_DOWNLOAD_IDENTIFIER))) + .thenReturn(INVALID_DOWNLOAD_IDENTIFIER); /* Mock AsyncTaskUtils. */ mockStatic(AsyncTaskUtils.class); @@ -108,6 +109,7 @@ public void setUp() throws Exception { when(AsyncTaskUtils.execute(anyString(), isA(DownloadManagerRemoveTask.class))).thenReturn(mRemoveTask); /* Run handler immediately. */ + mockStatic(HandlerUtils.class); when(HandlerUtils.getMainHandler()).thenReturn(mMainHandler); when(mMainHandler.postAtTime(any(Runnable.class), anyString(), anyLong())).thenAnswer(new Answer() { @@ -160,16 +162,11 @@ public void resumeStartsUpdateTask() { AsyncTaskUtils.execute(anyString(), isA(DownloadManagerUpdateTask.class), any()); } - @Test - public void resumeDoesNothingAfterCancellation() { - mReleaseDownloader.cancel(); - mReleaseDownloader.resume(); - verifyStatic(AsyncTaskUtils.class, never()); - AsyncTaskUtils.execute(anyString(), isA(DownloadManagerUpdateTask.class), any()); - } - @Test public void cancelClearsEverything() { + when(SharedPreferencesManager.getLong(eq(PREFERENCE_KEY_DOWNLOAD_ID), eq(INVALID_DOWNLOAD_IDENTIFIER))) + .thenReturn(DOWNLOAD_ID) + .thenReturn(INVALID_DOWNLOAD_IDENTIFIER); /* Update status. */ mReleaseDownloader.resume(); @@ -477,8 +474,8 @@ public void exceptionOnClosingFileDescriptor() throws IOException { /* Verify. */ verify(mFileDescriptor).close(); - verify(mListener).onComplete(any(Uri.class)); - verify(mListener, never()).onError(anyString()); + verify(mListener, never()).onComplete(any(Uri.class)); + verify(mListener).onError(anyString()); } @Test diff --git a/sdk/build.gradle b/sdk/build.gradle index 35936a3e0..9cac73094 100644 --- a/sdk/build.gradle +++ b/sdk/build.gradle @@ -63,7 +63,7 @@ subprojects { testOptions { unitTests { all { - jvmArgs '-noverify', '-Djdk.attach.allowAttachSelf=true' + jvmArgs '-noverify', '-Djdk.attach.allowAttachSelf=true', '-Djdk.module.illegalAccess.silent=true' } returnDefaultValues = true } From f56a2cd34d27b6eeff2790fbd21951442eea4736 Mon Sep 17 00:00:00 2001 From: Ivan Matkov Date: Sun, 5 Jun 2022 22:11:32 +0200 Subject: [PATCH 58/72] Add missing javadocs --- .../appcenter/distribute/Distribute.java | 2 +- .../appcenter/distribute/UpdateInstaller.java | 82 +++++++++++++------ .../appcenter/distribute/UpdateReceiver.java | 13 +++ .../install/AbstractReleaseInstaller.java | 16 ++-- .../distribute/install/ReleaseInstaller.java | 33 +++++++- .../install/ReleaseInstallerActivity.java | 34 +++++--- .../intent/IntentReleaseInstaller.java | 5 ++ .../session/SessionReleaseInstaller.java | 5 ++ 8 files changed, 143 insertions(+), 47 deletions(-) diff --git a/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/Distribute.java b/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/Distribute.java index 416212ba7..57e63c034 100644 --- a/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/Distribute.java +++ b/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/Distribute.java @@ -235,7 +235,7 @@ public class Distribute extends AbstractAppCenterService { */ private ReleaseDownloadListener mReleaseDownloaderListener; - private ReleaseInstaller mReleaseInstaller; + private UpdateInstaller mReleaseInstaller; /** * True when distribute workflow reached final state. diff --git a/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/UpdateInstaller.java b/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/UpdateInstaller.java index 9bece6fe9..9c969af2d 100644 --- a/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/UpdateInstaller.java +++ b/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/UpdateInstaller.java @@ -1,3 +1,8 @@ +/* + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. + */ + package com.microsoft.appcenter.distribute; import static com.microsoft.appcenter.distribute.DistributeConstants.LOG_TAG; @@ -17,67 +22,90 @@ import java.util.Deque; import java.util.LinkedList; +/** + * Installer of downloaded package with awareness of service state and fallback mechanism. + */ class UpdateInstaller implements ReleaseInstaller, ReleaseInstaller.Listener { - private final Deque mInstallers = new LinkedList<>(); + + /** + * Store information about release we are currently working with. + */ private final ReleaseDetails mReleaseDetails; - private ReleaseInstaller mCurrent; + + /** + * Queue of available to use installers. + */ + private final Deque mInstallers = new LinkedList<>(); + + /** + * The installer that we currently use. + */ + private ReleaseInstaller mCurrentInstaller; + + /** + * Keep Uri of downloaded package to pass it in case of using fallback. + */ private Uri mLocalUri; + + /** + * The flag to track the cancellation event. + */ private boolean mCancelled; UpdateInstaller(Context context, ReleaseDetails releaseDetails) { + mReleaseDetails = releaseDetails; + + /* Initialize installers list. Use separate thread for background operations. */ HandlerThread thread = new HandlerThread("AppCenter.Installer"); thread.start(); Handler handler = new Handler(thread.getLooper()); mInstallers.add(new SessionReleaseInstaller(context, handler, this)); mInstallers.add(new IntentReleaseInstaller(context, handler, this)); - mReleaseDetails = releaseDetails; - mCurrent = next(); + mCurrentInstaller = popNextInstaller(); } - private ReleaseInstaller next() { + private ReleaseInstaller popNextInstaller() { if (mInstallers.size() == 0) { return null; } - ReleaseInstaller next = mInstallers.pop(); - AppCenterLog.debug(LOG_TAG, "Trying to install update via " + next.toString() + "."); - return next; + ReleaseInstaller nextInstaller = mInstallers.pop(); + AppCenterLog.debug(LOG_TAG, "Trying to install update via " + nextInstaller.toString() + "."); + return nextInstaller; } @Override public synchronized void install(@NonNull Uri localUri) { mCancelled = false; mLocalUri = localUri; - if (mCurrent != null) { - mCurrent.install(localUri); + if (mCurrentInstaller != null) { + mCurrentInstaller.install(localUri); } } - @Override + /** + * Process resume distribute workflow. + */ public synchronized void resume() { // Show mandatory dialog if cancelled in background. if (mCancelled && mReleaseDetails.isMandatoryUpdate()) { Distribute.getInstance().showMandatoryDownloadReadyDialog(); - return; - } - if (mCurrent != null) { - mCurrent.resume(); } } @Override public synchronized void clear() { - if (mCurrent != null) { - mCurrent.clear(); + if (mCurrentInstaller != null) { + mCurrentInstaller.clear(); } } @Override - public synchronized void onError(String message) { - if (isRecoverableError(message)) { - mCurrent.clear(); - mCurrent = next(); - if (mCurrent != null) { - mCurrent.install(mLocalUri); + public synchronized void onError(String errorMessage) { + if (isRecoverableError(errorMessage)) { + mCurrentInstaller.clear(); + mCurrentInstaller = popNextInstaller(); + if (mCurrentInstaller != null) { + mCurrentInstaller.install(mLocalUri); return; } } @@ -94,10 +122,12 @@ public synchronized void onCancel() { } } - // MIUI fallback + /** + * Message to filter MIUI error to use fallback installer. + */ private static final String RECOVERABLE_ERROR = "INSTALL_FAILED_INTERNAL_ERROR: Permission Denied"; - private static boolean isRecoverableError(String message) { - return RECOVERABLE_ERROR.equals(message); + private static boolean isRecoverableError(String errorMessage) { + return RECOVERABLE_ERROR.equals(errorMessage); } } diff --git a/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/UpdateReceiver.java b/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/UpdateReceiver.java index 2939839dc..57e84624c 100644 --- a/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/UpdateReceiver.java +++ b/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/UpdateReceiver.java @@ -1,3 +1,8 @@ +/* + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. + */ + package com.microsoft.appcenter.distribute; import static com.microsoft.appcenter.distribute.DistributeConstants.LOG_TAG; @@ -11,6 +16,9 @@ import com.microsoft.appcenter.utils.AppNameHelper; import com.microsoft.appcenter.utils.DeviceInfoHelper; +/** + * Receiver of package replaced (update installed) callback. + */ public class UpdateReceiver extends BroadcastReceiver { @Override @@ -22,6 +30,11 @@ public void onReceive(Context context, Intent intent) { } } + /** + * Package replaced callback. Posting notification that the installation finished. + * + * @param context the context in which the receiver is running. + */ private void onPackageReplaced(Context context) { AppCenterLog.debug(LOG_TAG, "Post a notification as the installation finished in background."); String title = context.getString(R.string.appcenter_distribute_install_completed_title); diff --git a/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/install/AbstractReleaseInstaller.java b/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/install/AbstractReleaseInstaller.java index f5d83dd50..5af59d2ad 100644 --- a/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/install/AbstractReleaseInstaller.java +++ b/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/install/AbstractReleaseInstaller.java @@ -1,3 +1,8 @@ +/* + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. + */ + package com.microsoft.appcenter.distribute.install; import static com.microsoft.appcenter.distribute.DistributeConstants.LOG_TAG; @@ -22,6 +27,7 @@ protected void post(Runnable runnable) { mInstallerHandler.post(runnable); } + @SuppressWarnings("SameParameterValue") protected void postDelayed(Runnable runnable, long delayMillis) { mInstallerHandler.postDelayed(runnable, delayMillis); } @@ -31,9 +37,9 @@ protected void onError(String message) { mListener.onError(message); } - protected void onError(String message, Throwable throwable) { - AppCenterLog.error(LOG_TAG, "Failed to install a new release: " + message, throwable); - mListener.onError(message); + protected void onError(String errorMessage, Throwable throwable) { + AppCenterLog.error(LOG_TAG, "Failed to install a new release: " + errorMessage, throwable); + mListener.onError(errorMessage); } protected void onCancel() { @@ -41,10 +47,6 @@ protected void onCancel() { mListener.onCancel(); } - @Override - public void resume() { - } - @Override public void clear() { } diff --git a/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/install/ReleaseInstaller.java b/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/install/ReleaseInstaller.java index aed16ecd7..65a4ef331 100644 --- a/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/install/ReleaseInstaller.java +++ b/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/install/ReleaseInstaller.java @@ -1,3 +1,8 @@ +/* + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. + */ + package com.microsoft.appcenter.distribute.install; import android.net.Uri; @@ -5,17 +10,39 @@ import androidx.annotation.AnyThread; import androidx.annotation.NonNull; +/** + * Interface for installing release. + */ public interface ReleaseInstaller { + /** + * Start installation of downloaded release. + * + * @param localUri path to local file. + */ @AnyThread void install(@NonNull Uri localUri); - void resume(); - + /** + * Clear resources that were opened for installation. + */ void clear(); + /** + * Listener for installing progress. + */ interface Listener { - void onError(String message); + + /** + * Called when an error occurs during the downloading. + * + * @param errorMessage The message of the exception. + */ + void onError(String errorMessage); + + /** + * Called when the installation is cancelled. + */ void onCancel(); } } diff --git a/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/install/ReleaseInstallerActivity.java b/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/install/ReleaseInstallerActivity.java index 3c8f46a5a..1c97bfbca 100644 --- a/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/install/ReleaseInstallerActivity.java +++ b/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/install/ReleaseInstallerActivity.java @@ -1,3 +1,8 @@ +/* + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. + */ + package com.microsoft.appcenter.distribute.install; import static com.microsoft.appcenter.distribute.DistributeConstants.LOG_TAG; @@ -11,8 +16,14 @@ import com.microsoft.appcenter.utils.async.AppCenterFuture; import com.microsoft.appcenter.utils.async.DefaultAppCenterFuture; +/** + * Invisible activity used for wrapping system dialogs. + */ public class ReleaseInstallerActivity extends Activity { + /** + * Result of system dialog. + */ public static class Result { public final int code; public final String message; @@ -23,11 +34,21 @@ public Result(int code, String message) { } } + /** + * Tracking last request to send result to the listener. + */ private static DefaultAppCenterFuture sResultFuture; + /** + * Starts wrapper activity and system dialog inside. + * + * @param context any context. + * @param trackedIntent intent of tracked system dialog. + * @return future with result. + */ public static AppCenterFuture startActivityForResult(Context context, Intent trackedIntent) { if (sResultFuture != null) { - AppCenterLog.error(LOG_TAG, "ALREADY IN PROGRESS"); + AppCenterLog.error(LOG_TAG, "Another installing activity already in progress."); return null; } sResultFuture = new DefaultAppCenterFuture<>(); @@ -36,8 +57,6 @@ public static AppCenterFuture startActivityForResult(Context context, In intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP); intent.addFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION); intent.putExtra(Intent.EXTRA_INTENT, trackedIntent); - - // FIXME: StrictMode policy violation; ~duration=13 ms: android.os.strictmode.DiskReadViolation context.startActivity(intent); return sResultFuture; } @@ -54,7 +73,7 @@ protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); final Intent intent = getIntent().getParcelableExtra(Intent.EXTRA_INTENT); if (intent == null) { - AppCenterLog.warn(LOG_TAG, "MISSING EXTRA INTENT"); + AppCenterLog.warn(LOG_TAG, "Missing extra intent."); finish(); return; } @@ -67,12 +86,7 @@ protected void onCreate(Bundle savedInstanceState) { @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { - AppCenterLog.warn(LOG_TAG, "onActivityResult: " + resultCode); - if (data != null && data.getExtras() != null) { - for (String key : data.getExtras().keySet()) { - AppCenterLog.warn(LOG_TAG, key + ": " + data.getExtras().get(key)); - } - } + AppCenterLog.verbose(LOG_TAG, "Release installer activity result=" + resultCode); complete(new Result(resultCode, null)); finish(); } diff --git a/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/install/intent/IntentReleaseInstaller.java b/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/install/intent/IntentReleaseInstaller.java index 1959d76cc..bc8ea6ae2 100644 --- a/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/install/intent/IntentReleaseInstaller.java +++ b/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/install/intent/IntentReleaseInstaller.java @@ -1,3 +1,8 @@ +/* + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. + */ + package com.microsoft.appcenter.distribute.install.intent; import static android.app.Activity.RESULT_CANCELED; diff --git a/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/install/session/SessionReleaseInstaller.java b/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/install/session/SessionReleaseInstaller.java index 0aa35b234..54d9cc562 100644 --- a/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/install/session/SessionReleaseInstaller.java +++ b/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/install/session/SessionReleaseInstaller.java @@ -1,3 +1,8 @@ +/* + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. + */ + package com.microsoft.appcenter.distribute.install.session; import static android.content.pm.PackageInstaller.SessionParams.MODE_FULL_INSTALL; From 9d7898f42324544aa0deba7150ce3480a539fef4 Mon Sep 17 00:00:00 2001 From: Ivan Matkov Date: Sun, 5 Jun 2022 23:07:35 +0200 Subject: [PATCH 59/72] Adding tests for new classes --- .../appcenter/distribute/UpdateInstaller.java | 34 ++- .../install/ReleaseInstallerActivity.java | 4 +- .../distribute/UpdateInstallerTest.java | 73 ++++++ .../distribute/UpdateReceiverTest.java | 89 +++++++ .../install/ReleaseInstallerActivityTest.java | 51 ++++ .../intent/IntentReleaseInstallerTest.java | 175 ++++++++++++++ .../session/InstallStatusReceiverTest.java | 217 ++++++++++++++++++ .../session/PackageInstallerListenerTest.java | 62 +++++ .../session/SessionReleaseInstallerTest.java | 206 +++++++++++++++++ 9 files changed, 902 insertions(+), 9 deletions(-) create mode 100644 sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/UpdateInstallerTest.java create mode 100644 sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/UpdateReceiverTest.java create mode 100644 sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/install/ReleaseInstallerActivityTest.java create mode 100644 sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/install/intent/IntentReleaseInstallerTest.java create mode 100644 sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/install/session/InstallStatusReceiverTest.java create mode 100644 sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/install/session/PackageInstallerListenerTest.java create mode 100644 sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/install/session/SessionReleaseInstallerTest.java diff --git a/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/UpdateInstaller.java b/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/UpdateInstaller.java index 9c969af2d..0c92aed45 100644 --- a/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/UpdateInstaller.java +++ b/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/UpdateInstaller.java @@ -13,12 +13,15 @@ import android.os.HandlerThread; import androidx.annotation.NonNull; +import androidx.annotation.VisibleForTesting; import com.microsoft.appcenter.distribute.install.ReleaseInstaller; import com.microsoft.appcenter.distribute.install.intent.IntentReleaseInstaller; import com.microsoft.appcenter.distribute.install.session.SessionReleaseInstaller; import com.microsoft.appcenter.utils.AppCenterLog; +import java.util.ArrayList; +import java.util.Collection; import java.util.Deque; import java.util.LinkedList; @@ -35,7 +38,7 @@ class UpdateInstaller implements ReleaseInstaller, ReleaseInstaller.Listener { /** * Queue of available to use installers. */ - private final Deque mInstallers = new LinkedList<>(); + private final Deque mInstallers; /** * The installer that we currently use. @@ -54,16 +57,27 @@ class UpdateInstaller implements ReleaseInstaller, ReleaseInstaller.Listener { UpdateInstaller(Context context, ReleaseDetails releaseDetails) { mReleaseDetails = releaseDetails; + mInstallers = createInstallers(context); + mCurrentInstaller = popNextInstaller(); + } - /* Initialize installers list. Use separate thread for background operations. */ - HandlerThread thread = new HandlerThread("AppCenter.Installer"); - thread.start(); - Handler handler = new Handler(thread.getLooper()); - mInstallers.add(new SessionReleaseInstaller(context, handler, this)); - mInstallers.add(new IntentReleaseInstaller(context, handler, this)); + @VisibleForTesting + UpdateInstaller(ReleaseDetails releaseDetails, Deque installers) { + mReleaseDetails = releaseDetails; + mInstallers = installers; mCurrentInstaller = popNextInstaller(); } + private Deque createInstallers(Context context) { + + /* Initialize installers list. Use separate thread for background operations. */ + Handler handler = createInstallerHandler(); + Deque installers = new LinkedList<>(); + installers.add(new SessionReleaseInstaller(context, handler, this)); + installers.add(new IntentReleaseInstaller(context, handler, this)); + return installers; + } + private ReleaseInstaller popNextInstaller() { if (mInstallers.size() == 0) { return null; @@ -122,6 +136,12 @@ public synchronized void onCancel() { } } + private static Handler createInstallerHandler() { + HandlerThread thread = new HandlerThread("AppCenter.Installer"); + thread.start(); + return new Handler(thread.getLooper()); + } + /** * Message to filter MIUI error to use fallback installer. */ diff --git a/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/install/ReleaseInstallerActivity.java b/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/install/ReleaseInstallerActivity.java index 1c97bfbca..caeeb15c0 100644 --- a/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/install/ReleaseInstallerActivity.java +++ b/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/install/ReleaseInstallerActivity.java @@ -69,7 +69,7 @@ private static void complete(Result result) { } @Override - protected void onCreate(Bundle savedInstanceState) { + public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); final Intent intent = getIntent().getParcelableExtra(Intent.EXTRA_INTENT); if (intent == null) { @@ -85,7 +85,7 @@ protected void onCreate(Bundle savedInstanceState) { } @Override - protected void onActivityResult(int requestCode, int resultCode, Intent data) { + public void onActivityResult(int requestCode, int resultCode, Intent data) { AppCenterLog.verbose(LOG_TAG, "Release installer activity result=" + resultCode); complete(new Result(resultCode, null)); finish(); diff --git a/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/UpdateInstallerTest.java b/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/UpdateInstallerTest.java new file mode 100644 index 000000000..c3e7e34cd --- /dev/null +++ b/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/UpdateInstallerTest.java @@ -0,0 +1,73 @@ +/* + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. + */ + +package com.microsoft.appcenter.distribute; + +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; +import static org.powermock.api.mockito.PowerMockito.mock; + +import android.content.Context; +import android.net.Uri; + +import com.microsoft.appcenter.distribute.install.ReleaseInstaller; + +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.junit.MockitoJUnitRunner; +import org.powermock.modules.junit4.rule.PowerMockRule; + +import java.util.LinkedList; + +@RunWith(MockitoJUnitRunner.class) +public class UpdateInstallerTest { + + @Mock + private ReleaseDetails mReleaseDetails; + + @Mock + private ReleaseInstaller mReleaseInstaller; + + private UpdateInstaller mInstaller; + + @Before + public void setUp() throws Exception { + mInstaller = new UpdateInstaller(mReleaseDetails, new LinkedList() {{ + add(mReleaseInstaller); + }}); + } + + @Test + public void install() { + Uri uri = mock(Uri.class); + mInstaller.install(uri); + + verify(mReleaseInstaller).install(eq(uri)); + } + + @Test + public void resume() { + mInstaller.resume(); + } + + @Test + public void clear() { + mInstaller.clear(); + } + + @Test + public void onError() { + mInstaller.onError("Some error"); + } + + @Test + public void onCancel() { + mInstaller.onCancel(); + } +} \ No newline at end of file diff --git a/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/UpdateReceiverTest.java b/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/UpdateReceiverTest.java new file mode 100644 index 000000000..d1d8397b4 --- /dev/null +++ b/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/UpdateReceiverTest.java @@ -0,0 +1,89 @@ +/* + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. + */ + +package com.microsoft.appcenter.distribute; + +import static android.content.Intent.ACTION_MY_PACKAGE_REPLACED; +import static org.mockito.ArgumentMatchers.eq; +import static org.powermock.api.mockito.PowerMockito.mockStatic; +import static org.powermock.api.mockito.PowerMockito.verifyStatic; +import static org.powermock.api.mockito.PowerMockito.when; + +import android.content.Context; +import android.content.Intent; +import android.content.pm.PackageInfo; + +import com.microsoft.appcenter.utils.AppNameHelper; +import com.microsoft.appcenter.utils.DeviceInfoHelper; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.powermock.core.classloader.annotations.PrepareForTest; +import org.powermock.modules.junit4.PowerMockRunner; + +@PrepareForTest({ + AppNameHelper.class, + DeviceInfoHelper.class, + DistributeUtils.class +}) +@RunWith(PowerMockRunner.class) +public class UpdateReceiverTest { + + @Mock + private Context mContext; + + @Mock + private Intent mIntent; + + @Mock + private Intent mResumeIntent; + + private UpdateReceiver mUpdateReceiver; + + @Before + public void setUp() { + mockStatic(AppNameHelper.class); + mockStatic(DeviceInfoHelper.class); + mockStatic(DistributeUtils.class); + + when(DistributeUtils.getResumeAppIntent(mContext)).thenReturn(mResumeIntent); + + when(AppNameHelper.getAppName(mContext)).thenReturn("Contoso"); + + when(mIntent.getAction()).thenReturn(ACTION_MY_PACKAGE_REPLACED); + when(mContext.getString(R.string.appcenter_distribute_install_completed_title)) + .thenReturn("Title"); + when(mContext.getString(R.string.appcenter_distribute_install_completed_message)) + .thenReturn("%1$s %2$s (%3$d)"); + + mUpdateReceiver = new UpdateReceiver(); + } + + @Test + public void receiveMyPackageReplaced() { + PackageInfo packageInfo = new PackageInfo(); + packageInfo.versionName = "1.0"; + when(DeviceInfoHelper.getPackageInfo(mContext)).thenReturn(packageInfo); + when(DeviceInfoHelper.getVersionCode(packageInfo)).thenReturn(1); + + /* Call method with ACTION_MY_PACKAGE_REPLACED action. */ + mUpdateReceiver.onReceive(mContext, mIntent); + + verifyStatic(DistributeUtils.class); + DistributeUtils.postNotification(eq(mContext), eq("Title"), eq("Contoso 1.0 (1)"), eq(mResumeIntent)); + } + + @Test + public void receiveMyPackageReplacedWithoutPackageInfo() { + + /* Call method with ACTION_MY_PACKAGE_REPLACED action. */ + mUpdateReceiver.onReceive(mContext, mIntent); + + verifyStatic(DistributeUtils.class); + DistributeUtils.postNotification(eq(mContext), eq("Title"), eq("Contoso ? (0)"), eq(mResumeIntent)); + } +} \ No newline at end of file diff --git a/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/install/ReleaseInstallerActivityTest.java b/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/install/ReleaseInstallerActivityTest.java new file mode 100644 index 000000000..3b9ffb3bd --- /dev/null +++ b/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/install/ReleaseInstallerActivityTest.java @@ -0,0 +1,51 @@ +/* + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. + */ + +package com.microsoft.appcenter.distribute.install; + +import static org.powermock.api.mockito.PowerMockito.mock; +import static org.powermock.api.mockito.PowerMockito.when; + +import android.content.Context; +import android.content.Intent; +import android.os.Bundle; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.Spy; +import org.powermock.modules.junit4.PowerMockRunner; + +@RunWith(PowerMockRunner.class) +public class ReleaseInstallerActivityTest { + + @Mock + private Intent mIntent; + + @Spy + private ReleaseInstallerActivity mActivity = new ReleaseInstallerActivity(); + + @Before + public void setUp() { + when(mActivity.getIntent()).thenReturn(mIntent); + } + + @Test + public void startActivityForResult() { + Context context = mock(Context.class); + ReleaseInstallerActivity.startActivityForResult(context, mIntent); + } + + @Test + public void onCreate() { + mActivity.onCreate(mock(Bundle.class)); + } + + @Test + public void onActivityResult() { + mActivity.onActivityResult(0, 0, null); + } +} \ No newline at end of file diff --git a/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/install/intent/IntentReleaseInstallerTest.java b/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/install/intent/IntentReleaseInstallerTest.java new file mode 100644 index 000000000..98b034fb5 --- /dev/null +++ b/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/install/intent/IntentReleaseInstallerTest.java @@ -0,0 +1,175 @@ +/* + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. + */ + +package com.microsoft.appcenter.distribute.install.intent; + +import static android.app.Activity.RESULT_CANCELED; +import static android.app.Activity.RESULT_FIRST_USER; +import static android.app.Activity.RESULT_OK; +import static org.junit.Assert.assertEquals; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoInteractions; +import static org.mockito.Mockito.verifyNoMoreInteractions; +import static org.powermock.api.mockito.PowerMockito.mock; +import static org.powermock.api.mockito.PowerMockito.mockStatic; +import static org.powermock.api.mockito.PowerMockito.verifyStatic; +import static org.powermock.api.mockito.PowerMockito.when; +import static org.powermock.api.mockito.PowerMockito.whenNew; + +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.net.Uri; + +import com.microsoft.appcenter.distribute.install.ReleaseInstaller; +import com.microsoft.appcenter.distribute.install.ReleaseInstallerActivity; +import com.microsoft.appcenter.utils.async.AppCenterConsumer; +import com.microsoft.appcenter.utils.async.AppCenterFuture; + +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.mockito.ArgumentCaptor; +import org.mockito.Captor; +import org.mockito.Mock; +import org.powermock.core.classloader.annotations.PrepareForTest; +import org.powermock.modules.junit4.rule.PowerMockRule; + +@PrepareForTest({ + Intent.class, + IntentReleaseInstaller.class, + ReleaseInstallerActivity.class +}) +public class IntentReleaseInstallerTest { + + @Rule + public PowerMockRule mRule = new PowerMockRule(); + + @Mock + Context mContext; + + @Mock + ReleaseInstaller.Listener mListener; + + @Mock + Intent mIntent; + + @Mock + AppCenterFuture mResultFuture; + + @Captor + ArgumentCaptor> mConsumerArgumentCaptor; + + IntentReleaseInstaller mInstaller; + + @Before + public void setUp() throws Exception { + + /* Mock intent. */ + mockStatic(Intent.class); + whenNew(Intent.class).withAnyArguments().thenReturn(mIntent); + when(mIntent.resolveActivity(any())).thenReturn(mock(ComponentName.class)); + + /* Mock activity. */ + mockStatic(ReleaseInstallerActivity.class); + when(ReleaseInstallerActivity.startActivityForResult(eq(mContext), any(Intent.class))) + .thenReturn(mResultFuture); + + /* Create installer instance. */ + mInstaller = new IntentReleaseInstaller(mContext, null, mListener); + } + + @Test + public void cannotResolveActivity() { + when(mIntent.resolveActivity(any())).thenReturn(null); + Uri uri = mock(Uri.class); + mInstaller.install(uri); + + /* Verify error if activity cannot be resolved. */ + verify(mIntent).setData(eq(uri)); + verify(mListener).onError(anyString()); + verifyNoMoreInteractions(mListener); + } + + @Test + public void cannotStartActivity() { + when(ReleaseInstallerActivity.startActivityForResult(eq(mContext), any(Intent.class))) + .thenReturn(null); + Uri uri = mock(Uri.class); + mInstaller.install(uri); + + /* Verify that we tried to start activity. */ + verify(mIntent).setData(eq(uri)); + verifyStatic(ReleaseInstallerActivity.class); + ReleaseInstallerActivity.startActivityForResult(eq(mContext), eq(mIntent)); + + /* + * It should not be a case, it's expected to have no more than one installer in time. + * So, don't inform listener to avoid break state of the service. + */ + verifyNoInteractions(mListener); + } + + @Test + public void installSuccess() { + Uri uri = mock(Uri.class); + mInstaller.install(uri); + + /* Verify that activity has been started. */ + verify(mIntent).setData(eq(uri)); + verify(mResultFuture).thenAccept(mConsumerArgumentCaptor.capture()); + + /* Pass result to captured callback. */ + ReleaseInstallerActivity.Result result = new ReleaseInstallerActivity.Result(RESULT_OK, null); + mConsumerArgumentCaptor.getValue().accept(result); + + /* In case of success the application will be killed, there is no callback for that case. */ + verifyNoInteractions(mListener); + } + + @Test + public void installError() { + Uri uri = mock(Uri.class); + mInstaller.install(uri); + + /* Verify that activity has been started. */ + verify(mIntent).setData(eq(uri)); + verify(mResultFuture).thenAccept(mConsumerArgumentCaptor.capture()); + + /* Pass result to captured callback. */ + ReleaseInstallerActivity.Result result = new ReleaseInstallerActivity.Result(RESULT_FIRST_USER, null); + mConsumerArgumentCaptor.getValue().accept(result); + + /* Verify error callback. */ + verify(mListener).onError(anyString()); + verifyNoMoreInteractions(mListener); + } + + @Test + public void installCancel() { + Uri uri = mock(Uri.class); + mInstaller.install(uri); + + /* Verify that activity has been started. */ + verify(mIntent).setData(eq(uri)); + verify(mResultFuture).thenAccept(mConsumerArgumentCaptor.capture()); + + /* Pass result to captured callback. */ + ReleaseInstallerActivity.Result result = new ReleaseInstallerActivity.Result(RESULT_CANCELED, null); + mConsumerArgumentCaptor.getValue().accept(result); + + /* Verify error cancel. */ + verify(mListener).onCancel(); + verifyNoMoreInteractions(mListener); + } + + @Test + public void testToString() { + assertEquals("ACTION_INSTALL_PACKAGE", mInstaller.toString()); + } +} \ No newline at end of file diff --git a/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/install/session/InstallStatusReceiverTest.java b/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/install/session/InstallStatusReceiverTest.java new file mode 100644 index 000000000..fcfffe1b5 --- /dev/null +++ b/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/install/session/InstallStatusReceiverTest.java @@ -0,0 +1,217 @@ +/* + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. + */ + +package com.microsoft.appcenter.distribute.install.session; + +import static com.microsoft.appcenter.distribute.DistributeConstants.LOG_TAG; +import static com.microsoft.appcenter.distribute.install.session.InstallStatusReceiver.INSTALL_STATUS_ACTION; +import static org.junit.Assert.assertEquals; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoInteractions; +import static org.powermock.api.mockito.PowerMockito.mock; +import static org.powermock.api.mockito.PowerMockito.mockStatic; +import static org.powermock.api.mockito.PowerMockito.verifyNoMoreInteractions; +import static org.powermock.api.mockito.PowerMockito.verifyStatic; +import static org.powermock.api.mockito.PowerMockito.when; + +import android.app.PendingIntent; +import android.content.Context; +import android.content.Intent; +import android.content.IntentSender; +import android.content.pm.PackageInstaller; +import android.os.Build; +import android.os.Bundle; + +import com.microsoft.appcenter.utils.AppCenterLog; +import com.microsoft.appcenter.utils.DeviceInfoHelper; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.powermock.core.classloader.annotations.PrepareForTest; +import org.powermock.modules.junit4.PowerMockRunner; +import org.powermock.reflect.Whitebox; + +@PrepareForTest({ + AppCenterLog.class, + DeviceInfoHelper.class, + PendingIntent.class +}) +@RunWith(PowerMockRunner.class) +public class InstallStatusReceiverTest { + + private static final int SESSION_ID = 42; + + @Mock + private Context mContext; + + @Mock + private Intent mIntent; + + @Mock + private Bundle mExtra; + + @Mock + private SessionReleaseInstaller mInstaller; + + private InstallStatusReceiver mInstallStatusReceiver; + + private void installStatus(int status) { + when(mIntent.getAction()).thenReturn(INSTALL_STATUS_ACTION); + when(mExtra.getInt(eq(PackageInstaller.EXTRA_SESSION_ID))).thenReturn(SESSION_ID); + when(mExtra.getInt(eq(PackageInstaller.EXTRA_STATUS))).thenReturn(status); + } + + @Before + public void setUp() { + when(mIntent.getExtras()).thenReturn(mExtra); + + /* Mock static classes. */ + mockStatic(AppCenterLog.class); + mockStatic(DeviceInfoHelper.class); + + /* Init receiver. */ + mInstallStatusReceiver = new InstallStatusReceiver(mInstaller); + } + + @Test + public void receiveInstallStatusPendingUserAction() { + installStatus(PackageInstaller.STATUS_PENDING_USER_ACTION); + Intent confirmIntent = mock(Intent.class); + when(mExtra.getParcelable(eq(Intent.EXTRA_INTENT))).thenReturn(confirmIntent); + + /* Call method with STATUS_PENDING_USER_ACTION action. */ + mInstallStatusReceiver.onReceive(mContext, mIntent); + + /* Verify callback has been passed through. */ + verify(mInstaller).onInstallConfirmation(eq(SESSION_ID), eq(confirmIntent)); + verifyNoMoreInteractions(mInstaller); + } + + @Test + public void receiveInstallStatusSuccess() { + installStatus(PackageInstaller.STATUS_SUCCESS); + + /* Call method with STATUS_SUCCESS action. */ + mInstallStatusReceiver.onReceive(mContext, mIntent); + + /* No-op in this case, just make sure that it's logged. */ + verifyStatic(AppCenterLog.class); + AppCenterLog.info(eq(LOG_TAG), anyString()); + verifyNoInteractions(mInstaller); + } + + @Test + public void receiveInstallStatusFailureAborted() { + installStatus(PackageInstaller.STATUS_FAILURE_ABORTED); + + /* Call method with failure action. */ + mInstallStatusReceiver.onReceive(mContext, mIntent); + + /* Verify callback has been passed through. */ + verify(mInstaller).onInstallCancel(eq(SESSION_ID)); + verifyNoMoreInteractions(mInstaller); + } + + @Test + public void receiveInstallStatusFailure() { + receiveInstallStatusFailure(PackageInstaller.STATUS_FAILURE); + } + + @Test + public void receiveInstallStatusFailureBlocked() { + receiveInstallStatusFailure(PackageInstaller.STATUS_FAILURE_BLOCKED); + } + + @Test + public void receiveInstallStatusFailureConflict() { + receiveInstallStatusFailure(PackageInstaller.STATUS_FAILURE_CONFLICT); + } + + @Test + public void receiveInstallStatusFailureIncompatible() { + receiveInstallStatusFailure(PackageInstaller.STATUS_FAILURE_INCOMPATIBLE); + } + + @Test + public void receiveInstallStatusFailureInvalid() { + receiveInstallStatusFailure(PackageInstaller.STATUS_FAILURE_INVALID); + } + + @Test + public void receiveInstallStatusFailureStorage() { + receiveInstallStatusFailure(PackageInstaller.STATUS_FAILURE_STORAGE); + } + + private void receiveInstallStatusFailure(int status) { + installStatus(status); + String errorMessage = "Some error message, status=" + status; + when(mExtra.getString(eq(PackageInstaller.EXTRA_STATUS_MESSAGE))).thenReturn(errorMessage); + + /* Call method with failure action. */ + mInstallStatusReceiver.onReceive(mContext, mIntent); + + /* Verify callback has been passed through. */ + verify(mInstaller).onInstallError(eq(SESSION_ID), eq(errorMessage)); + verifyNoMoreInteractions(mInstaller); + } + + @Test + public void onReceiveWithStartIntentWithUnrecognizedStatus() { + final int UNKNOWN_STATUS = 42; + installStatus(UNKNOWN_STATUS); + + /* Call method with wrong action. */ + mInstallStatusReceiver.onReceive(mContext, mIntent); + + /* Verify that log was called. */ + verifyStatic(AppCenterLog.class); + AppCenterLog.warn(eq(LOG_TAG), anyString()); + verifyNoInteractions(mInstaller); + } + + @Test + public void onReceiverWithUnknownAction() { + when(mIntent.getAction()).thenReturn("UnknownAction"); + + /* Call method with wrong action. */ + mInstallStatusReceiver.onReceive(mContext, mIntent); + + /* No-op in this case. */ + verifyNoInteractions(mInstaller); + } + + @Test + public void createIntentSenderOnAndroidS() { + + /* Mock SDK_INT to S. */ + Whitebox.setInternalState(Build.VERSION.class, "SDK_INT", Build.VERSION_CODES.S); + createIntentSender(PendingIntent.FLAG_MUTABLE); + } + + @Test + public void createIntentSenderOnAndroidLowS() { + + /* Mock SDK_INT to M. */ + Whitebox.setInternalState(Build.VERSION.class, "SDK_INT", Build.VERSION_CODES.M); + createIntentSender(0); + } + + private void createIntentSender(int expectedFlag) { + mockStatic(PendingIntent.class); + PendingIntent intent = mock(PendingIntent.class); + IntentSender sender = mock(IntentSender.class); + when(intent.getIntentSender()).thenReturn(sender); + when(PendingIntent.getBroadcast(any(Context.class), eq(SESSION_ID), any(Intent.class), eq(expectedFlag))) + .thenReturn(intent); + + /* Getting install status intent sender. */ + assertEquals(sender, InstallStatusReceiver.getInstallStatusIntentSender(mContext, SESSION_ID)); + } +} diff --git a/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/install/session/PackageInstallerListenerTest.java b/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/install/session/PackageInstallerListenerTest.java new file mode 100644 index 000000000..a656b6878 --- /dev/null +++ b/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/install/session/PackageInstallerListenerTest.java @@ -0,0 +1,62 @@ +/* + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. + */ + +package com.microsoft.appcenter.distribute.install.session; + +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoInteractions; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.junit.MockitoJUnitRunner; + +@RunWith(MockitoJUnitRunner.class) +public class PackageInstallerListenerTest { + + private static final int SESSION_ID = 42; + + @Mock + private SessionReleaseInstaller mInstaller; + + private PackageInstallerListener mListener; + + @Before + public void setUp() { + mListener = new PackageInstallerListener(mInstaller); + } + + @Test + public void onCreated() { + mListener.onCreated(SESSION_ID); + verifyNoInteractions(mInstaller); + } + + @Test + public void onBadgingChanged() { + mListener.onBadgingChanged(SESSION_ID); + verifyNoInteractions(mInstaller); + } + + @Test + public void onActiveChanged() { + mListener.onActiveChanged(SESSION_ID, true); + verifyNoInteractions(mInstaller); + } + + @Test + public void onProgressChanged() { + mListener.onProgressChanged(SESSION_ID, 0.5f); + verify(mInstaller).onInstallProgress(eq(SESSION_ID)); + } + + @Test + public void onFinished() { + mListener.onFinished(SESSION_ID, true); + verifyNoInteractions(mInstaller); + } +} \ No newline at end of file diff --git a/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/install/session/SessionReleaseInstallerTest.java b/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/install/session/SessionReleaseInstallerTest.java new file mode 100644 index 000000000..6ea33f414 --- /dev/null +++ b/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/install/session/SessionReleaseInstallerTest.java @@ -0,0 +1,206 @@ +/* + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. + */ + +package com.microsoft.appcenter.distribute.install.session; + +import static org.junit.Assert.assertEquals; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.anyLong; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoInteractions; +import static org.powermock.api.mockito.PowerMockito.mock; +import static org.powermock.api.mockito.PowerMockito.mockStatic; +import static org.powermock.api.mockito.PowerMockito.when; +import static org.powermock.api.mockito.PowerMockito.whenNew; + +import android.content.ContentResolver; +import android.content.Context; +import android.content.Intent; +import android.content.IntentSender; +import android.content.pm.PackageInstaller; +import android.content.pm.PackageManager; +import android.net.Uri; +import android.os.Handler; +import android.os.ParcelFileDescriptor; + +import com.microsoft.appcenter.distribute.install.ReleaseInstaller; + +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.mockito.Mock; +import org.mockito.invocation.InvocationOnMock; +import org.mockito.stubbing.Answer; +import org.powermock.core.classloader.annotations.PrepareForTest; +import org.powermock.modules.junit4.rule.PowerMockRule; + +import java.io.FileInputStream; +import java.io.IOException; +import java.io.OutputStream; + +@PrepareForTest({ + InstallStatusReceiver.class, + PackageInstallerListener.class, + SessionReleaseInstaller.class +}) +public class SessionReleaseInstallerTest { + + private static final int SESSION_ID = 42; + + @Rule + public PowerMockRule mRule = new PowerMockRule(); + + @Mock + private Context mContext; + + @Mock + private Handler mHandler; + + @Mock + private ReleaseInstaller.Listener mListener; + + @Mock + private PackageInstaller mPackageInstaller; + + @Mock + private InstallStatusReceiver mInstallStatusReceiver; + + @Mock + private PackageInstallerListener mPackageInstallerListener; + + @Mock + private FileInputStream mInputStream; + + @Mock + private OutputStream mOutputStream; + + @Mock + private PackageInstaller.Session mSession; + + private SessionReleaseInstaller mInstaller; + + @Before + public void setUp() throws Exception { + + /* Mock package installer. */ + PackageManager mockPackageManager = mock(PackageManager.class); + when(mockPackageManager.getPackageInstaller()).thenReturn(mPackageInstaller); + when(mContext.getPackageManager()).thenReturn(mockPackageManager); + + /* Mock input file. */ + ContentResolver contentResolver = mock(ContentResolver.class); + when(mContext.getContentResolver()).thenReturn(contentResolver); + ParcelFileDescriptor fileDescriptor = mock(ParcelFileDescriptor.class); + when(contentResolver.openFileDescriptor(any(Uri.class), eq("r"))).thenReturn(fileDescriptor); + whenNew(FileInputStream.class).withAnyArguments().thenReturn(mInputStream); + + /* Mock session. */ + when(mPackageInstaller.openSession(anyInt())).thenReturn(mSession); + when(mSession.openWrite(anyString(), anyLong(), anyLong())).thenReturn(mOutputStream); + + mockStatic(InstallStatusReceiver.class); + whenNew(InstallStatusReceiver.class).withAnyArguments().thenReturn(mInstallStatusReceiver); + when(InstallStatusReceiver.getInstallStatusIntentSender(any(Context.class), anyInt())) + .thenReturn(mock(IntentSender.class)); + + mockStatic(PackageInstallerListener.class); + whenNew(PackageInstallerListener.class).withAnyArguments().thenReturn(mPackageInstallerListener); + + when(mHandler.post(any(Runnable.class))).then(new Answer() { + + @Override + public Boolean answer(InvocationOnMock invocation) { + invocation.getArgument(0).run(); + return true; + } + }); + + mInstaller = new SessionReleaseInstaller(mContext, mHandler, mListener); + } + + @Test + public void install() throws IOException { + /* Mock data. */ + when(mInputStream.read(any())).thenReturn(10).thenReturn(-1); + + Uri uri = mock(Uri.class); + mInstaller.install(uri); + + verify(mHandler).post(any(Runnable.class)); + verify(mContext).registerReceiver(eq(mInstallStatusReceiver), any()); + verify(mPackageInstaller).registerSessionCallback(eq(mPackageInstallerListener)); + verify(mInputStream).close(); + verify(mOutputStream).close(); + verify(mSession).commit(any(IntentSender.class)); + verify(mSession, never()).abandon(); + verify(mSession).close(); + verifyNoInteractions(mListener); + } + + @Test + public void throwIOExceptionWhenTryToOpenWriteSession() throws IOException { + + /* Throw error when try to open write to session. */ + when(mSession.openWrite(anyString(), anyLong(), anyLong())) + .thenThrow(new IOException()); + + /* Call install method. */ + Uri uri = mock(Uri.class); + mInstaller.install(uri); + + /* Verify. */ + verify(mInputStream, never()).close(); + verify(mSession).abandon(); + } + + @Test + public void throwIOExceptionWhenTryToCreateSession() throws IOException { + + /* Throw error when try to create session. */ + when(mPackageInstaller.createSession(any(PackageInstaller.SessionParams.class))) + .thenThrow(new IOException()); + + /* Call install method. */ + Uri uri = mock(Uri.class); + mInstaller.install(uri); + + /* Verify that the session wasn't created. */ + verify(mPackageInstaller, never()).openSession(anyInt()); + } + + @Test + public void clear() { + mInstaller.clear(); + } + + @Test + public void testToString() { + assertEquals("PackageInstaller", mInstaller.toString()); + } + + @Test + public void onInstallProgress() { + mInstaller.onInstallProgress(SESSION_ID); + } + + @Test + public void onInstallConfirmation() { + mInstaller.onInstallConfirmation(SESSION_ID, mock(Intent.class)); + } + + @Test + public void onInstallError() { + mInstaller.onInstallError(SESSION_ID, "Some error"); + } + + @Test + public void onInstallCancel() { + mInstaller.onInstallCancel(SESSION_ID); + } +} \ No newline at end of file From a180afae6b41d642f30b48229e711d7aa93107ca Mon Sep 17 00:00:00 2001 From: Ivan Matkov Date: Mon, 6 Jun 2022 14:37:21 +0200 Subject: [PATCH 60/72] Fix test coverage --- .../appcenter/distribute/Distribute.java | 25 +-- .../appcenter/distribute/UpdateInstaller.java | 12 +- .../DownloadManagerReleaseDownloader.java | 6 +- .../install/ReleaseInstallerActivity.java | 15 +- .../intent/IntentReleaseInstaller.java | 27 +-- .../session/SessionReleaseInstaller.java | 71 ++++++-- .../distribute/AbstractDistributeTest.java | 1 + .../DistributeBeforeDownloadTest.java | 13 +- .../appcenter/distribute/DistributeTest.java | 86 ++++++--- .../distribute/DistributeUtilsTest.java | 76 ++++++-- .../DistributeWarnUnknownSourcesTest.java | 46 ++--- .../ReleaseDownloadListenerTest.java | 13 +- .../distribute/UpdateInstallerTest.java | 139 +++++++++++++-- .../distribute/UpdateReceiverTest.java | 15 ++ .../ReleaseDownloaderFactoryTest.java | 6 +- .../DownloadManagerReleaseDownloaderTest.java | 20 ++- .../install/ReleaseInstallerActivityTest.java | 95 +++++++++- .../session/InstallStatusReceiverTest.java | 39 ++++- .../session/SessionReleaseInstallerTest.java | 163 ++++++++++++++++-- 19 files changed, 724 insertions(+), 144 deletions(-) diff --git a/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/Distribute.java b/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/Distribute.java index 57e63c034..817d1b42b 100644 --- a/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/Distribute.java +++ b/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/Distribute.java @@ -69,7 +69,6 @@ import com.microsoft.appcenter.distribute.ingestion.DistributeIngestion; import com.microsoft.appcenter.distribute.ingestion.models.DistributionStartSessionLog; import com.microsoft.appcenter.distribute.ingestion.models.json.DistributionStartSessionLogFactory; -import com.microsoft.appcenter.distribute.install.ReleaseInstaller; import com.microsoft.appcenter.http.HttpException; import com.microsoft.appcenter.http.HttpResponse; import com.microsoft.appcenter.http.HttpUtils; @@ -557,13 +556,7 @@ protected synchronized void applyEnabledState(boolean enabled) { @WorkerThread private void resumeWorkflowIfForeground() { if (mForegroundActivity != null) { - HandlerUtils.runOnUiThread(new Runnable() { - - @Override - public void run() { - resumeDistributeWorkflow(); - } - }); + HandlerUtils.runOnUiThread(this::resumeDistributeWorkflow); } else { AppCenterLog.debug(LOG_TAG, "Distribute workflow will be resumed on activity resume event."); } @@ -671,13 +664,7 @@ private synchronized void setInstanceEnabledForDebuggableBuild(boolean enabled) * Implements {@link #checkForUpdate()}. */ private void instanceCheckForUpdate() { - post(new Runnable() { - - @Override - public void run() { - handleCheckForUpdate(); - } - }); + post(this::handleCheckForUpdate); } @WorkerThread @@ -822,7 +809,7 @@ private synchronized void resumeDistributeWorkflow() { if (mReleaseDetails.isMandatoryUpdate()) { /* Show a new modal dialog with only install button. */ - showMandatoryDownloadReadyDialog(); + showMandatoryDownloadReadyDialog(mReleaseDetails); } else { /* Resume installing by ensuring that download completed. */ @@ -1775,7 +1762,10 @@ private synchronized void showDownloadProgress() { /** * Show modal dialog with install button if mandatory update ready and user cancelled install. */ - synchronized void showMandatoryDownloadReadyDialog() { + synchronized void showMandatoryDownloadReadyDialog(@NonNull final ReleaseDetails releaseDetails) { + if (releaseDetails != mReleaseDetails) { + return; + } /* Do not attempt to show dialog if application is in the background. */ if (mForegroundActivity == null) { @@ -1784,7 +1774,6 @@ synchronized void showMandatoryDownloadReadyDialog() { if (!shouldRefreshDialog(mCompletedDownloadDialog)) { return; } - final ReleaseDetails releaseDetails = mReleaseDetails; AlertDialog.Builder dialogBuilder = new AlertDialog.Builder(mForegroundActivity); dialogBuilder.setCancelable(false); dialogBuilder.setTitle(R.string.appcenter_distribute_install_ready_title); diff --git a/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/UpdateInstaller.java b/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/UpdateInstaller.java index 0c92aed45..383cb07ca 100644 --- a/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/UpdateInstaller.java +++ b/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/UpdateInstaller.java @@ -20,8 +20,6 @@ import com.microsoft.appcenter.distribute.install.session.SessionReleaseInstaller; import com.microsoft.appcenter.utils.AppCenterLog; -import java.util.ArrayList; -import java.util.Collection; import java.util.Deque; import java.util.LinkedList; @@ -100,9 +98,10 @@ public synchronized void install(@NonNull Uri localUri) { * Process resume distribute workflow. */ public synchronized void resume() { - // Show mandatory dialog if cancelled in background. + + /* Show mandatory dialog if cancelled in background. */ if (mCancelled && mReleaseDetails.isMandatoryUpdate()) { - Distribute.getInstance().showMandatoryDownloadReadyDialog(); + Distribute.getInstance().showMandatoryDownloadReadyDialog(mReleaseDetails); } } @@ -130,7 +129,7 @@ public synchronized void onError(String errorMessage) { public synchronized void onCancel() { mCancelled = true; if (mReleaseDetails.isMandatoryUpdate()) { - Distribute.getInstance().showMandatoryDownloadReadyDialog(); + Distribute.getInstance().showMandatoryDownloadReadyDialog(mReleaseDetails); } else { Distribute.getInstance().completeWorkflow(mReleaseDetails); } @@ -145,7 +144,8 @@ private static Handler createInstallerHandler() { /** * Message to filter MIUI error to use fallback installer. */ - private static final String RECOVERABLE_ERROR = "INSTALL_FAILED_INTERNAL_ERROR: Permission Denied"; + @VisibleForTesting + static final String RECOVERABLE_ERROR = "INSTALL_FAILED_INTERNAL_ERROR: Permission Denied"; private static boolean isRecoverableError(String errorMessage) { return RECOVERABLE_ERROR.equals(errorMessage); diff --git a/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/download/manager/DownloadManagerReleaseDownloader.java b/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/download/manager/DownloadManagerReleaseDownloader.java index c41379888..3083d1fd4 100644 --- a/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/download/manager/DownloadManagerReleaseDownloader.java +++ b/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/download/manager/DownloadManagerReleaseDownloader.java @@ -89,7 +89,8 @@ public synchronized boolean isDownloading() { @AnyThread @Override public synchronized void resume() { - // Force resume means that we want another completion event + + /* Force resume means that we want another completion event. */ mCompleted = false; /* @@ -221,7 +222,8 @@ synchronized void onDownloadComplete() { if (isCompleted()) { return; } - // Mark completed + + /* Mark download completed. */ mCompleted = true; if (!isDownloadedFileValid()) { mListener.onError("Downloaded package file is invalid."); diff --git a/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/install/ReleaseInstallerActivity.java b/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/install/ReleaseInstallerActivity.java index caeeb15c0..6ab6b9da8 100644 --- a/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/install/ReleaseInstallerActivity.java +++ b/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/install/ReleaseInstallerActivity.java @@ -12,6 +12,8 @@ import android.content.Intent; import android.os.Bundle; +import androidx.annotation.VisibleForTesting; + import com.microsoft.appcenter.utils.AppCenterLog; import com.microsoft.appcenter.utils.async.AppCenterFuture; import com.microsoft.appcenter.utils.async.DefaultAppCenterFuture; @@ -37,7 +39,8 @@ public Result(int code, String message) { /** * Tracking last request to send result to the listener. */ - private static DefaultAppCenterFuture sResultFuture; + @VisibleForTesting + static DefaultAppCenterFuture sResultFuture; /** * Starts wrapper activity and system dialog inside. @@ -72,6 +75,8 @@ private static void complete(Result result) { public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); final Intent intent = getIntent().getParcelableExtra(Intent.EXTRA_INTENT); + + /* Precaution check of external usage of this wrapper activity. */ if (intent == null) { AppCenterLog.warn(LOG_TAG, "Missing extra intent."); finish(); @@ -80,7 +85,10 @@ public void onCreate(Bundle savedInstanceState) { try { startActivityForResult(intent, 0); } catch (SecurityException e) { + + /* SecurityException means that installation is blocked by user. */ complete(new Result(RESULT_FIRST_USER, e.getMessage())); + finish(); } } @@ -94,6 +102,11 @@ public void onActivityResult(int requestCode, int resultCode, Intent data) { @Override public void finish() { super.finish(); + + /* + * Prevent closing animation because we don't need any animation or visual effect + * from this wrapper activity. + */ overridePendingTransition(0, 0); } } \ No newline at end of file diff --git a/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/install/intent/IntentReleaseInstaller.java b/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/install/intent/IntentReleaseInstaller.java index bc8ea6ae2..5dd2ec094 100644 --- a/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/install/intent/IntentReleaseInstaller.java +++ b/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/install/intent/IntentReleaseInstaller.java @@ -35,20 +35,25 @@ public void install(@NonNull Uri localUri) { onError("Cannot resolve install intent for " + localUri); return; } + + /* Use proxy activity to handle activity result. */ AppCenterFuture confirmFuture = ReleaseInstallerActivity.startActivityForResult(mContext, installIntent); - if (confirmFuture != null) { - confirmFuture.thenAccept(new AppCenterConsumer() { + if (confirmFuture == null) { - @Override - public void accept(ReleaseInstallerActivity.Result result) { - if (result.code == RESULT_FIRST_USER) { - onError("Install failed"); - } else if (result.code == RESULT_CANCELED) { - onCancel(); - } - } - }); + /* Another installing activity already in progress. Precaution for unexpected case. */ + return; } + confirmFuture.thenAccept(new AppCenterConsumer() { + + @Override + public void accept(ReleaseInstallerActivity.Result result) { + if (result.code == RESULT_FIRST_USER) { + onError("Install failed"); + } else if (result.code == RESULT_CANCELED) { + onCancel(); + } + } + }); } @NonNull diff --git a/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/install/session/SessionReleaseInstaller.java b/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/install/session/SessionReleaseInstaller.java index 54d9cc562..1451a81fe 100644 --- a/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/install/session/SessionReleaseInstaller.java +++ b/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/install/session/SessionReleaseInstaller.java @@ -32,6 +32,9 @@ import java.io.InputStream; import java.io.OutputStream; +/** + * Installer based on PackageInstaller.Session API. + */ public class SessionReleaseInstaller extends AbstractReleaseInstaller { /** @@ -44,23 +47,42 @@ public class SessionReleaseInstaller extends AbstractReleaseInstaller { */ private static final int BUFFER_CAPACITY = 64 * 1024; + /** + * {@link PackageInstaller.SessionInfo#INVALID_ID} requires requires API level 29, + * so use our own constant. + */ private static final int INVALID_SESSION_ID = -1; + /** + * Timeout to check cancellation state in milliseconds. + */ private static final long CANCEL_TIMEOUT = 1000; + /** + * Install status receiver. Keep the reference to unsubscribe it. + */ private BroadcastReceiver mInstallStatusReceiver; + /** + * Install session callback. Keep the reference to unsubscribe it. + */ private PackageInstaller.SessionCallback mSessionCallback; + /** + * Tracking if user confirmation was requested. + * Needed to cancel the process in case of missing system callback. + */ private boolean mUserConfirmationRequested; + /** + * Current install session id. + */ private int mSessionId = INVALID_SESSION_ID; public SessionReleaseInstaller(Context context, Handler installerHandler, Listener listener) { super(context, installerHandler, listener); } - private PackageInstaller getPackageInstaller() { return mContext.getPackageManager().getPackageInstaller(); } @@ -73,7 +95,11 @@ public void install(@NonNull Uri localUri) { @Override public void run() { + + /* Abandon previous session if needed. */ abandonSession(); + + /* Start install session from background thread. */ startInstallSession(localUri); } }); @@ -81,6 +107,7 @@ public void run() { @Override public synchronized void clear() { + super.clear(); unregisterListeners(); abandonSession(); } @@ -95,6 +122,14 @@ synchronized void onInstallProgress(int sessionId) { if (mSessionId != sessionId) { return; } + + /* + * Reset confirmation flag. + * Any progress event during user confirmation means that installation is not cancelled. + * The only reason to track it is that system dialog might be closed by clicking + * outside. In this case, Android doesn't produce any event, so we have to use it + * as workaround to mark installation as cancelled and re-try in case of mandatory update. + */ mUserConfirmationRequested = false; } @@ -104,16 +139,23 @@ synchronized void onInstallConfirmation(int sessionId, Intent confirmIntent) { } AppCenterLog.info(LOG_TAG, "Ask confirmation to install a new release."); mUserConfirmationRequested = true; + + /* Use proxy activity to handle closing event. */ AppCenterFuture confirmFuture = ReleaseInstallerActivity.startActivityForResult(mContext, confirmIntent); - if (confirmFuture != null) { - confirmFuture.thenAccept(new AppCenterConsumer() { + if (confirmFuture == null) { - @Override - public void accept(ReleaseInstallerActivity.Result result) { - cancelIfNoProgress(); - } - }); + /* Another installing activity already in progress. Precaution for unexpected case. */ + return; } + confirmFuture.thenAccept(new AppCenterConsumer() { + + @Override + public void accept(ReleaseInstallerActivity.Result result) { + + /* Check for progress events after closing confirmation dialog. */ + cancelIfNoProgress(); + } + }); } synchronized void onInstallError(int sessionId, String message) { @@ -133,10 +175,17 @@ synchronized void onInstallCancel(int sessionId) { } private void cancelIfNoProgress() { + + /* + * In some cases progress event might be delayed a bit. + * Use threshold timeout to prevent false-alarming cancelling. + */ postDelayed(new Runnable() { @Override public void run() { + + /* Cancels installation if this flag hasn't been reset by progress event. */ if (mUserConfirmationRequested) { onCancel(); } @@ -214,12 +263,14 @@ private PackageInstaller.Session createSession(ParcelFileDescriptor fileDescript try { return packageInstaller.openSession(mSessionId); - // IllegalStateException - Too many active sessions + /* IllegalStateException might be thrown in case of too many active sessions. */ } catch (IllegalStateException e) { AppCenterLog.warn(LOG_TAG, "Cannot open session, trying to cleanup previous ones.", e); - // Leaked sessions can prevent opening new ones. + /* Trying to clean up leaked sessions that prevent opening new ones. */ cleanPreviousSessions(); + + /* Second try to open install session. */ return packageInstaller.openSession(mSessionId); } } diff --git a/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/AbstractDistributeTest.java b/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/AbstractDistributeTest.java index 19002a931..b8d9030ba 100644 --- a/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/AbstractDistributeTest.java +++ b/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/AbstractDistributeTest.java @@ -220,6 +220,7 @@ public Void answer(InvocationOnMock invocation) { /* Mock app name and other string resources. */ mockStatic(AppNameHelper.class); when(AppNameHelper.getAppName(mContext)).thenReturn("unit-test-app"); + when(mContext.getString(anyInt())).thenReturn("localized-string"); when(mContext.getString(R.string.appcenter_distribute_update_dialog_message_optional)).thenReturn("%s%s%d"); when(mContext.getString(R.string.appcenter_distribute_update_dialog_message_mandatory)).thenReturn("%s%s%d"); when(mContext.getString(R.string.appcenter_distribute_install_ready_message)).thenReturn("%s%s%d"); diff --git a/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/DistributeBeforeDownloadTest.java b/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/DistributeBeforeDownloadTest.java index 53bd52969..556d23201 100644 --- a/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/DistributeBeforeDownloadTest.java +++ b/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/DistributeBeforeDownloadTest.java @@ -55,7 +55,6 @@ import com.microsoft.appcenter.http.HttpResponse; import com.microsoft.appcenter.http.ServiceCall; import com.microsoft.appcenter.http.ServiceCallback; -import com.microsoft.appcenter.test.TestUtils; import com.microsoft.appcenter.utils.AppCenterLog; import com.microsoft.appcenter.utils.AppNameHelper; import com.microsoft.appcenter.utils.AsyncTaskUtils; @@ -70,12 +69,14 @@ import org.mockito.stubbing.Answer; import org.powermock.api.mockito.PowerMockito; import org.powermock.core.classloader.annotations.PrepareForTest; +import org.powermock.reflect.Whitebox; import java.util.HashMap; import java.util.Map; import java.util.UUID; import java.util.concurrent.atomic.AtomicReference; +@SuppressWarnings({"Convert2Lambda", "RedundantSuppression"}) @PrepareForTest({ DistributeUtils.class, SessionContext.class @@ -94,7 +95,7 @@ private void mockSessionContext() { @Test public void moreRecentWithIncompatibleMinApiLevel() throws Exception { mockSessionContext(); - TestUtils.setInternalState(Build.VERSION.class, "SDK_INT", Build.VERSION_CODES.LOLLIPOP); + Whitebox.setInternalState(Build.VERSION.class, "SDK_INT", Build.VERSION_CODES.LOLLIPOP); when(mHttpClient.callAsync(anyString(), anyString(), anyMap(), any(HttpClient.CallTemplate.class), any(ServiceCallback.class))).thenAnswer(new Answer() { @Override @@ -156,7 +157,7 @@ public ServiceCall answer(InvocationOnMock invocation) { String distributionGroupId = UUID.randomUUID().toString(); when(releaseDetails.getDistributionGroupId()).thenReturn(distributionGroupId); when(ReleaseDetails.parse(anyString())).thenReturn(releaseDetails); - TestUtils.setInternalState(Build.VERSION.class, "SDK_INT", Build.VERSION_CODES.N_MR1); + Whitebox.setInternalState(Build.VERSION.class, "SDK_INT", Build.VERSION_CODES.N_MR1); /* Trigger call. */ start(); @@ -898,7 +899,7 @@ public ServiceCall answer(InvocationOnMock invocation) { } @Test - public void shouldNotRemoveReleaseHashStorageIfHashesDontMatch() throws Exception { + public void shouldNotRemoveReleaseHashStorageIfHashesDoNotMatch() throws Exception { /* Mock release hash storage. */ when(SharedPreferencesManager.getString(PREFERENCE_KEY_DOWNLOADED_RELEASE_HASH)).thenReturn("fake-hash"); @@ -1092,7 +1093,7 @@ public ServiceCall answer(InvocationOnMock invocation) { } @After - public void tearDown() throws Exception { - TestUtils.setInternalState(Build.VERSION.class, "SDK_INT", 0); + public void tearDown() { + Whitebox.setInternalState(Build.VERSION.class, "SDK_INT", 0); } } diff --git a/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/DistributeTest.java b/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/DistributeTest.java index 40865c569..a7232a3ed 100644 --- a/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/DistributeTest.java +++ b/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/DistributeTest.java @@ -44,7 +44,7 @@ import android.content.DialogInterface; import android.content.Intent; import android.content.pm.PackageInfo; -import android.os.Build; +import android.net.Uri; import com.microsoft.appcenter.DependencyConfiguration; import com.microsoft.appcenter.distribute.download.ReleaseDownloader; @@ -57,11 +57,9 @@ import com.microsoft.appcenter.http.ServiceCall; import com.microsoft.appcenter.http.ServiceCallback; import com.microsoft.appcenter.ingestion.models.json.LogFactory; -import com.microsoft.appcenter.test.TestUtils; import com.microsoft.appcenter.utils.DeviceInfoHelper; import com.microsoft.appcenter.utils.storage.SharedPreferencesManager; -import org.junit.After; import org.junit.Before; import org.junit.Test; import org.mockito.ArgumentCaptor; @@ -97,11 +95,6 @@ public void setUp() throws Exception { whenNew(UpdateInstaller.class).withAnyArguments().thenReturn(mReleaseInstaller); } - @After - public void tearDown() throws Exception { - TestUtils.setInternalState(Build.VERSION.class, "SDK_INT", 0); - } - @Test public void singleton() { assertSame(Distribute.getInstance(), Distribute.getInstance()); @@ -177,10 +170,6 @@ public void recreateLauncherActivityBeforeFullInitializationNullIntent() { @Test public void recreateLauncherActivityBeforeFullInitializationChannelNotNull() { - /* SharedPreferencesManager isn't initialized yet. */ - when(SharedPreferencesManager.getInt(anyString(), anyInt())) - .thenThrow(new NullPointerException()); - /* Our activity is launched once. */ Intent intent = mock(Intent.class); when(mPackageManager.getLaunchIntentForPackage(anyString())).thenReturn(intent); @@ -188,10 +177,6 @@ public void recreateLauncherActivityBeforeFullInitializationChannelNotNull() { when(intent.resolveActivity(mPackageManager)).thenReturn(componentName); when(componentName.getClassName()).thenReturn(mActivity.getClass().getName()); - /* Mock download completed. */ - mockStatic(SharedPreferencesManager.class); - when(SharedPreferencesManager.getInt(eq(PREFERENCE_KEY_DOWNLOAD_STATE), eq(DOWNLOAD_STATE_COMPLETED))).thenReturn(DOWNLOAD_STATE_COMPLETED); - /* Channel initialization. */ start(); Distribute.getInstance().onApplicationEnterForeground(); @@ -202,18 +187,13 @@ public void recreateLauncherActivityBeforeFullInitializationChannelNotNull() { @Test public void recreateLauncherActivityBeforeFullInitializationChannelNotNullNoDownload() { - /* SharedPreferencesManager isn't initialized yet. */ - when(SharedPreferencesManager.getInt(anyString(), anyInt())) - .thenThrow(new NullPointerException()); - /* Our activity is launched once. */ Intent intent = mock(Intent.class); when(mPackageManager.getLaunchIntentForPackage(anyString())).thenReturn(intent); ComponentName componentName = mock(ComponentName.class); when(intent.resolveActivity(mPackageManager)).thenReturn(componentName); when(componentName.getClassName()).thenReturn(mActivity.getClass().getName()); - mockStatic(SharedPreferencesManager.class); - when(SharedPreferencesManager.getInt(eq(PREFERENCE_KEY_DOWNLOAD_STATE), eq(DOWNLOAD_STATE_COMPLETED))).thenReturn(-1); + when(DistributeUtils.getStoredDownloadState()).thenReturn(DOWNLOAD_STATE_AVAILABLE); /* Callback. */ start(); @@ -289,6 +269,22 @@ public void setInstallingTest() { verifyStatic(SharedPreferencesManager.class); SharedPreferencesManager.putInt(eq(PREFERENCE_KEY_DOWNLOAD_STATE), eq(DOWNLOAD_STATE_INSTALLING)); verify(mReleaseInstaller).install(mUri); + + /* Start installing second time from notified state. */ + when(DistributeUtils.getStoredDownloadState()).thenReturn(DOWNLOAD_STATE_NOTIFIED); + Distribute.getInstance().onActivityPaused(mActivity); + Distribute.getInstance().setInstalling(mReleaseDetails, mUri); + verifyStatic(SharedPreferencesManager.class, times(2)); + SharedPreferencesManager.putInt(eq(PREFERENCE_KEY_DOWNLOAD_STATE), eq(DOWNLOAD_STATE_INSTALLING)); + verify(mReleaseInstaller, times(2)).install(mUri); + + /* Installer should be aware of resume event. */ + Distribute.getInstance().onActivityResumed(mActivity); + verify(mReleaseInstaller).resume(); + + /* Completing workflow clears installer. */ + Distribute.getInstance().completeWorkflow(mReleaseDetails); + verify(mReleaseInstaller).clear(); } private void mockReleaseDetails(boolean mandatoryUpdate) { @@ -317,6 +313,35 @@ public void cancelingNotification() { DistributeUtils.cancelNotification(mContext); } + @Test + public void postDownloadNotification() { + when(DistributeUtils.getStoredDownloadState()) + .thenReturn(DOWNLOAD_STATE_ENQUEUED) + .thenReturn(DOWNLOAD_STATE_NOTIFIED); + + /* Mock that download time is bigger than packageInfo.lastUpdateTime. */ + when(SharedPreferencesManager.getLong(eq(PREFERENCE_KEY_DOWNLOAD_TIME))).thenReturn(3L); + + mockReleaseDetails(false); + Intent intent = mock(Intent.class); + when(DistributeUtils.getResumeAppIntent(any(Context.class))).thenReturn(intent); + + /* Set installing state. */ + Distribute.getInstance().startFromBackground(mContext); + Distribute.getInstance().onStarted(mContext, mChannel, "0", null, false); + Distribute.getInstance().setInstalling(mReleaseDetails, mock(Uri.class)); + + /* Verify. */ + verifyStatic(DistributeUtils.class); + DistributeUtils.postNotification(eq(mContext), anyString(), anyString(), eq(intent)); + verifyStatic(SharedPreferencesManager.class); + SharedPreferencesManager.putInt(eq(PREFERENCE_KEY_DOWNLOAD_STATE), eq(DOWNLOAD_STATE_NOTIFIED)); + + /* Resume after posting notification resumes downloader that produces one more completion event. */ + Distribute.getInstance().onActivityResumed(mActivity); + verify(mReleaseDownloader).resume(); + } + @Test public void updateReleaseDetailsFromBackground() { mockReleaseDetails(false); @@ -333,6 +358,10 @@ public void updateReleaseDetailsFromBackground() { when(DistributeUtils.loadCachedReleaseDetails()).thenReturn(null); Distribute.getInstance().startFromBackground(mContext); verify(mReleaseDownloader).cancel(); + + /* Cannot resume download without release details. */ + Distribute.getInstance().resumeDownload(); + verify(mReleaseDownloader, never()).resume(); } @Test @@ -583,13 +612,24 @@ public void showMandatoryDownloadReadyDialogTest() { /* mReleaseDetails is not null and it's a mandatory update. */ mockReleaseDetails(true); - resumeWorkflow(mock(Activity.class)); + resumeWorkflow(mActivity); + verify(mDialog, never()).show(); /* * Call resume twice when download already checked, mReleaseDetails are not null, package is installing. * current dialog is null. */ Distribute.getInstance().onActivityResumed(mock(Activity.class)); + verify(mDialog).show(); + + /* Ignore if release details are not equal. */ + Distribute.getInstance().showMandatoryDownloadReadyDialog(mock(ReleaseDetails.class)); + verifyNoMoreInteractions(mDialog); + + /* Ignore in background. */ + Distribute.getInstance().onActivityPaused(mActivity); + Distribute.getInstance().showMandatoryDownloadReadyDialog(mReleaseDetails); + verifyNoMoreInteractions(mDialog); /* Call resume third time to trigger dialog refresh but it's not yet showing. */ when(mDialog.isShowing()).thenReturn(false).thenReturn(true); diff --git a/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/DistributeUtilsTest.java b/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/DistributeUtilsTest.java index 27c0bb0ef..2c95a4c44 100644 --- a/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/DistributeUtilsTest.java +++ b/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/DistributeUtilsTest.java @@ -5,27 +5,40 @@ package com.microsoft.appcenter.distribute; -import com.microsoft.appcenter.utils.storage.SharedPreferencesManager; - -import org.json.JSONException; -import org.junit.Assert; -import org.junit.Before; -import org.junit.Rule; -import org.junit.Test; -import org.powermock.core.classloader.annotations.PrepareForTest; -import org.powermock.modules.junit4.rule.PowerMockRule; - +import static android.content.Context.NOTIFICATION_SERVICE; import static com.microsoft.appcenter.distribute.DistributeConstants.PREFERENCE_KEY_RELEASE_DETAILS; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotEquals; import static org.junit.Assert.assertNull; +import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.RETURNS_SELF; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; +import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import static org.powermock.api.mockito.PowerMockito.mockStatic; import static org.powermock.api.mockito.PowerMockito.verifyStatic; +import static org.powermock.api.mockito.PowerMockito.whenNew; + +import android.app.Notification; +import android.app.NotificationManager; +import android.content.Context; +import android.content.Intent; +import android.content.pm.ApplicationInfo; +import android.os.Build; + +import com.microsoft.appcenter.utils.storage.SharedPreferencesManager; + +import org.json.JSONException; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.mockito.Mock; +import org.powermock.core.classloader.annotations.PrepareForTest; +import org.powermock.modules.junit4.rule.PowerMockRule; +import org.powermock.reflect.Whitebox; @PrepareForTest({ DistributeUtils.class, @@ -37,6 +50,9 @@ public class DistributeUtilsTest { @Rule public PowerMockRule mPowerMockRule = new PowerMockRule(); + @Mock + private Context mContext; + @Before public void setUp() { mockStatic(SharedPreferencesManager.class); @@ -97,4 +113,44 @@ public void loadCachedReleaseDetailsJsonException() throws JSONException { verifyStatic(SharedPreferencesManager.class); SharedPreferencesManager.remove(eq(PREFERENCE_KEY_RELEASE_DETAILS)); } + + @Test + public void firstDownloadNotificationApi26() throws Exception { + firstDownloadNotification(Build.VERSION_CODES.O); + } + + @Test + public void firstDownloadNotificationApi21() throws Exception { + firstDownloadNotification(Build.VERSION_CODES.LOLLIPOP); + } + + private void firstDownloadNotification(int apiLevel) throws Exception { + Whitebox.setInternalState(Build.VERSION.class, "SDK_INT", apiLevel); + NotificationManager manager = mock(NotificationManager.class); + when(mContext.getSystemService(NOTIFICATION_SERVICE)).thenReturn(manager); + when(mContext.getApplicationInfo()).thenReturn(mock(ApplicationInfo.class)); + Notification.Builder builder = mock(Notification.Builder.class, RETURNS_SELF); + whenNew(Notification.Builder.class).withAnyArguments().thenReturn(builder); + Notification notification = mock(Notification.class); + when(builder.build()).thenReturn(notification); + + /* Post notification. */ + Intent intent = mock(Intent.class); + DistributeUtils.postNotification(mContext,"Title", "Message", intent); + + /* Verify. */ + verify(builder).setContentTitle(eq("Title")); + verify(builder).setContentText(eq("Message")); + verify(manager).notify(eq(DistributeUtils.getNotificationId()), eq(notification)); + } + + @Test + public void checkNotificationState() { + NotificationManager manager = mock(NotificationManager.class); + when(mContext.getSystemService(NOTIFICATION_SERVICE)).thenReturn(manager); + + /* Cancel notification. */ + DistributeUtils.cancelNotification(mContext); + verify(manager).cancel(eq(DistributeUtils.getNotificationId())); + } } diff --git a/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/DistributeWarnUnknownSourcesTest.java b/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/DistributeWarnUnknownSourcesTest.java index 2d5986cc2..9519670d9 100644 --- a/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/DistributeWarnUnknownSourcesTest.java +++ b/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/DistributeWarnUnknownSourcesTest.java @@ -5,6 +5,25 @@ package com.microsoft.appcenter.distribute; +import static com.microsoft.appcenter.distribute.DistributeConstants.PREFERENCE_KEY_DISTRIBUTION_GROUP_ID; +import static com.microsoft.appcenter.distribute.DistributeConstants.PREFERENCE_KEY_DOWNLOAD_STATE; +import static com.microsoft.appcenter.distribute.DistributeConstants.PREFERENCE_KEY_UPDATE_TOKEN; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyMap; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.doThrow; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.reset; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; +import static org.mockito.internal.verification.VerificationModeFactory.times; +import static org.powermock.api.mockito.PowerMockito.doAnswer; +import static org.powermock.api.mockito.PowerMockito.mockStatic; +import static org.powermock.api.mockito.PowerMockito.verifyStatic; +import static org.powermock.api.mockito.PowerMockito.whenNew; + import android.annotation.SuppressLint; import android.app.Activity; import android.app.AlertDialog; @@ -18,7 +37,6 @@ import com.microsoft.appcenter.http.HttpResponse; import com.microsoft.appcenter.http.ServiceCall; import com.microsoft.appcenter.http.ServiceCallback; -import com.microsoft.appcenter.test.TestUtils; import com.microsoft.appcenter.utils.AsyncTaskUtils; import com.microsoft.appcenter.utils.storage.SharedPreferencesManager; @@ -33,29 +51,11 @@ import org.mockito.invocation.InvocationOnMock; import org.mockito.stubbing.Answer; import org.powermock.core.classloader.annotations.PrepareForTest; +import org.powermock.reflect.Whitebox; import java.util.Arrays; import java.util.Collection; -import static com.microsoft.appcenter.distribute.DistributeConstants.PREFERENCE_KEY_DISTRIBUTION_GROUP_ID; -import static com.microsoft.appcenter.distribute.DistributeConstants.PREFERENCE_KEY_DOWNLOAD_STATE; -import static com.microsoft.appcenter.distribute.DistributeConstants.PREFERENCE_KEY_UPDATE_TOKEN; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.anyMap; -import static org.mockito.ArgumentMatchers.anyString; -import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.Mockito.doThrow; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.never; -import static org.mockito.Mockito.reset; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; -import static org.mockito.internal.verification.VerificationModeFactory.times; -import static org.powermock.api.mockito.PowerMockito.doAnswer; -import static org.powermock.api.mockito.PowerMockito.mockStatic; -import static org.powermock.api.mockito.PowerMockito.verifyStatic; -import static org.powermock.api.mockito.PowerMockito.whenNew; - @SuppressWarnings("CanBeFinal") @RunWith(Parameterized.class) public class DistributeWarnUnknownSourcesTest extends AbstractDistributeTest { @@ -147,7 +147,7 @@ public void clickSettingsNotActivity() throws Exception { @After public void tearDown() throws Exception { - TestUtils.setInternalState(Build.VERSION.class, "SDK_INT", 0); + Whitebox.setInternalState(Build.VERSION.class, "SDK_INT", 0); } @Test @@ -318,14 +318,14 @@ public void clickSettingsOnAndroidO() throws Exception { verify(mDialogBuilder).setPositiveButton(eq(R.string.appcenter_distribute_unknown_sources_dialog_settings), clickListener.capture()); /* Verify behaviour on old version. */ - TestUtils.setInternalState(Build.VERSION.class, "SDK_INT", BuildConfig.MIN_SDK_VERSION); + Whitebox.setInternalState(Build.VERSION.class, "SDK_INT", BuildConfig.MIN_SDK_VERSION); clickListener.getValue().onClick(mUnknownSourcesDialog, DialogInterface.BUTTON_POSITIVE); verify(mFirstActivity).startActivity(intentSecuritySettings); verify(mFirstActivity, never()).startActivity(intentManageUnknownAppSources); reset(mFirstActivity); /* Verify behaviour on Android O. */ - TestUtils.setInternalState(Build.VERSION.class, "SDK_INT", Build.VERSION_CODES.O); + Whitebox.setInternalState(Build.VERSION.class, "SDK_INT", Build.VERSION_CODES.O); clickListener.getValue().onClick(mUnknownSourcesDialog, DialogInterface.BUTTON_POSITIVE); verify(mFirstActivity, never()).startActivity(intentSecuritySettings); verify(mFirstActivity).startActivity(intentManageUnknownAppSources); diff --git a/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/ReleaseDownloadListenerTest.java b/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/ReleaseDownloadListenerTest.java index 13cbbd5ae..cb0297492 100644 --- a/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/ReleaseDownloadListenerTest.java +++ b/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/ReleaseDownloadListenerTest.java @@ -48,7 +48,7 @@ import org.powermock.core.classloader.annotations.PrepareForTest; import org.powermock.modules.junit4.rule.PowerMockRule; -@SuppressWarnings({"deprecation", "RedundantSuppression"}) +@SuppressWarnings({"deprecation", "RedundantSuppression", "Convert2Lambda", "ConstantConditions"}) @PrepareForTest({ AppCenter.class, AppCenterLog.class, @@ -352,4 +352,15 @@ public void onErrorNullMessage() throws Exception { /* Verify that completeWorkflow() is called on error. */ verify(mDistribute).completeWorkflow(mockReleaseDetails); } + + @Test + public void onComplete() throws Exception { + ReleaseDetails releaseDetails = mockReleaseDetails(true); + ReleaseDownloadListener releaseDownloadListener = new ReleaseDownloadListener(mContext, releaseDetails); + Uri uri = mock(Uri.class); + releaseDownloadListener.onComplete(uri); + + /* Verify that setInstalling() is called. */ + verify(mDistribute).setInstalling(eq(releaseDetails), eq(uri)); + } } diff --git a/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/UpdateInstallerTest.java b/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/UpdateInstallerTest.java index c3e7e34cd..1ce15721b 100644 --- a/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/UpdateInstallerTest.java +++ b/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/UpdateInstallerTest.java @@ -5,29 +5,52 @@ package com.microsoft.appcenter.distribute; +import static com.microsoft.appcenter.distribute.UpdateInstaller.RECOVERABLE_ERROR; import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoInteractions; +import static org.mockito.Mockito.verifyNoMoreInteractions; import static org.mockito.Mockito.when; import static org.powermock.api.mockito.PowerMockito.mock; +import static org.powermock.api.mockito.PowerMockito.mockStatic; +import static org.powermock.api.mockito.PowerMockito.whenNew; import android.content.Context; import android.net.Uri; +import android.os.Handler; +import android.os.HandlerThread; import com.microsoft.appcenter.distribute.install.ReleaseInstaller; +import com.microsoft.appcenter.distribute.install.intent.IntentReleaseInstaller; +import com.microsoft.appcenter.distribute.install.session.SessionReleaseInstaller; import org.junit.Before; import org.junit.Rule; import org.junit.Test; -import org.junit.runner.RunWith; import org.mockito.Mock; -import org.mockito.junit.MockitoJUnitRunner; +import org.powermock.core.classloader.annotations.PrepareForTest; import org.powermock.modules.junit4.rule.PowerMockRule; import java.util.LinkedList; -@RunWith(MockitoJUnitRunner.class) +@PrepareForTest({ + Distribute.class, + Handler.class, + HandlerThread.class, + IntentReleaseInstaller.class, + SessionReleaseInstaller.class, + UpdateInstaller.class +}) public class UpdateInstallerTest { + @Rule + public PowerMockRule mRule = new PowerMockRule(); + + @Mock + private Distribute mDistribute; + @Mock private ReleaseDetails mReleaseDetails; @@ -38,36 +61,132 @@ public class UpdateInstallerTest { @Before public void setUp() throws Exception { + mockStatic(Distribute.class); + when(Distribute.getInstance()).thenReturn(mDistribute); mInstaller = new UpdateInstaller(mReleaseDetails, new LinkedList() {{ add(mReleaseInstaller); }}); } @Test - public void install() { + public void installerPassThrough() { + + /* Start installation. */ Uri uri = mock(Uri.class); mInstaller.install(uri); - verify(mReleaseInstaller).install(eq(uri)); + + /* Clear installer. */ + mInstaller.clear(); + verify(mReleaseInstaller).clear(); } @Test public void resume() { + + /* No-op if not cancelled. */ + mInstaller.resume(); + verifyNoInteractions(mDistribute); + + /* Start installation. */ + Uri uri = mock(Uri.class); + mInstaller.install(uri); + verify(mReleaseInstaller).install(eq(uri)); + + /* Cancel installation. */ + mInstaller.onCancel(); + verify(mDistribute).completeWorkflow(eq(mReleaseDetails)); + + /* No-op if not mandatory. */ mInstaller.resume(); + verifyNoMoreInteractions(mDistribute); } @Test - public void clear() { - mInstaller.clear(); + public void resumeMandatoryUpdate() { + when(mReleaseDetails.isMandatoryUpdate()).thenReturn(true); + + /* No-op if not cancelled. */ + mInstaller.resume(); + verifyNoInteractions(mDistribute); + + /* Start installation. */ + Uri uri = mock(Uri.class); + mInstaller.install(uri); + verify(mReleaseInstaller).install(eq(uri)); + + /* Cancel installation. */ + mInstaller.onCancel(); + verify(mDistribute).showMandatoryDownloadReadyDialog(eq(mReleaseDetails)); + + /* Show dialog if mandatory update was cancelled in background. */ + mInstaller.resume(); + verify(mDistribute, times(2)).showMandatoryDownloadReadyDialog(eq(mReleaseDetails)); + + /* No-op after restarting installation. */ + mInstaller.install(uri); + mInstaller.resume(); + verifyNoMoreInteractions(mDistribute); } @Test - public void onError() { + public void nonRecoverableError() { + Uri uri = mock(Uri.class); + mInstaller.install(uri); + verify(mReleaseInstaller).install(eq(uri)); + + /* Complete workflow on non-recoverable error. */ mInstaller.onError("Some error"); + verify(mDistribute).completeWorkflow(eq(mReleaseDetails)); + verifyNoMoreInteractions(mReleaseInstaller); } @Test - public void onCancel() { - mInstaller.onCancel(); + public void fallbackOnRecoverableError() throws Exception { + + /* Mock Handler. */ + mockStatic(Handler.class); + Handler handler = mock(Handler.class); + whenNew(Handler.class).withAnyArguments().thenReturn(handler); + + /* Mock HandlerThread. */ + mockStatic(HandlerThread.class); + HandlerThread handlerThread = mock(HandlerThread.class); + whenNew(HandlerThread.class).withAnyArguments().thenReturn(handlerThread); + + /* Mock SessionReleaseInstaller. */ + mockStatic(SessionReleaseInstaller.class); + SessionReleaseInstaller sessionInstaller = mock(SessionReleaseInstaller.class); + whenNew(SessionReleaseInstaller.class).withAnyArguments().thenReturn(sessionInstaller); + + /* Mock IntentReleaseInstaller. */ + mockStatic(IntentReleaseInstaller.class); + IntentReleaseInstaller intentInstaller = mock(IntentReleaseInstaller.class); + whenNew(IntentReleaseInstaller.class).withAnyArguments().thenReturn(intentInstaller); + + /* Create installer instance. */ + mInstaller = new UpdateInstaller(mock(Context.class), mReleaseDetails); + + /* Start installation. */ + Uri uri = mock(Uri.class); + mInstaller.install(uri); + verify(sessionInstaller).install(eq(uri)); + + /* Fallback on recoverable error. */ + mInstaller.onError(RECOVERABLE_ERROR); + verify(mDistribute, never()).completeWorkflow(eq(mReleaseDetails)); + verify(sessionInstaller).clear(); + verify(intentInstaller).install(eq(uri)); + + /* Complete workflow without fallback. */ + mInstaller.onError(RECOVERABLE_ERROR); + verify(mDistribute).completeWorkflow(eq(mReleaseDetails)); + verify(intentInstaller).clear(); + + /* No-op on future calls without fallback installer. */ + mInstaller.install(uri); + mInstaller.clear(); + verifyNoMoreInteractions(sessionInstaller); + verifyNoMoreInteractions(intentInstaller); } } \ No newline at end of file diff --git a/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/UpdateReceiverTest.java b/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/UpdateReceiverTest.java index d1d8397b4..811967375 100644 --- a/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/UpdateReceiverTest.java +++ b/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/UpdateReceiverTest.java @@ -6,7 +6,10 @@ package com.microsoft.appcenter.distribute; import static android.content.Intent.ACTION_MY_PACKAGE_REPLACED; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.never; import static org.powermock.api.mockito.PowerMockito.mockStatic; import static org.powermock.api.mockito.PowerMockito.verifyStatic; import static org.powermock.api.mockito.PowerMockito.when; @@ -86,4 +89,16 @@ public void receiveMyPackageReplacedWithoutPackageInfo() { verifyStatic(DistributeUtils.class); DistributeUtils.postNotification(eq(mContext), eq("Title"), eq("Contoso ? (0)"), eq(mResumeIntent)); } + + @Test + public void receiveUnknownAction() { + when(mIntent.getAction()).thenReturn("UnknownAction"); + + /* Call method with wrong action. */ + mUpdateReceiver.onReceive(mContext, mIntent); + + /* No-op in this case. */ + verifyStatic(DistributeUtils.class, never()); + DistributeUtils.postNotification(any(), anyString(), anyString(), any()); + } } \ No newline at end of file diff --git a/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/download/ReleaseDownloaderFactoryTest.java b/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/download/ReleaseDownloaderFactoryTest.java index 95c8e802b..92ef4997a 100644 --- a/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/download/ReleaseDownloaderFactoryTest.java +++ b/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/download/ReleaseDownloaderFactoryTest.java @@ -13,13 +13,13 @@ import com.microsoft.appcenter.distribute.ReleaseDetails; import com.microsoft.appcenter.distribute.download.manager.DownloadManagerReleaseDownloader; -import com.microsoft.appcenter.test.TestUtils; import org.junit.After; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; import org.powermock.modules.junit4.PowerMockRunner; +import org.powermock.reflect.Whitebox; @RunWith(PowerMockRunner.class) public class ReleaseDownloaderFactoryTest { @@ -35,7 +35,7 @@ public class ReleaseDownloaderFactoryTest { @After public void tearDown() throws Exception { - TestUtils.setInternalState(Build.VERSION.class, "SDK_INT", 0); + Whitebox.setInternalState(Build.VERSION.class, "SDK_INT", 0); } @SuppressWarnings({"ObviousNullCheck", "InstantiationOfUtilityClass"}) @@ -48,7 +48,7 @@ public void generatedConstructor() { @Test public void createOnLollipop() throws Exception { - TestUtils.setInternalState(Build.VERSION.class, "SDK_INT", Build.VERSION_CODES.LOLLIPOP); + Whitebox.setInternalState(Build.VERSION.class, "SDK_INT", Build.VERSION_CODES.LOLLIPOP); ReleaseDownloader releaseDownloader = ReleaseDownloaderFactory.create(mockContext, mockReleaseDetails, mockReleaseDownloaderListener); assertTrue(releaseDownloader instanceof DownloadManagerReleaseDownloader); } diff --git a/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/download/manager/DownloadManagerReleaseDownloaderTest.java b/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/download/manager/DownloadManagerReleaseDownloaderTest.java index bb04679dc..3a57551ef 100644 --- a/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/download/manager/DownloadManagerReleaseDownloaderTest.java +++ b/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/download/manager/DownloadManagerReleaseDownloaderTest.java @@ -20,6 +20,7 @@ import static org.mockito.Mockito.never; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoMoreInteractions; import static org.mockito.Mockito.when; import static org.powermock.api.mockito.PowerMockito.mock; import static org.powermock.api.mockito.PowerMockito.mockStatic; @@ -380,13 +381,26 @@ public void doNotScheduleAnotherUpdateIfListenerDoNotWantsIt() { @Test public void doNotOnDownloadProgressAfterCancellation() { + Cursor cursor = mock(Cursor.class); + when(mListener.onProgress(anyLong(), anyLong())).thenReturn(true); + ArgumentCaptor runnableCaptor = ArgumentCaptor.forClass(Runnable.class); + when(mMainHandler.postAtTime(runnableCaptor.capture(), anyString(), anyLong())).thenReturn(true); /* Update download progress. */ + mReleaseDownloader.onDownloadProgress(cursor); + verify(mListener).onProgress(anyLong(), anyLong()); + + /* Cancel downloading. */ mReleaseDownloader.cancel(); - mReleaseDownloader.onDownloadProgress(mock(Cursor.class)); - /* Verify. */ - verify(mListener, never()).onProgress(anyLong(), anyLong()); + /* Delayed update does nothing after cancellation. */ + runnableCaptor.getValue().run(); + verifyStatic(AsyncTaskUtils.class, never()); + AsyncTaskUtils.execute(anyString(), isA(DownloadManagerUpdateTask.class), any()); + + /* Update download progress does nothing after cancellation. */ + mReleaseDownloader.onDownloadProgress(cursor); + verifyNoMoreInteractions(mListener); } @Test diff --git a/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/install/ReleaseInstallerActivityTest.java b/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/install/ReleaseInstallerActivityTest.java index 3b9ffb3bd..067dbf08f 100644 --- a/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/install/ReleaseInstallerActivityTest.java +++ b/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/install/ReleaseInstallerActivityTest.java @@ -5,6 +5,21 @@ package com.microsoft.appcenter.distribute.install; +import static android.app.Activity.RESULT_FIRST_USER; +import static android.app.Activity.RESULT_OK; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.doThrow; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoMoreInteractions; import static org.powermock.api.mockito.PowerMockito.mock; import static org.powermock.api.mockito.PowerMockito.when; @@ -12,9 +27,14 @@ import android.content.Intent; import android.os.Bundle; +import com.microsoft.appcenter.utils.async.AppCenterFuture; +import com.microsoft.appcenter.utils.async.DefaultAppCenterFuture; + +import org.junit.After; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; +import org.mockito.ArgumentCaptor; import org.mockito.Mock; import org.mockito.Spy; import org.powermock.modules.junit4.PowerMockRunner; @@ -33,19 +53,90 @@ public void setUp() { when(mActivity.getIntent()).thenReturn(mIntent); } + @After + public void tearDown() { + ReleaseInstallerActivity.sResultFuture = null; + } + @Test public void startActivityForResult() { Context context = mock(Context.class); - ReleaseInstallerActivity.startActivityForResult(context, mIntent); + AppCenterFuture confirmFuture = + ReleaseInstallerActivity.startActivityForResult(context, mIntent); + assertNotNull(confirmFuture); + assertFalse(confirmFuture.isDone()); + assertEquals(confirmFuture, ReleaseInstallerActivity.sResultFuture); + + /* Verify that we start our activity. */ + verify(context).startActivity(any()); + + /* Second call does nothing. */ + assertNull(ReleaseInstallerActivity.startActivityForResult(context, mIntent)); + assertEquals(confirmFuture, ReleaseInstallerActivity.sResultFuture); + verifyNoMoreInteractions(context); } @Test public void onCreate() { + Intent extraIntent = mock(Intent.class); + when(mIntent.getParcelableExtra(Intent.EXTRA_INTENT)).thenReturn(extraIntent); + + /* Simulate onCreate lifecycle event. */ + mActivity.onCreate(mock(Bundle.class)); + + /* Verify that extra intent has been started. */ + verify(mActivity).startActivityForResult(eq(extraIntent), anyInt()); + verify(mActivity, never()).finish(); + } + + @Test + public void onCreateWithoutIntent() { + + /* Simulate onCreate lifecycle event. */ mActivity.onCreate(mock(Bundle.class)); + + /* Immediately finish incorrectly initialized activity. */ + verify(mActivity).finish(); + } + + @Test + public void onCreateThrowsSecurityException() { + Intent extraIntent = mock(Intent.class); + when(mIntent.getParcelableExtra(Intent.EXTRA_INTENT)).thenReturn(extraIntent); + doThrow(new SecurityException()).when(mActivity).startActivityForResult(eq(extraIntent), anyInt()); + + /* Initialize result future. */ + AppCenterFuture confirmFuture = + ReleaseInstallerActivity.startActivityForResult(mock(Context.class), mIntent); + assertNotNull(confirmFuture); + + /* Simulate onCreate lifecycle event. */ + mActivity.onCreate(mock(Bundle.class)); + + /* Verify that future is completed and activity finished. */ + assertTrue(confirmFuture.isDone()); + assertEquals(RESULT_FIRST_USER, confirmFuture.get().code); + verify(mActivity).finish(); } @Test public void onActivityResult() { - mActivity.onActivityResult(0, 0, null); + + /* Simulate onActivityResult event without initialized future. */ + mActivity.onActivityResult(0, RESULT_OK, null); + verify(mActivity).finish(); + + /* Initialize result future. */ + AppCenterFuture confirmFuture = + ReleaseInstallerActivity.startActivityForResult(mock(Context.class), mIntent); + assertNotNull(confirmFuture); + + /* Simulate onActivityResult event. */ + mActivity.onActivityResult(0, RESULT_OK, null); + + /* Verify that future is completed and activity finished. */ + assertTrue(confirmFuture.isDone()); + assertEquals(RESULT_OK, confirmFuture.get().code); + verify(mActivity, times(2)).finish(); } } \ No newline at end of file diff --git a/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/install/session/InstallStatusReceiverTest.java b/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/install/session/InstallStatusReceiverTest.java index fcfffe1b5..cf461e8ca 100644 --- a/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/install/session/InstallStatusReceiverTest.java +++ b/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/install/session/InstallStatusReceiverTest.java @@ -18,10 +18,12 @@ import static org.powermock.api.mockito.PowerMockito.verifyNoMoreInteractions; import static org.powermock.api.mockito.PowerMockito.verifyStatic; import static org.powermock.api.mockito.PowerMockito.when; +import static org.powermock.api.mockito.PowerMockito.whenNew; import android.app.PendingIntent; import android.content.Context; import android.content.Intent; +import android.content.IntentFilter; import android.content.IntentSender; import android.content.pm.PackageInstaller; import android.os.Build; @@ -31,23 +33,30 @@ import com.microsoft.appcenter.utils.DeviceInfoHelper; import org.junit.Before; +import org.junit.Rule; import org.junit.Test; -import org.junit.runner.RunWith; import org.mockito.Mock; import org.powermock.core.classloader.annotations.PrepareForTest; -import org.powermock.modules.junit4.PowerMockRunner; +import org.powermock.modules.junit4.rule.PowerMockRule; import org.powermock.reflect.Whitebox; +import java.util.HashSet; +import java.util.Set; + @PrepareForTest({ AppCenterLog.class, DeviceInfoHelper.class, + InstallStatusReceiver.class, + IntentFilter.class, PendingIntent.class }) -@RunWith(PowerMockRunner.class) public class InstallStatusReceiverTest { private static final int SESSION_ID = 42; + @Rule + public PowerMockRule mRule = new PowerMockRule(); + @Mock private Context mContext; @@ -57,20 +66,29 @@ public class InstallStatusReceiverTest { @Mock private Bundle mExtra; + private final Set mExtraKeySet = new HashSet<>(); + @Mock private SessionReleaseInstaller mInstaller; private InstallStatusReceiver mInstallStatusReceiver; + private void addExtraInt(String key, int value) { + mExtraKeySet.add(key); + when(mExtra.get(eq(key))).thenReturn(value); + when(mExtra.getInt(eq(key))).thenReturn(value); + } + private void installStatus(int status) { when(mIntent.getAction()).thenReturn(INSTALL_STATUS_ACTION); - when(mExtra.getInt(eq(PackageInstaller.EXTRA_SESSION_ID))).thenReturn(SESSION_ID); - when(mExtra.getInt(eq(PackageInstaller.EXTRA_STATUS))).thenReturn(status); + addExtraInt(PackageInstaller.EXTRA_SESSION_ID, SESSION_ID); + addExtraInt(PackageInstaller.EXTRA_STATUS, status); } @Before public void setUp() { when(mIntent.getExtras()).thenReturn(mExtra); + when(mExtra.keySet()).thenReturn(mExtraKeySet); /* Mock static classes. */ mockStatic(AppCenterLog.class); @@ -187,6 +205,17 @@ public void onReceiverWithUnknownAction() { verifyNoInteractions(mInstaller); } + @Test + public void installerReceiverFilter() throws Exception { + mockStatic(IntentFilter.class); + IntentFilter filter = mock(IntentFilter.class); + whenNew(IntentFilter.class).withAnyArguments().thenReturn(filter); + + /* Verify that we add install status to filter. */ + assertEquals(filter, InstallStatusReceiver.getInstallerReceiverFilter()); + verify(filter).addAction(INSTALL_STATUS_ACTION); + } + @Test public void createIntentSenderOnAndroidS() { diff --git a/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/install/session/SessionReleaseInstallerTest.java b/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/install/session/SessionReleaseInstallerTest.java index 6ea33f414..4a98e311b 100644 --- a/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/install/session/SessionReleaseInstallerTest.java +++ b/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/install/session/SessionReleaseInstallerTest.java @@ -5,6 +5,7 @@ package com.microsoft.appcenter.distribute.install.session; +import static android.app.Activity.RESULT_OK; import static org.junit.Assert.assertEquals; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyInt; @@ -14,8 +15,10 @@ import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verifyNoInteractions; +import static org.mockito.Mockito.verifyNoMoreInteractions; import static org.powermock.api.mockito.PowerMockito.mock; import static org.powermock.api.mockito.PowerMockito.mockStatic; +import static org.powermock.api.mockito.PowerMockito.verifyStatic; import static org.powermock.api.mockito.PowerMockito.when; import static org.powermock.api.mockito.PowerMockito.whenNew; @@ -30,10 +33,15 @@ import android.os.ParcelFileDescriptor; import com.microsoft.appcenter.distribute.install.ReleaseInstaller; +import com.microsoft.appcenter.distribute.install.ReleaseInstallerActivity; +import com.microsoft.appcenter.utils.async.AppCenterConsumer; +import com.microsoft.appcenter.utils.async.AppCenterFuture; import org.junit.Before; import org.junit.Rule; import org.junit.Test; +import org.mockito.ArgumentCaptor; +import org.mockito.Captor; import org.mockito.Mock; import org.mockito.invocation.InvocationOnMock; import org.mockito.stubbing.Answer; @@ -43,10 +51,13 @@ import java.io.FileInputStream; import java.io.IOException; import java.io.OutputStream; +import java.util.ArrayList; +import java.util.List; @PrepareForTest({ InstallStatusReceiver.class, PackageInstallerListener.class, + ReleaseInstallerActivity.class, SessionReleaseInstaller.class }) public class SessionReleaseInstallerTest { @@ -83,6 +94,12 @@ public class SessionReleaseInstallerTest { @Mock private PackageInstaller.Session mSession; + @Mock + private AppCenterFuture mResultFuture; + + @Captor + private ArgumentCaptor> mConsumerArgumentCaptor; + private SessionReleaseInstaller mInstaller; @Before @@ -99,19 +116,29 @@ public void setUp() throws Exception { ParcelFileDescriptor fileDescriptor = mock(ParcelFileDescriptor.class); when(contentResolver.openFileDescriptor(any(Uri.class), eq("r"))).thenReturn(fileDescriptor); whenNew(FileInputStream.class).withAnyArguments().thenReturn(mInputStream); + when(mInputStream.read(any())).thenReturn(10).thenReturn(-1); /* Mock session. */ when(mPackageInstaller.openSession(anyInt())).thenReturn(mSession); + when(mPackageInstaller.createSession(any(PackageInstaller.SessionParams.class))).thenReturn(SESSION_ID); when(mSession.openWrite(anyString(), anyLong(), anyLong())).thenReturn(mOutputStream); + /* Mock install status receiver. */ mockStatic(InstallStatusReceiver.class); whenNew(InstallStatusReceiver.class).withAnyArguments().thenReturn(mInstallStatusReceiver); when(InstallStatusReceiver.getInstallStatusIntentSender(any(Context.class), anyInt())) .thenReturn(mock(IntentSender.class)); + /* Mock package installer listener. */ mockStatic(PackageInstallerListener.class); whenNew(PackageInstallerListener.class).withAnyArguments().thenReturn(mPackageInstallerListener); + /* Mock activity. */ + mockStatic(ReleaseInstallerActivity.class); + when(ReleaseInstallerActivity.startActivityForResult(eq(mContext), any(Intent.class))) + .thenReturn(mResultFuture); + + /* Run posted runnable immediately. */ when(mHandler.post(any(Runnable.class))).then(new Answer() { @Override @@ -121,17 +148,16 @@ public Boolean answer(InvocationOnMock invocation) { } }); + /* Create SessionReleaseInstaller instance. */ mInstaller = new SessionReleaseInstaller(mContext, mHandler, mListener); } @Test - public void install() throws IOException { - /* Mock data. */ - when(mInputStream.read(any())).thenReturn(10).thenReturn(-1); - + public void installSuccess() throws IOException { Uri uri = mock(Uri.class); mInstaller.install(uri); + /* Verify that all required things called. */ verify(mHandler).post(any(Runnable.class)); verify(mContext).registerReceiver(eq(mInstallStatusReceiver), any()); verify(mPackageInstaller).registerSessionCallback(eq(mPackageInstallerListener)); @@ -141,6 +167,14 @@ public void install() throws IOException { verify(mSession, never()).abandon(); verify(mSession).close(); verifyNoInteractions(mListener); + + /* Try to star install second time. It's valid case if something goes wrong with previous try. */ + mInstaller.install(uri); + + /* Cancel previous session and re-use callbacks. */ + verify(mContext).registerReceiver(eq(mInstallStatusReceiver), any()); + verify(mPackageInstaller).registerSessionCallback(eq(mPackageInstallerListener)); + verify(mPackageInstaller).abandonSession(eq(SESSION_ID)); } @Test @@ -174,9 +208,54 @@ public void throwIOExceptionWhenTryToCreateSession() throws IOException { verify(mPackageInstaller, never()).openSession(anyInt()); } + @Test + public void tooManyActiveSessions() throws Exception { + when(mPackageInstaller.openSession(anyInt())) + .thenThrow(new IllegalStateException()) + .thenReturn(mSession); + + /* Mock some active sessions. */ + List mySessions = new ArrayList<>(); + PackageInstaller.SessionInfo session1 = mock(PackageInstaller.SessionInfo.class); + when(session1.getSessionId()).thenReturn(1); + mySessions.add(session1); + PackageInstaller.SessionInfo session2 = mock(PackageInstaller.SessionInfo.class); + when(session2.getSessionId()).thenReturn(2); + mySessions.add(session2); + when(mPackageInstaller.getMySessions()).thenReturn(mySessions); + + /* Call install method. */ + Uri uri = mock(Uri.class); + mInstaller.install(uri); + + /* Abandon previous sessions. */ + verify(mPackageInstaller).abandonSession(eq(1)); + verify(mPackageInstaller).abandonSession(eq(2)); + } + @Test public void clear() { + + /* Clear before install does nothing. */ + mInstaller.clear(); + verifyNoInteractions(mContext); + verifyNoInteractions(mPackageInstaller); + + /* Call install method. */ + Uri uri = mock(Uri.class); + mInstaller.install(uri); + + /* Registering callbacks. */ + verify(mContext).registerReceiver(eq(mInstallStatusReceiver), any()); + verify(mPackageInstaller).registerSessionCallback(eq(mPackageInstallerListener)); + + /* Clear after start should clear registered callbacks and abandon the session. */ mInstaller.clear(); + + /* Verify. */ + verify(mContext).unregisterReceiver(eq(mInstallStatusReceiver)); + verify(mPackageInstaller).unregisterSessionCallback(eq(mPackageInstallerListener)); + verify(mPackageInstaller).abandonSession(eq(SESSION_ID)); } @Test @@ -185,22 +264,86 @@ public void testToString() { } @Test - public void onInstallProgress() { + public void ignoreAnotherSessionEvents() { + final int ANOTHER_SESSION_ID = 7; + mInstaller.onInstallProgress(ANOTHER_SESSION_ID); + mInstaller.onInstallConfirmation(ANOTHER_SESSION_ID, mock(Intent.class)); + mInstaller.onInstallError(ANOTHER_SESSION_ID, "Some error"); + mInstaller.onInstallCancel(ANOTHER_SESSION_ID); + + /* No-op before starting install or from another session. */ + verifyNoInteractions(mListener); + } + + @Test + public void installConfirmation() { + Uri uri = mock(Uri.class); + mInstaller.install(uri); + + /* Ask for user confirmation. */ + Intent confirmIntent = mock(Intent.class); + mInstaller.onInstallConfirmation(SESSION_ID, confirmIntent); + verify(mResultFuture).thenAccept(mConsumerArgumentCaptor.capture()); + + /* Pass result to captured callback. */ + ReleaseInstallerActivity.Result result = new ReleaseInstallerActivity.Result(RESULT_OK, null); + mConsumerArgumentCaptor.getValue().accept(result); + + /* Capture delayed action. */ + ArgumentCaptor runnableCaptor = ArgumentCaptor.forClass(Runnable.class); + verify(mHandler).postDelayed(runnableCaptor.capture(), anyLong()); + + /* Run without progress cancels the process. */ + runnableCaptor.getValue().run(); + verify(mListener).onCancel(); + + /* Run with progress event does nothing. */ mInstaller.onInstallProgress(SESSION_ID); + runnableCaptor.getValue().run(); + verifyNoMoreInteractions(mListener); } @Test - public void onInstallConfirmation() { - mInstaller.onInstallConfirmation(SESSION_ID, mock(Intent.class)); + public void cannotStartConfirmationActivity() { + when(ReleaseInstallerActivity.startActivityForResult(eq(mContext), any(Intent.class))) + .thenReturn(null); + Uri uri = mock(Uri.class); + mInstaller.install(uri); + + /* Ask for user confirmation. */ + Intent confirmIntent = mock(Intent.class); + mInstaller.onInstallConfirmation(SESSION_ID, confirmIntent); + + /* Verify. */ + verifyStatic(ReleaseInstallerActivity.class); + ReleaseInstallerActivity.startActivityForResult(eq(mContext), eq(confirmIntent)); + + /* + * It should not be a case, it's expected to have no more than one installer in time. + * So, don't inform listener to avoid break state of the service. + */ + verifyNoInteractions(mListener); } @Test - public void onInstallError() { + public void installError() { + Uri uri = mock(Uri.class); + mInstaller.install(uri); mInstaller.onInstallError(SESSION_ID, "Some error"); + + /* Verify error callback. */ + verify(mListener).onError(anyString()); + verifyNoMoreInteractions(mListener); } @Test - public void onInstallCancel() { + public void installCancel() { + Uri uri = mock(Uri.class); + mInstaller.install(uri); mInstaller.onInstallCancel(SESSION_ID); + + /* Verify cancel callback. */ + verify(mListener).onCancel(); + verifyNoMoreInteractions(mListener); } -} \ No newline at end of file +} From b0d7bc875edcda45e1cf69d8b7678e3aeb99c962 Mon Sep 17 00:00:00 2001 From: Ivan Matkov Date: Mon, 6 Jun 2022 18:54:56 +0200 Subject: [PATCH 61/72] Update changelog and some javadocs --- CHANGELOG.md | 2 ++ .../com/microsoft/appcenter/distribute/Distribute.java | 10 ++++++---- .../install/intent/IntentReleaseInstaller.java | 3 +++ .../install/session/SessionReleaseInstaller.java | 2 +- 4 files changed, 12 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index cb317e6be..31484671b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,9 +5,11 @@ ### App Center Distribute * **[Improvement]** Remove optional `SYSTEM_ALERT_WINDOW` permission that was required to automatically restart the app after installing the update. +* **[Improvement]** Add fallback to the old [ACTION_INSTALL_PACKAGE](https://developer.android.com/reference/android/content/Intent#ACTION_INSTALL_PACKAGE) installation method if the update cannot be installed by `PackageInstaller` API (e.g. when MIUI optimizations block installation). * **[Fix]** Fix possible crash on resume event before initialization. * **[Fix]** Fix clicks on the download completion notification. * **[Fix]** Fix ANR on installing large packages. +* **[Fix]** Fix cancellation handling of confirmation dialog for mandatory updates. * **[Fix]** Fix strict mode issues. ___ diff --git a/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/Distribute.java b/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/Distribute.java index 817d1b42b..3a6879047 100644 --- a/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/Distribute.java +++ b/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/Distribute.java @@ -41,12 +41,12 @@ import android.app.Activity; import android.app.AlertDialog; import android.app.Dialog; -import android.app.DownloadManager; import android.content.ActivityNotFoundException; import android.content.Context; import android.content.DialogInterface; import android.content.Intent; import android.content.pm.PackageInfo; +import android.content.pm.PackageInstaller; import android.content.pm.PackageManager; import android.net.Uri; import android.os.Build; @@ -225,7 +225,7 @@ public class Distribute extends AbstractAppCenterService { private WeakReference mLastActivityWithDialog = new WeakReference<>(null); /** - * Release downloader. It can use {@link DownloadManager} or direct HTTPS downloading. + * Release downloader. It will use {@link android.app.DownloadManager}. */ private ReleaseDownloader mReleaseDownloader; @@ -234,6 +234,9 @@ public class Distribute extends AbstractAppCenterService { */ private ReleaseDownloadListener mReleaseDownloaderListener; + /** + * Release installer. It can use {@link PackageInstaller.Session} or {@link Intent#ACTION_INSTALL_PACKAGE}. + */ private UpdateInstaller mReleaseInstaller; /** @@ -724,7 +727,7 @@ private synchronized void resumeDistributeWorkflow() { return; } - /* Do nothing during installing a new release. */ + /* The installer takes care of resume event during installing a new release. */ if (mReleaseInstaller != null) { mReleaseInstaller.resume(); return; @@ -1626,7 +1629,6 @@ private synchronized void goToUnknownAppsSettings(ReleaseDetails releaseDetails) * And a no U.I. activity of our own must finish in onCreate, * so it cannot receive a result. */ - // FIXME: StrictMode policy violation; mForegroundActivity.startActivity(intent); } catch (ActivityNotFoundException e) { diff --git a/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/install/intent/IntentReleaseInstaller.java b/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/install/intent/IntentReleaseInstaller.java index 5dd2ec094..da180e130 100644 --- a/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/install/intent/IntentReleaseInstaller.java +++ b/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/install/intent/IntentReleaseInstaller.java @@ -21,6 +21,9 @@ import com.microsoft.appcenter.utils.async.AppCenterConsumer; import com.microsoft.appcenter.utils.async.AppCenterFuture; +/** + * Installer based on {@link Intent#ACTION_INSTALL_PACKAGE}. + */ public class IntentReleaseInstaller extends AbstractReleaseInstaller { public IntentReleaseInstaller(Context context, Handler installerHandler, Listener listener) { diff --git a/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/install/session/SessionReleaseInstaller.java b/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/install/session/SessionReleaseInstaller.java index 1451a81fe..e1634c05a 100644 --- a/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/install/session/SessionReleaseInstaller.java +++ b/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/install/session/SessionReleaseInstaller.java @@ -33,7 +33,7 @@ import java.io.OutputStream; /** - * Installer based on PackageInstaller.Session API. + * Installer based on {@link PackageInstaller.Session} API. */ public class SessionReleaseInstaller extends AbstractReleaseInstaller { From 6a07c657c4ed9dec3a75ba571060560b5a1e20f9 Mon Sep 17 00:00:00 2001 From: Ivan Matkov Date: Tue, 7 Jun 2022 12:34:30 +0200 Subject: [PATCH 62/72] Restore install error toast --- .../appcenter/distribute/Distribute.java | 19 ++++++++++++++++-- .../distribute/ReleaseDownloadListener.java | 17 +++++++--------- .../appcenter/distribute/UpdateInstaller.java | 1 + .../main/res/values/appcenter_distribute.xml | 1 + .../appcenter/distribute/DistributeTest.java | 20 +++++++++++++++++++ 5 files changed, 46 insertions(+), 12 deletions(-) diff --git a/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/Distribute.java b/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/Distribute.java index 3a6879047..d8b214a21 100644 --- a/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/Distribute.java +++ b/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/Distribute.java @@ -711,7 +711,7 @@ private synchronized void resumeDistributeWorkflow() { return; } - /* Don't go any further it this is a debug app. */ + /* Don't go any further if this is a debug app. */ if ((mContext.getApplicationInfo().flags & FLAG_DEBUGGABLE) == FLAG_DEBUGGABLE && !mEnabledForDebuggableBuild) { AppCenterLog.info(LOG_TAG, "Not checking for in-app updates in debuggable build."); mWorkflowCompleted = true; @@ -1696,7 +1696,22 @@ synchronized void enqueueDownloadOrShowUnknownSourcesDialog(final ReleaseDetails * that will likely never happen but we guard for it. */ private void showDisabledToast() { - Toast.makeText(mContext, R.string.appcenter_distribute_dialog_actioned_on_disabled_toast, Toast.LENGTH_SHORT).show(); + showToast(R.string.appcenter_distribute_dialog_actioned_on_disabled_toast); + } + + /** + * Show toast using foreground activity context if possible. + * + * @param messageId the resource id of the string resource to use. + */ + void showToast(int messageId) { + + /* + * Use Activity context if possible to avoid + * StrictMode policy violation: android.os.strictmode.IncorrectContextUseViolation + */ + Context context = mForegroundActivity != null ? mForegroundActivity : mContext; + Toast.makeText(context, messageId, Toast.LENGTH_SHORT).show(); } /** diff --git a/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/ReleaseDownloadListener.java b/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/ReleaseDownloadListener.java index 1fdffa3e4..60a74e08d 100644 --- a/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/ReleaseDownloadListener.java +++ b/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/ReleaseDownloadListener.java @@ -5,17 +5,20 @@ package com.microsoft.appcenter.distribute; +import static com.microsoft.appcenter.distribute.DistributeConstants.HANDLER_TOKEN_CHECK_PROGRESS; +import static com.microsoft.appcenter.distribute.DistributeConstants.KIBIBYTE_IN_BYTES; +import static com.microsoft.appcenter.distribute.DistributeConstants.LOG_TAG; +import static com.microsoft.appcenter.distribute.DistributeConstants.MEBIBYTE_IN_BYTES; + import android.app.Activity; import android.content.Context; +import android.net.Uri; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.annotation.UiThread; import androidx.annotation.WorkerThread; -import android.net.Uri; -import android.widget.Toast; - import com.microsoft.appcenter.distribute.download.ReleaseDownloader; import com.microsoft.appcenter.utils.AppCenterLog; import com.microsoft.appcenter.utils.HandlerUtils; @@ -23,11 +26,6 @@ import java.text.NumberFormat; import java.util.Locale; -import static com.microsoft.appcenter.distribute.DistributeConstants.HANDLER_TOKEN_CHECK_PROGRESS; -import static com.microsoft.appcenter.distribute.DistributeConstants.KIBIBYTE_IN_BYTES; -import static com.microsoft.appcenter.distribute.DistributeConstants.LOG_TAG; -import static com.microsoft.appcenter.distribute.DistributeConstants.MEBIBYTE_IN_BYTES; - /** * Listener for downloading progress. */ @@ -112,8 +110,7 @@ public void onError(@Nullable String errorMessage) { @Override public void run() { - // FIXME: StrictMode policy violation: android.os.strictmode.IncorrectContextUseViolation - Toast.makeText(mContext, R.string.appcenter_distribute_downloading_error, Toast.LENGTH_SHORT).show(); + Distribute.getInstance().showToast(R.string.appcenter_distribute_downloading_error); Distribute.getInstance().completeWorkflow(mReleaseDetails); } }); diff --git a/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/UpdateInstaller.java b/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/UpdateInstaller.java index 383cb07ca..e4e623e0e 100644 --- a/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/UpdateInstaller.java +++ b/sdk/appcenter-distribute/src/main/java/com/microsoft/appcenter/distribute/UpdateInstaller.java @@ -122,6 +122,7 @@ public synchronized void onError(String errorMessage) { return; } } + Distribute.getInstance().showToast(R.string.appcenter_distribute_install_error); Distribute.getInstance().completeWorkflow(mReleaseDetails); } diff --git a/sdk/appcenter-distribute/src/main/res/values/appcenter_distribute.xml b/sdk/appcenter-distribute/src/main/res/values/appcenter_distribute.xml index 7a65386c8..f97655481 100644 --- a/sdk/appcenter-distribute/src/main/res/values/appcenter_distribute.xml +++ b/sdk/appcenter-distribute/src/main/res/values/appcenter_distribute.xml @@ -24,6 +24,7 @@ %1$s %2$s (%3$d) has been downloaded and is ready to be installed. App update installed %1$s %2$s (%3$d) has been installed. + Failed to install app update Install App updates In-app updates disabled diff --git a/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/DistributeTest.java b/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/DistributeTest.java index a7232a3ed..b61c17e18 100644 --- a/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/DistributeTest.java +++ b/sdk/appcenter-distribute/src/test/java/com/microsoft/appcenter/distribute/DistributeTest.java @@ -45,6 +45,7 @@ import android.content.Intent; import android.content.pm.PackageInfo; import android.net.Uri; +import android.widget.Toast; import com.microsoft.appcenter.DependencyConfiguration; import com.microsoft.appcenter.distribute.download.ReleaseDownloader; @@ -752,4 +753,23 @@ public void checkUpdateReleaseAfterInterruptDownloading() { /* Verify that check release for update was called again. */ verify(mHttpClient, times(2)).callAsync(anyString(), anyString(), eq(Collections.emptyMap()), any(HttpClient.CallTemplate.class), httpCallback.capture()); } + + @Test + public void showToast() { + int messageId = R.string.appcenter_distribute_dialog_actioned_on_disabled_toast; + start(); + + /* Without activity. */ + Distribute.getInstance().showToast(messageId); + verify(mToast).show(); + verifyStatic(Toast.class); + Toast.makeText(eq(mContext), eq(messageId), anyInt()); + + /* With activity. */ + Distribute.getInstance().onActivityResumed(mActivity); + Distribute.getInstance().showToast(messageId); + verify(mToast, times(2)).show(); + verifyStatic(Toast.class); + Toast.makeText(eq(mActivity), eq(messageId), anyInt()); + } } From ecfaf016dc683efa74fbe9ea7359d86acb243c03 Mon Sep 17 00:00:00 2001 From: Ivan Matkov Date: Wed, 8 Jun 2022 14:54:10 +0200 Subject: [PATCH 63/72] Use protected storage context if needed --- .../com/microsoft/appcenter/AppCenter.java | 24 ++++++++++--------- .../appcenter/ApplicationContextUtils.java | 23 ++++++++++++++++++ 2 files changed, 36 insertions(+), 11 deletions(-) create mode 100644 sdk/appcenter/src/main/java/com/microsoft/appcenter/ApplicationContextUtils.java diff --git a/sdk/appcenter/src/main/java/com/microsoft/appcenter/AppCenter.java b/sdk/appcenter/src/main/java/com/microsoft/appcenter/AppCenter.java index 82ec58477..388917e59 100644 --- a/sdk/appcenter/src/main/java/com/microsoft/appcenter/AppCenter.java +++ b/sdk/appcenter/src/main/java/com/microsoft/appcenter/AppCenter.java @@ -138,6 +138,7 @@ public class AppCenter { * Application context. */ private Application mApplication; + private Context mContext; /** * Application lifecycle listener. @@ -712,6 +713,7 @@ public void run() { /* Store state. */ mApplication = application; + mContext = ApplicationContextUtils.getApplicationContext(application); /* Start looper. */ mHandlerThread = new HandlerThread("AppCenter.Looper"); @@ -825,11 +827,11 @@ public void run() { private void finishConfiguration(boolean configureFromApp) { /* Load some global constants. */ - Constants.loadFromContext(mApplication); + Constants.loadFromContext(mContext); /* If parameters are valid, init context related resources. */ - FileManager.initialize(mApplication); - SharedPreferencesManager.initialize(mApplication); + FileManager.initialize(mContext); + SharedPreferencesManager.initialize(mContext); /* Set network requests allowed. */ if (mAllowedNetworkRequests != null) { @@ -845,13 +847,13 @@ private void finishConfiguration(boolean configureFromApp) { /* Instantiate HTTP client if it doesn't exist as a dependency. */ HttpClient httpClient = DependencyConfiguration.getHttpClient(); if (httpClient == null) { - httpClient = createHttpClient(mApplication); + httpClient = createHttpClient(mContext); } /* Init channel. */ mLogSerializer = new DefaultLogSerializer(); mLogSerializer.addLogFactory(StartServiceLog.TYPE, new StartServiceLogFactory()); - mChannel = new DefaultChannel(mApplication, mAppSecret, mLogSerializer, httpClient, mHandler); + mChannel = new DefaultChannel(mContext, mAppSecret, mLogSerializer, httpClient, mHandler); /* Complete set maximum storage size future if starting from app. */ if (configureFromApp) { @@ -877,7 +879,7 @@ private void finishConfiguration(boolean configureFromApp) { /* Disable listening network if we start while being disabled. */ if (!enabled) { - NetworkStateHelper.getSharedInstance(mApplication).close(); + NetworkStateHelper.getSharedInstance(mContext).close(); } /* Init uncaught exception handler. */ @@ -902,7 +904,7 @@ private final synchronized void startServices(final boolean startFromApp, Class< AppCenterLog.error(LOG_TAG, "Cannot start services, services array is null. Failed to start services."); return; } - if (mApplication == null) { + if (!isInstanceConfigured()) { StringBuilder serviceNames = new StringBuilder(); for (Class service : services) { serviceNames.append("\t").append(service.getName()).append("\n"); @@ -1011,10 +1013,10 @@ private void finishStartServices(Iterable updatedServices, Ite service.setInstanceEnabled(false); } if (startFromApp) { - service.onStarted(mApplication, mChannel, mAppSecret, mTransmissionTargetToken, true); + service.onStarted(mContext, mChannel, mAppSecret, mTransmissionTargetToken, true); AppCenterLog.info(LOG_TAG, service.getClass().getSimpleName() + " service started from application."); } else { - service.onStarted(mApplication, mChannel, null, null, false); + service.onStarted(mContext, mChannel, null, null, false); AppCenterLog.info(LOG_TAG, service.getClass().getSimpleName() + " service started from library."); } } @@ -1118,10 +1120,10 @@ private void setInstanceEnabled(boolean enabled) { /* Update uncaught exception subscription. */ if (switchToEnabled) { mUncaughtExceptionHandler.register(); - NetworkStateHelper.getSharedInstance(mApplication).reopen(); + NetworkStateHelper.getSharedInstance(mContext).reopen(); } else if (switchToDisabled) { mUncaughtExceptionHandler.unregister(); - NetworkStateHelper.getSharedInstance(mApplication).close(); + NetworkStateHelper.getSharedInstance(mContext).close(); } /* Update state now if true, services are checking this. */ diff --git a/sdk/appcenter/src/main/java/com/microsoft/appcenter/ApplicationContextUtils.java b/sdk/appcenter/src/main/java/com/microsoft/appcenter/ApplicationContextUtils.java new file mode 100644 index 000000000..0e5da4fab --- /dev/null +++ b/sdk/appcenter/src/main/java/com/microsoft/appcenter/ApplicationContextUtils.java @@ -0,0 +1,23 @@ +/* + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. + */ + +package com.microsoft.appcenter; + +import android.app.Application; +import android.content.Context; +import android.os.Build; +import android.os.UserManager; + +class ApplicationContextUtils { + static Context getApplicationContext(Application application) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { + UserManager userManager = (UserManager) application.getSystemService(Context.USER_SERVICE); + if (!userManager.isUserUnlocked()) { + return application.createDeviceProtectedStorageContext(); + } + } + return application.getApplicationContext(); + } +} From 2223df564844802c3ca7a4a30551a8744a647da9 Mon Sep 17 00:00:00 2001 From: dorofeevs Date: Fri, 10 Jun 2022 16:48:13 +0400 Subject: [PATCH 64/72] Add unit tests --- .../ApplicationContextUtilsTest.java | 72 +++++++++++++++++++ 1 file changed, 72 insertions(+) create mode 100644 sdk/appcenter/src/test/java/com/microsoft/appcenter/ApplicationContextUtilsTest.java diff --git a/sdk/appcenter/src/test/java/com/microsoft/appcenter/ApplicationContextUtilsTest.java b/sdk/appcenter/src/test/java/com/microsoft/appcenter/ApplicationContextUtilsTest.java new file mode 100644 index 000000000..115c95531 --- /dev/null +++ b/sdk/appcenter/src/test/java/com/microsoft/appcenter/ApplicationContextUtilsTest.java @@ -0,0 +1,72 @@ +/* + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. + */ + +package com.microsoft.appcenter; + +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import android.app.Application; +import android.content.Context; +import android.os.Build; +import android.os.UserManager; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.powermock.modules.junit4.PowerMockRunner; +import org.powermock.reflect.Whitebox; + +@RunWith(PowerMockRunner.class) +public class ApplicationContextUtilsTest { + + @Mock + private Application mApplication; + + @Mock + private UserManager mUserManager; + + @SuppressWarnings("InstantiationOfUtilityClass") + @Test + public void ConstructorCoverage() { + new ApplicationContextUtils(); + } + + @Test + public void getContextWhenVersionIsHigherOrEqualThenNAndUnlockedIsFalse() { + Whitebox.setInternalState(Build.VERSION.class, "SDK_INT", Build.VERSION_CODES.N); + + when(mApplication.getSystemService(Context.USER_SERVICE)).thenReturn(mUserManager); + when(mUserManager.isUserUnlocked()).thenReturn(false); + + ApplicationContextUtils.getApplicationContext(mApplication); + + verify(mApplication).createDeviceProtectedStorageContext(); + } + + @Test + public void getContextWhenVersionIsHigherOrEqualThenNAndUnlockedIsTrue() { + Whitebox.setInternalState(Build.VERSION.class, "SDK_INT", Build.VERSION_CODES.N); + + when(mApplication.getSystemService(Context.USER_SERVICE)).thenReturn(mUserManager); + when(mUserManager.isUserUnlocked()).thenReturn(true); + + ApplicationContextUtils.getApplicationContext(mApplication); + + verify(mApplication).getApplicationContext(); + } + + @Test + public void getContextWhenVersionIsLowerThenN() { + Whitebox.setInternalState(Build.VERSION.class, "SDK_INT", Build.VERSION_CODES.M); + + when(mApplication.getSystemService(Context.USER_SERVICE)).thenReturn(mUserManager); + when(mUserManager.isUserUnlocked()).thenReturn(false); + + ApplicationContextUtils.getApplicationContext(mApplication); + + verify(mApplication).getApplicationContext(); + } +} From ee5d81742f3b89157482a81d80a7dc6faa92dac7 Mon Sep 17 00:00:00 2001 From: dorofeevs Date: Fri, 10 Jun 2022 16:50:14 +0400 Subject: [PATCH 65/72] Update Changelog --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 31484671b..5b4c97f96 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,10 @@ ## Version 4.4.4 (Under active development) +### App Center + +* **[Fix]** Crash when direct boot is used. + ### App Center Distribute * **[Improvement]** Remove optional `SYSTEM_ALERT_WINDOW` permission that was required to automatically restart the app after installing the update. From 8339057fab11c5b50cd11b9d0df081ae31084261 Mon Sep 17 00:00:00 2001 From: dorofeevs Date: Fri, 10 Jun 2022 18:53:12 +0400 Subject: [PATCH 66/72] Add comments and asserts for tests --- CHANGELOG.md | 2 +- .../com/microsoft/appcenter/AppCenter.java | 4 ++ .../appcenter/ApplicationContextUtils.java | 7 +++ .../ApplicationContextUtilsTest.java | 57 ++++++++++++++++--- 4 files changed, 60 insertions(+), 10 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5b4c97f96..a554e0ae1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,7 +4,7 @@ ### App Center -* **[Fix]** Crash when direct boot is used. +* **[Fix]** Fix crash when storage is encrypted during direct boot. Please note that settings and pending logs database are not shared between regular and encrypted storage. ### App Center Distribute diff --git a/sdk/appcenter/src/main/java/com/microsoft/appcenter/AppCenter.java b/sdk/appcenter/src/main/java/com/microsoft/appcenter/AppCenter.java index 388917e59..83aa353cc 100644 --- a/sdk/appcenter/src/main/java/com/microsoft/appcenter/AppCenter.java +++ b/sdk/appcenter/src/main/java/com/microsoft/appcenter/AppCenter.java @@ -138,6 +138,10 @@ public class AppCenter { * Application context. */ private Application mApplication; + + /** + * Context. + */ private Context mContext; /** diff --git a/sdk/appcenter/src/main/java/com/microsoft/appcenter/ApplicationContextUtils.java b/sdk/appcenter/src/main/java/com/microsoft/appcenter/ApplicationContextUtils.java index 0e5da4fab..5b8ed28d9 100644 --- a/sdk/appcenter/src/main/java/com/microsoft/appcenter/ApplicationContextUtils.java +++ b/sdk/appcenter/src/main/java/com/microsoft/appcenter/ApplicationContextUtils.java @@ -10,7 +10,14 @@ import android.os.Build; import android.os.UserManager; +/** + * Context utility. + */ class ApplicationContextUtils { + + /** + * Conditions for taking a context. + */ static Context getApplicationContext(Application application) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { UserManager userManager = (UserManager) application.getSystemService(Context.USER_SERVICE); diff --git a/sdk/appcenter/src/test/java/com/microsoft/appcenter/ApplicationContextUtilsTest.java b/sdk/appcenter/src/test/java/com/microsoft/appcenter/ApplicationContextUtilsTest.java index 115c95531..69cc18253 100644 --- a/sdk/appcenter/src/test/java/com/microsoft/appcenter/ApplicationContextUtilsTest.java +++ b/sdk/appcenter/src/test/java/com/microsoft/appcenter/ApplicationContextUtilsTest.java @@ -5,6 +5,7 @@ package com.microsoft.appcenter; +import static org.junit.Assert.assertEquals; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @@ -13,6 +14,8 @@ import android.os.Build; import android.os.UserManager; +import org.junit.After; +import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; @@ -22,12 +25,22 @@ @RunWith(PowerMockRunner.class) public class ApplicationContextUtilsTest { + @Mock + private Context mProtectedContext; + @Mock private Application mApplication; @Mock private UserManager mUserManager; + @Before + public void setUp() { + when(mApplication.getApplicationContext()).thenReturn(mApplication); + when(mApplication.createDeviceProtectedStorageContext()).thenReturn(mProtectedContext); + when(mApplication.getSystemService(Context.USER_SERVICE)).thenReturn(mUserManager); + } + @SuppressWarnings("InstantiationOfUtilityClass") @Test public void ConstructorCoverage() { @@ -35,38 +48,64 @@ public void ConstructorCoverage() { } @Test - public void getContextWhenVersionIsHigherOrEqualThenNAndUnlockedIsFalse() { + public void getContextWhenVersionIsHigherOrEqualThanNAndUnlockedIsFalse() { + + /* Mock SDK_INT to N. */ Whitebox.setInternalState(Build.VERSION.class, "SDK_INT", Build.VERSION_CODES.N); - when(mApplication.getSystemService(Context.USER_SERVICE)).thenReturn(mUserManager); + /* When user is locked. */ when(mUserManager.isUserUnlocked()).thenReturn(false); - ApplicationContextUtils.getApplicationContext(mApplication); + /* Method call. */ + Context result = ApplicationContextUtils.getApplicationContext(mApplication); + + /* Compare of two results. */ + assertEquals(mProtectedContext, result); + /* Verify create protected storage. */ verify(mApplication).createDeviceProtectedStorageContext(); } @Test - public void getContextWhenVersionIsHigherOrEqualThenNAndUnlockedIsTrue() { + public void getContextWhenVersionIsHigherOrEqualThanNAndUnlockedIsTrue() { + + /* Mock SDK_INT to N. */ Whitebox.setInternalState(Build.VERSION.class, "SDK_INT", Build.VERSION_CODES.N); - when(mApplication.getSystemService(Context.USER_SERVICE)).thenReturn(mUserManager); + /* When user is unlocked. */ when(mUserManager.isUserUnlocked()).thenReturn(true); - ApplicationContextUtils.getApplicationContext(mApplication); + /* Method call. */ + Context result = ApplicationContextUtils.getApplicationContext(mApplication); + /* Compare of two results. */ + assertEquals(mApplication, result); + + /* Verify get application context. */ verify(mApplication).getApplicationContext(); } @Test - public void getContextWhenVersionIsLowerThenN() { + public void getContextWhenVersionIsLowerThanN() { + + /* Mock SDK_INT to M. */ Whitebox.setInternalState(Build.VERSION.class, "SDK_INT", Build.VERSION_CODES.M); - when(mApplication.getSystemService(Context.USER_SERVICE)).thenReturn(mUserManager); + /* When user is locked. */ when(mUserManager.isUserUnlocked()).thenReturn(false); - ApplicationContextUtils.getApplicationContext(mApplication); + /* Method call. */ + Context result = ApplicationContextUtils.getApplicationContext(mApplication); + + /* Compare of two results. */ + assertEquals(mApplication, result); + /* Verify get application context. */ verify(mApplication).getApplicationContext(); } + + @After + public void tearDown() throws Exception { + Whitebox.setInternalState(Build.VERSION.class, "SDK_INT", 0); + } } From 4c74308e20c8565dcf61c3d85e2547caafc3e00a Mon Sep 17 00:00:00 2001 From: dorofeevs Date: Tue, 14 Jun 2022 14:13:58 +0400 Subject: [PATCH 67/72] Change some tests --- .../appcenter/AbstractAppCenterTest.java | 35 +++-- .../appcenter/AppCenterLibraryTest.java | 24 ++-- .../microsoft/appcenter/AppCenterTest.java | 124 +++++++++--------- 3 files changed, 96 insertions(+), 87 deletions(-) diff --git a/sdk/appcenter/src/test/java/com/microsoft/appcenter/AbstractAppCenterTest.java b/sdk/appcenter/src/test/java/com/microsoft/appcenter/AbstractAppCenterTest.java index 67783d828..de0ceee36 100644 --- a/sdk/appcenter/src/test/java/com/microsoft/appcenter/AbstractAppCenterTest.java +++ b/sdk/appcenter/src/test/java/com/microsoft/appcenter/AbstractAppCenterTest.java @@ -5,6 +5,19 @@ package com.microsoft.appcenter; +import static com.microsoft.appcenter.AppCenter.KEY_VALUE_DELIMITER; +import static com.microsoft.appcenter.AppCenter.TRANSMISSION_TARGET_TOKEN_KEY; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyBoolean; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.when; +import static org.powermock.api.mockito.PowerMockito.doAnswer; +import static org.powermock.api.mockito.PowerMockito.mockStatic; +import static org.powermock.api.mockito.PowerMockito.whenNew; + import android.app.Application; import android.content.Context; import android.content.pm.ApplicationInfo; @@ -43,20 +56,8 @@ import java.util.HashMap; import java.util.Map; -import static com.microsoft.appcenter.AppCenter.KEY_VALUE_DELIMITER; -import static com.microsoft.appcenter.AppCenter.TRANSMISSION_TARGET_TOKEN_KEY; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.anyBoolean; -import static org.mockito.ArgumentMatchers.anyString; -import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.spy; -import static org.mockito.Mockito.when; -import static org.powermock.api.mockito.PowerMockito.doAnswer; -import static org.powermock.api.mockito.PowerMockito.mockStatic; -import static org.powermock.api.mockito.PowerMockito.whenNew; - @PrepareForTest({ + ApplicationContextUtils.class, AppCenter.class, UncaughtExceptionHandler.class, DefaultChannel.class, @@ -84,6 +85,9 @@ public class AbstractAppCenterTest { @Rule public PowerMockRule mPowerMockRule = new PowerMockRule(); + @Mock + Context mContext; + @Mock DefaultChannel mChannel; @@ -124,6 +128,11 @@ public void setUp() throws Exception { mApplicationInfo.flags = ApplicationInfo.FLAG_DEBUGGABLE; when(mApplication.getApplicationInfo()).thenReturn(mApplicationInfo); + /* Mock ApplicationContextUtils. */ + mockStatic(ApplicationContextUtils.class); + when(ApplicationContextUtils.getApplicationContext(mApplication)).thenReturn(mContext); + + /* Mock static classes. */ mockStatic(Constants.class); mockStatic(AppCenterLog.class); mockStatic(FileManager.class); diff --git a/sdk/appcenter/src/test/java/com/microsoft/appcenter/AppCenterLibraryTest.java b/sdk/appcenter/src/test/java/com/microsoft/appcenter/AppCenterLibraryTest.java index 2715af1a0..5c9b50adc 100644 --- a/sdk/appcenter/src/test/java/com/microsoft/appcenter/AppCenterLibraryTest.java +++ b/sdk/appcenter/src/test/java/com/microsoft/appcenter/AppCenterLibraryTest.java @@ -5,17 +5,6 @@ package com.microsoft.appcenter; -import android.content.Context; - -import com.microsoft.appcenter.channel.Channel; -import com.microsoft.appcenter.channel.OneCollectorChannelListener; -import com.microsoft.appcenter.utils.AppCenterLog; - -import org.junit.Test; - -import java.util.ArrayList; -import java.util.List; - import static com.microsoft.appcenter.AppCenter.CORE_GROUP; import static com.microsoft.appcenter.AppCenter.LOG_TAG; import static com.microsoft.appcenter.AppCenter.PAIR_DELIMITER; @@ -35,6 +24,17 @@ import static org.powermock.api.mockito.PowerMockito.verifyStatic; import static org.powermock.api.mockito.PowerMockito.whenNew; +import android.content.Context; + +import com.microsoft.appcenter.channel.Channel; +import com.microsoft.appcenter.channel.OneCollectorChannelListener; +import com.microsoft.appcenter.utils.AppCenterLog; + +import org.junit.Test; + +import java.util.ArrayList; +import java.util.List; + public class AppCenterLibraryTest extends AbstractAppCenterTest { @Test @@ -347,7 +347,7 @@ public void startFromLibraryDoesNotStartFromApp() { AppCenter.startFromLibrary(mApplication, DummyService.class); /* Verify second service started without secrets with library flag. */ - verify(DummyService.getInstance()).onStarted(mApplication, mChannel, null, null, false); + verify(DummyService.getInstance()).onStarted(mContext, mChannel, null, null, false); /* Now start from app. */ AppCenter.start(DummyService.class); diff --git a/sdk/appcenter/src/test/java/com/microsoft/appcenter/AppCenterTest.java b/sdk/appcenter/src/test/java/com/microsoft/appcenter/AppCenterTest.java index f62a66e3a..06f13b021 100644 --- a/sdk/appcenter/src/test/java/com/microsoft/appcenter/AppCenterTest.java +++ b/sdk/appcenter/src/test/java/com/microsoft/appcenter/AppCenterTest.java @@ -5,32 +5,6 @@ package com.microsoft.appcenter; -import android.app.Activity; -import android.content.Context; - -import com.microsoft.appcenter.channel.Channel; -import com.microsoft.appcenter.channel.OneCollectorChannelListener; -import com.microsoft.appcenter.ingestion.models.StartServiceLog; -import com.microsoft.appcenter.ingestion.models.WrapperSdk; -import com.microsoft.appcenter.utils.AppCenterLog; -import com.microsoft.appcenter.utils.ApplicationLifecycleListener; -import com.microsoft.appcenter.utils.DeviceInfoHelper; -import com.microsoft.appcenter.utils.PrefStorageConstants; -import com.microsoft.appcenter.utils.ShutdownHelper; -import com.microsoft.appcenter.utils.storage.SharedPreferencesManager; - -import org.junit.Assert; -import org.junit.Test; -import org.mockito.ArgumentCaptor; -import org.mockito.invocation.InvocationOnMock; -import org.mockito.stubbing.Answer; - -import java.util.ArrayList; -import java.util.List; -import java.util.Set; -import java.util.concurrent.Semaphore; -import java.util.concurrent.TimeUnit; - import static com.microsoft.appcenter.AppCenter.APP_SECRET_KEY; import static com.microsoft.appcenter.AppCenter.CORE_GROUP; import static com.microsoft.appcenter.AppCenter.KEY_VALUE_DELIMITER; @@ -63,6 +37,32 @@ import static org.powermock.api.mockito.PowerMockito.verifyStatic; import static org.powermock.api.mockito.PowerMockito.whenNew; +import android.app.Activity; +import android.content.Context; + +import com.microsoft.appcenter.channel.Channel; +import com.microsoft.appcenter.channel.OneCollectorChannelListener; +import com.microsoft.appcenter.ingestion.models.StartServiceLog; +import com.microsoft.appcenter.ingestion.models.WrapperSdk; +import com.microsoft.appcenter.utils.AppCenterLog; +import com.microsoft.appcenter.utils.ApplicationLifecycleListener; +import com.microsoft.appcenter.utils.DeviceInfoHelper; +import com.microsoft.appcenter.utils.PrefStorageConstants; +import com.microsoft.appcenter.utils.ShutdownHelper; +import com.microsoft.appcenter.utils.storage.SharedPreferencesManager; + +import org.junit.Assert; +import org.junit.Test; +import org.mockito.ArgumentCaptor; +import org.mockito.invocation.InvocationOnMock; +import org.mockito.stubbing.Answer; + +import java.util.ArrayList; +import java.util.List; +import java.util.Set; +import java.util.concurrent.Semaphore; +import java.util.concurrent.TimeUnit; + public class AppCenterTest extends AbstractAppCenterTest { @Test @@ -97,7 +97,7 @@ public void useDummyServiceTest() { DummyService service = DummyService.getInstance(); assertTrue(AppCenter.getInstance().getServices().contains(service)); verify(service).getLogFactories(); - verify(service).onStarted(any(Context.class), any(Channel.class), eq(DUMMY_APP_SECRET), isNull(), eq(true)); + verify(service).onStarted(eq(mContext), any(Channel.class), eq(DUMMY_APP_SECRET), isNull(), eq(true)); verify(mApplication).registerActivityLifecycleCallbacks(service); verify(mChannel).enqueue(eq(mStartServiceLog), eq(CORE_GROUP), eq(DEFAULTS)); verify(mChannel).setMaxStorageSize(AppCenter.DEFAULT_MAX_STORAGE_SIZE_IN_BYTES); @@ -151,7 +151,7 @@ public void useDummyServiceTestSplitCall() { DummyService service = DummyService.getInstance(); assertTrue(AppCenter.getInstance().getServices().contains(service)); verify(service).getLogFactories(); - verify(service).onStarted(any(Context.class), any(Channel.class), eq(DUMMY_APP_SECRET), isNull(), eq(true)); + verify(service).onStarted(eq(mContext), any(Channel.class), eq(DUMMY_APP_SECRET), isNull(), eq(true)); verify(mApplication).registerActivityLifecycleCallbacks(service); verify(mChannel).enqueue(eq(mStartServiceLog), eq(CORE_GROUP), eq(DEFAULTS)); List services = new ArrayList<>(); @@ -170,7 +170,7 @@ public void configureAndStartTwiceTest() { DummyService service = DummyService.getInstance(); assertTrue(AppCenter.getInstance().getServices().contains(service)); verify(service).getLogFactories(); - verify(service).onStarted(any(Context.class), any(Channel.class), eq(DUMMY_APP_SECRET), isNull(), eq(true)); + verify(service).onStarted(eq(mContext), any(Channel.class), eq(DUMMY_APP_SECRET), isNull(), eq(true)); verify(service, never()).onStarted(any(Context.class), any(Channel.class), eq(DUMMY_APP_SECRET + "a"), isNull(), eq(true)); verify(mApplication).registerActivityLifecycleCallbacks(service); verify(mChannel).enqueue(eq(mStartServiceLog), eq(CORE_GROUP), eq(DEFAULTS)); @@ -190,7 +190,7 @@ public void startTargetTokenThenStartWithAppSecretTest() { DummyService service = DummyService.getInstance(); assertTrue(AppCenter.getInstance().getServices().contains(service)); verify(service).getLogFactories(); - verify(service).onStarted(any(Context.class), any(Channel.class), isNull(), eq(DUMMY_TRANSMISSION_TARGET_TOKEN), eq(true)); + verify(service).onStarted(eq(mContext), any(Channel.class), isNull(), eq(DUMMY_TRANSMISSION_TARGET_TOKEN), eq(true)); verify(service, never()).onStarted(any(Context.class), any(Channel.class), eq(DUMMY_APP_SECRET), isNull(), eq(true)); verify(mApplication).registerActivityLifecycleCallbacks(service); verify(mChannel).enqueue(eq(mStartServiceLog), eq(CORE_GROUP), eq(DEFAULTS)); @@ -211,7 +211,7 @@ public void startAppSecretThenStartWithTargetTokenTest() { assertTrue(AppCenter.getInstance().getServices().contains(service)); verify(service).getLogFactories(); verify(service, never()).onStarted(any(Context.class), any(Channel.class), isNull(), eq(DUMMY_TRANSMISSION_TARGET_TOKEN), eq(true)); - verify(service).onStarted(any(Context.class), any(Channel.class), eq(DUMMY_APP_SECRET), isNull(), eq(true)); + verify(service).onStarted(eq(mContext), any(Channel.class), eq(DUMMY_APP_SECRET), isNull(), eq(true)); verify(mApplication).registerActivityLifecycleCallbacks(service); verify(mChannel).enqueue(eq(mStartServiceLog), eq(CORE_GROUP), eq(DEFAULTS)); List services = new ArrayList<>(); @@ -231,7 +231,7 @@ public void configureTwiceTest() { DummyService service = DummyService.getInstance(); assertTrue(AppCenter.getInstance().getServices().contains(service)); verify(service).getLogFactories(); - verify(service).onStarted(any(Context.class), any(Channel.class), eq(DUMMY_APP_SECRET), isNull(), eq(true)); + verify(service).onStarted(eq(mContext), any(Channel.class), eq(DUMMY_APP_SECRET), isNull(), eq(true)); verify(service, never()).onStarted(any(Context.class), any(Channel.class), eq(DUMMY_APP_SECRET + "a"), isNull(), eq(true)); verify(mApplication).registerActivityLifecycleCallbacks(service); verify(mChannel).enqueue(eq(mStartServiceLog), eq(CORE_GROUP), eq(DEFAULTS)); @@ -253,13 +253,13 @@ public void startTwoServicesTest() { /* Verify first service started. */ assertTrue(AppCenter.getInstance().getServices().contains(DummyService.getInstance())); verify(DummyService.getInstance()).getLogFactories(); - verify(DummyService.getInstance()).onStarted(any(Context.class), any(Channel.class), eq(DUMMY_APP_SECRET), isNull(), eq(true)); + verify(DummyService.getInstance()).onStarted(eq(mContext), any(Channel.class), eq(DUMMY_APP_SECRET), isNull(), eq(true)); verify(mApplication).registerActivityLifecycleCallbacks(DummyService.getInstance()); /* Verify second service started. */ assertTrue(AppCenter.getInstance().getServices().contains(AnotherDummyService.getInstance())); verify(AnotherDummyService.getInstance()).getLogFactories(); - verify(AnotherDummyService.getInstance()).onStarted(any(Context.class), any(Channel.class), eq(DUMMY_APP_SECRET), isNull(), eq(true)); + verify(AnotherDummyService.getInstance()).onStarted(eq(mContext), any(Channel.class), eq(DUMMY_APP_SECRET), isNull(), eq(true)); verify(mApplication).registerActivityLifecycleCallbacks(AnotherDummyService.getInstance()); /* Verify start service log is sent. */ @@ -280,13 +280,13 @@ public void startTwoServicesSplit() { { assertTrue(AppCenter.getInstance().getServices().contains(DummyService.getInstance())); verify(DummyService.getInstance()).getLogFactories(); - verify(DummyService.getInstance()).onStarted(any(Context.class), any(Channel.class), eq(DUMMY_APP_SECRET), isNull(), eq(true)); + verify(DummyService.getInstance()).onStarted(eq(mContext), any(Channel.class), eq(DUMMY_APP_SECRET), isNull(), eq(true)); verify(mApplication).registerActivityLifecycleCallbacks(DummyService.getInstance()); } { assertTrue(AppCenter.getInstance().getServices().contains(AnotherDummyService.getInstance())); verify(AnotherDummyService.getInstance()).getLogFactories(); - verify(AnotherDummyService.getInstance()).onStarted(any(Context.class), any(Channel.class), eq(DUMMY_APP_SECRET), isNull(), eq(true)); + verify(AnotherDummyService.getInstance()).onStarted(eq(mContext), any(Channel.class), eq(DUMMY_APP_SECRET), isNull(), eq(true)); verify(mApplication).registerActivityLifecycleCallbacks(AnotherDummyService.getInstance()); } verify(mChannel).enqueue(eq(mStartServiceLog), eq(CORE_GROUP), eq(DEFAULTS)); @@ -307,13 +307,13 @@ public void startTwoServicesSplitEvenMore() { { assertTrue(AppCenter.getInstance().getServices().contains(DummyService.getInstance())); verify(DummyService.getInstance()).getLogFactories(); - verify(DummyService.getInstance()).onStarted(any(Context.class), any(Channel.class), eq(DUMMY_APP_SECRET), isNull(), eq(true)); + verify(DummyService.getInstance()).onStarted(eq(mContext), any(Channel.class), eq(DUMMY_APP_SECRET), isNull(), eq(true)); verify(mApplication).registerActivityLifecycleCallbacks(DummyService.getInstance()); } { assertTrue(AppCenter.getInstance().getServices().contains(AnotherDummyService.getInstance())); verify(AnotherDummyService.getInstance()).getLogFactories(); - verify(AnotherDummyService.getInstance()).onStarted(any(Context.class), any(Channel.class), eq(DUMMY_APP_SECRET), isNull(), eq(true)); + verify(AnotherDummyService.getInstance()).onStarted(eq(mContext), any(Channel.class), eq(DUMMY_APP_SECRET), isNull(), eq(true)); verify(mApplication).registerActivityLifecycleCallbacks(AnotherDummyService.getInstance()); } verify(mChannel, times(2)).enqueue(any(StartServiceLog.class), eq(CORE_GROUP), eq(DEFAULTS)); @@ -334,13 +334,13 @@ public void startTwoServicesWithSomeInvalidReferences() { { assertTrue(AppCenter.getInstance().getServices().contains(DummyService.getInstance())); verify(DummyService.getInstance()).getLogFactories(); - verify(DummyService.getInstance()).onStarted(any(Context.class), any(Channel.class), eq(DUMMY_APP_SECRET), isNull(), eq(true)); + verify(DummyService.getInstance()).onStarted(eq(mContext), any(Channel.class), eq(DUMMY_APP_SECRET), isNull(), eq(true)); verify(mApplication).registerActivityLifecycleCallbacks(DummyService.getInstance()); } { assertTrue(AppCenter.getInstance().getServices().contains(AnotherDummyService.getInstance())); verify(AnotherDummyService.getInstance()).getLogFactories(); - verify(AnotherDummyService.getInstance()).onStarted(any(Context.class), any(Channel.class), eq(DUMMY_APP_SECRET), isNull(), eq(true)); + verify(AnotherDummyService.getInstance()).onStarted(eq(mContext), any(Channel.class), eq(DUMMY_APP_SECRET), isNull(), eq(true)); verify(mApplication).registerActivityLifecycleCallbacks(AnotherDummyService.getInstance()); } verify(mChannel).enqueue(eq(mStartServiceLog), eq(CORE_GROUP), eq(DEFAULTS)); @@ -361,13 +361,13 @@ public void startTwoServicesWithSomeInvalidReferencesSplit() { { assertTrue(AppCenter.getInstance().getServices().contains(DummyService.getInstance())); verify(DummyService.getInstance()).getLogFactories(); - verify(DummyService.getInstance()).onStarted(any(Context.class), any(Channel.class), eq(DUMMY_APP_SECRET), isNull(), eq(true)); + verify(DummyService.getInstance()).onStarted(eq(mContext), any(Channel.class), eq(DUMMY_APP_SECRET), isNull(), eq(true)); verify(mApplication).registerActivityLifecycleCallbacks(DummyService.getInstance()); } { assertTrue(AppCenter.getInstance().getServices().contains(AnotherDummyService.getInstance())); verify(AnotherDummyService.getInstance()).getLogFactories(); - verify(AnotherDummyService.getInstance()).onStarted(any(Context.class), any(Channel.class), eq(DUMMY_APP_SECRET), isNull(), eq(true)); + verify(AnotherDummyService.getInstance()).onStarted(eq(mContext), any(Channel.class), eq(DUMMY_APP_SECRET), isNull(), eq(true)); verify(mApplication).registerActivityLifecycleCallbacks(AnotherDummyService.getInstance()); } verify(mChannel, times(2)).enqueue(any(StartServiceLog.class), eq(CORE_GROUP), eq(DEFAULTS)); @@ -391,7 +391,7 @@ public void startServiceTwice() { DummyService service = DummyService.getInstance(); assertTrue(AppCenter.getInstance().getServices().contains(service)); verify(service).getLogFactories(); - verify(service).onStarted(any(Context.class), any(Channel.class), eq(DUMMY_APP_SECRET), isNull(), eq(true)); + verify(service).onStarted(eq(mContext), any(Channel.class), eq(DUMMY_APP_SECRET), isNull(), eq(true)); verify(mApplication).registerActivityLifecycleCallbacks(service); /* Start twice, this call is ignored. */ @@ -400,7 +400,7 @@ public void startServiceTwice() { /* Verify that single service has been loaded and configured (only once interaction). */ assertEquals(1, AppCenter.getInstance().getServices().size()); verify(service).getLogFactories(); - verify(service).onStarted(any(Context.class), any(Channel.class), eq(DUMMY_APP_SECRET), isNull(), eq(true)); + verify(service).onStarted(eq(mContext), any(Channel.class), eq(DUMMY_APP_SECRET), isNull(), eq(true)); verify(mApplication).registerActivityLifecycleCallbacks(service); verify(mChannel).enqueue(eq(mStartServiceLog), eq(CORE_GROUP), eq(DEFAULTS)); List services = new ArrayList<>(); @@ -503,9 +503,9 @@ public void enableTest() { /* Check factories / channel only once interactions. */ verify(dummyService).getLogFactories(); - verify(dummyService).onStarted(any(Context.class), any(Channel.class), eq(DUMMY_APP_SECRET), isNull(), eq(true)); + verify(dummyService).onStarted(eq(mContext), any(Channel.class), eq(DUMMY_APP_SECRET), isNull(), eq(true)); verify(anotherDummyService).getLogFactories(); - verify(anotherDummyService).onStarted(any(Context.class), any(Channel.class), eq(DUMMY_APP_SECRET), isNull(), eq(true)); + verify(anotherDummyService).onStarted(eq(mContext), any(Channel.class), eq(DUMMY_APP_SECRET), isNull(), eq(true)); } @Test @@ -682,7 +682,7 @@ private void checkStartedWithoutAppSecret() { assertTrue(AppCenter.isConfigured()); /* Verify service started with null secrets from application. */ - verify(DummyService.getInstance()).onStarted(any(Context.class), any(Channel.class), isNull(), isNull(), eq(true)); + verify(DummyService.getInstance()).onStarted(eq(mContext), any(Channel.class), isNull(), isNull(), eq(true)); /* We must not be able to reconfigure app secret from null/empty state. */ AppCenter.start(mApplication, DUMMY_APP_SECRET, DummyService.class, AnotherDummyService.class); @@ -690,7 +690,7 @@ private void checkStartedWithoutAppSecret() { AppCenter.start(mApplication, "", DummyService.class, AnotherDummyService.class); /* Verify start not called again (1 total call). */ - verify(DummyService.getInstance()).onStarted(any(Context.class), any(Channel.class), any(), any(), anyBoolean()); + verify(DummyService.getInstance()).onStarted(eq(mContext), any(Channel.class), any(), any(), anyBoolean()); /* And not updated either. */ verify(DummyService.getInstance(), never()).onConfigurationUpdated(anyString(), anyString()); @@ -703,49 +703,49 @@ private void checkStartedWithoutAppSecret() { public void appSecretKeyWithoutAppSecretTest() { String secret = APP_SECRET_KEY + KEY_VALUE_DELIMITER; AppCenter.start(mApplication, secret, DummyService.class); - verify(DummyService.getInstance()).onStarted(any(Context.class), any(Channel.class), isNull(), isNull(), eq(true)); + verify(DummyService.getInstance()).onStarted(eq(mContext), any(Channel.class), isNull(), isNull(), eq(true)); } @Test public void appSecretWithKeyValueDelimiter() { String secret = KEY_VALUE_DELIMITER + DUMMY_APP_SECRET; AppCenter.start(mApplication, secret, DummyService.class); - verify(DummyService.getInstance()).onStarted(any(Context.class), any(Channel.class), isNull(), isNull(), eq(true)); + verify(DummyService.getInstance()).onStarted(eq(mContext), any(Channel.class), isNull(), isNull(), eq(true)); } @Test public void appSecretWithPairDelimiterAfter() { String secret = DUMMY_APP_SECRET + PAIR_DELIMITER; AppCenter.start(mApplication, secret, DummyService.class); - verify(DummyService.getInstance()).onStarted(any(Context.class), any(Channel.class), eq(DUMMY_APP_SECRET), isNull(), eq(true)); + verify(DummyService.getInstance()).onStarted(eq(mContext), any(Channel.class), eq(DUMMY_APP_SECRET), isNull(), eq(true)); } @Test public void appSecretWithPairDelimiterBefore() { String secret = PAIR_DELIMITER + DUMMY_APP_SECRET; AppCenter.start(mApplication, secret, DummyService.class); - verify(DummyService.getInstance()).onStarted(any(Context.class), any(Channel.class), eq(DUMMY_APP_SECRET), isNull(), eq(true)); + verify(DummyService.getInstance()).onStarted(eq(mContext), any(Channel.class), eq(DUMMY_APP_SECRET), isNull(), eq(true)); } @Test public void appSecretWithTargetTokenTest() { String secret = DUMMY_APP_SECRET + PAIR_DELIMITER + DUMMY_TARGET_TOKEN_STRING; AppCenter.start(mApplication, secret, DummyService.class); - verify(DummyService.getInstance()).onStarted(any(Context.class), any(Channel.class), eq(DUMMY_APP_SECRET), eq(DUMMY_TRANSMISSION_TARGET_TOKEN), eq(true)); + verify(DummyService.getInstance()).onStarted(eq(mContext), any(Channel.class), eq(DUMMY_APP_SECRET), eq(DUMMY_TRANSMISSION_TARGET_TOKEN), eq(true)); } @Test public void keyedAppSecretTest() { String secret = APP_SECRET_KEY + KEY_VALUE_DELIMITER + DUMMY_APP_SECRET; AppCenter.start(mApplication, secret, DummyService.class); - verify(DummyService.getInstance()).onStarted(any(Context.class), any(Channel.class), eq(DUMMY_APP_SECRET), isNull(), eq(true)); + verify(DummyService.getInstance()).onStarted(eq(mContext), any(Channel.class), eq(DUMMY_APP_SECRET), isNull(), eq(true)); } @Test public void keyedAppSecretWithDelimiterTest() { String secret = APP_SECRET_KEY + KEY_VALUE_DELIMITER + DUMMY_APP_SECRET + PAIR_DELIMITER; AppCenter.start(mApplication, secret, DummyService.class); - verify(DummyService.getInstance()).onStarted(any(Context.class), any(Channel.class), eq(DUMMY_APP_SECRET), isNull(), eq(true)); + verify(DummyService.getInstance()).onStarted(eq(mContext), any(Channel.class), eq(DUMMY_APP_SECRET), isNull(), eq(true)); } @Test @@ -753,14 +753,14 @@ public void keyedAppSecretWithTargetTokenTest() { String secret = APP_SECRET_KEY + KEY_VALUE_DELIMITER + DUMMY_APP_SECRET + PAIR_DELIMITER + DUMMY_TARGET_TOKEN_STRING; AppCenter.start(mApplication, secret, DummyService.class); - verify(DummyService.getInstance()).onStarted(any(Context.class), any(Channel.class), eq(DUMMY_APP_SECRET), eq(DUMMY_TRANSMISSION_TARGET_TOKEN), eq(true)); + verify(DummyService.getInstance()).onStarted(eq(mContext), any(Channel.class), eq(DUMMY_APP_SECRET), eq(DUMMY_TRANSMISSION_TARGET_TOKEN), eq(true)); } @Test public void targetTokenTest() { AppCenter.start(mApplication, DUMMY_TARGET_TOKEN_STRING, DummyService.class, AnotherDummyService.class); - verify(DummyService.getInstance()).onStarted(any(Context.class), any(Channel.class), isNull(), eq(DUMMY_TRANSMISSION_TARGET_TOKEN), eq(true)); - verify(AnotherDummyService.getInstance(), never()).onStarted(any(Context.class), any(Channel.class), isNull(), eq(DUMMY_TRANSMISSION_TARGET_TOKEN), eq(true)); + verify(DummyService.getInstance()).onStarted(eq(mContext), any(Channel.class), isNull(), eq(DUMMY_TRANSMISSION_TARGET_TOKEN), eq(true)); + verify(AnotherDummyService.getInstance(), never()).onStarted(eq(mContext), any(Channel.class), isNull(), eq(DUMMY_TRANSMISSION_TARGET_TOKEN), eq(true)); } @Test @@ -768,7 +768,7 @@ public void targetTokenWithAppSecretTest() { String secret = DUMMY_TARGET_TOKEN_STRING + PAIR_DELIMITER + DUMMY_APP_SECRET; AppCenter.start(mApplication, secret, DummyService.class); - verify(DummyService.getInstance()).onStarted(any(Context.class), any(Channel.class), eq(DUMMY_APP_SECRET), eq(DUMMY_TRANSMISSION_TARGET_TOKEN), eq(true)); + verify(DummyService.getInstance()).onStarted(eq(mContext), any(Channel.class), eq(DUMMY_APP_SECRET), eq(DUMMY_TRANSMISSION_TARGET_TOKEN), eq(true)); } @Test @@ -776,7 +776,7 @@ public void targetTokenWithUnKeyedAppSecretTest() { String secret = DUMMY_TARGET_TOKEN_STRING + PAIR_DELIMITER + APP_SECRET_KEY + KEY_VALUE_DELIMITER + DUMMY_APP_SECRET; AppCenter.start(mApplication, secret, DummyService.class); - verify(DummyService.getInstance()).onStarted(any(Context.class), any(Channel.class), eq(DUMMY_APP_SECRET), eq(DUMMY_TRANSMISSION_TARGET_TOKEN), eq(true)); + verify(DummyService.getInstance()).onStarted(eq(mContext), any(Channel.class), eq(DUMMY_APP_SECRET), eq(DUMMY_TRANSMISSION_TARGET_TOKEN), eq(true)); } @Test @@ -786,7 +786,7 @@ public void unknownKeyTest() { "unknown" + KEY_VALUE_DELIMITER + "value"; AppCenter.start(mApplication, secret, DummyService.class); assertTrue(AppCenter.isEnabled().get()); - verify(DummyService.getInstance()).onStarted(any(Context.class), any(Channel.class), eq(DUMMY_APP_SECRET), eq(DUMMY_TRANSMISSION_TARGET_TOKEN), eq(true)); + verify(DummyService.getInstance()).onStarted(eq(mContext), any(Channel.class), eq(DUMMY_APP_SECRET), eq(DUMMY_TRANSMISSION_TARGET_TOKEN), eq(true)); } @Test @@ -1088,7 +1088,7 @@ public void useApplicationLifecycleListener() { /* Verify that the service started. */ DummyService service = DummyService.getInstance(); - verify(service).onStarted(any(Context.class), any(Channel.class), eq(DUMMY_APP_SECRET), isNull(), eq(true)); + verify(service).onStarted(eq(mContext), any(Channel.class), eq(DUMMY_APP_SECRET), isNull(), eq(true)); /* Verify that the listener is subscribed to application events. */ verify(mApplication).registerActivityLifecycleCallbacks(isA(ApplicationLifecycleListener.class)); From 844c41556bfd8c6c35fc3a99c177d76e4d578675 Mon Sep 17 00:00:00 2001 From: Ivan Matkov Date: Wed, 15 Jun 2022 13:50:22 +0200 Subject: [PATCH 68/72] Add more detailed comments and log warning --- .../com/microsoft/appcenter/AppCenter.java | 24 ++++++++++++++--- .../appcenter/ApplicationContextUtils.java | 27 +++++++++++++++++-- 2 files changed, 45 insertions(+), 6 deletions(-) diff --git a/sdk/appcenter/src/main/java/com/microsoft/appcenter/AppCenter.java b/sdk/appcenter/src/main/java/com/microsoft/appcenter/AppCenter.java index 83aa353cc..87962c733 100644 --- a/sdk/appcenter/src/main/java/com/microsoft/appcenter/AppCenter.java +++ b/sdk/appcenter/src/main/java/com/microsoft/appcenter/AppCenter.java @@ -52,6 +52,8 @@ import static android.content.pm.ApplicationInfo.FLAG_DEBUGGABLE; import static android.util.Log.VERBOSE; +import static com.microsoft.appcenter.ApplicationContextUtils.getApplicationContext; +import static com.microsoft.appcenter.ApplicationContextUtils.isDeviceProtectedStorage; import static com.microsoft.appcenter.Constants.DEFAULT_TRIGGER_COUNT; import static com.microsoft.appcenter.Constants.DEFAULT_TRIGGER_INTERVAL; import static com.microsoft.appcenter.Constants.DEFAULT_TRIGGER_MAX_PARALLEL_REQUESTS; @@ -135,12 +137,15 @@ public class AppCenter { private String mLogUrl; /** - * Application context. + * Android application object that was passed during configuration. + * It shouldn't be used directly, but only for getting right context. + * It's null until SDK is configured. */ private Application mApplication; /** - * Context. + * Application context. Might be special context with device-protected storage. + * See {@link ApplicationContextUtils#getApplicationContext(Application)}. */ private Context mContext; @@ -715,9 +720,20 @@ public void run() { return true; } - /* Store state. */ + /* Store application to use it later for registering services as lifecycle callbacks. */ mApplication = application; - mContext = ApplicationContextUtils.getApplicationContext(application); + mContext = getApplicationContext(application); + if (isDeviceProtectedStorage(mContext)) { + + /* + * In this mode storing sensitive is strongly discouraged, but App Center considers regular storage as + * not secure as well, so all tokens are encrypted separately anyway. Just warn about it. + */ + AppCenterLog.warn(LOG_TAG, + "A user is locked, credential-protected private app data storage is not available.\n" + + "App Center will use device-protected data storage that available without user authentication.\n" + + "Please note that it's a separate storage, all settings and pending logs won't be shared with regular storage."); + } /* Start looper. */ mHandlerThread = new HandlerThread("AppCenter.Looper"); diff --git a/sdk/appcenter/src/main/java/com/microsoft/appcenter/ApplicationContextUtils.java b/sdk/appcenter/src/main/java/com/microsoft/appcenter/ApplicationContextUtils.java index 5b8ed28d9..3408ee53a 100644 --- a/sdk/appcenter/src/main/java/com/microsoft/appcenter/ApplicationContextUtils.java +++ b/sdk/appcenter/src/main/java/com/microsoft/appcenter/ApplicationContextUtils.java @@ -11,20 +11,43 @@ import android.os.UserManager; /** - * Context utility. + * Helper application context utility class to deal with direct boot where regular storage is not available. */ class ApplicationContextUtils { /** - * Conditions for taking a context. + * Get application context with device-protected storage if needed. + * Note that this method might return a new instance of device-protected storage context object each call. + * See {@link Context#createDeviceProtectedStorageContext()}. + * + * @param application android application. + * @return application context with device-protected storage if needed. */ static Context getApplicationContext(Application application) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { UserManager userManager = (UserManager) application.getSystemService(Context.USER_SERVICE); + + /* + * On devices with direct boot, a user is unlocked only after they've entered + * their credentials (such as a lock pattern or PIN). + */ if (!userManager.isUserUnlocked()) { return application.createDeviceProtectedStorageContext(); } } return application.getApplicationContext(); } + + /** + * Indicates if the storage APIs of this Context are backed by device-protected storage. + * + * @param context context to check. + * @return true if the storage APIs of this Context are backed by device-protected storage. + */ + static boolean isDeviceProtectedStorage(Context context) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { + return context.isDeviceProtectedStorage(); + } + return false; + } } From 417840daa7a5c9d474a2e67fd87106cd9794392b Mon Sep 17 00:00:00 2001 From: Ivan Matkov Date: Wed, 15 Jun 2022 14:04:56 +0200 Subject: [PATCH 69/72] Update CHANGELOG.md --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a554e0ae1..9b9fa44ef 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,7 +4,7 @@ ### App Center -* **[Fix]** Fix crash when storage is encrypted during direct boot. Please note that settings and pending logs database are not shared between regular and encrypted storage. +* **[Fix]** Fix crash when storage is encrypted during direct boot. Please note that settings and pending logs database are not shared between regular and device-protected storage. ### App Center Distribute From 108ee7f266e31f92863ff121aa15bba39f6bb43b Mon Sep 17 00:00:00 2001 From: dorofeevs Date: Wed, 15 Jun 2022 20:49:10 +0400 Subject: [PATCH 70/72] Add new tests for boolean flag --- .../appcenter/AbstractAppCenterTest.java | 21 ++++++++-------- .../microsoft/appcenter/AppCenterTest.java | 15 ++++++++++++ .../ApplicationContextUtilsTest.java | 24 +++++++++++++++++++ 3 files changed, 50 insertions(+), 10 deletions(-) diff --git a/sdk/appcenter/src/test/java/com/microsoft/appcenter/AbstractAppCenterTest.java b/sdk/appcenter/src/test/java/com/microsoft/appcenter/AbstractAppCenterTest.java index de0ceee36..f3fbe965e 100644 --- a/sdk/appcenter/src/test/java/com/microsoft/appcenter/AbstractAppCenterTest.java +++ b/sdk/appcenter/src/test/java/com/microsoft/appcenter/AbstractAppCenterTest.java @@ -57,22 +57,22 @@ import java.util.Map; @PrepareForTest({ - ApplicationContextUtils.class, AppCenter.class, - UncaughtExceptionHandler.class, - DefaultChannel.class, - Constants.class, AppCenterLog.class, - StartServiceLog.class, + ApplicationContextUtils.class, + Constants.class, + DefaultChannel.class, + DeviceInfoHelper.class, FileManager.class, - SharedPreferencesManager.class, IdHelper.class, - DeviceInfoHelper.class, - Thread.class, - ShutdownHelper.class, InstrumentationRegistryHelper.class, + JSONUtils.class, NetworkStateHelper.class, - JSONUtils.class + SharedPreferencesManager.class, + ShutdownHelper.class, + StartServiceLog.class, + Thread.class, + UncaughtExceptionHandler.class }) public class AbstractAppCenterTest { @@ -131,6 +131,7 @@ public void setUp() throws Exception { /* Mock ApplicationContextUtils. */ mockStatic(ApplicationContextUtils.class); when(ApplicationContextUtils.getApplicationContext(mApplication)).thenReturn(mContext); + when(ApplicationContextUtils.isDeviceProtectedStorage(mContext)).thenReturn(false); /* Mock static classes. */ mockStatic(Constants.class); diff --git a/sdk/appcenter/src/test/java/com/microsoft/appcenter/AppCenterTest.java b/sdk/appcenter/src/test/java/com/microsoft/appcenter/AppCenterTest.java index 06f13b021..81915533f 100644 --- a/sdk/appcenter/src/test/java/com/microsoft/appcenter/AppCenterTest.java +++ b/sdk/appcenter/src/test/java/com/microsoft/appcenter/AppCenterTest.java @@ -667,6 +667,21 @@ public void startWithoutAppSecretTest() { checkStartedWithoutAppSecret(); } + @Test + public void configureWithDeviceProtectedStorage() { + when(ApplicationContextUtils.isDeviceProtectedStorage(mContext)).thenReturn(true); + + /* Configure App Center. */ + AppCenter.configure(mApplication); + + /* App Center is configured that way. */ + assertTrue(AppCenter.isConfigured()); + + /* Verify warning call. */ + verifyStatic(AppCenterLog.class); + AppCenterLog.warn(eq(LOG_TAG), anyString()); + } + @Test public void configureWithoutAppSecretTest() { diff --git a/sdk/appcenter/src/test/java/com/microsoft/appcenter/ApplicationContextUtilsTest.java b/sdk/appcenter/src/test/java/com/microsoft/appcenter/ApplicationContextUtilsTest.java index 69cc18253..0602ef023 100644 --- a/sdk/appcenter/src/test/java/com/microsoft/appcenter/ApplicationContextUtilsTest.java +++ b/sdk/appcenter/src/test/java/com/microsoft/appcenter/ApplicationContextUtilsTest.java @@ -6,6 +6,7 @@ package com.microsoft.appcenter; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @@ -104,6 +105,29 @@ public void getContextWhenVersionIsLowerThanN() { verify(mApplication).getApplicationContext(); } + @Test + public void ifTheDeviceAPIVersionSupportsDeviceProtectedStorage() { + + /* Mock SDK_INT to N. */ + Whitebox.setInternalState(Build.VERSION.class, "SDK_INT", Build.VERSION_CODES.N); + + /* Method call. */ + ApplicationContextUtils.isDeviceProtectedStorage(mProtectedContext); + + /* Verify protected storage. */ + verify(mProtectedContext).isDeviceProtectedStorage(); + } + + @Test + public void ifTheDeviceAPIVersionDoesNotSupportDeviceProtectedStorage() { + + /* Mock SDK_INT to M. */ + Whitebox.setInternalState(Build.VERSION.class, "SDK_INT", Build.VERSION_CODES.M); + + /* Verify isDeviceProtectedStorage is false. */ + assertFalse(ApplicationContextUtils.isDeviceProtectedStorage(mProtectedContext)); + } + @After public void tearDown() throws Exception { Whitebox.setInternalState(Build.VERSION.class, "SDK_INT", 0); From 72d8cdbafd6f9d0e1e4f739b41a077d9b6a9f7fe Mon Sep 17 00:00:00 2001 From: Ivan Matkov Date: Mon, 20 Jun 2022 11:23:35 +0200 Subject: [PATCH 71/72] Improve tests --- .../ApplicationContextUtilsTest.java | 56 +++++++++---------- 1 file changed, 25 insertions(+), 31 deletions(-) diff --git a/sdk/appcenter/src/test/java/com/microsoft/appcenter/ApplicationContextUtilsTest.java b/sdk/appcenter/src/test/java/com/microsoft/appcenter/ApplicationContextUtilsTest.java index 0602ef023..2e483f29f 100644 --- a/sdk/appcenter/src/test/java/com/microsoft/appcenter/ApplicationContextUtilsTest.java +++ b/sdk/appcenter/src/test/java/com/microsoft/appcenter/ApplicationContextUtilsTest.java @@ -7,7 +7,9 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoInteractions; import static org.mockito.Mockito.when; import android.app.Application; @@ -27,7 +29,7 @@ public class ApplicationContextUtilsTest { @Mock - private Context mProtectedContext; + private Context mDeviceProtectedStorageContext; @Mock private Application mApplication; @@ -38,18 +40,20 @@ public class ApplicationContextUtilsTest { @Before public void setUp() { when(mApplication.getApplicationContext()).thenReturn(mApplication); - when(mApplication.createDeviceProtectedStorageContext()).thenReturn(mProtectedContext); + when(mApplication.isDeviceProtectedStorage()).thenReturn(false); + when(mApplication.createDeviceProtectedStorageContext()).thenReturn(mDeviceProtectedStorageContext); when(mApplication.getSystemService(Context.USER_SERVICE)).thenReturn(mUserManager); + when(mDeviceProtectedStorageContext.isDeviceProtectedStorage()).thenReturn(true); } @SuppressWarnings("InstantiationOfUtilityClass") @Test - public void ConstructorCoverage() { + public void constructorCoverage() { new ApplicationContextUtils(); } @Test - public void getContextWhenVersionIsHigherOrEqualThanNAndUnlockedIsFalse() { + public void contextOnNewLockedDevice() { /* Mock SDK_INT to N. */ Whitebox.setInternalState(Build.VERSION.class, "SDK_INT", Build.VERSION_CODES.N); @@ -57,18 +61,15 @@ public void getContextWhenVersionIsHigherOrEqualThanNAndUnlockedIsFalse() { /* When user is locked. */ when(mUserManager.isUserUnlocked()).thenReturn(false); - /* Method call. */ - Context result = ApplicationContextUtils.getApplicationContext(mApplication); - - /* Compare of two results. */ - assertEquals(mProtectedContext, result); + /* It should be device-protected storage context. */ + assertEquals(mDeviceProtectedStorageContext, ApplicationContextUtils.getApplicationContext(mApplication)); /* Verify create protected storage. */ verify(mApplication).createDeviceProtectedStorageContext(); } @Test - public void getContextWhenVersionIsHigherOrEqualThanNAndUnlockedIsTrue() { + public void contextOnNewUnlockedDevice() { /* Mock SDK_INT to N. */ Whitebox.setInternalState(Build.VERSION.class, "SDK_INT", Build.VERSION_CODES.N); @@ -76,18 +77,15 @@ public void getContextWhenVersionIsHigherOrEqualThanNAndUnlockedIsTrue() { /* When user is unlocked. */ when(mUserManager.isUserUnlocked()).thenReturn(true); - /* Method call. */ - Context result = ApplicationContextUtils.getApplicationContext(mApplication); - - /* Compare of two results. */ - assertEquals(mApplication, result); + /* It should be regular context. */ + assertEquals(mApplication, ApplicationContextUtils.getApplicationContext(mApplication)); /* Verify get application context. */ verify(mApplication).getApplicationContext(); } @Test - public void getContextWhenVersionIsLowerThanN() { + public void contextOnOldDevice() { /* Mock SDK_INT to M. */ Whitebox.setInternalState(Build.VERSION.class, "SDK_INT", Build.VERSION_CODES.M); @@ -95,41 +93,37 @@ public void getContextWhenVersionIsLowerThanN() { /* When user is locked. */ when(mUserManager.isUserUnlocked()).thenReturn(false); - /* Method call. */ - Context result = ApplicationContextUtils.getApplicationContext(mApplication); - - /* Compare of two results. */ - assertEquals(mApplication, result); + /* It should be regular context. */ + assertEquals(mApplication, ApplicationContextUtils.getApplicationContext(mApplication)); /* Verify get application context. */ verify(mApplication).getApplicationContext(); } @Test - public void ifTheDeviceAPIVersionSupportsDeviceProtectedStorage() { + public void checkDeviceProtectedStorageOnNewDevice() { /* Mock SDK_INT to N. */ Whitebox.setInternalState(Build.VERSION.class, "SDK_INT", Build.VERSION_CODES.N); - /* Method call. */ - ApplicationContextUtils.isDeviceProtectedStorage(mProtectedContext); - - /* Verify protected storage. */ - verify(mProtectedContext).isDeviceProtectedStorage(); + /* It should rely on context. */ + assertTrue(ApplicationContextUtils.isDeviceProtectedStorage(mDeviceProtectedStorageContext)); + assertFalse(ApplicationContextUtils.isDeviceProtectedStorage(mApplication)); } @Test - public void ifTheDeviceAPIVersionDoesNotSupportDeviceProtectedStorage() { + public void checkDeviceProtectedStorageOnOldDevice() { /* Mock SDK_INT to M. */ Whitebox.setInternalState(Build.VERSION.class, "SDK_INT", Build.VERSION_CODES.M); - /* Verify isDeviceProtectedStorage is false. */ - assertFalse(ApplicationContextUtils.isDeviceProtectedStorage(mProtectedContext)); + /* It cannot be device-protected storage on old devices. */ + assertFalse(ApplicationContextUtils.isDeviceProtectedStorage(mApplication)); + verifyNoInteractions(mApplication); } @After - public void tearDown() throws Exception { + public void tearDown() { Whitebox.setInternalState(Build.VERSION.class, "SDK_INT", 0); } } From 7d05fc48ac13cceb022d6ebf1665888b81632592 Mon Sep 17 00:00:00 2001 From: Dmitriy Kirakosyan Date: Tue, 21 Jun 2022 14:49:54 +0700 Subject: [PATCH 72/72] Update Changelog --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9b9fa44ef..22d48de4f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,6 @@ # App Center SDK for Android Change Log -## Version 4.4.4 (Under active development) +## Version 4.4.4 ### App Center