From d87c9a0b823e7b328d93d1024dd2b720cc12fb86 Mon Sep 17 00:00:00 2001 From: Ram Peri Date: Thu, 18 May 2023 23:14:37 -0400 Subject: merge upstream-google' into aosp Test: atest MyRoboTests Bug: 281899632 Merged-In: I3f837c5db845de7dcf2cee799ed2af128d9e2eda Change-Id: I09a0b977ae9c39586c1cf7a871d7021d6297dca7 --- .github/workflows/gradle_tasks_validation.yml | 23 +- README.md | 17 +- annotations/build.gradle | 2 +- build.gradle | 19 +- buildSrc/build.gradle | 8 +- buildSrc/settings.gradle | 7 + .../src/main/groovy/CheckApiChangesPlugin.groovy | 1 - .../robolectric/gradle/RoboJavaModulePlugin.groovy | 4 +- dependencies.gradle | 47 +--- errorprone/build.gradle | 22 +- gradle.properties | 2 +- gradle/libs.versions.toml | 237 +++++++++++++++++++++ integration_tests/agp/build.gradle | 9 +- integration_tests/agp/testsupport/build.gradle | 1 + integration_tests/androidx/build.gradle | 23 +- integration_tests/androidx_test/build.gradle | 49 ++--- integration_tests/compat-target28/build.gradle | 7 +- .../compat/target28/NormalCompatibilityTest.kt | 7 + integration_tests/ctesque/build.gradle | 39 ++-- integration_tests/dependency-on-stubs/build.gradle | 8 +- integration_tests/jacoco-offline/build.gradle | 4 +- integration_tests/kotlin/build.gradle | 6 +- .../ParameterizedRobolectricTestRunnerTest.kt | 28 +++ integration_tests/libphonenumber/build.gradle | 8 +- integration_tests/memoryleaks/build.gradle | 7 +- .../mockito-experimental/build.gradle | 6 +- integration_tests/mockito-kotlin/build.gradle | 8 +- integration_tests/mockito/build.gradle | 8 +- integration_tests/mockk/build.gradle | 6 +- integration_tests/nativegraphics/build.gradle | 11 +- integration_tests/play_services/build.gradle | 6 +- integration_tests/powermock/build.gradle | 11 +- integration_tests/security-providers/build.gradle | 10 +- integration_tests/sparsearray/build.gradle | 7 +- junit/build.gradle | 4 +- nativeruntime/build.gradle | 12 +- .../DefaultNativeRuntimeLoaderTest.java | 6 +- pluginapi/build.gradle | 10 +- plugins/maven-dependency-resolver/build.gradle | 19 +- .../internal/dependency/MavenArtifactFetcher.java | 51 ++++- .../dependency/MavenDependencyResolver.java | 0 preinstrumented/build.gradle | 10 +- processor/build.gradle | 22 +- resources/build.gradle | 12 +- .../org/robolectric/manifest/AndroidManifest.java | 8 + robolectric/build.gradle | 31 +-- .../android/internal/AndroidTestEnvironment.java | 36 +++- .../org/robolectric/internal/AndroidSandbox.java | 0 .../dependency/PropertiesDependencyResolver.java | 0 .../org/robolectric/CustomAppComponentFactory.java | 22 ++ .../CustomConstructorReceiverWrapper.java | 32 +++ .../android/DrawableResourceLoaderTest.java | 4 +- .../robolectric/android/ResourceLoaderTest.java | 15 +- .../ResourceTableFactoryIntegrationTest.java | 4 +- .../android/XmlResourceParserImplTest.java | 4 +- ...ndroidTestEnvironmentCreateApplicationTest.java | 2 +- .../internal/AndroidTestEnvironmentTest.java | 4 +- .../AndroidInterceptorsIntegrationTest.java | 2 +- .../internal/MavenManifestFactoryTest.java | 0 .../PropertiesDependencyResolverTest.java | 0 .../robolectric/res/StyleResourceLoaderTest.java | 8 +- .../shadows/CellIdentityNrBuilderTest.java | 90 ++++++++ .../robolectric/shadows/CellInfoNrBuilderTest.java | 76 +++++++ .../shadows/CellSignalStrengthNrBuilderTest.java | 84 ++++++++ .../shadows/ShadowAssetManagerTest.java | 21 +- .../shadows/ShadowContextWrapperTest.java | 17 ++ .../shadows/ShadowLauncherAppsTest.java | 14 ++ .../org/robolectric/shadows/ShadowPaintTest.java | 9 + .../shadows/ShadowPausedLooperTest.java | 39 ++++ .../robolectric/shadows/ShadowResourcesTest.java | 7 +- .../shadows/ShadowSQLiteConnectionTest.java | 24 +-- .../shadows/ShadowTelephonyManagerTest.java | 36 ++++ .../robolectric/shadows/ShadowVibratorTest.java | 43 ---- .../robolectric/util/SQLiteLibraryLoaderTest.java | 4 +- .../TestAndroidManifestWithAppComponentFactory.xml | 16 ++ sandbox/build.gradle | 24 +-- shadowapi/build.gradle | 10 +- .../org/robolectric/util/ReflectionHelpers.java | 56 +++-- .../robolectric/util/ReflectionHelpersTest.java | 38 +++- shadows/framework/build.gradle | 25 ++- .../java/org/robolectric/RuntimeEnvironment.java | 0 .../shadows/AssociationInfoBuilder.java | 47 ++-- .../robolectric/shadows/CellIdentityNrBuilder.java | 135 ++++++++++++ .../robolectric/shadows/CellInfoLteBuilder.java | 8 +- .../org/robolectric/shadows/CellInfoNrBuilder.java | 93 ++++++++ .../shadows/CellSignalStrengthNrBuilder.java | 140 ++++++++++++ .../robolectric/shadows/ShadowArscApkAssets9.java | 0 .../shadows/ShadowArscAssetManager.java | 0 .../shadows/ShadowDisplayManagerGlobal.java | 27 ++- .../robolectric/shadows/ShadowLauncherApps.java | 17 +- .../org/robolectric/shadows/ShadowLoadedApk.java | 53 +++++ .../shadows/ShadowNativeFontsFontFamily.java | 11 +- .../java/org/robolectric/shadows/ShadowPaint.java | 9 + .../shadows/ShadowPausedMessageQueue.java | 41 +++- .../robolectric/shadows/ShadowSystemVibrator.java | 18 +- .../shadows/ShadowTelephonyManager.java | 25 +++ .../org/robolectric/shadows/ShadowVibrator.java | 15 +- .../org/robolectric/shadows/ShadowWebView.java | 2 +- shadows/httpclient/build.gradle | 14 +- shadows/playservices/build.gradle | 19 +- testapp/build.gradle | 1 + utils/build.gradle | 28 +-- utils/reflector/build.gradle | 10 +- utils/src/main/java/org/robolectric/util/Util.java | 0 104 files changed, 1806 insertions(+), 516 deletions(-) create mode 100644 buildSrc/settings.gradle create mode 100644 gradle/libs.versions.toml create mode 100644 integration_tests/kotlin/src/test/kotlin/org/robolectric/integrationtests/kotlin/ParameterizedRobolectricTestRunnerTest.kt mode change 100755 => 100644 plugins/maven-dependency-resolver/src/main/java/org/robolectric/internal/dependency/MavenDependencyResolver.java mode change 100755 => 100644 robolectric/src/main/java/org/robolectric/internal/AndroidSandbox.java mode change 100755 => 100644 robolectric/src/main/java/org/robolectric/internal/dependency/PropertiesDependencyResolver.java create mode 100644 robolectric/src/test/java/org/robolectric/CustomAppComponentFactory.java create mode 100644 robolectric/src/test/java/org/robolectric/CustomConstructorReceiverWrapper.java mode change 100755 => 100644 robolectric/src/test/java/org/robolectric/internal/MavenManifestFactoryTest.java mode change 100755 => 100644 robolectric/src/test/java/org/robolectric/internal/dependency/PropertiesDependencyResolverTest.java create mode 100644 robolectric/src/test/java/org/robolectric/shadows/CellIdentityNrBuilderTest.java create mode 100644 robolectric/src/test/java/org/robolectric/shadows/CellInfoNrBuilderTest.java create mode 100644 robolectric/src/test/java/org/robolectric/shadows/CellSignalStrengthNrBuilderTest.java create mode 100644 robolectric/src/test/resources/TestAndroidManifestWithAppComponentFactory.xml mode change 100755 => 100644 shadows/framework/src/main/java/org/robolectric/RuntimeEnvironment.java create mode 100644 shadows/framework/src/main/java/org/robolectric/shadows/CellIdentityNrBuilder.java create mode 100644 shadows/framework/src/main/java/org/robolectric/shadows/CellInfoNrBuilder.java create mode 100644 shadows/framework/src/main/java/org/robolectric/shadows/CellSignalStrengthNrBuilder.java mode change 100755 => 100644 shadows/framework/src/main/java/org/robolectric/shadows/ShadowArscApkAssets9.java mode change 100755 => 100644 shadows/framework/src/main/java/org/robolectric/shadows/ShadowArscAssetManager.java mode change 100755 => 100644 utils/src/main/java/org/robolectric/util/Util.java diff --git a/.github/workflows/gradle_tasks_validation.yml b/.github/workflows/gradle_tasks_validation.yml index 96b4b3307..9762b9e51 100644 --- a/.github/workflows/gradle_tasks_validation.yml +++ b/.github/workflows/gradle_tasks_validation.yml @@ -15,6 +15,23 @@ permissions: contents: read jobs: + run_checkForApiChanges: + runs-on: ubuntu-20.04 + + steps: + - uses: actions/checkout@v3 + + - name: Set up JDK + uses: actions/setup-java@v3 + with: + distribution: 'zulu' + java-version: 11 + + - uses: gradle/gradle-build-action@v2 + + - name: Run checkForApiChanges + run: ./gradlew checkForApiChanges + run_aggregateDocs: runs-on: ubuntu-20.04 @@ -30,7 +47,7 @@ jobs: - uses: gradle/gradle-build-action@v2 - name: Run aggregateDocs - run: SKIP_NATIVERUNTIME_BUILD=true ./gradlew clean aggregateDocs # building the native runtime is not required for checking javadoc + run: ./gradlew clean aggregateDocs run_instrumentAll: runs-on: ubuntu-20.04 @@ -50,7 +67,7 @@ jobs: - uses: gradle/gradle-build-action@v2 - name: Run :preinstrumented:instrumentAll - run: SKIP_NATIVERUNTIME_BUILD=true ./gradlew :preinstrumented:instrumentAll + run: ./gradlew :preinstrumented:instrumentAll - name: Run :preinstrumented:instrumentAll with SDK 33 - run: SKIP_NATIVERUNTIME_BUILD=true PREINSTRUMENTED_SDK_VERSIONS=33 ./gradlew :preinstrumented:instrumentAll + run: PREINSTRUMENTED_SDK_VERSIONS=33 ./gradlew :preinstrumented:instrumentAll diff --git a/README.md b/README.md index 6b91ba31d..b183647e0 100644 --- a/README.md +++ b/README.md @@ -40,7 +40,7 @@ If you'd like to start a new project with Robolectric tests you can refer to `de ```groovy testImplementation "junit:junit:4.13.2" -testImplementation "org.robolectric:robolectric:4.10-alpha-1" +testImplementation "org.robolectric:robolectric:4.10.3" ``` ## Building And Contributing @@ -79,18 +79,3 @@ Run compatibility test suites on opening Emulator: ./gradlew connectedCheck -### Using Snapshots - -If you would like to live on the bleeding edge, you can try running against a snapshot build. Keep in mind that snapshots represent the most recent changes on master and may contain bugs. - -#### build.gradle: - -```groovy -repositories { - maven { url "https://oss.sonatype.org/content/repositories/snapshots" } -} - -dependencies { - testImplementation "org.robolectric:robolectric:4.10-SNAPSHOT" -} -``` diff --git a/annotations/build.gradle b/annotations/build.gradle index 65b4f06bb..d8bd113c5 100644 --- a/annotations/build.gradle +++ b/annotations/build.gradle @@ -5,6 +5,6 @@ apply plugin: RoboJavaModulePlugin apply plugin: DeployedRoboJavaModulePlugin dependencies { - compileOnly "com.google.code.findbugs:jsr305:3.0.2" + compileOnly libs.findbugs.jsr305 compileOnly AndroidSdk.MAX_SDK.coordinates } diff --git a/build.gradle b/build.gradle index 1eb662b1f..f3ab387aa 100644 --- a/build.gradle +++ b/build.gradle @@ -7,18 +7,15 @@ buildscript { google() mavenCentral() gradlePluginPortal() - maven { - url "https://plugins.gradle.org/m2/" - } } dependencies { gradle - classpath 'com.android.tools.build:gradle:7.4.2' - classpath 'net.ltgt.gradle:gradle-errorprone-plugin:3.0.1' - classpath 'com.netflix.nebula:gradle-aggregate-javadocs-plugin:3.0.1' - classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlinVersion" - classpath "com.diffplug.spotless:spotless-plugin-gradle:6.17.0" + classpath libs.android.gradle + classpath libs.error.prone.gradle + classpath libs.aggregate.javadocs.gradle + classpath libs.kotlin.gradle + classpath libs.spotless.gradle } } @@ -120,7 +117,7 @@ task aggregateDocs { dependsOn ':aggregateJsondocs' } -task prefetchSdks() { +tasks.register('prefetchSdks') { AndroidSdk.ALL_SDKS.each { androidSdk -> doLast { println("Prefetching ${androidSdk.coordinates}...") @@ -139,7 +136,7 @@ task prefetchSdks() { } } -task prefetchInstrumentedSdks() { +tasks.register('prefetchInstrumentedSdks') { AndroidSdk.ALL_SDKS.each { androidSdk -> doLast { println("Prefetching ${androidSdk.preinstrumentedCoordinates}...") @@ -169,7 +166,7 @@ private void shellExec(String mvnCommand) { if (process.exitValue() != 0) System.exit(1) } -task prefetchDependencies() { +tasks.register('prefetchDependencies') { doLast { allprojects.each { p -> p.configurations.each { config -> diff --git a/buildSrc/build.gradle b/buildSrc/build.gradle index 612366308..67279efee 100644 --- a/buildSrc/build.gradle +++ b/buildSrc/build.gradle @@ -11,8 +11,8 @@ dependencies { implementation gradleApi() implementation localGroovy() - api "com.google.guava:guava:31.1-jre" - api 'org.jetbrains:annotations:24.0.1' - implementation "org.ow2.asm:asm-tree:9.4" - implementation 'com.android.tools.build:gradle:7.4.2' + api libs.guava + api libs.jetbrains.annotations + implementation libs.asm.tree + implementation libs.android.gradle } diff --git a/buildSrc/settings.gradle b/buildSrc/settings.gradle new file mode 100644 index 000000000..6f31e6ef7 --- /dev/null +++ b/buildSrc/settings.gradle @@ -0,0 +1,7 @@ +dependencyResolutionManagement { + versionCatalogs { + libs { + from(files("../gradle/libs.versions.toml")) + } + } +} diff --git a/buildSrc/src/main/groovy/CheckApiChangesPlugin.groovy b/buildSrc/src/main/groovy/CheckApiChangesPlugin.groovy index c0671c598..2f1476cf3 100644 --- a/buildSrc/src/main/groovy/CheckApiChangesPlugin.groovy +++ b/buildSrc/src/main/groovy/CheckApiChangesPlugin.groovy @@ -28,7 +28,6 @@ class CheckApiChangesPlugin implements Plugin { project.checkApiChanges.from.each { project.dependencies.checkApiChangesFrom(it) { transitive = false - force = true } } diff --git a/buildSrc/src/main/groovy/org/robolectric/gradle/RoboJavaModulePlugin.groovy b/buildSrc/src/main/groovy/org/robolectric/gradle/RoboJavaModulePlugin.groovy index 6c0e05894..deb97c994 100644 --- a/buildSrc/src/main/groovy/org/robolectric/gradle/RoboJavaModulePlugin.groovy +++ b/buildSrc/src/main/groovy/org/robolectric/gradle/RoboJavaModulePlugin.groovy @@ -13,8 +13,8 @@ class RoboJavaModulePlugin implements Plugin { if (!skipErrorprone) { apply plugin: "net.ltgt.errorprone" project.dependencies { - errorprone("com.google.errorprone:error_prone_core:$errorproneVersion") - errorproneJavac("com.google.errorprone:javac:$errorproneJavacVersion") + errorprone(libs.error.prone.core) + errorproneJavac(libs.error.prone.javac) } } diff --git a/dependencies.gradle b/dependencies.gradle index b890dc16d..204e3d354 100644 --- a/dependencies.gradle +++ b/dependencies.gradle @@ -1,40 +1,11 @@ ext { - apiCompatVersion='4.9.2' - - errorproneVersion='2.18.0' - errorproneJavacVersion='9+181-r4173-1' - - // AndroidX test versions - axtMonitorVersion='1.6.1' - axtRunnerVersion='1.5.2' - axtRulesVersion='1.5.0' - axtCoreVersion='1.5.0' - axtTruthVersion='1.5.0' - espressoVersion='3.5.1' - axtJunitVersion='1.1.4' - axtTestServicesVersion='1.4.2' - - // AndroidX versions - coreVersion='1.9.0' - appCompatVersion='1.6.1' - constraintlayoutVersion='2.1.4' - windowVersion='1.0.0' - fragmentVersion='1.5.5' - - truthVersion='1.1.3' - - junitVersion='4.13.2' - - mockitoVersion='4.11.0' - - jacocoVersion='0.8.8' - - guavaJREVersion='31.1-jre' - - asmVersion='9.4' - - kotlinVersion='1.8.10' - autoServiceVersion='1.0.1' - multidexVersion='2.0.1' - sqlite4javaVersion='1.0.392' + apiCompatVersion = libs.versions.robolectric.compat.get() + + // https://github.com/gradle/gradle/issues/21267 + axtCoreVersion = libs.versions.androidx.test.core.get() + axtJunitVersion = libs.versions.androidx.test.ext.junit.get() + axtMonitorVersion = libs.versions.androidx.test.monitor.get() + axtRunnerVersion = libs.versions.androidx.test.runner.get() + axtTruthVersion = libs.versions.androidx.test.ext.truth.get() + espressoVersion = libs.versions.androidx.test.espresso.get() } diff --git a/errorprone/build.gradle b/errorprone/build.gradle index 1932066ae..5fc561605 100644 --- a/errorprone/build.gradle +++ b/errorprone/build.gradle @@ -20,14 +20,14 @@ dependencies { implementation project(":shadowapi") // Compile dependencies - implementation "com.google.errorprone:error_prone_annotation:$errorproneVersion" - implementation "com.google.errorprone:error_prone_refaster:$errorproneVersion" - implementation "com.google.errorprone:error_prone_check_api:$errorproneVersion" - compileOnly "com.google.auto.service:auto-service-annotations:$autoServiceVersion" - compileOnly(AndroidSdk.MAX_SDK.coordinates) { force = true } + implementation libs.error.prone.annotations + implementation libs.error.prone.refaster + implementation libs.error.prone.check.api + compileOnly libs.auto.service.annotations + compileOnly(AndroidSdk.MAX_SDK.coordinates) - annotationProcessor "com.google.auto.service:auto-service:$autoServiceVersion" - annotationProcessor "com.google.errorprone:error_prone_core:$errorproneVersion" + annotationProcessor libs.auto.service + annotationProcessor libs.error.prone.core // in jdk 9, tools.jar disappears! def toolsJar = Jvm.current().getToolsJar() @@ -36,10 +36,10 @@ dependencies { } // Testing dependencies - testImplementation "junit:junit:${junitVersion}" - testImplementation "com.google.truth:truth:${truthVersion}" - testImplementation("com.google.errorprone:error_prone_test_helpers:${errorproneVersion}") { + testImplementation libs.junit4 + testImplementation libs.truth + testImplementation(libs.error.prone.test.helpers) { exclude group: 'junit', module: 'junit' // because it depends on a snapshot!? } - testCompileOnly(AndroidSdk.MAX_SDK.coordinates) { force = true } + testCompileOnly(AndroidSdk.MAX_SDK.coordinates) } diff --git a/gradle.properties b/gradle.properties index dc9eb66c0..d97ed211a 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,3 +1,3 @@ -thisVersion=4.10-SNAPSHOT +thisVersion=4.11-SNAPSHOT android.useAndroidX=true kotlin.stdlib.default.dependency=false diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml new file mode 100644 index 000000000..7b43dfa1f --- /dev/null +++ b/gradle/libs.versions.toml @@ -0,0 +1,237 @@ +[versions] +robolectric-compat = "4.10.2" +robolectric-nativeruntime-dist-compat = "1.0.1" + +# https://developer.android.com/studio/releases +android-gradle = "7.4.2" + +# https://github.com/google/conscrypt/tags +conscrypt = "2.5.2" + +# https://github.com/bcgit/bc-java/tags +bouncycastle = "1.73" + +# https://github.com/findbugsproject/findbugs/tags +findbugs-jsr305 = "3.0.2" + +# https://github.com/hamcrest/JavaHamcrest/releases +hamcrest = "2.0.0.0" + +# https://github.com/nebula-plugins/gradle-aggregate-javadocs-plugin/releases +aggregate-javadocs-gradle = "3.0.1" + +# https://github.com/google/error-prone/releases +error-prone = "2.19.1" +error-prone-javac = "9+181-r4173-1" + +# https://github.com/tbroyer/gradle-errorprone-plugin/releases +error-prone-gradle = "3.1.0" + +# https://kotlinlang.org/docs/releases.html#release-details +kotlin = "1.8.10" + +# https://github.com/diffplug/spotless/blob/main/CHANGES.md +spotless-gradle = "6.18.0" + +# https://hc.apache.org/news.html +apache-http-core = "4.0.1" +apache-http-client = "4.0.3" + +# https://asm.ow2.io/versions.html +asm = "9.5" + +# https://github.com/google/auto/releases +auto-common = "1.2.1" +auto-service = "1.0.1" +auto-value = "1.10.1" + +compile-testing = "0.21.0" + +# https://github.com/google/guava/releases +guava-jre = "31.1-jre" + +# https://github.com/google/gson/releases +gson = "2.10.1" + +# https://github.com/google/truth/releases +truth = "1.1.3" + +# https://github.com/unicode-org/icu/releases +icu4j = "73.1" + +jacoco = "0.8.10" + +# https://github.com/javaee/javax.annotation/tags +javax-annotation-api = "1.3.2" +javax-annotation-jsr250-api = "1.0" +javax-inject = "1" + +# https://github.com/JetBrains/java-annotations/releases +jetbrains-annotations = "24.0.1" + +# https://junit.org/junit4/ +junit4 = "4.13.2" + +# https://github.com/google/libphonenumber/releases +libphonenumber = "8.13.11" + +# https://github.com/mockito/mockito/releases +mockito = "4.11.0" + +# https://github.com/mockk/mockk/releases +mockk = "1.13.5" + +# https://square.github.io/okhttp/changelogs/changelog/ +okhttp = "4.11.0" + +# https://github.com/powermock/powermock/releases +powermock = "2.0.9" + +sqlite4java = "1.0.392" + +# https://developer.android.com/jetpack/androidx/versions +androidx-annotation = "1.3.0" +androidx-appcompat = "1.6.1" +androidx-constraintlayout = "2.1.4" +androidx-core = "1.10.1" +androidx-fragment = "1.5.7" +androidx-multidex = "2.0.1" +androidx-window = "1.0.0" + +# https://github.com/android/android-test/tags +androidx-test-annotation = "1.0.1" +androidx-test-core = "1.5.0" +androidx-test-espresso = "3.5.1" +androidx-test-ext-junit = "1.1.5" +androidx-test-ext-truth = "1.5.0" +androidx-test-monitor="1.6.1" +androidx-test-orchestrator="1.4.2" +androidx-test-runner = "1.5.2" +androidx-test-services = "1.4.2" + +# for shadows/playservices/build.gradle +androidx-fragment-for-shadows = "1.2.0" +play-services-base-for-shadows = "8.4.0" + +[libraries] +android-gradle = { module = "com.android.tools.build:gradle", version.ref = "android-gradle" } +kotlin-gradle = { module = "org.jetbrains.kotlin:kotlin-gradle-plugin", version.ref = "kotlin" } +spotless-gradle = { module = "com.diffplug.spotless:spotless-plugin-gradle", version.ref = "spotless-gradle" } + +kotlin-stdlib = { module = "org.jetbrains.kotlin:kotlin-stdlib", version.ref = "kotlin" } + +auto-common = { module = "com.google.auto:auto-common", version.ref = "auto-common" } +auto-service-annotations = { module = "com.google.auto.service:auto-service-annotations", version.ref = "auto-service" } +auto-service = { module = "com.google.auto.service:auto-service", version.ref = "auto-service" } +auto-value-annotations = { module = "com.google.auto.value:auto-value-annotations", version.ref = "auto-value" } +auto-value = { module = "com.google.auto.value:auto-value", version.ref = "auto-value" } + +apache-http-core = { module = "org.apache.httpcomponents:httpcore", version.ref = "apache-http-core" } +apache-http-client = { module = "org.apache.httpcomponents:httpclient", version.ref = "apache-http-client" } + +asm = { module = "org.ow2.asm:asm", version.ref = "asm" } +asm-commons = { module = "org.ow2.asm:asm-commons", version.ref = "asm" } +asm-util = { module = "org.ow2.asm:asm-util", version.ref = "asm" } +asm-tree = { module = "org.ow2.asm:asm-tree", version.ref = "asm" } + +compile-testing = { module = "com.google.testing.compile:compile-testing", version.ref = "compile-testing" } + +aggregate-javadocs-gradle = { module = "com.netflix.nebula:gradle-aggregate-javadocs-plugin", version.ref = "aggregate-javadocs-gradle" } + +error-prone-core = { module = "com.google.errorprone:error_prone_core", version.ref = "error-prone" } +error-prone-annotations = { module = "com.google.errorprone:error_prone_annotation", version.ref = "error-prone" } +error-prone-refaster= { module = "com.google.errorprone:error_prone_refaster", version.ref = "error-prone" } +error-prone-check-api = { module = "com.google.errorprone:error_prone_check_api", version.ref = "error-prone" } +error-prone-test-helpers = { module = "com.google.errorprone:error_prone_test_helpers", version.ref = "error-prone" } +error-prone-javac = { module = "com.google.errorprone:javac", version.ref = "error-prone-javac" } + +error-prone-gradle = { module = "net.ltgt.gradle:gradle-errorprone-plugin", version.ref = "error-prone-gradle" } + +conscrypt-openjdk-uber = { module = "org.conscrypt:conscrypt-openjdk-uber", version.ref = "conscrypt" } +bcprov-jdk18on = { module = "org.bouncycastle:bcprov-jdk18on", version.ref = "bouncycastle" } +findbugs-jsr305 = { module = "com.google.code.findbugs:jsr305", version.ref = "findbugs-jsr305" } + +guava = { module = "com.google.guava:guava", version.ref = "guava-jre" } +guava-testlib = { module = "com.google.guava:guava-testlib", version.ref = "guava-jre" } +gson = { module = "com.google.code.gson:gson", version.ref = "gson" } +hamcrest-junit = { module = "org.hamcrest:hamcrest-junit", version.ref = "hamcrest" } + +icu4j = { module = "com.ibm.icu:icu4j", version.ref = "icu4j" } + +jacoco-agent = { module = "org.jacoco:org.jacoco.agent", version.ref = "jacoco" } +junit4 = { module = "junit:junit", version.ref = "junit4" } + +javax-annotation-api = { module = "javax.annotation:javax.annotation-api", version.ref = "javax-annotation-api" } +javax-annotation-jsr250-api = { module = "javax.annotation:jsr250-api", version.ref = "javax-annotation-jsr250-api" } +javax-inject = { module = "javax.inject:javax.inject", version.ref = "javax.inject" } + +jetbrains-annotations = { module = "org.jetbrains:annotations", version.ref = "jetbrains-annotations" } + +libphonenumber = { module = "com.googlecode.libphonenumber:libphonenumber", version.ref = "libphonenumber" } + +okhttp = { module = "com.squareup.okhttp3:okhttp" } +okhttp-bom = { module = "com.squareup.okhttp3:okhttp-bom", version.ref = "okhttp" } + +powermock-module-junit4 = { module = "org.powermock:powermock-module-junit4", version.ref = "powermock" } +powermock-module-junit4-rule = { module = "org.powermock:powermock-module-junit4-rule", version.ref = "powermock" } +powermock-api-mockito2 = { module = "org.powermock:powermock-api-mockito2", version.ref = "powermock" } +powermock-classloading-xstream = { module = "org.powermock:powermock-classloading-xstream", version.ref = "powermock" } + +robolectric-nativeruntime-dist-compat = { module = "org.robolectric:nativeruntime-dist-compat", version.ref = "robolectric-nativeruntime-dist-compat" } + +sqlite4java = { module = "com.almworks.sqlite4java:sqlite4java", version.ref = "sqlite4java" } +sqlite4java-osx = { module = "com.almworks.sqlite4java:libsqlite4java-osx", version.ref = "sqlite4java" } +sqlite4java-linux-amd64 = { module = "com.almworks.sqlite4java:libsqlite4java-linux-amd64", version.ref = "sqlite4java" } +sqlite4java-win32-x64 = { module = "com.almworks.sqlite4java:sqlite4java-win32-x64", version.ref = "sqlite4java" } +sqlite4java-linux-i386 = { module = "com.almworks.sqlite4java:libsqlite4java-linux-i386", version.ref = "sqlite4java" } +sqlite4java-win32-x86 = { module = "com.almworks.sqlite4java:sqlite4java-win32-x86", version.ref = "sqlite4java" } + +truth = { module = "com.google.truth:truth", version.ref = "truth" } +truth-java8-extension = { module = "com.google.truth.extensions:truth-java8-extension", version.ref = "truth" } + +mockito = { module = "org.mockito:mockito-core", version.ref = "mockito" } +mockito-inline = { module = "org.mockito:mockito-inline", version.ref = "mockito" } +mockk = { module = "io.mockk:mockk", version.ref = "mockk" } + +androidx-annotation = { module = "androidx.annotation:annotation", version.ref = "androidx-annotation" } +androidx-appcompat = { module = "androidx.appcompat:appcompat", version.ref = "androidx-appcompat" } +androidx-constraintlayout = { module = "androidx.constraintlayout:constraintlayout", version.ref = "androidx-constraintlayout" } +androidx-core = { module = "androidx.core:core", version.ref = "androidx-core" } +androidx-fragment = { module = "androidx.fragment:fragment", version.ref = "androidx-fragment" } +androidx-fragment-testing = { module = "androidx.fragment:fragment-testing", version.ref = "androidx-fragment" } +androidx-multidex = { module = "androidx.multidex:multidex", version.ref = "androidx-multidex" } +androidx-window = { module = "androidx.window:window", version.ref = "androidx-window" } + +androidx-test-annotation = { module = "androidx.test:annotation", version.ref = "androidx-test-annotation" } +androidx-test-core = { module = "androidx.test:core", version.ref = "androidx-test-core" } +androidx-test-monitor = { module = "androidx.test:monitor", version.ref = "androidx-test-monitor" } +androidx-test-orchestrator = { module = "androidx.test:orchestrator", version.ref = "androidx-test-orchestrator" } +androidx-test-rules = { module = "androidx.test:rules", version.ref = "androidx-test-core" } +androidx-test-runner = { module = "androidx.test:runner", version.ref = "androidx-test-runner" } +androidx-test-services = { module = "androidx.test.services:test-services", version.ref = "androidx-test-services" } +androidx-test-services-storage = { module = "androidx.test.services:storage", version.ref = "androidx-test-services" } + +androidx-test-espresso-core = { module = "androidx.test.espresso:espresso-core", version.ref = "androidx-test-espresso" } +androidx-test-espresso-accessibility = { module = "androidx.test.espresso:espresso-accessibility", version.ref = "androidx-test-espresso" } +androidx-test-espresso-contrib = { module = "androidx.test.espresso:espresso-contrib", version.ref = "androidx-test-espresso" } +androidx-test-espresso-intents = { module = "androidx.test.espresso:espresso-intents", version.ref = "androidx-test-espresso" } +androidx-test-espresso-remote = { module = "androidx.test.espresso:espresso-remote", version.ref = "androidx-test-espresso" } +androidx-test-espresso-web = { module = "androidx.test.espresso:espresso-web", version.ref = "androidx-test-espresso" } + +androidx-test-espresso-idling-resource = { module = "androidx.test.espresso:espresso-idling-resource", version.ref = "androidx-test-espresso" } +androidx-test-espresso-idling-concurrent = { module = "androidx.test.espresso.idling:idling-concurrent", version.ref = "androidx-test-espresso" } +androidx-test-espresso-idling-net = { module = "androidx.test.espresso.idling:idling-net", version.ref = "androidx-test-espresso" } + +androidx-test-ext-junit = { module = "androidx.test.ext:junit", version.ref = "androidx-test-ext-junit" } +androidx-test-ext-truth = { module = "androidx.test.ext:truth", version.ref = "androidx-test-ext-truth" } + +androidx-fragment-for-shadows = { module = "androidx.fragment:fragment", version.ref = "androidx-fragment-for-shadows" } +play-services-base-for-shadows = { module = "com.google.android.gms:play-services-base", version.ref = "play-services-base-for-shadows" } +play-services-basement-for-shadows = { module = "com.google.android.gms:play-services-basement", version.ref = "play-services-base-for-shadows" } + +[bundles] +play-services-base-for-shadows = [ "androidx-fragment-for-shadows", "play-services-base-for-shadows", "play-services-basement-for-shadows" ] +powermock = [ "powermock-module-junit4", "powermock-module-junit4-rule", "powermock-api-mockito2", "powermock-classloading-xstream" ] +sqlite4java-native = [ "sqlite4java-osx", "sqlite4java-linux-amd64", "sqlite4java-win32-x64", "sqlite4java-linux-i386", "sqlite4java-win32-x86" ] + +[plugins] diff --git a/integration_tests/agp/build.gradle b/integration_tests/agp/build.gradle index a079d1ecf..55d90516f 100644 --- a/integration_tests/agp/build.gradle +++ b/integration_tests/agp/build.gradle @@ -5,6 +5,7 @@ apply plugin: AndroidProjectConfigPlugin android { compileSdk 33 + namespace 'org.robolectric.integrationtests.agp' defaultConfig { minSdk 16 @@ -25,8 +26,8 @@ dependencies { testImplementation project(":robolectric") testImplementation project(":integration_tests:agp:testsupport") - testImplementation "junit:junit:${junitVersion}" - testImplementation("androidx.test:core:$axtCoreVersion") - testImplementation("androidx.test:runner:$axtRunnerVersion") - testImplementation("androidx.test.ext:junit:$axtJunitVersion") + testImplementation libs.junit4 + testImplementation libs.androidx.test.core + testImplementation libs.androidx.test.runner + testImplementation libs.androidx.test.ext.junit } diff --git a/integration_tests/agp/testsupport/build.gradle b/integration_tests/agp/testsupport/build.gradle index dcec3d41d..e87274f2b 100644 --- a/integration_tests/agp/testsupport/build.gradle +++ b/integration_tests/agp/testsupport/build.gradle @@ -2,6 +2,7 @@ apply plugin: 'com.android.library' android { compileSdk 33 + namespace 'org.robolectric.integrationtests.agp.testsupport' defaultConfig { minSdk 16 diff --git a/integration_tests/androidx/build.gradle b/integration_tests/androidx/build.gradle index 96535e1dd..10cc8c650 100644 --- a/integration_tests/androidx/build.gradle +++ b/integration_tests/androidx/build.gradle @@ -5,6 +5,7 @@ apply plugin: AndroidProjectConfigPlugin android { compileSdk 33 + namespace 'org.robolectric.integrationtests.androidx' defaultConfig { minSdk 16 @@ -25,19 +26,19 @@ android { } dependencies { - implementation("androidx.appcompat:appcompat:$appCompatVersion") - implementation("androidx.window:window:$windowVersion") + implementation libs.androidx.appcompat + implementation libs.androidx.window // Testing dependencies testImplementation project(path: ':testapp') testImplementation project(":robolectric") - testImplementation "junit:junit:$junitVersion" - testImplementation("androidx.test:core:$axtCoreVersion") - testImplementation("androidx.core:core:$coreVersion") - testImplementation("androidx.test:runner:$axtRunnerVersion") - testImplementation("androidx.test:rules:$axtRulesVersion") - testImplementation("androidx.test.espresso:espresso-intents:$espressoVersion") - testImplementation("androidx.test.ext:truth:$axtTruthVersion") - testImplementation("androidx.test.ext:junit:$axtJunitVersion") - testImplementation("com.google.truth:truth:$truthVersion") + testImplementation libs.junit4 + testImplementation libs.androidx.test.core + testImplementation libs.androidx.core + testImplementation libs.androidx.test.runner + testImplementation libs.androidx.test.rules + testImplementation libs.androidx.test.espresso.intents + testImplementation libs.androidx.test.ext.truth + testImplementation libs.androidx.test.ext.junit + testImplementation libs.truth } diff --git a/integration_tests/androidx_test/build.gradle b/integration_tests/androidx_test/build.gradle index 7f6f621b6..d07ef2ed6 100644 --- a/integration_tests/androidx_test/build.gradle +++ b/integration_tests/androidx_test/build.gradle @@ -7,6 +7,7 @@ apply plugin: GradleManagedDevicePlugin android { compileSdk 33 + namespace 'org.robolectric.integration.axt' defaultConfig { minSdk 16 @@ -41,33 +42,33 @@ android { } dependencies { - implementation "androidx.appcompat:appcompat:$appCompatVersion" - implementation "androidx.constraintlayout:constraintlayout:$constraintlayoutVersion" - implementation "androidx.multidex:multidex:$multidexVersion" + implementation libs.androidx.appcompat + implementation libs.androidx.constraintlayout + implementation libs.androidx.multidex // Testing dependencies testImplementation project(":robolectric") - testImplementation "androidx.test:runner:$axtRunnerVersion" - testImplementation "junit:junit:$junitVersion" - testImplementation "androidx.test:rules:$axtRulesVersion" - testImplementation "androidx.test.espresso:espresso-intents:$espressoVersion" - testImplementation "androidx.test.espresso:espresso-core:$espressoVersion" - testImplementation "androidx.test.ext:truth:$axtTruthVersion" - testImplementation "androidx.test:core:$axtCoreVersion" - testImplementation "androidx.fragment:fragment:$fragmentVersion" - testImplementation "androidx.fragment:fragment-testing:$fragmentVersion" - testImplementation "androidx.test.ext:junit:$axtJunitVersion" - testImplementation "com.google.truth:truth:$truthVersion" + testImplementation libs.androidx.test.runner + testImplementation libs.junit4 + testImplementation libs.androidx.test.rules + testImplementation libs.androidx.test.espresso.intents + testImplementation libs.androidx.test.espresso.core + testImplementation libs.androidx.test.ext.truth + testImplementation libs.androidx.test.core + testImplementation libs.androidx.fragment + testImplementation libs.androidx.fragment.testing + testImplementation libs.androidx.test.ext.junit + testImplementation libs.truth androidTestImplementation project(':annotations') - androidTestImplementation "androidx.test:runner:$axtRunnerVersion" - androidTestImplementation "junit:junit:$junitVersion" - androidTestImplementation "androidx.test:rules:$axtRulesVersion" - androidTestImplementation "androidx.test.espresso:espresso-intents:$espressoVersion" - androidTestImplementation "androidx.test.espresso:espresso-core:$espressoVersion" - androidTestImplementation "androidx.test.ext:truth:$axtTruthVersion" - androidTestImplementation "androidx.test:core:$axtCoreVersion" - androidTestImplementation "androidx.test.ext:junit:$axtJunitVersion" - androidTestImplementation "com.google.truth:truth:$truthVersion" - androidTestUtil "androidx.test.services:test-services:$axtTestServicesVersion" + androidTestImplementation libs.androidx.test.runner + androidTestImplementation libs.junit4 + androidTestImplementation libs.androidx.test.rules + androidTestImplementation libs.androidx.test.espresso.intents + androidTestImplementation libs.androidx.test.espresso.core + androidTestImplementation libs.androidx.test.ext.truth + androidTestImplementation libs.androidx.test.core + androidTestImplementation libs.androidx.test.ext.junit + androidTestImplementation libs.truth + androidTestUtil libs.androidx.test.services } diff --git a/integration_tests/compat-target28/build.gradle b/integration_tests/compat-target28/build.gradle index 37a856856..1fc7485ae 100644 --- a/integration_tests/compat-target28/build.gradle +++ b/integration_tests/compat-target28/build.gradle @@ -14,6 +14,7 @@ spotless { android { compileSdk 28 + namespace 'org.robolectric.integrationtests.compattarget28' defaultConfig { minSdk 16 @@ -30,10 +31,10 @@ android { } dependencies { - implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlinVersion" + implementation libs.kotlin.stdlib testImplementation project(path: ':testapp') testImplementation project(":robolectric") - testImplementation "junit:junit:$junitVersion" - testImplementation "com.google.truth:truth:$truthVersion" + testImplementation libs.junit4 + testImplementation libs.truth } diff --git a/integration_tests/compat-target28/src/test/java/org/robolectric/integration/compat/target28/NormalCompatibilityTest.kt b/integration_tests/compat-target28/src/test/java/org/robolectric/integration/compat/target28/NormalCompatibilityTest.kt index ee56fc6d2..69bbf73e0 100644 --- a/integration_tests/compat-target28/src/test/java/org/robolectric/integration/compat/target28/NormalCompatibilityTest.kt +++ b/integration_tests/compat-target28/src/test/java/org/robolectric/integration/compat/target28/NormalCompatibilityTest.kt @@ -1,7 +1,9 @@ package org.robolectric.integration.compat.target28 import android.content.Context +import android.content.Context.VIBRATOR_SERVICE import android.os.Build +import android.os.Vibrator import android.speech.SpeechRecognizer import com.google.common.truth.Truth.assertThat import org.junit.Test @@ -47,4 +49,9 @@ class NormalCompatibilityTest { fun `Create speech recognizer succeed`() { assertThat(SpeechRecognizer.createSpeechRecognizer(application)).isNotNull() } + + @Test + fun `Get default Vibrator succeed`() { + assertThat(application.getSystemService(VIBRATOR_SERVICE) as Vibrator).isNotNull() + } } diff --git a/integration_tests/ctesque/build.gradle b/integration_tests/ctesque/build.gradle index 3efa7d1b0..3b40c88de 100644 --- a/integration_tests/ctesque/build.gradle +++ b/integration_tests/ctesque/build.gradle @@ -7,6 +7,7 @@ apply plugin: GradleManagedDevicePlugin android { compileSdk 33 + namespace 'org.robolectric.integrationtests.ctesque' defaultConfig { minSdk 16 @@ -48,26 +49,26 @@ dependencies { implementation project(':testapp') testImplementation project(':robolectric') - testImplementation "junit:junit:${junitVersion}" - testImplementation("androidx.test:monitor:$axtMonitorVersion") - testImplementation("androidx.test:runner:$axtRunnerVersion") - testImplementation("androidx.test:rules:$axtRulesVersion") - testImplementation("androidx.test.ext:junit:$axtJunitVersion") - testImplementation("androidx.test.ext:truth:$axtTruthVersion") - testImplementation("androidx.test:core:$axtCoreVersion") - testImplementation("androidx.test.espresso:espresso-core:$espressoVersion") - testImplementation("com.google.truth:truth:${truthVersion}") - testImplementation("com.google.guava:guava:$guavaJREVersion") + testImplementation libs.junit4 + testImplementation libs.androidx.test.monitor + testImplementation libs.androidx.test.runner + testImplementation libs.androidx.test.rules + testImplementation libs.androidx.test.ext.junit + testImplementation libs.androidx.test.ext.truth + testImplementation libs.androidx.test.core + testImplementation libs.androidx.test.espresso.core + testImplementation libs.truth + testImplementation libs.guava // Testing dependencies androidTestImplementation project(':shadowapi') - androidTestImplementation("androidx.test:monitor:$axtMonitorVersion") - androidTestImplementation("androidx.test:runner:$axtRunnerVersion") - androidTestImplementation("androidx.test:rules:$axtRulesVersion") - androidTestImplementation("androidx.test.ext:junit:$axtJunitVersion") - androidTestImplementation("androidx.test.ext:truth:$axtTruthVersion") - androidTestImplementation("androidx.test.espresso:espresso-core:$espressoVersion") - androidTestImplementation("com.google.truth:truth:${truthVersion}") - androidTestImplementation("com.google.guava:guava:$guavaJREVersion") - androidTestUtil "androidx.test.services:test-services:$axtTestServicesVersion" + androidTestImplementation libs.androidx.test.monitor + androidTestImplementation libs.androidx.test.runner + androidTestImplementation libs.androidx.test.rules + androidTestImplementation libs.androidx.test.ext.junit + androidTestImplementation libs.androidx.test.ext.truth + androidTestImplementation libs.androidx.test.espresso.core + androidTestImplementation libs.truth + androidTestImplementation libs.guava + androidTestUtil libs.androidx.test.services } diff --git a/integration_tests/dependency-on-stubs/build.gradle b/integration_tests/dependency-on-stubs/build.gradle index 6efe51373..683de182d 100644 --- a/integration_tests/dependency-on-stubs/build.gradle +++ b/integration_tests/dependency-on-stubs/build.gradle @@ -6,13 +6,13 @@ apply plugin: RoboJavaModulePlugin dependencies { api project(":robolectric") - api "junit:junit:${junitVersion}" + api libs.junit4 testImplementation files("${System.getenv("ANDROID_HOME")}/platforms/android-29/android.jar") testCompileOnly AndroidSdk.MAX_SDK.coordinates // compile against latest Android SDK testRuntimeOnly AndroidSdk.MAX_SDK.coordinates - testImplementation "com.google.truth:truth:${truthVersion}" - testImplementation "org.mockito:mockito-core:${mockitoVersion}" - testImplementation "org.hamcrest:hamcrest-junit:2.0.0.0" + testImplementation libs.truth + testImplementation libs.mockito + testImplementation libs.hamcrest.junit } diff --git a/integration_tests/jacoco-offline/build.gradle b/integration_tests/jacoco-offline/build.gradle index e5d3bb5eb..3db34c00a 100644 --- a/integration_tests/jacoco-offline/build.gradle +++ b/integration_tests/jacoco-offline/build.gradle @@ -3,6 +3,8 @@ import org.robolectric.gradle.RoboJavaModulePlugin apply plugin: RoboJavaModulePlugin apply plugin: "jacoco" +def jacocoVersion = libs.versions.jacoco.get() + jacoco { toolVersion = jacocoVersion } @@ -18,7 +20,7 @@ dependencies { testRuntimeOnly AndroidSdk.MAX_SDK.coordinates testImplementation project(":robolectric") - testImplementation "junit:junit:$junitVersion" + testImplementation libs.junit4 testImplementation "org.jacoco:org.jacoco.agent:$jacocoVersion:runtime" } diff --git a/integration_tests/kotlin/build.gradle b/integration_tests/kotlin/build.gradle index 68c5c677f..fd52d973d 100644 --- a/integration_tests/kotlin/build.gradle +++ b/integration_tests/kotlin/build.gradle @@ -21,8 +21,8 @@ dependencies { testCompileOnly AndroidSdk.MAX_SDK.coordinates testRuntimeOnly AndroidSdk.MAX_SDK.coordinates - testImplementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlinVersion" - testImplementation "junit:junit:$junitVersion" - testImplementation "com.google.truth:truth:$truthVersion" + testImplementation libs.kotlin.stdlib + testImplementation libs.junit4 + testImplementation libs.truth testImplementation "androidx.test:core:$axtCoreVersion@aar" } diff --git a/integration_tests/kotlin/src/test/kotlin/org/robolectric/integrationtests/kotlin/ParameterizedRobolectricTestRunnerTest.kt b/integration_tests/kotlin/src/test/kotlin/org/robolectric/integrationtests/kotlin/ParameterizedRobolectricTestRunnerTest.kt new file mode 100644 index 000000000..6d77aa91f --- /dev/null +++ b/integration_tests/kotlin/src/test/kotlin/org/robolectric/integrationtests/kotlin/ParameterizedRobolectricTestRunnerTest.kt @@ -0,0 +1,28 @@ +package org.robolectric.integrationtests.kotlin + +import android.net.Uri +import com.google.common.truth.Truth.assertThat +import org.junit.Test +import org.junit.runner.RunWith +import org.robolectric.ParameterizedRobolectricTestRunner +import org.robolectric.ParameterizedRobolectricTestRunner.Parameters +import org.robolectric.annotation.Config + +@RunWith(ParameterizedRobolectricTestRunner::class) +class ParameterizedRobolectricTestRunnerTest(private var uri: Uri) { + @Test + @Config(manifest = Config.NONE) + fun parse() { + val currentUri = Uri.parse("http://host/") + assertThat(currentUri).isEqualTo(uri) + } + + companion object { + @Parameters + @JvmStatic + fun getTestData(): Collection<*> { + val data = arrayOf(Uri.parse("http://host/")) + return listOf(data) + } + } +} diff --git a/integration_tests/libphonenumber/build.gradle b/integration_tests/libphonenumber/build.gradle index 2c27a7968..61120f227 100644 --- a/integration_tests/libphonenumber/build.gradle +++ b/integration_tests/libphonenumber/build.gradle @@ -4,10 +4,10 @@ apply plugin: RoboJavaModulePlugin dependencies { api project(":robolectric") - api "junit:junit:${junitVersion}" + api libs.junit4 compileOnly AndroidSdk.MAX_SDK.coordinates testRuntimeOnly AndroidSdk.MAX_SDK.coordinates - testImplementation "com.google.truth:truth:${truthVersion}" - testImplementation 'com.googlecode.libphonenumber:libphonenumber:8.13.8' -} \ No newline at end of file + testImplementation libs.truth + testImplementation libs.libphonenumber +} diff --git a/integration_tests/memoryleaks/build.gradle b/integration_tests/memoryleaks/build.gradle index 2cc51247c..91c5eb01e 100644 --- a/integration_tests/memoryleaks/build.gradle +++ b/integration_tests/memoryleaks/build.gradle @@ -5,6 +5,7 @@ apply plugin: AndroidProjectConfigPlugin android { compileSdk 33 + namespace 'org.robolectric.integrationtests.memoryleaks' defaultConfig { minSdk 16 @@ -28,7 +29,7 @@ dependencies { // Testing dependencies testImplementation project(path: ':testapp') testImplementation project(":robolectric") - testImplementation "junit:junit:$junitVersion" - testImplementation "com.google.guava:guava-testlib:$guavaJREVersion" - testImplementation "androidx.fragment:fragment:$fragmentVersion" + testImplementation libs.junit4 + testImplementation libs.guava.testlib + testImplementation libs.androidx.fragment } diff --git a/integration_tests/mockito-experimental/build.gradle b/integration_tests/mockito-experimental/build.gradle index 4aafcbc08..f5172d6af 100644 --- a/integration_tests/mockito-experimental/build.gradle +++ b/integration_tests/mockito-experimental/build.gradle @@ -8,7 +8,7 @@ dependencies { testCompileOnly AndroidSdk.MAX_SDK.coordinates testRuntimeOnly AndroidSdk.MAX_SDK.coordinates - testImplementation "junit:junit:${junitVersion}" - testImplementation "com.google.truth:truth:${truthVersion}" - testImplementation "org.mockito:mockito-inline:${mockitoVersion}" + testImplementation libs.junit4 + testImplementation libs.truth + testImplementation libs.mockito.inline } diff --git a/integration_tests/mockito-kotlin/build.gradle b/integration_tests/mockito-kotlin/build.gradle index ae97f1b7a..776f33bd2 100644 --- a/integration_tests/mockito-kotlin/build.gradle +++ b/integration_tests/mockito-kotlin/build.gradle @@ -18,8 +18,8 @@ dependencies { testCompileOnly AndroidSdk.MAX_SDK.coordinates testRuntimeOnly AndroidSdk.MAX_SDK.coordinates testImplementation "androidx.test.ext:junit:$axtJunitVersion@aar" - testImplementation "junit:junit:$junitVersion" - testImplementation "com.google.truth:truth:$truthVersion" - testImplementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlinVersion" - testImplementation "org.mockito:mockito-core:$mockitoVersion" + testImplementation libs.junit4 + testImplementation libs.truth + testImplementation libs.kotlin.stdlib + testImplementation libs.mockito } diff --git a/integration_tests/mockito/build.gradle b/integration_tests/mockito/build.gradle index e199cd74d..31e6ce675 100644 --- a/integration_tests/mockito/build.gradle +++ b/integration_tests/mockito/build.gradle @@ -8,7 +8,7 @@ dependencies { testCompileOnly AndroidSdk.MAX_SDK.coordinates testRuntimeOnly AndroidSdk.MAX_SDK.coordinates - testImplementation "junit:junit:${junitVersion}" - testImplementation "com.google.truth:truth:${truthVersion}" - testImplementation "org.mockito:mockito-core:${mockitoVersion}" -} \ No newline at end of file + testImplementation libs.junit4 + testImplementation libs.truth + testImplementation libs.mockito +} diff --git a/integration_tests/mockk/build.gradle b/integration_tests/mockk/build.gradle index 78344a9aa..1d590714e 100644 --- a/integration_tests/mockk/build.gradle +++ b/integration_tests/mockk/build.gradle @@ -21,7 +21,7 @@ dependencies { testCompileOnly AndroidSdk.MAX_SDK.coordinates testRuntimeOnly AndroidSdk.MAX_SDK.coordinates - testImplementation "junit:junit:${junitVersion}" - testImplementation "com.google.truth:truth:${truthVersion}" - testImplementation 'io.mockk:mockk:1.13.4' + testImplementation libs.junit4 + testImplementation libs.truth + testImplementation libs.mockk } diff --git a/integration_tests/nativegraphics/build.gradle b/integration_tests/nativegraphics/build.gradle index 10e8f6138..f88f53a3f 100644 --- a/integration_tests/nativegraphics/build.gradle +++ b/integration_tests/nativegraphics/build.gradle @@ -7,6 +7,7 @@ apply plugin: GradleManagedDevicePlugin android { compileSdk 33 + namespace 'org.robolectric.integrationtests.nativegraphics' defaultConfig { minSdk 26 @@ -32,9 +33,9 @@ dependencies { testImplementation AndroidSdk.MAX_SDK.coordinates testImplementation project(':robolectric') - testImplementation "androidx.core:core:$coreVersion" - testImplementation "androidx.test.ext:junit:$axtJunitVersion" - testImplementation "com.google.truth:truth:${truthVersion}" - testImplementation "junit:junit:${junitVersion}" - testImplementation "org.mockito:mockito-core:${mockitoVersion}" + testImplementation libs.androidx.core + testImplementation libs.androidx.test.ext.junit + testImplementation libs.truth + testImplementation libs.junit4 + testImplementation libs.mockito } diff --git a/integration_tests/play_services/build.gradle b/integration_tests/play_services/build.gradle index f7499dd73..0e05dd6b3 100644 --- a/integration_tests/play_services/build.gradle +++ b/integration_tests/play_services/build.gradle @@ -9,7 +9,7 @@ dependencies { testCompileOnly AndroidSdk.MAX_SDK.coordinates testRuntimeOnly AndroidSdk.MAX_SDK.coordinates - testImplementation "junit:junit:$junitVersion" - testImplementation "com.google.truth:truth:$truthVersion" + testImplementation libs.junit4 + testImplementation libs.truth testImplementation "com.google.android.gms:play-services-basement:18.0.1" -} \ No newline at end of file +} diff --git a/integration_tests/powermock/build.gradle b/integration_tests/powermock/build.gradle index be4180cf5..6d5cf689d 100644 --- a/integration_tests/powermock/build.gradle +++ b/integration_tests/powermock/build.gradle @@ -7,11 +7,8 @@ dependencies { compileOnly AndroidSdk.MAX_SDK.coordinates testRuntimeOnly AndroidSdk.MAX_SDK.coordinates - testImplementation "junit:junit:${junitVersion}" - testImplementation "com.google.truth:truth:${truthVersion}" + testImplementation libs.junit4 + testImplementation libs.truth - testImplementation "org.powermock:powermock-module-junit4:2.0.9" - testImplementation "org.powermock:powermock-module-junit4-rule:2.0.9" - testImplementation "org.powermock:powermock-api-mockito2:2.0.9" - testImplementation "org.powermock:powermock-classloading-xstream:2.0.9" -} \ No newline at end of file + testImplementation libs.bundles.powermock +} diff --git a/integration_tests/security-providers/build.gradle b/integration_tests/security-providers/build.gradle index f96df56b9..8ac0264a8 100644 --- a/integration_tests/security-providers/build.gradle +++ b/integration_tests/security-providers/build.gradle @@ -4,12 +4,12 @@ apply plugin: RoboJavaModulePlugin dependencies { api project(":robolectric") - api "junit:junit:${junitVersion}" + api libs.junit4 compileOnly AndroidSdk.MAX_SDK.coordinates testRuntimeOnly AndroidSdk.MAX_SDK.coordinates - testImplementation "com.google.truth:truth:${truthVersion}" - testImplementation "org.conscrypt:conscrypt-openjdk-uber:2.4.0" - testImplementation "com.squareup.okhttp3:okhttp" - testImplementation platform("com.squareup.okhttp3:okhttp-bom:4.10.0") + testImplementation libs.truth + testImplementation libs.conscrypt.openjdk.uber + testImplementation libs.okhttp + testImplementation platform(libs.okhttp.bom) } diff --git a/integration_tests/sparsearray/build.gradle b/integration_tests/sparsearray/build.gradle index 762717794..1e4ba1ddf 100644 --- a/integration_tests/sparsearray/build.gradle +++ b/integration_tests/sparsearray/build.gradle @@ -14,6 +14,7 @@ spotless { android { compileSdk 33 + namespace 'org.robolectric.sparsearray' defaultConfig { minSdk 16 @@ -41,7 +42,7 @@ dependencies { testCompileOnly AndroidSdk.MAX_SDK.coordinates testRuntimeOnly AndroidSdk.MAX_SDK.coordinates testImplementation project(":robolectric") - testImplementation "junit:junit:$junitVersion" - testImplementation "com.google.truth:truth:$truthVersion" - testImplementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlinVersion" + testImplementation libs.junit4 + testImplementation libs.truth + testImplementation libs.kotlin.stdlib } diff --git a/junit/build.gradle b/junit/build.gradle index 9a1197871..5d5855254 100644 --- a/junit/build.gradle +++ b/junit/build.gradle @@ -11,6 +11,6 @@ dependencies { api project(":shadowapi") api project(":utils:reflector") - compileOnly "com.google.code.findbugs:jsr305:3.0.2" - compileOnly "junit:junit:${junitVersion}" + compileOnly libs.findbugs.jsr305 + compileOnly libs.junit4 } diff --git a/nativeruntime/build.gradle b/nativeruntime/build.gradle index f8d496d5b..646613fa1 100644 --- a/nativeruntime/build.gradle +++ b/nativeruntime/build.gradle @@ -64,17 +64,17 @@ if (System.getenv('PUBLISH_NATIVERUNTIME_DIST_COMPAT') == "true") { dependencies { api project(":utils") api project(":utils:reflector") - api "com.google.guava:guava:$guavaJREVersion" + api libs.guava - implementation "org.robolectric:nativeruntime-dist-compat:1.0.1" + implementation libs.robolectric.nativeruntime.dist.compat - annotationProcessor "com.google.auto.service:auto-service:$autoServiceVersion" - compileOnly "com.google.auto.service:auto-service-annotations:$autoServiceVersion" + annotationProcessor libs.auto.service + compileOnly libs.auto.service.annotations compileOnly AndroidSdk.MAX_SDK.coordinates testCompileOnly AndroidSdk.MAX_SDK.coordinates testRuntimeOnly AndroidSdk.MAX_SDK.coordinates testImplementation project(":robolectric") - testImplementation "junit:junit:${junitVersion}" - testImplementation "com.google.truth:truth:${truthVersion}" + testImplementation libs.junit4 + testImplementation libs.truth } diff --git a/nativeruntime/src/test/java/org/robolectric/nativeruntime/DefaultNativeRuntimeLoaderTest.java b/nativeruntime/src/test/java/org/robolectric/nativeruntime/DefaultNativeRuntimeLoaderTest.java index 03911593e..e5d395f2e 100644 --- a/nativeruntime/src/test/java/org/robolectric/nativeruntime/DefaultNativeRuntimeLoaderTest.java +++ b/nativeruntime/src/test/java/org/robolectric/nativeruntime/DefaultNativeRuntimeLoaderTest.java @@ -2,7 +2,7 @@ package org.robolectric.nativeruntime; import static android.os.Build.VERSION_CODES.O; import static com.google.common.truth.Truth.assertThat; -import static org.junit.Assume.assumeTrue; +import static com.google.common.truth.TruthJUnit.assume; import android.database.CursorWindow; import android.database.sqlite.SQLiteDatabase; @@ -33,8 +33,8 @@ public final class DefaultNativeRuntimeLoaderTest { @Test public void extracts_fontsAndIcuData() { - assumeTrue(hasResource("fonts")); - assumeTrue(hasResource("icu/icudt68l.dat")); + assume().that(hasResource("fonts")).isTrue(); + assume().that(hasResource("icu/icudt68l.dat")).isTrue(); DefaultNativeRuntimeLoader defaultNativeRuntimeLoader = new DefaultNativeRuntimeLoader(); defaultNativeRuntimeLoader.ensureLoaded(); // Check that extraction of some key files worked. diff --git a/pluginapi/build.gradle b/pluginapi/build.gradle index 375cd10e4..9d7885291 100644 --- a/pluginapi/build.gradle +++ b/pluginapi/build.gradle @@ -5,11 +5,11 @@ apply plugin: RoboJavaModulePlugin apply plugin: DeployedRoboJavaModulePlugin dependencies { - compileOnly 'com.google.code.findbugs:jsr305:3.0.2' + compileOnly libs.findbugs.jsr305 api project(":annotations") - api "com.google.guava:guava:$guavaJREVersion" + api libs.guava - testImplementation "junit:junit:${junitVersion}" - testImplementation "com.google.truth:truth:${truthVersion}" - testImplementation "org.mockito:mockito-core:${mockitoVersion}" + testImplementation libs.junit4 + testImplementation libs.truth + testImplementation libs.mockito } diff --git a/plugins/maven-dependency-resolver/build.gradle b/plugins/maven-dependency-resolver/build.gradle index de20b2b8f..2aa33d9f5 100644 --- a/plugins/maven-dependency-resolver/build.gradle +++ b/plugins/maven-dependency-resolver/build.gradle @@ -1,3 +1,4 @@ +import org.jetbrains.kotlin.gradle.dsl.JvmTarget import org.robolectric.gradle.DeployedRoboJavaModulePlugin import org.robolectric.gradle.RoboJavaModulePlugin @@ -13,7 +14,7 @@ spotless { } } -tasks.withType(GenerateModuleMetadata) { +tasks.withType(GenerateModuleMetadata).configureEach { // We don't want to release gradle module metadata now to avoid // potential compatibility problems. enabled = false @@ -22,11 +23,11 @@ tasks.withType(GenerateModuleMetadata) { compileKotlin { // Use java/main classes directory to replace default kotlin/main to // avoid d8 error when dexing & desugaring kotlin classes with non-exist - // kotlin/main directory because utils module doesn't have kotlin code + // kotlin/main directory because this module doesn't have kotlin code // in production. If utils module starts to add Kotlin code in main source // set, we can remove this destinationDirectory modification. destinationDirectory = file("${projectDir}/build/classes/java/main") - compilerOptions.jvmTarget = org.jetbrains.kotlin.gradle.dsl.JvmTarget.JVM_1_8 + compilerOptions.jvmTarget = JvmTarget.JVM_1_8 } afterEvaluate { @@ -48,10 +49,12 @@ afterEvaluate { dependencies { api project(":pluginapi") api project(":utils") - api "com.google.guava:guava:$guavaJREVersion" + api libs.auto.value.annotations + api libs.guava + annotationProcessor libs.auto.value - testImplementation "junit:junit:$junitVersion" - testImplementation "org.mockito:mockito-core:$mockitoVersion" - testImplementation "com.google.truth:truth:$truthVersion" - testImplementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlinVersion" + testImplementation libs.junit4 + testImplementation libs.mockito + testImplementation libs.truth + testImplementation libs.kotlin.stdlib } diff --git a/plugins/maven-dependency-resolver/src/main/java/org/robolectric/internal/dependency/MavenArtifactFetcher.java b/plugins/maven-dependency-resolver/src/main/java/org/robolectric/internal/dependency/MavenArtifactFetcher.java index 60f852dbc..adeda9e78 100644 --- a/plugins/maven-dependency-resolver/src/main/java/org/robolectric/internal/dependency/MavenArtifactFetcher.java +++ b/plugins/maven-dependency-resolver/src/main/java/org/robolectric/internal/dependency/MavenArtifactFetcher.java @@ -2,6 +2,7 @@ package org.robolectric.internal.dependency; import static java.nio.charset.StandardCharsets.UTF_8; +import com.google.auto.value.AutoValue; import com.google.common.base.Strings; import com.google.common.hash.HashCode; import com.google.common.hash.Hashing; @@ -24,6 +25,7 @@ import java.net.URLConnection; import java.util.Base64; import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutorService; +import javax.annotation.Nonnull; import org.robolectric.util.Logger; /** @@ -82,15 +84,27 @@ public class MavenArtifactFetcher { return Futures.immediateFuture(null); } createArtifactSubdirectory(artifact, localRepositoryDir); - boolean pomValid = + ValidationResult pomResult = validateStagedFiles(artifact.pomPath(), artifact.pomSha512Path()); - if (!pomValid) { - throw new AssertionError("SHA512 mismatch for POM file fetched in " + artifact); + if (!pomResult.isSuccess()) { + throw new AssertionError( + "SHA-512 mismatch for POM file for " + + artifact + + ", expected SHA-512=" + + pomResult.expectedHashCode() + + ", actual SHA-512=" + + pomResult.calculatedHashCode()); } - boolean jarValid = + ValidationResult jarResult = validateStagedFiles(artifact.jarPath(), artifact.jarSha512Path()); - if (!jarValid) { - throw new AssertionError("SHA512 mismatch for JAR file fetched in " + artifact); + if (!jarResult.isSuccess()) { + throw new AssertionError( + "SHA-512 mismatch for POM file for " + + artifact + + ", expected SHA-512=" + + jarResult.expectedHashCode() + + ", actual SHA-512=" + + jarResult.calculatedHashCode()); } Logger.info( String.format( @@ -123,7 +137,8 @@ public class MavenArtifactFetcher { new File(repositoryDir, artifact.pomSha512Path()).delete(); } - private boolean validateStagedFiles(String filePath, String sha512Path) throws IOException { + private ValidationResult validateStagedFiles(String filePath, String sha512Path) + throws IOException { File tempFile = new File(this.stagingRepositoryDir, filePath); File sha512File = new File(this.stagingRepositoryDir, sha512Path); @@ -131,7 +146,24 @@ public class MavenArtifactFetcher { HashCode.fromString(new String(Files.asByteSource(sha512File).read(), UTF_8)); HashCode actual = Files.asByteSource(tempFile).hash(Hashing.sha512()); - return expected.equals(actual); + return ValidationResult.create(expected.equals(actual), expected.toString(), actual.toString()); + } + + @AutoValue + abstract static class ValidationResult { + abstract boolean isSuccess(); + + @Nonnull + abstract String expectedHashCode(); + + @Nonnull + abstract String calculatedHashCode(); + + static ValidationResult create( + boolean isSuccess, String expectedHashCode, String calculatedHashCode) { + return new AutoValue_MavenArtifactFetcher_ValidationResult( + isSuccess, expectedHashCode, calculatedHashCode); + } } private void createArtifactSubdirectory(MavenJarArtifact artifact, File repositoryDir) @@ -218,6 +250,9 @@ public class MavenArtifactFetcher { try (InputStream inputStream = connection.getInputStream(); FileOutputStream outputStream = new FileOutputStream(localFile)) { ByteStreams.copy(inputStream, outputStream); + // Ensure all contents are written to disk. + outputStream.flush(); + outputStream.getFD().sync(); } return Futures.immediateFuture(null); } diff --git a/plugins/maven-dependency-resolver/src/main/java/org/robolectric/internal/dependency/MavenDependencyResolver.java b/plugins/maven-dependency-resolver/src/main/java/org/robolectric/internal/dependency/MavenDependencyResolver.java old mode 100755 new mode 100644 diff --git a/preinstrumented/build.gradle b/preinstrumented/build.gradle index 95d533e4d..c489a115a 100644 --- a/preinstrumented/build.gradle +++ b/preinstrumented/build.gradle @@ -17,11 +17,11 @@ java { } dependencies { - implementation "com.google.guava:guava:$guavaJREVersion" + implementation libs.guava implementation project(":sandbox") } -task instrumentAll { +tasks.register('instrumentAll') { dependsOn ':prefetchSdks' dependsOn 'build' @@ -42,11 +42,11 @@ task instrumentAll { } } -task('sourcesJar', type: Jar) { +tasks.register('sourcesJar', Jar) { archiveClassifier = "sources" } -task('javadocJar', type: Jar) { +tasks.register('javadocJar', Jar) { archiveClassifier = "javadoc" } @@ -132,4 +132,4 @@ clean.doFirst { AndroidSdk.ALL_SDKS.each { androidSdk -> delete "${buildDir}/${androidSdk.preinstrumentedJarFileName}" } -} \ No newline at end of file +} diff --git a/processor/build.gradle b/processor/build.gradle index ac14e4282..9185d088a 100644 --- a/processor/build.gradle +++ b/processor/build.gradle @@ -35,21 +35,21 @@ dependencies { api project(":annotations") api project(":shadowapi") - compileOnly "com.google.code.findbugs:jsr305:3.0.2" - api "org.ow2.asm:asm:${asmVersion}" - api "org.ow2.asm:asm-commons:${asmVersion}" - api "com.google.guava:guava:$guavaJREVersion" - api "com.google.code.gson:gson:2.10.1" - implementation 'com.google.auto:auto-common:1.1.2' + compileOnly libs.findbugs.jsr305 + api libs.asm + api libs.asm.commons + api libs.guava + api libs.gson + implementation libs.auto.common def toolsJar = Jvm.current().getToolsJar() if (toolsJar != null) { implementation files(toolsJar) } - testImplementation "javax.annotation:jsr250-api:1.0" - testImplementation "junit:junit:${junitVersion}" - testImplementation "org.mockito:mockito-core:${mockitoVersion}" - testImplementation "com.google.testing.compile:compile-testing:0.21.0" - testImplementation "com.google.truth:truth:${truthVersion}" + testImplementation libs.javax.annotation.jsr250.api + testImplementation libs.junit4 + testImplementation libs.mockito + testImplementation libs.compile.testing + testImplementation libs.truth } diff --git a/resources/build.gradle b/resources/build.gradle index 129dc20ad..077cdd3b3 100644 --- a/resources/build.gradle +++ b/resources/build.gradle @@ -9,11 +9,11 @@ dependencies { api project(":annotations") api project(":pluginapi") - api "com.google.guava:guava:$guavaJREVersion" - compileOnly "com.google.code.findbugs:jsr305:3.0.2" + api libs.guava + compileOnly libs.findbugs.jsr305 - testImplementation "junit:junit:${junitVersion}" - testImplementation "com.google.truth:truth:${truthVersion}" - testImplementation "com.google.testing.compile:compile-testing:0.21.0" - testImplementation "org.mockito:mockito-core:${mockitoVersion}" + testImplementation libs.junit4 + testImplementation libs.truth + testImplementation libs.compile.testing + testImplementation libs.mockito } diff --git a/resources/src/main/java/org/robolectric/manifest/AndroidManifest.java b/resources/src/main/java/org/robolectric/manifest/AndroidManifest.java index cfabcbb2b..7402b4982 100644 --- a/resources/src/main/java/org/robolectric/manifest/AndroidManifest.java +++ b/resources/src/main/java/org/robolectric/manifest/AndroidManifest.java @@ -53,6 +53,7 @@ public class AndroidManifest implements UsesSdk { private String processName; private String themeRef; private String labelRef; + private String appComponentFactory; // Added from SDK 28 private Integer minSdkVersion; private Integer targetSdkVersion; private Integer maxSdkVersion; @@ -191,6 +192,7 @@ public class AndroidManifest implements UsesSdk { rClassName = packageName + ".R"; Node applicationNode = findApplicationNode(manifestDocument); + // Parse application node of the AndroidManifest.xml if (applicationNode != null) { NamedNodeMap attributes = applicationNode.getAttributes(); int attrCount = attributes.getLength(); @@ -204,6 +206,7 @@ public class AndroidManifest implements UsesSdk { processName = applicationAttributes.get("android:process"); themeRef = applicationAttributes.get("android:theme"); labelRef = applicationAttributes.get("android:label"); + appComponentFactory = applicationAttributes.get("android:appComponentFactory"); parseReceivers(applicationNode); parseServices(applicationNode); @@ -605,6 +608,11 @@ public class AndroidManifest implements UsesSdk { return labelRef; } + public String getAppComponentFactory() { + parseAndroidManifest(); + return appComponentFactory; + } + /** * Returns the minimum Android SDK version that this package expects to be runnable on, as * specified in the manifest. diff --git a/robolectric/build.gradle b/robolectric/build.gradle index faaa8b3a0..b826e9232 100644 --- a/robolectric/build.gradle +++ b/robolectric/build.gradle @@ -5,8 +5,8 @@ apply plugin: RoboJavaModulePlugin apply plugin: DeployedRoboJavaModulePlugin dependencies { - annotationProcessor "com.google.auto.service:auto-service:$autoServiceVersion" - annotationProcessor "com.google.errorprone:error_prone_core:$errorproneVersion" + annotationProcessor libs.auto.service + annotationProcessor libs.error.prone.core api project(":annotations") api project(":junit") @@ -16,33 +16,34 @@ dependencies { api project(":utils") api project(":utils:reflector") api project(":plugins:maven-dependency-resolver") - api "javax.inject:javax.inject:1" - compileOnly "com.google.auto.service:auto-service-annotations:$autoServiceVersion" - api "javax.annotation:javax.annotation-api:1.3.2" + api libs.javax.inject + compileOnly libs.auto.service.annotations + api libs.javax.annotation.api // We need to have shadows-framework.jar on the runtime system classpath so ServiceLoader // can find its META-INF/services/org.robolectric.shadows.ShadowAdapter. api project(":shadows:framework") - implementation 'org.conscrypt:conscrypt-openjdk-uber:2.5.2' - api "org.bouncycastle:bcprov-jdk18on:1.72" - compileOnly "com.google.code.findbugs:jsr305:3.0.2" + implementation libs.conscrypt.openjdk.uber + api libs.bcprov.jdk18on + compileOnly libs.findbugs.jsr305 compileOnly AndroidSdk.MAX_SDK.coordinates - compileOnly "junit:junit:${junitVersion}" + compileOnly libs.junit4 + api "androidx.test:monitor:$axtMonitorVersion@aar" implementation "androidx.test.espresso:espresso-idling-resource:$espressoVersion@aar" - testImplementation "junit:junit:${junitVersion}" - testImplementation "com.google.truth:truth:${truthVersion}" - testImplementation "com.google.truth.extensions:truth-java8-extension:${truthVersion}" - testImplementation "org.mockito:mockito-core:${mockitoVersion}" - testImplementation "org.hamcrest:hamcrest-junit:2.0.0.0" + testImplementation libs.junit4 + testImplementation libs.truth + testImplementation libs.truth.java8.extension + testImplementation libs.mockito + testImplementation libs.hamcrest.junit testImplementation "androidx.test:core:$axtCoreVersion@aar" testImplementation "androidx.test.ext:junit:$axtJunitVersion@aar" testImplementation "androidx.test.ext:truth:$axtTruthVersion@aar" testImplementation "androidx.test:runner:$axtRunnerVersion@aar" - testImplementation("com.google.guava:guava:$guavaJREVersion") + testImplementation libs.guava testCompileOnly AndroidSdk.MAX_SDK.coordinates // compile against latest Android SDK testRuntimeOnly AndroidSdk.MAX_SDK.coordinates // run against whatever this JDK supports } diff --git a/robolectric/src/main/java/org/robolectric/android/internal/AndroidTestEnvironment.java b/robolectric/src/main/java/org/robolectric/android/internal/AndroidTestEnvironment.java index c1587fc17..f250381f4 100644 --- a/robolectric/src/main/java/org/robolectric/android/internal/AndroidTestEnvironment.java +++ b/robolectric/src/main/java/org/robolectric/android/internal/AndroidTestEnvironment.java @@ -87,6 +87,7 @@ import org.robolectric.shadows.ShadowPackageManager; import org.robolectric.shadows.ShadowPackageParser; import org.robolectric.shadows.ShadowPackageParser._Package_; import org.robolectric.shadows.ShadowView; +import org.robolectric.util.Logger; import org.robolectric.util.PerfStatsCollector; import org.robolectric.util.ReflectionHelpers; import org.robolectric.util.Scheduler; @@ -362,7 +363,7 @@ public class AndroidTestEnvironment implements TestEnvironment { // Preload fonts resources FontsContract.setApplicationContextForResources(application); } - registerBroadcastReceivers(application, appManifest); + registerBroadcastReceivers(application, appManifest, loadedApk); appResources.updateConfiguration(androidConfiguration, displayMetrics); // propagate any updates to configuration via RuntimeEnvironment.setQualifiers @@ -413,6 +414,11 @@ public class AndroidTestEnvironment implements TestEnvironment { Path packageFile = appManifest.getApkFile(); parsedPackage = ShadowPackageParser.callParsePackage(packageFile); } + if (parsedPackage != null + && parsedPackage.applicationInfo != null + && RuntimeEnvironment.getApiLevel() >= P) { + parsedPackage.applicationInfo.appComponentFactory = appManifest.getAppComponentFactory(); + } return parsedPackage; } @@ -697,15 +703,39 @@ public class AndroidTestEnvironment implements TestEnvironment { .toString(); } + private static BroadcastReceiver newBroadcastReceiverFromP( + String receiverClassName, LoadedApk loadedApk) { + ClassLoader classLoader = Shadow.class.getClassLoader(); + if (loadedApk == null || loadedApk.getAppFactory() == null) { + return (BroadcastReceiver) newInstanceOf(receiverClassName); + } else { + try { + return loadedApk.getAppFactory().instantiateReceiver(classLoader, receiverClassName, null); + } catch (ReflectiveOperationException e) { + Logger.warn( + "Failed to initialize receiver %s with AppComponentFactory %s: %s", + receiverClassName, loadedApk.getAppFactory(), e); + } + } + return null; + } + // TODO move/replace this with packageManager @VisibleForTesting - static void registerBroadcastReceivers(Application application, AndroidManifest androidManifest) { + static void registerBroadcastReceivers( + Application application, AndroidManifest androidManifest, LoadedApk loadedApk) { for (BroadcastReceiverData receiver : androidManifest.getBroadcastReceivers()) { IntentFilter filter = new IntentFilter(); for (String action : receiver.getActions()) { filter.addAction(action); } - application.registerReceiver((BroadcastReceiver) newInstanceOf(receiver.getName()), filter); + String receiverClassName = receiver.getName(); + if (loadedApk != null && RuntimeEnvironment.getApiLevel() >= P) { + application.registerReceiver( + newBroadcastReceiverFromP(receiverClassName, loadedApk), filter); + } else { + application.registerReceiver((BroadcastReceiver) newInstanceOf(receiverClassName), filter); + } } } diff --git a/robolectric/src/main/java/org/robolectric/internal/AndroidSandbox.java b/robolectric/src/main/java/org/robolectric/internal/AndroidSandbox.java old mode 100755 new mode 100644 diff --git a/robolectric/src/main/java/org/robolectric/internal/dependency/PropertiesDependencyResolver.java b/robolectric/src/main/java/org/robolectric/internal/dependency/PropertiesDependencyResolver.java old mode 100755 new mode 100644 diff --git a/robolectric/src/test/java/org/robolectric/CustomAppComponentFactory.java b/robolectric/src/test/java/org/robolectric/CustomAppComponentFactory.java new file mode 100644 index 000000000..22df750a9 --- /dev/null +++ b/robolectric/src/test/java/org/robolectric/CustomAppComponentFactory.java @@ -0,0 +1,22 @@ +package org.robolectric; + +import android.app.AppComponentFactory; +import android.content.BroadcastReceiver; +import android.content.Intent; +import org.robolectric.CustomConstructorReceiverWrapper.CustomConstructorWithEmptyActionReceiver; +import org.robolectric.CustomConstructorReceiverWrapper.CustomConstructorWithOneActionReceiver; + +public final class CustomAppComponentFactory extends AppComponentFactory { + @Override + public BroadcastReceiver instantiateReceiver(ClassLoader cl, String className, Intent intent) + throws InstantiationException, IllegalAccessException, ClassNotFoundException { + if (className != null) { + if (className.contains(CustomConstructorWithOneActionReceiver.class.getName())) { + return new CustomConstructorWithOneActionReceiver(100); + } else if (className.contains(CustomConstructorWithEmptyActionReceiver.class.getName())) { + return new CustomConstructorWithEmptyActionReceiver(100); + } + } + return super.instantiateReceiver(cl, className, intent); + } +} diff --git a/robolectric/src/test/java/org/robolectric/CustomConstructorReceiverWrapper.java b/robolectric/src/test/java/org/robolectric/CustomConstructorReceiverWrapper.java new file mode 100644 index 000000000..1132f6eb7 --- /dev/null +++ b/robolectric/src/test/java/org/robolectric/CustomConstructorReceiverWrapper.java @@ -0,0 +1,32 @@ +package org.robolectric; + +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; + +public class CustomConstructorReceiverWrapper { + private static class CustomConstructorReceiver extends BroadcastReceiver { + private final int intValue; + + public CustomConstructorReceiver(int intValue) { + // We don't use intValue actually, and we only want to use this class to test the + // initialization of BroadcastReceiver with a custom constructor. + this.intValue = intValue; + } + + @Override + public void onReceive(Context context, Intent intent) {} + } + + public static class CustomConstructorWithOneActionReceiver extends CustomConstructorReceiver { + public CustomConstructorWithOneActionReceiver(int intValue) { + super(intValue); + } + } + + public static class CustomConstructorWithEmptyActionReceiver extends CustomConstructorReceiver { + public CustomConstructorWithEmptyActionReceiver(int intValue) { + super(intValue); + } + } +} diff --git a/robolectric/src/test/java/org/robolectric/android/DrawableResourceLoaderTest.java b/robolectric/src/test/java/org/robolectric/android/DrawableResourceLoaderTest.java index 87ddb071b..0428c44e1 100644 --- a/robolectric/src/test/java/org/robolectric/android/DrawableResourceLoaderTest.java +++ b/robolectric/src/test/java/org/robolectric/android/DrawableResourceLoaderTest.java @@ -3,9 +3,9 @@ package org.robolectric.android; import static android.os.Build.VERSION_CODES.KITKAT_WATCH; import static android.os.Build.VERSION_CODES.LOLLIPOP; import static com.google.common.truth.Truth.assertThat; +import static com.google.common.truth.TruthJUnit.assume; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; -import static org.junit.Assume.assumeTrue; import static org.robolectric.shadows.ShadowAssetManager.useLegacy; import android.animation.Animator; @@ -31,7 +31,7 @@ public class DrawableResourceLoaderTest { @Before public void setup() throws Exception { - assumeTrue(useLegacy()); + assume().that(useLegacy()).isTrue(); resources = ApplicationProvider.getApplicationContext().getResources(); } diff --git a/robolectric/src/test/java/org/robolectric/android/ResourceLoaderTest.java b/robolectric/src/test/java/org/robolectric/android/ResourceLoaderTest.java index b895d6503..15ca52c68 100644 --- a/robolectric/src/test/java/org/robolectric/android/ResourceLoaderTest.java +++ b/robolectric/src/test/java/org/robolectric/android/ResourceLoaderTest.java @@ -2,7 +2,7 @@ package org.robolectric.android; import static android.os.Build.VERSION_CODES.O; import static com.google.common.truth.Truth.assertThat; -import static org.junit.Assume.assumeTrue; +import static com.google.common.truth.TruthJUnit.assume; import static org.robolectric.shadows.ShadowAssetManager.useLegacy; import android.content.res.Configuration; @@ -32,7 +32,7 @@ public class ResourceLoaderTest { @Before public void setUp() { - assumeTrue(useLegacy()); + assume().that(useLegacy()).isTrue(); optsForO = RuntimeEnvironment.getApiLevel() >= O ? "nowidecg-lowdr-" @@ -71,7 +71,11 @@ public class ResourceLoaderTest { private void checkForPollutionHelper() { assertThat(RuntimeEnvironment.getQualifiers()) - .isEqualTo("en-rUS-ldltr-sw320dp-w320dp-h470dp-normal-notlong-notround-" + optsForO + "port-notnight-mdpi-finger-keyssoft-nokeys-navhidden-nonav-v" + Build.VERSION.RESOURCES_SDK_INT); + .isEqualTo( + "en-rUS-ldltr-sw320dp-w320dp-h470dp-normal-notlong-notround-" + + optsForO + + "port-notnight-mdpi-finger-keyssoft-nokeys-navhidden-nonav-v" + + Build.VERSION.RESOURCES_SDK_INT); View view = LayoutInflater.from(ApplicationProvider.getApplicationContext()) @@ -97,7 +101,10 @@ public class ResourceLoaderTest { assertThat(resId).isNotNull(); assertThat(resourceProvider.getResName(resId)).isEqualTo(internalResource); - Class internalRIdClass = Robolectric.class.getClassLoader().loadClass("com.android.internal.R$" + internalResource.type); + Class internalRIdClass = + Robolectric.class + .getClassLoader() + .loadClass("com.android.internal.R$" + internalResource.type); int internalResourceId; internalResourceId = (Integer) internalRIdClass.getDeclaredField(internalResource.name).get(null); assertThat(resId).isEqualTo(internalResourceId); diff --git a/robolectric/src/test/java/org/robolectric/android/ResourceTableFactoryIntegrationTest.java b/robolectric/src/test/java/org/robolectric/android/ResourceTableFactoryIntegrationTest.java index 0ae467557..d4395c5a7 100644 --- a/robolectric/src/test/java/org/robolectric/android/ResourceTableFactoryIntegrationTest.java +++ b/robolectric/src/test/java/org/robolectric/android/ResourceTableFactoryIntegrationTest.java @@ -1,7 +1,7 @@ package org.robolectric.android; import static com.google.common.truth.Truth.assertThat; -import static org.junit.Assume.assumeTrue; +import static com.google.common.truth.TruthJUnit.assume; import static org.robolectric.shadows.ShadowAssetManager.useLegacy; import android.os.Build; @@ -17,7 +17,7 @@ import org.robolectric.res.ResName; public class ResourceTableFactoryIntegrationTest { @Test public void shouldIncludeStyleableAttributesThatDoNotHaveACorrespondingEntryInAttrClass() throws Exception { - assumeTrue(useLegacy()); + assume().that(useLegacy()).isTrue(); // This covers a corner case in Framework resources where an attribute is mentioned in a styleable array, e.g: R.styleable.Toolbar_buttonGravity but there is no corresponding R.attr.buttonGravity assertThat(RuntimeEnvironment.getSystemResourceTable() .getResourceId(new ResName("android", "attr", "buttonGravity"))).isGreaterThan(0); diff --git a/robolectric/src/test/java/org/robolectric/android/XmlResourceParserImplTest.java b/robolectric/src/test/java/org/robolectric/android/XmlResourceParserImplTest.java index b57f606ee..0c8d977d0 100644 --- a/robolectric/src/test/java/org/robolectric/android/XmlResourceParserImplTest.java +++ b/robolectric/src/test/java/org/robolectric/android/XmlResourceParserImplTest.java @@ -2,11 +2,11 @@ package org.robolectric.android; import static com.google.common.truth.Truth.assertThat; import static com.google.common.truth.Truth.assertWithMessage; +import static com.google.common.truth.TruthJUnit.assume; import static java.nio.charset.StandardCharsets.UTF_8; import static java.util.Arrays.asList; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; -import static org.junit.Assume.assumeTrue; import android.app.Application; import android.content.res.XmlResourceParser; @@ -276,7 +276,7 @@ public class XmlResourceParserImplTest { @Test public void testIsWhitespace() throws Exception { - assumeTrue(RuntimeEnvironment.useLegacyResources()); + assume().that(RuntimeEnvironment.useLegacyResources()).isTrue(); XmlResourceParserImpl parserImpl = (XmlResourceParserImpl) parser; assertThat(parserImpl.isWhitespace("bar")).isFalse(); diff --git a/robolectric/src/test/java/org/robolectric/android/internal/AndroidTestEnvironmentCreateApplicationTest.java b/robolectric/src/test/java/org/robolectric/android/internal/AndroidTestEnvironmentCreateApplicationTest.java index 6edc42886..dc6ac3138 100644 --- a/robolectric/src/test/java/org/robolectric/android/internal/AndroidTestEnvironmentCreateApplicationTest.java +++ b/robolectric/src/test/java/org/robolectric/android/internal/AndroidTestEnvironmentCreateApplicationTest.java @@ -88,7 +88,7 @@ public class AndroidTestEnvironmentCreateApplicationTest { Application application = AndroidTestEnvironment.createApplication(appManifest, null, new ApplicationInfo()); shadowOf(application).callAttach(RuntimeEnvironment.systemContext); - registerBroadcastReceivers(application, appManifest); + registerBroadcastReceivers(application, appManifest, null); List receivers = shadowOf(application).getRegisteredReceivers(); assertThat(receivers).hasSize(1); diff --git a/robolectric/src/test/java/org/robolectric/android/internal/AndroidTestEnvironmentTest.java b/robolectric/src/test/java/org/robolectric/android/internal/AndroidTestEnvironmentTest.java index 8b5551a08..375699417 100644 --- a/robolectric/src/test/java/org/robolectric/android/internal/AndroidTestEnvironmentTest.java +++ b/robolectric/src/test/java/org/robolectric/android/internal/AndroidTestEnvironmentTest.java @@ -2,8 +2,8 @@ package org.robolectric.android.internal; import static android.os.Build.VERSION_CODES.O; import static com.google.common.truth.Truth.assertThat; +import static com.google.common.truth.TruthJUnit.assume; import static org.junit.Assert.fail; -import static org.junit.Assume.assumeTrue; import static org.robolectric.annotation.ConscryptMode.Mode.OFF; import static org.robolectric.annotation.ConscryptMode.Mode.ON; import static org.robolectric.annotation.LooperMode.Mode.LEGACY; @@ -241,7 +241,7 @@ public class AndroidTestEnvironmentTest { @Test public void testResourceNotFound() { // not relevant for binary resources mode - assumeTrue(bootstrapWrapper.isLegacyResources()); + assume().that(bootstrapWrapper.isLegacyResources()).isTrue(); try { bootstrapWrapper.changeAppManifest(new ThrowingManifest(bootstrapWrapper.getAppManifest())); diff --git a/robolectric/src/test/java/org/robolectric/interceptors/AndroidInterceptorsIntegrationTest.java b/robolectric/src/test/java/org/robolectric/interceptors/AndroidInterceptorsIntegrationTest.java index 679b456f2..af1d8ba86 100644 --- a/robolectric/src/test/java/org/robolectric/interceptors/AndroidInterceptorsIntegrationTest.java +++ b/robolectric/src/test/java/org/robolectric/interceptors/AndroidInterceptorsIntegrationTest.java @@ -187,6 +187,6 @@ public class AndroidInterceptorsIntegrationTest { callsite .dynamicInvoker() .invokeWithArguments( - Arrays.stream(params).map(param -> param.val).collect(Collectors.toList())); + Arrays.stream(params).map(param -> param.value).collect(Collectors.toList())); } } diff --git a/robolectric/src/test/java/org/robolectric/internal/MavenManifestFactoryTest.java b/robolectric/src/test/java/org/robolectric/internal/MavenManifestFactoryTest.java old mode 100755 new mode 100644 diff --git a/robolectric/src/test/java/org/robolectric/internal/dependency/PropertiesDependencyResolverTest.java b/robolectric/src/test/java/org/robolectric/internal/dependency/PropertiesDependencyResolverTest.java old mode 100755 new mode 100644 diff --git a/robolectric/src/test/java/org/robolectric/res/StyleResourceLoaderTest.java b/robolectric/src/test/java/org/robolectric/res/StyleResourceLoaderTest.java index 8b493ba71..dc2026601 100644 --- a/robolectric/src/test/java/org/robolectric/res/StyleResourceLoaderTest.java +++ b/robolectric/src/test/java/org/robolectric/res/StyleResourceLoaderTest.java @@ -2,7 +2,7 @@ package org.robolectric.res; import static android.os.Build.VERSION_CODES.JELLY_BEAN; import static com.google.common.truth.Truth.assertThat; -import static org.junit.Assume.assumeTrue; +import static com.google.common.truth.TruthJUnit.assume; import static org.robolectric.util.TestUtil.sdkResources; import org.junit.Before; @@ -18,14 +18,16 @@ public class StyleResourceLoaderTest { @Before public void setUp() throws Exception { - assumeTrue(RuntimeEnvironment.useLegacyResources()); + assume().that(RuntimeEnvironment.useLegacyResources()).isTrue(); ResourcePath resourcePath = sdkResources(JELLY_BEAN); resourceTable = new ResourceTableFactory().newResourceTable("android", resourcePath); } @Test public void testStyleDataIsLoadedCorrectly() throws Exception { - TypedResource typedResource = resourceTable.getValue(new ResName("android", "style", "Theme_Holo"), new ResTable_config()); + TypedResource typedResource = + resourceTable.getValue( + new ResName("android", "style", "Theme_Holo"), new ResTable_config()); StyleData styleData = (StyleData) typedResource.getData(); assertThat(styleData.getName()).isEqualTo("Theme_Holo"); assertThat(styleData.getParent()).isEqualTo("Theme"); diff --git a/robolectric/src/test/java/org/robolectric/shadows/CellIdentityNrBuilderTest.java b/robolectric/src/test/java/org/robolectric/shadows/CellIdentityNrBuilderTest.java new file mode 100644 index 000000000..8474b2136 --- /dev/null +++ b/robolectric/src/test/java/org/robolectric/shadows/CellIdentityNrBuilderTest.java @@ -0,0 +1,90 @@ +package org.robolectric.shadows; + +import static com.google.common.truth.Truth.assertThat; + +import android.os.Build; +import android.telephony.AccessNetworkConstants; +import android.telephony.CellIdentityNr; +import android.telephony.CellInfo; +import androidx.test.ext.junit.runners.AndroidJUnit4; +import com.google.common.collect.ImmutableList; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.robolectric.annotation.Config; + +/** Test for {@link CellIdentityNrBuilder} */ +@RunWith(AndroidJUnit4.class) +@Config(minSdk = Build.VERSION_CODES.Q) +public class CellIdentityNrBuilderTest { + + private static final int PCI = 1; + private static final int TAC = 2; + private static final int NRARFCN = 4; + private static final int[] BANDS = + new int[] { + AccessNetworkConstants.NgranBands.BAND_1, AccessNetworkConstants.NgranBands.BAND_2 + }; + private static final String MCC = "310"; + private static final String MNC = "260"; + private static final int NCI = 0; + private static final String LONG_OPERATOR_NAME = "long operator name"; + private static final String SHORT_OPERATOR_NAME = "short operator name"; + private static final ImmutableList ADDITIONAL_PLMNS = ImmutableList.of("310240"); + + @Test + public void build_noArguments() { + // The intent is to primarily verify that there are no issues setting default values i.e., no + // exceptions thrown or invalid inputs. + CellIdentityNr cellIdentity = CellIdentityNrBuilder.newBuilder().build(); + + assertThat(cellIdentity.getPci()).isEqualTo(CellInfo.UNAVAILABLE); + } + + @Test + @Config(minSdk = Build.VERSION_CODES.Q, maxSdk = Build.VERSION_CODES.R) + public void build_sdkQtoR() { + CellIdentityNr cellIdentity = getCellIdentityNr(); + + assertCellIdentityFieldsForAllSdks(cellIdentity); + } + + @Test + @Config(minSdk = Build.VERSION_CODES.S) + public void build_fromSdkS() { + CellIdentityNr cellIdentity = getCellIdentityNr(); + + assertCellIdentityFieldsForAllSdks(cellIdentity); + assertThat(cellIdentity.getBands()).isEqualTo(BANDS); + assertThat(cellIdentity.getAdditionalPlmns()).containsExactlyElementsIn(ADDITIONAL_PLMNS); + } + + /** + * Assertions on {@link android.telephony.CellIdentityNr} values that are common across all tested + * SDKs. + */ + private void assertCellIdentityFieldsForAllSdks(CellIdentityNr cellIdentity) { + assertThat(cellIdentity.getPci()).isEqualTo(PCI); + assertThat(cellIdentity.getTac()).isEqualTo(TAC); + assertThat(cellIdentity.getNrarfcn()).isEqualTo(NRARFCN); + assertThat(cellIdentity.getMccString()).isEqualTo(MCC); + assertThat(cellIdentity.getMncString()).isEqualTo(MNC); + assertThat(cellIdentity.getNci()).isEqualTo(NCI); + assertThat(cellIdentity.getOperatorAlphaLong().toString()).isEqualTo(LONG_OPERATOR_NAME); + assertThat(cellIdentity.getOperatorAlphaShort().toString()).isEqualTo(SHORT_OPERATOR_NAME); + } + + private CellIdentityNr getCellIdentityNr() { + return CellIdentityNrBuilder.newBuilder() + .setPci(PCI) + .setTac(TAC) + .setNrarfcn(NRARFCN) + .setBands(BANDS) + .setMcc(MCC) + .setMnc(MNC) + .setNci(NCI) + .setLongOperatorName(LONG_OPERATOR_NAME) + .setShortOperatorName(SHORT_OPERATOR_NAME) + .setAdditionalPlmns(ADDITIONAL_PLMNS) + .build(); + } +} diff --git a/robolectric/src/test/java/org/robolectric/shadows/CellInfoNrBuilderTest.java b/robolectric/src/test/java/org/robolectric/shadows/CellInfoNrBuilderTest.java new file mode 100644 index 000000000..80e7dcc6e --- /dev/null +++ b/robolectric/src/test/java/org/robolectric/shadows/CellInfoNrBuilderTest.java @@ -0,0 +1,76 @@ +package org.robolectric.shadows; + +import static com.google.common.truth.Truth.assertThat; + +import android.os.Build; +import android.telephony.CellIdentityNr; +import android.telephony.CellInfoNr; +import android.telephony.CellSignalStrengthNr; +import androidx.test.ext.junit.runners.AndroidJUnit4; +import java.time.Duration; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.robolectric.annotation.Config; + +/** Test for {@link CellInfoNrBuilder} */ +@RunWith(AndroidJUnit4.class) +@Config(minSdk = Build.VERSION_CODES.Q) +public class CellInfoNrBuilderTest { + + private static final boolean REGISTERED = false; + private static final long TIMESTAMP_NANOS = 123L; + private static final long TIMESTAMP_MILLIS = Duration.ofNanos(TIMESTAMP_NANOS).toMillis(); + private static final int CELL_CONNECTION_STATUS = 1; + + private static final CellIdentityNr cellIdentity = + CellIdentityNrBuilder.newBuilder().setMcc("310").build(); + private static final CellSignalStrengthNr cellSignalStrength = + CellSignalStrengthNrBuilder.newBuilder().setCsiRsrp(-100).build(); + + @Test + public void build_noArguments() { + // The intent is to primarily verify that there are no issues setting default values i.e., no + // exceptions thrown or invalid inputs. + CellInfoNr cellInfo = CellInfoNrBuilder.newBuilder().build(); + + assertThat(cellInfo.getTimeStamp()).isEqualTo(0); + } + + @Test + @Config(sdk = Build.VERSION_CODES.Q) + public void build_sdkQ() { + CellInfoNr cellInfo = getCellInfoNr(); + + assertCellInfoFieldsForAllSdks(cellInfo); + } + + @Test + @Config(minSdk = Build.VERSION_CODES.R) + public void build_fromSdkR() { + CellInfoNr cellInfo = getCellInfoNr(); + + assertCellInfoFieldsForAllSdks(cellInfo); + assertThat(cellInfo.getTimestampMillis()).isEqualTo(TIMESTAMP_MILLIS); + } + + /** + * Assertions on {@link android.telephony.CellInfo} values that are common across all tested SDKs. + */ + private void assertCellInfoFieldsForAllSdks(CellInfoNr cellInfo) { + assertThat(cellInfo.isRegistered()).isFalse(); + assertThat(cellInfo.getTimeStamp()).isEqualTo(TIMESTAMP_NANOS); + assertThat(cellInfo.getCellConnectionStatus()).isEqualTo(CELL_CONNECTION_STATUS); + assertThat(cellInfo.getCellIdentity()).isEqualTo(cellIdentity); + assertThat(cellInfo.getCellSignalStrength()).isEqualTo(cellSignalStrength); + } + + private CellInfoNr getCellInfoNr() { + return CellInfoNrBuilder.newBuilder() + .setRegistered(REGISTERED) + .setTimeStampNanos(TIMESTAMP_NANOS) + .setCellConnectionStatus(CELL_CONNECTION_STATUS) + .setCellIdentity(cellIdentity) + .setCellSignalStrength(cellSignalStrength) + .build(); + } +} diff --git a/robolectric/src/test/java/org/robolectric/shadows/CellSignalStrengthNrBuilderTest.java b/robolectric/src/test/java/org/robolectric/shadows/CellSignalStrengthNrBuilderTest.java new file mode 100644 index 000000000..1f18ee8dc --- /dev/null +++ b/robolectric/src/test/java/org/robolectric/shadows/CellSignalStrengthNrBuilderTest.java @@ -0,0 +1,84 @@ +package org.robolectric.shadows; + +import static com.google.common.truth.Truth.assertThat; + +import android.os.Build; +import android.telephony.CellInfo; +import android.telephony.CellSignalStrengthNr; +import androidx.test.ext.junit.runners.AndroidJUnit4; +import com.google.common.collect.ImmutableList; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.robolectric.annotation.Config; + +/** Test for {@link CellSignalStrengthNrBuilder} */ +@RunWith(AndroidJUnit4.class) +@Config(minSdk = Build.VERSION_CODES.Q) +public class CellSignalStrengthNrBuilderTest { + + // The platform enforces that some of these values are within a certain range - otherwise, it will + // default to {@link android.telephony.CellInfo.UNAVAILABLE}. + private static final int CSI_RSRP = -100; + private static final int CSI_RSRQ = -10; + private static final int CSI_SINR = -20; + private static final int CSI_CQI_TABLE_INDEX = 1; + private static final ImmutableList CSI_CQI_REPORT = ImmutableList.of((byte) 7); + private static final int SS_RSRP = -140; + private static final int SS_RSRQ = -15; + private static final int SS_SINR = -20; + private static final int TIMING_ADVANCE = 10; + + @Test + public void build_noArguments() { + // The intent is to primarily verify that there are no issues setting default values i.e., no + // exceptions thrown or invalid inputs. + CellSignalStrengthNr cellSignalStrength = CellSignalStrengthNrBuilder.newBuilder().build(); + + assertThat(cellSignalStrength.getCsiRsrp()).isEqualTo(CellInfo.UNAVAILABLE); + } + + @Test + @Config(minSdk = Build.VERSION_CODES.Q, maxSdk = Build.VERSION_CODES.S_V2) + public void build_sdkQtoS() { + CellSignalStrengthNr cellSignalStrength = getCellSignalStrength(); + + assertCellSignalStrengthFieldsForAllSdks(cellSignalStrength); + } + + @Test + @Config(minSdk = Build.VERSION_CODES.TIRAMISU) + public void build_fromSdkT() { + CellSignalStrengthNr cellSignalStrength = getCellSignalStrength(); + + assertCellSignalStrengthFieldsForAllSdks(cellSignalStrength); + assertThat(cellSignalStrength.getCsiCqiTableIndex()).isEqualTo(CSI_CQI_TABLE_INDEX); + assertThat(cellSignalStrength.getCsiCqiReport()).containsExactly(7); + } + + /** + * Assertions on {@link android.telephony.CellSignalStrengthNr} values that are common across all + * tested SDKs. + */ + private void assertCellSignalStrengthFieldsForAllSdks(CellSignalStrengthNr cellSignalStrength) { + assertThat(cellSignalStrength.getCsiRsrp()).isEqualTo(CSI_RSRP); + assertThat(cellSignalStrength.getCsiRsrq()).isEqualTo(CSI_RSRQ); + assertThat(cellSignalStrength.getCsiSinr()).isEqualTo(CSI_SINR); + assertThat(cellSignalStrength.getSsRsrp()).isEqualTo(SS_RSRP); + assertThat(cellSignalStrength.getSsRsrq()).isEqualTo(SS_RSRQ); + assertThat(cellSignalStrength.getSsSinr()).isEqualTo(SS_SINR); + } + + private CellSignalStrengthNr getCellSignalStrength() { + return CellSignalStrengthNrBuilder.newBuilder() + .setCsiRsrp(CSI_RSRP) + .setCsiRsrq(CSI_RSRQ) + .setCsiSinr(CSI_SINR) + .setCsiCqiTableIndex(CSI_CQI_TABLE_INDEX) + .setCsiCqiReport(CSI_CQI_REPORT) + .setSsRsrp(SS_RSRP) + .setSsRsrq(SS_RSRQ) + .setSsSinr(SS_SINR) + .setTimingAdvance(TIMING_ADVANCE) + .build(); + } +} diff --git a/robolectric/src/test/java/org/robolectric/shadows/ShadowAssetManagerTest.java b/robolectric/src/test/java/org/robolectric/shadows/ShadowAssetManagerTest.java index 9ecb63d06..5de84d80d 100644 --- a/robolectric/src/test/java/org/robolectric/shadows/ShadowAssetManagerTest.java +++ b/robolectric/src/test/java/org/robolectric/shadows/ShadowAssetManagerTest.java @@ -1,8 +1,8 @@ package org.robolectric.shadows; import static com.google.common.truth.Truth.assertThat; +import static com.google.common.truth.TruthJUnit.assume; import static org.junit.Assert.fail; -import static org.junit.Assume.assumeTrue; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; import static org.robolectric.shadows.ShadowAssetManager.legacyShadowOf; @@ -46,7 +46,7 @@ public class ShadowAssetManagerTest { @Test public void openFd_shouldProvideFileDescriptorForDeflatedAsset() throws Exception { - assumeTrue(!useLegacy()); + assume().that(useLegacy()).isFalse(); expectedException.expect(FileNotFoundException.class); expectedException.expectMessage( "This file can not be opened as a file descriptor; it is probably compressed"); @@ -79,7 +79,7 @@ public class ShadowAssetManagerTest { @Test public void openNonAssetShouldThrowExceptionWhenFileDoesNotExist() throws IOException { - assumeTrue(useLegacy()); + assume().that(useLegacy()).isTrue(); expectedException.expect(IOException.class); expectedException.expectMessage( @@ -90,7 +90,7 @@ public class ShadowAssetManagerTest { @Test public void unknownResourceIdsShouldReportPackagesSearched() throws IOException { - assumeTrue(useLegacy()); + assume().that(useLegacy()).isTrue(); expectedException.expect(Resources.NotFoundException.class); expectedException.expectMessage("Resource ID #0xffffffff"); @@ -102,7 +102,7 @@ public class ShadowAssetManagerTest { @Test public void forSystemResources_unknownResourceIdsShouldReportPackagesSearched() throws IOException { - if (!useLegacy()) return; + assume().that(useLegacy()).isTrue(); expectedException.expect(Resources.NotFoundException.class); expectedException.expectMessage("Resource ID #0xffffffff"); @@ -113,8 +113,7 @@ public class ShadowAssetManagerTest { @Test @Config(qualifiers = "mdpi") public void openNonAssetShouldOpenCorrectAssetBasedOnQualifierMdpi() throws IOException { - if (!useLegacy()) return; - + assume().that(useLegacy()).isTrue(); InputStream inputStream = assetManager.openNonAsset(0, "res/drawable/robolectric.png", 0); assertThat(countBytes(inputStream)).isEqualTo(8141); } @@ -122,8 +121,7 @@ public class ShadowAssetManagerTest { @Test @Config(qualifiers = "hdpi") public void openNonAssetShouldOpenCorrectAssetBasedOnQualifierHdpi() throws IOException { - if (!useLegacy()) return; - + assume().that(useLegacy()).isTrue(); InputStream inputStream = assetManager.openNonAsset(0, "res/drawable/robolectric.png", 0); assertThat(countBytes(inputStream)).isEqualTo(23447); } @@ -178,8 +176,7 @@ public class ShadowAssetManagerTest { @Test public void attrsToTypedArray_shouldAllowMockedAttributeSets() { - if (!useLegacy()) return; - + assume().that(useLegacy()).isTrue(); AttributeSet mockAttributeSet = mock(AttributeSet.class); when(mockAttributeSet.getAttributeCount()).thenReturn(1); when(mockAttributeSet.getAttributeNameResource(0)).thenReturn(android.R.attr.windowBackground); @@ -191,7 +188,7 @@ public class ShadowAssetManagerTest { @Test public void whenStyleAttrResolutionFails_attrsToTypedArray_returnsNiceErrorMessage() { - if (!useLegacy()) return; + assume().that(useLegacy()).isTrue(); expectedException.expect(RuntimeException.class); expectedException.expectMessage( "no value for org.robolectric:attr/styleNotSpecifiedInAnyTheme in theme with applied" diff --git a/robolectric/src/test/java/org/robolectric/shadows/ShadowContextWrapperTest.java b/robolectric/src/test/java/org/robolectric/shadows/ShadowContextWrapperTest.java index 4d71cbfb4..03d9d732c 100644 --- a/robolectric/src/test/java/org/robolectric/shadows/ShadowContextWrapperTest.java +++ b/robolectric/src/test/java/org/robolectric/shadows/ShadowContextWrapperTest.java @@ -4,6 +4,7 @@ import static android.content.pm.PackageManager.PERMISSION_DENIED; import static android.content.pm.PackageManager.PERMISSION_GRANTED; import static android.os.Build.VERSION_CODES.KITKAT; import static android.os.Build.VERSION_CODES.M; +import static android.os.Build.VERSION_CODES.P; import static com.google.common.truth.Truth.assertThat; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotSame; @@ -46,6 +47,8 @@ import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.robolectric.ConfigTestReceiver; +import org.robolectric.CustomConstructorReceiverWrapper.CustomConstructorWithEmptyActionReceiver; +import org.robolectric.CustomConstructorReceiverWrapper.CustomConstructorWithOneActionReceiver; import org.robolectric.R; import org.robolectric.Robolectric; import org.robolectric.RuntimeEnvironment; @@ -94,6 +97,20 @@ public class ShadowContextWrapperTest { assertThat(receiver.intentsReceived).hasSize(1); } + @Test + @Config(manifest = "TestAndroidManifestWithAppComponentFactory.xml", minSdk = P) + public void registerReceiver_shouldGetReceiverWithCustomConstructorEmptyAction() { + BroadcastReceiver receiver = getReceiverOfClass(CustomConstructorWithEmptyActionReceiver.class); + assertThat(receiver).isInstanceOf(CustomConstructorWithEmptyActionReceiver.class); + } + + @Test + @Config(manifest = "TestAndroidManifestWithAppComponentFactory.xml", minSdk = P) + public void registerReceiver_shouldGetReceiverWithCustomConstructorAndOneAction() { + BroadcastReceiver receiver = getReceiverOfClass(CustomConstructorWithOneActionReceiver.class); + assertThat(receiver).isInstanceOf(CustomConstructorWithOneActionReceiver.class); + } + @Test public void registerReceiver_shouldRegisterForAllIntentFilterActions() throws Exception { BroadcastReceiver receiver = broadcastReceiver("Larry"); diff --git a/robolectric/src/test/java/org/robolectric/shadows/ShadowLauncherAppsTest.java b/robolectric/src/test/java/org/robolectric/shadows/ShadowLauncherAppsTest.java index 4b648f89e..47c16b4f4 100644 --- a/robolectric/src/test/java/org/robolectric/shadows/ShadowLauncherAppsTest.java +++ b/robolectric/src/test/java/org/robolectric/shadows/ShadowLauncherAppsTest.java @@ -197,6 +197,20 @@ public class ShadowLauncherAppsTest { .containsExactly(launcherActivityInfo1, launcherActivityInfo2); } + @Test + @Config(minSdk = L) + public void testIsActivityEnabled() { + ComponentName c1 = new ComponentName(ApplicationProvider.getApplicationContext(), "Activity1"); + ComponentName c2 = new ComponentName(ApplicationProvider.getApplicationContext(), "Activity2"); + ComponentName c3 = new ComponentName("other", "Activity1"); + assertThat(launcherApps.isActivityEnabled(c1, USER_HANDLE)).isFalse(); + + shadowOf(launcherApps).setActivityEnabled(USER_HANDLE, c1); + assertThat(launcherApps.isActivityEnabled(c1, USER_HANDLE)).isTrue(); + assertThat(launcherApps.isActivityEnabled(c2, USER_HANDLE)).isFalse(); + assertThat(launcherApps.isActivityEnabled(c3, USER_HANDLE)).isFalse(); + } + @Test @Config(minSdk = O) public void testGetApplicationInfo_packageNotFound() throws Exception { diff --git a/robolectric/src/test/java/org/robolectric/shadows/ShadowPaintTest.java b/robolectric/src/test/java/org/robolectric/shadows/ShadowPaintTest.java index 6ae57f951..7744b9507 100644 --- a/robolectric/src/test/java/org/robolectric/shadows/ShadowPaintTest.java +++ b/robolectric/src/test/java/org/robolectric/shadows/ShadowPaintTest.java @@ -70,6 +70,15 @@ public class ShadowPaintTest { assertThat(paint.isUnderlineText()).isFalse(); } + @Test + public void shouldSetStrikeThruText() { + Paint paint = new Paint(); + paint.setStrikeThruText(true); + assertThat(paint.isStrikeThruText()).isTrue(); + paint.setStrikeThruText(false); + assertThat(paint.isStrikeThruText()).isFalse(); + } + @Test public void measureTextActuallyMeasuresLength() { Paint paint = new Paint(); diff --git a/robolectric/src/test/java/org/robolectric/shadows/ShadowPausedLooperTest.java b/robolectric/src/test/java/org/robolectric/shadows/ShadowPausedLooperTest.java index 8801f1e1a..71e85c9b6 100644 --- a/robolectric/src/test/java/org/robolectric/shadows/ShadowPausedLooperTest.java +++ b/robolectric/src/test/java/org/robolectric/shadows/ShadowPausedLooperTest.java @@ -14,6 +14,7 @@ import static org.mockito.Mockito.when; import static org.robolectric.Shadows.shadowOf; import static org.robolectric.shadows.ShadowLooper.shadowMainLooper; +import android.os.Build.VERSION_CODES; import android.os.Handler; import android.os.HandlerThread; import android.os.Looper; @@ -33,6 +34,7 @@ import org.junit.Rule; import org.junit.Test; import org.junit.rules.TestName; import org.junit.runner.RunWith; +import org.robolectric.annotation.Config; import org.robolectric.annotation.LooperMode; import org.robolectric.res.android.Ref; import org.robolectric.shadow.api.Shadow; @@ -621,6 +623,43 @@ public class ShadowPausedLooperTest { assertThat(foregroundThreadReceived.get()).isTrue(); } + @Test + @Config(minSdk = VERSION_CODES.M) + public void runOneTask_ignoreSyncBarrier() { + int barrier = Looper.getMainLooper().getQueue().postSyncBarrier(); + + final AtomicBoolean wasRun = new AtomicBoolean(false); + new Handler(Looper.getMainLooper()).post(() -> wasRun.set(true)); + + ShadowPausedLooper shadowPausedLooper = Shadow.extract(Looper.getMainLooper()); + shadowPausedLooper.runOneTask(); + + // tasks should not be executed when blocked by a sync barrier + assertThat(wasRun.get()).isFalse(); + // sync barrier will throw if the barrier was not found. + Looper.getMainLooper().getQueue().removeSyncBarrier(barrier); + + shadowPausedLooper.runOneTask(); + assertThat(wasRun.get()).isTrue(); + } + + @Test + @Config(minSdk = VERSION_CODES.P) + public void runOneTask_ignoreSyncBarrier_with_async() { + int barrier = Looper.getMainLooper().getQueue().postSyncBarrier(); + + final AtomicBoolean wasRun = new AtomicBoolean(false); + Handler.createAsync(Looper.getMainLooper()).post(() -> wasRun.set(true)); + + ShadowPausedLooper shadowPausedLooper = Shadow.extract(Looper.getMainLooper()); + shadowPausedLooper.runOneTask(); + + // tasks should be executed as the handler is async + assertThat(wasRun.get()).isTrue(); + // sync barrier will throw if the barrier was not found. + Looper.getMainLooper().getQueue().removeSyncBarrier(barrier); + } + private static class BlockingRunnable implements Runnable { CountDownLatch latch = new CountDownLatch(1); diff --git a/robolectric/src/test/java/org/robolectric/shadows/ShadowResourcesTest.java b/robolectric/src/test/java/org/robolectric/shadows/ShadowResourcesTest.java index da3440139..a4732b920 100644 --- a/robolectric/src/test/java/org/robolectric/shadows/ShadowResourcesTest.java +++ b/robolectric/src/test/java/org/robolectric/shadows/ShadowResourcesTest.java @@ -2,8 +2,7 @@ package org.robolectric.shadows; import static android.os.Build.VERSION_CODES.N_MR1; import static com.google.common.truth.Truth.assertThat; -import static org.junit.Assume.assumeFalse; -import static org.junit.Assume.assumeTrue; +import static com.google.common.truth.TruthJUnit.assume; import static org.robolectric.Shadows.shadowOf; import static org.robolectric.shadows.ShadowAssetManager.useLegacy; @@ -85,7 +84,7 @@ public class ShadowResourcesTest { @Test public void openRawResourceFd_shouldReturnsNullForLegacyResource() throws Exception { - assumeTrue(useLegacy()); + assume().that(useLegacy()).isTrue(); try (AssetFileDescriptor afd = resources.openRawResourceFd(R.raw.raw_resource)) { assertThat(afd).isNull(); } @@ -93,7 +92,7 @@ public class ShadowResourcesTest { @Test public void openRawResourceFd_shouldReturnsValidFdForUnCompressFile() throws Exception { - assumeFalse(useLegacy()); + assume().that(useLegacy()).isFalse(); try (AssetFileDescriptor afd = resources.openRawResourceFd(R.raw.raw_resource)) { assertThat(afd).isNotNull(); } diff --git a/robolectric/src/test/java/org/robolectric/shadows/ShadowSQLiteConnectionTest.java b/robolectric/src/test/java/org/robolectric/shadows/ShadowSQLiteConnectionTest.java index 323511fc3..878777c10 100644 --- a/robolectric/src/test/java/org/robolectric/shadows/ShadowSQLiteConnectionTest.java +++ b/robolectric/src/test/java/org/robolectric/shadows/ShadowSQLiteConnectionTest.java @@ -3,8 +3,8 @@ package org.robolectric.shadows; import static android.os.Build.VERSION_CODES.LOLLIPOP; import static com.google.common.truth.Truth.assertThat; import static com.google.common.truth.Truth.assertWithMessage; +import static com.google.common.truth.TruthJUnit.assume; import static org.junit.Assert.fail; -import static org.junit.Assume.assumeTrue; import static org.robolectric.annotation.SQLiteMode.Mode.LEGACY; import static org.robolectric.shadows.ShadowLegacySQLiteConnection.convertSQLWithLocalizedUnicodeCollator; @@ -64,7 +64,7 @@ public class ShadowSQLiteConnectionTest { @Test public void testSqlConversion() { - assumeTrue(SQLiteLibraryLoader.isOsSupported()); + assume().that(SQLiteLibraryLoader.isOsSupported()).isTrue(); assertThat(convertSQLWithLocalizedUnicodeCollator("select * from `routine`")) .isEqualTo("select * from `routine`"); @@ -88,7 +88,7 @@ public class ShadowSQLiteConnectionTest { @Test public void testSQLWithLocalizedOrUnicodeCollatorShouldBeSortedAsNoCase() { - assumeTrue(SQLiteLibraryLoader.isOsSupported()); + assume().that(SQLiteLibraryLoader.isOsSupported()).isTrue(); database.execSQL("insert into routine(name) values ('الصحافة اليدوية')"); database.execSQL("insert into routine(name) values ('Hand press 1')"); database.execSQL("insert into routine(name) values ('hand press 2')"); @@ -116,28 +116,28 @@ public class ShadowSQLiteConnectionTest { @Test public void nativeOpen_addsConnectionToPool() { - assumeTrue(SQLiteLibraryLoader.isOsSupported()); + assume().that(SQLiteLibraryLoader.isOsSupported()).isTrue(); assertThat(conn).isNotNull(); assertWithMessage("open").that(conn.isOpen()).isTrue(); } @Test public void nativeClose_closesConnection() { - assumeTrue(SQLiteLibraryLoader.isOsSupported()); + assume().that(SQLiteLibraryLoader.isOsSupported()).isTrue(); ShadowLegacySQLiteConnection.nativeClose(ptr); assertWithMessage("open").that(conn.isOpen()).isFalse(); } @Test public void reset_closesConnection() { - assumeTrue(SQLiteLibraryLoader.isOsSupported()); + assume().that(SQLiteLibraryLoader.isOsSupported()).isTrue(); ShadowLegacySQLiteConnection.reset(); assertWithMessage("open").that(conn.isOpen()).isFalse(); } @Test public void reset_clearsConnectionCache() { - assumeTrue(SQLiteLibraryLoader.isOsSupported()); + assume().that(SQLiteLibraryLoader.isOsSupported()).isTrue(); final Map connectionsMap = ReflectionHelpers.getField(connections, "connectionsMap"); @@ -149,7 +149,7 @@ public class ShadowSQLiteConnectionTest { @Test public void reset_clearsStatementCache() { - assumeTrue(SQLiteLibraryLoader.isOsSupported()); + assume().that(SQLiteLibraryLoader.isOsSupported()).isTrue(); final Map statementsMap = ReflectionHelpers.getField(connections, "statementsMap"); @@ -161,7 +161,7 @@ public class ShadowSQLiteConnectionTest { @Test public void error_resultsInSpecificExceptionWithCause() { - assumeTrue(SQLiteLibraryLoader.isOsSupported()); + assume().that(SQLiteLibraryLoader.isOsSupported()).isTrue(); try { database.execSQL("insert into routine(name) values ('Hand press 1')"); ContentValues values = new ContentValues(1); @@ -178,7 +178,7 @@ public class ShadowSQLiteConnectionTest { @Test public void interruption_doesNotConcurrentlyModifyDatabase() { - assumeTrue(SQLiteLibraryLoader.isOsSupported()); + assume().that(SQLiteLibraryLoader.isOsSupported()).isTrue(); Thread.currentThread().interrupt(); try { database.execSQL("insert into routine(name) values ('الصحافة اليدوية')"); @@ -190,7 +190,7 @@ public class ShadowSQLiteConnectionTest { @Test public void test_setUseInMemoryDatabase() { - assumeTrue(SQLiteLibraryLoader.isOsSupported()); + assume().that(SQLiteLibraryLoader.isOsSupported()).isTrue(); assertThat(conn.isMemoryDatabase()).isFalse(); ShadowSQLiteConnection.setUseInMemoryDatabase(true); SQLiteDatabase inMemoryDb = createDatabase("in_memory.db"); @@ -201,7 +201,7 @@ public class ShadowSQLiteConnectionTest { @Test public void cancel_shouldCancelAllStatements() { - assumeTrue(SQLiteLibraryLoader.isOsSupported()); + assume().that(SQLiteLibraryLoader.isOsSupported()).isTrue(); SQLiteStatement statement1 = database.compileStatement("insert into routine(name) values ('Hand press 1')"); SQLiteStatement statement2 = diff --git a/robolectric/src/test/java/org/robolectric/shadows/ShadowTelephonyManagerTest.java b/robolectric/src/test/java/org/robolectric/shadows/ShadowTelephonyManagerTest.java index f6b09950d..ad3adebcf 100644 --- a/robolectric/src/test/java/org/robolectric/shadows/ShadowTelephonyManagerTest.java +++ b/robolectric/src/test/java/org/robolectric/shadows/ShadowTelephonyManagerTest.java @@ -27,6 +27,8 @@ import static android.telephony.TelephonyManager.CALL_STATE_OFFHOOK; import static android.telephony.TelephonyManager.CALL_STATE_RINGING; import static android.telephony.TelephonyManager.NETWORK_TYPE_EVDO_0; import static android.telephony.TelephonyManager.NETWORK_TYPE_LTE; +import static android.telephony.emergency.EmergencyNumber.EMERGENCY_NUMBER_SOURCE_DATABASE; +import static android.telephony.emergency.EmergencyNumber.EMERGENCY_SERVICE_CATEGORY_POLICE; import static com.google.common.truth.Truth.assertThat; import static com.google.common.util.concurrent.MoreExecutors.directExecutor; import static org.junit.Assert.assertEquals; @@ -74,10 +76,12 @@ import android.telephony.TelephonyManager.BootstrapAuthenticationCallback; import android.telephony.TelephonyManager.CellInfoCallback; import android.telephony.UiccSlotInfo; import android.telephony.VisualVoicemailSmsFilterSettings; +import android.telephony.emergency.EmergencyNumber; import android.telephony.gba.UaSecurityProtocolIdentifier; import androidx.test.core.app.ApplicationProvider; import androidx.test.ext.junit.runners.AndroidJUnit4; import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableMap; import java.util.Collections; import java.util.List; import java.util.Locale; @@ -1101,4 +1105,36 @@ public class ShadowTelephonyManagerTest { public void getEmergencyCallback_notSet_returnsFalse() { assertThat(telephonyManager.getEmergencyCallbackMode()).isFalse(); } + + @Test + @Config(minSdk = R) + public void getEmergencyNumbersList_notSet_returnsEmptyList() { + assertThat(telephonyManager.getEmergencyNumberList()).isEmpty(); + } + + @Test + @Config(minSdk = R) + public void getEmergencyNumbersList_wasSet_returnsCorrectList() throws Exception { + EmergencyNumber emergencyNumber = + EmergencyNumber.class + .getConstructor( + String.class, + String.class, + String.class, + int.class, + List.class, + int.class, + int.class) + .newInstance( + "911", + "us", + "30", + EMERGENCY_NUMBER_SOURCE_DATABASE, + ImmutableList.of(), + EMERGENCY_SERVICE_CATEGORY_POLICE, + EmergencyNumber.EMERGENCY_CALL_ROUTING_NORMAL); + ShadowTelephonyManager.setEmergencyNumberList( + ImmutableMap.of(0, ImmutableList.of(emergencyNumber))); + assertThat(telephonyManager.getEmergencyNumberList().get(0)).containsExactly(emergencyNumber); + } } diff --git a/robolectric/src/test/java/org/robolectric/shadows/ShadowVibratorTest.java b/robolectric/src/test/java/org/robolectric/shadows/ShadowVibratorTest.java index bc93daac0..ef5527abf 100644 --- a/robolectric/src/test/java/org/robolectric/shadows/ShadowVibratorTest.java +++ b/robolectric/src/test/java/org/robolectric/shadows/ShadowVibratorTest.java @@ -16,7 +16,6 @@ import android.content.Context; import android.media.AudioAttributes; import android.os.VibrationEffect; import android.os.Vibrator; -import android.os.vibrator.PrimitiveSegment; import androidx.test.core.app.ApplicationProvider; import androidx.test.ext.junit.runners.AndroidJUnit4; import com.google.common.collect.ImmutableList; @@ -130,48 +129,6 @@ public class ShadowVibratorTest { new PrimitiveEffect(EFFECT_CLICK, /* scale= */ 1f, /* delay= */ 2150))); } - @Config(minSdk = S) - @Test - public void getVibrationEffectSegments_composeOnce_shouldReturnSameFragment() { - vibrator.vibrate( - VibrationEffect.startComposition() - .addPrimitive(EFFECT_CLICK, /* scale= */ 0.5f, /* delay= */ 20) - .addPrimitive(EFFECT_CLICK, /* scale= */ 0.7f, /* delay= */ 50) - .addPrimitive(EFFECT_CLICK, /* scale= */ 0.9f, /* delay= */ 150) - .compose()); - - assertThat(shadowOf(vibrator).getVibrationEffectSegments()) - .isEqualTo( - ImmutableList.of( - new PrimitiveSegment(EFFECT_CLICK, /* scale= */ 0.5f, /* delay= */ 20), - new PrimitiveSegment(EFFECT_CLICK, /* scale= */ 0.7f, /* delay= */ 50), - new PrimitiveSegment(EFFECT_CLICK, /* scale= */ 0.9f, /* delay= */ 150))); - } - - @Config(minSdk = S) - @Test - public void getVibrationEffectSegments_composeTwice_shouldReturnTheLastComposition() { - vibrator.vibrate( - VibrationEffect.startComposition() - .addPrimitive(EFFECT_CLICK, /* scale= */ 0.5f, /* delay= */ 20) - .addPrimitive(EFFECT_CLICK, /* scale= */ 0.7f, /* delay= */ 50) - .addPrimitive(EFFECT_CLICK, /* scale= */ 0.9f, /* delay= */ 150) - .compose()); - vibrator.vibrate( - VibrationEffect.startComposition() - .addPrimitive(EFFECT_CLICK, /* scale= */ 0.4f, /* delay= */ 120) - .addPrimitive(EFFECT_CLICK, /* scale= */ 0.9f, /* delay= */ 150) - .addPrimitive(EFFECT_CLICK, /* scale= */ 1f, /* delay= */ 2150) - .compose()); - - assertThat(shadowOf(vibrator).getVibrationEffectSegments()) - .isEqualTo( - ImmutableList.of( - new PrimitiveSegment(EFFECT_CLICK, /* scale= */ 0.4f, /* delay= */ 120), - new PrimitiveSegment(EFFECT_CLICK, /* scale= */ 0.9f, /* delay= */ 150), - new PrimitiveSegment(EFFECT_CLICK, /* scale= */ 1f, /* delay= */ 2150))); - } - @Config(minSdk = S) @Test public void getPrimitiveSegmentsInPrimitiveEffects_composeOnce_shouldReturnSameFragment() { diff --git a/robolectric/src/test/java/org/robolectric/util/SQLiteLibraryLoaderTest.java b/robolectric/src/test/java/org/robolectric/util/SQLiteLibraryLoaderTest.java index 3b06a4e32..612fd3bfa 100644 --- a/robolectric/src/test/java/org/robolectric/util/SQLiteLibraryLoaderTest.java +++ b/robolectric/src/test/java/org/robolectric/util/SQLiteLibraryLoaderTest.java @@ -1,8 +1,8 @@ package org.robolectric.util; import static com.google.common.truth.Truth.assertThat; +import static com.google.common.truth.TruthJUnit.assume; import static org.junit.Assert.assertThrows; -import static org.junit.Assume.assumeTrue; import androidx.test.ext.junit.runners.AndroidJUnit4; import org.junit.After; @@ -56,7 +56,7 @@ public class SQLiteLibraryLoaderTest { @Test public void shouldExtractNativeLibrary() { - assumeTrue(SQLiteLibraryLoader.isOsSupported()); + assume().that(SQLiteLibraryLoader.isOsSupported()).isTrue(); assertThat(loader.isLoaded()).isFalse(); loader.doLoad(); assertThat(loader.isLoaded()).isTrue(); diff --git a/robolectric/src/test/resources/TestAndroidManifestWithAppComponentFactory.xml b/robolectric/src/test/resources/TestAndroidManifestWithAppComponentFactory.xml new file mode 100644 index 000000000..cbda17e65 --- /dev/null +++ b/robolectric/src/test/resources/TestAndroidManifestWithAppComponentFactory.xml @@ -0,0 +1,16 @@ + + + + + + + + + + + + + diff --git a/sandbox/build.gradle b/sandbox/build.gradle index 64accd737..358b027c2 100644 --- a/sandbox/build.gradle +++ b/sandbox/build.gradle @@ -5,24 +5,24 @@ apply plugin: RoboJavaModulePlugin apply plugin: DeployedRoboJavaModulePlugin dependencies { - annotationProcessor "com.google.auto.service:auto-service:$autoServiceVersion" - annotationProcessor "com.google.errorprone:error_prone_core:$errorproneVersion" + annotationProcessor libs.auto.service + annotationProcessor libs.error.prone.core api project(":annotations") api project(":utils") api project(":shadowapi") api project(":utils:reflector") - compileOnly "com.google.auto.service:auto-service-annotations:$autoServiceVersion" - api "javax.annotation:javax.annotation-api:1.3.2" - api "javax.inject:javax.inject:1" + compileOnly libs.auto.service.annotations + api libs.javax.annotation.api + api libs.javax.inject - api "org.ow2.asm:asm:${asmVersion}" - api "org.ow2.asm:asm-commons:${asmVersion}" - api "com.google.guava:guava:$guavaJREVersion" - compileOnly "com.google.code.findbugs:jsr305:3.0.2" + api libs.asm + api libs.asm.commons + api libs.guava + compileOnly libs.findbugs.jsr305 - testImplementation "junit:junit:${junitVersion}" - testImplementation "com.google.truth:truth:${truthVersion}" - testImplementation "org.mockito:mockito-core:${mockitoVersion}" + testImplementation libs.junit4 + testImplementation libs.truth + testImplementation libs.mockito testImplementation project(":junit") } diff --git a/shadowapi/build.gradle b/shadowapi/build.gradle index f63d048e1..3f0064fb7 100644 --- a/shadowapi/build.gradle +++ b/shadowapi/build.gradle @@ -5,11 +5,11 @@ apply plugin: RoboJavaModulePlugin apply plugin: DeployedRoboJavaModulePlugin dependencies { - compileOnly "com.google.code.findbugs:jsr305:3.0.2" + compileOnly libs.findbugs.jsr305 api project(":annotations") api project(":utils") - testImplementation "junit:junit:${junitVersion}" - testImplementation "com.google.truth:truth:${truthVersion}" - testImplementation "org.mockito:mockito-core:${mockitoVersion}" -} \ No newline at end of file + testImplementation libs.junit4 + testImplementation libs.truth + testImplementation libs.mockito +} diff --git a/shadowapi/src/main/java/org/robolectric/util/ReflectionHelpers.java b/shadowapi/src/main/java/org/robolectric/util/ReflectionHelpers.java index eaaee1a3d..8ae639971 100644 --- a/shadowapi/src/main/java/org/robolectric/util/ReflectionHelpers.java +++ b/shadowapi/src/main/java/org/robolectric/util/ReflectionHelpers.java @@ -10,7 +10,6 @@ import java.lang.reflect.Proxy; import java.util.Collections; import java.util.HashMap; import java.util.Map; -import javax.annotation.Nullable; /** Collection of helper methods for calling methods and accessing fields reflectively. */ @SuppressWarnings(value = {"unchecked", "TypeParameterUnusedInFormals", "NewApi"}) @@ -45,9 +44,10 @@ public class ReflectionHelpers { *

The returned object will be an instance of the given class, but all methods will return * either the "default" value for primitives, or another deep proxy for non-primitive types. * - *

This should be used rarely, for cases where we need to create deep proxies in order not - * to crash. The inner proxies are impossible to configure, so there is no way to create - * meaningful behavior from a deep proxy. It serves mainly to prevent Null Pointer Exceptions. + *

This should be used rarely, for cases where we need to create deep proxies in order not to + * crash. The inner proxies are impossible to configure, so there is no way to create meaningful + * behavior from a deep proxy. It serves mainly to prevent Null Pointer Exceptions. + * * @param clazz the class to provide a proxy instance of. * @return a new "Deep Proxy" instance of the given class. */ @@ -127,7 +127,8 @@ public class ReflectionHelpers { * @param fieldName The field name. * @param fieldNewValue New value. */ - public static void setField(final Object object, final String fieldName, final Object fieldNewValue) { + public static void setField( + final Object object, final String fieldName, final Object fieldNewValue) { try { traverseClassHierarchy( object.getClass(), @@ -152,7 +153,8 @@ public class ReflectionHelpers { * @param fieldName The field name. * @param fieldNewValue New value. */ - public static void setField(Class type, final Object object, final String fieldName, final Object fieldNewValue) { + public static void setField( + Class type, final Object object, final String fieldName, final Object fieldNewValue) { try { Field field = type.getDeclaredField(fieldName); field.setAccessible(true); @@ -162,6 +164,22 @@ public class ReflectionHelpers { } } + /** + * Reflectively check if a class has a given field (static or non static). + * + * @param clazz Target class. + * @param fieldName The field name. + * @return boolean to indicate whether the field exists or not in clazz. + */ + public static boolean hasField(Class clazz, String fieldName) { + try { + Field field = clazz.getDeclaredField(fieldName); + return (field != null); + } catch (NoSuchFieldException e) { + return false; + } + } + /** * Reflectively get the value of a static field. * @@ -392,7 +410,9 @@ public class ReflectionHelpers { public static T newInstance(Class cl) { try { return cl.getDeclaredConstructor().newInstance(); - } catch (InstantiationException | IllegalAccessException | NoSuchMethodException + } catch (InstantiationException + | IllegalAccessException + | NoSuchMethodException | InvocationTargetException e) { throw new RuntimeException(e); } @@ -465,15 +485,15 @@ public class ReflectionHelpers { */ public static class ClassParameter { public final Class clazz; - public final V val; + public final V value; - public ClassParameter(Class clazz, V val) { + public ClassParameter(Class clazz, V value) { this.clazz = clazz; - this.val = val; + this.value = value; } - public static ClassParameter from(Class clazz, V val) { - return new ClassParameter<>(clazz, val); + public static ClassParameter from(Class clazz, V value) { + return new ClassParameter<>(clazz, value); } public static ClassParameter[] fromComponentLists(Class[] classes, Object[] values) { @@ -496,7 +516,7 @@ public class ReflectionHelpers { public static Object[] getValues(ClassParameter... classParameters) { Object[] values = new Object[classParameters.length]; for (int i = 0; i < classParameters.length; i++) { - Object paramValue = classParameters[i].val; + Object paramValue = classParameters[i].value; values[i] = paramValue; } return values; @@ -510,15 +530,15 @@ public class ReflectionHelpers { */ public static class StringParameter { public final String className; - public final V val; + public final V value; - public StringParameter(String className, V val) { + public StringParameter(String className, V value) { this.className = className; - this.val = val; + this.value = value; } - public static StringParameter from(String className, V val) { - return new StringParameter<>(className, val); + public static StringParameter from(String className, V value) { + return new StringParameter<>(className, value); } } } diff --git a/shadowapi/src/test/java/org/robolectric/util/ReflectionHelpersTest.java b/shadowapi/src/test/java/org/robolectric/util/ReflectionHelpersTest.java index 56c489df1..e5f281ba6 100644 --- a/shadowapi/src/test/java/org/robolectric/util/ReflectionHelpersTest.java +++ b/shadowapi/src/test/java/org/robolectric/util/ReflectionHelpersTest.java @@ -141,7 +141,8 @@ public class ReflectionHelpersTest { } @Test - public void callInstanceMethodReflectively_whenMultipleSignaturesExistForAMethodName_callsMethodWithCorrectSignature() { + public void + callInstanceMethodReflectively_whenMultipleSignaturesExistForAMethodName_callsMethodWithCorrectSignature() { ExampleDescendant example = new ExampleDescendant(); int returnNumber = ReflectionHelpers.callInstanceMethod( @@ -282,23 +283,35 @@ public class ReflectionHelpersTest { } @Test - public void callConstructorReflectively_whenMultipleSignaturesExistForTheConstructor_callsConstructorWithCorrectSignature() { - ExampleClass ec = ReflectionHelpers.callConstructor(ExampleClass.class, ClassParameter.from(int.class, 16)); + public void + callConstructorReflectively_whenMultipleSignaturesExistForTheConstructor_callsConstructorWithCorrectSignature() { + ExampleClass ec = + ReflectionHelpers.callConstructor(ExampleClass.class, ClassParameter.from(int.class, 16)); assertWithMessage("index").that(ec.index).isEqualTo(16); assertWithMessage("name").that(ec.name).isNull(); } - @SuppressWarnings("serial") - private static class TestError extends Error { + @Test + public void callHasField_withstaticandregularmember() { + assertWithMessage("has field failed for member: unusedName") + .that(ReflectionHelpers.hasField(FieldTestClass.class, "unusedName")) + .isTrue(); + assertWithMessage("has field failed for member: unusedStaticName") + .that(ReflectionHelpers.hasField(FieldTestClass.class, "unusedStaticName")) + .isTrue(); + assertWithMessage("has field failed for non existant member: noname") + .that(ReflectionHelpers.hasField(FieldTestClass.class, "noname")) + .isFalse(); } @SuppressWarnings("serial") - private static class TestException extends Exception { - } + private static class TestError extends Error {} @SuppressWarnings("serial") - private static class TestRuntimeException extends RuntimeException { - } + private static class TestException extends Exception {} + + @SuppressWarnings("serial") + private static class TestRuntimeException extends RuntimeException {} @SuppressWarnings("unused") private static class ExampleBase { @@ -406,4 +419,11 @@ public class ReflectionHelpersTest { this.index = index; } } + + private static class FieldTestClass { + public String unusedName; + public static String unusedStaticName = "unusedStaticNameValue"; + + private FieldTestClass() {} + } } diff --git a/shadows/framework/build.gradle b/shadows/framework/build.gradle index a273d5a6f..7c9aa5879 100644 --- a/shadows/framework/build.gradle +++ b/shadows/framework/build.gradle @@ -15,6 +15,8 @@ configurations { sqlite4java } +def sqlite4javaVersion = libs.versions.sqlite4java.get() + task copySqliteNatives(type: Copy) { from project.configurations.sqlite4java { include '**/*.dll' @@ -47,20 +49,17 @@ dependencies { api project(":shadowapi") api project(":utils") api project(":utils:reflector") + api "androidx.test:monitor:$axtMonitorVersion@aar" - implementation "com.google.errorprone:error_prone_annotations:$errorproneVersion" - compileOnly "com.google.code.findbugs:jsr305:3.0.2" - api "com.almworks.sqlite4java:sqlite4java:$sqlite4javaVersion" - compileOnly(AndroidSdk.MAX_SDK.coordinates) { force = true } - api "com.ibm.icu:icu4j:72.1" - api "androidx.annotation:annotation:1.1.0" - api "com.google.auto.value:auto-value-annotations:1.10.1" - annotationProcessor "com.google.auto.value:auto-value:1.10.1" + implementation libs.error.prone.annotations + compileOnly libs.findbugs.jsr305 + api libs.sqlite4java + compileOnly(AndroidSdk.MAX_SDK.coordinates) + api libs.icu4j + api libs.androidx.annotation + api libs.auto.value.annotations + annotationProcessor libs.auto.value - sqlite4java "com.almworks.sqlite4java:libsqlite4java-osx:$sqlite4javaVersion" - sqlite4java "com.almworks.sqlite4java:libsqlite4java-linux-amd64:$sqlite4javaVersion" - sqlite4java "com.almworks.sqlite4java:sqlite4java-win32-x64:$sqlite4javaVersion" - sqlite4java "com.almworks.sqlite4java:libsqlite4java-linux-i386:$sqlite4javaVersion" - sqlite4java "com.almworks.sqlite4java:sqlite4java-win32-x86:$sqlite4javaVersion" + sqlite4java libs.bundles.sqlite4java.native } diff --git a/shadows/framework/src/main/java/org/robolectric/RuntimeEnvironment.java b/shadows/framework/src/main/java/org/robolectric/RuntimeEnvironment.java old mode 100755 new mode 100644 diff --git a/shadows/framework/src/main/java/org/robolectric/shadows/AssociationInfoBuilder.java b/shadows/framework/src/main/java/org/robolectric/shadows/AssociationInfoBuilder.java index 73c36a542..e2b8f0df3 100644 --- a/shadows/framework/src/main/java/org/robolectric/shadows/AssociationInfoBuilder.java +++ b/shadows/framework/src/main/java/org/robolectric/shadows/AssociationInfoBuilder.java @@ -80,19 +80,40 @@ public class AssociationInfoBuilder { public AssociationInfo build() { try { if (RuntimeEnvironment.getApiLevel() <= TIRAMISU) { - return ReflectionHelpers.callConstructor( - AssociationInfo.class, - ClassParameter.from(int.class, id), - ClassParameter.from(int.class, userId), - ClassParameter.from(String.class, packageName), - ClassParameter.from(MacAddress.class, MacAddress.fromString(deviceMacAddress)), - ClassParameter.from(CharSequence.class, displayName), - ClassParameter.from(String.class, deviceProfile), - ClassParameter.from(boolean.class, selfManaged), - ClassParameter.from(boolean.class, notifyOnDeviceNearby), - ClassParameter.from(boolean.class, false /*revoked*/), - ClassParameter.from(long.class, approvedMs), - ClassParameter.from(long.class, lastTimeConnectedMs)); + // We have two different constructors for AssociationInfo across + // T branches. aosp has the constructor that takes a new "revoked" parameter. + // Since there is not deterministic way to know which branch we are running in, + // we will reflect on the class to see if it has the mRevoked member. + // Based on the result we will either invoke the constructor with "revoked" or the + // one without this parameter. + if (ReflectionHelpers.hasField(AssociationInfo.class, "mRevoked")) { + return ReflectionHelpers.callConstructor( + AssociationInfo.class, + ClassParameter.from(int.class, id), + ClassParameter.from(int.class, userId), + ClassParameter.from(String.class, packageName), + ClassParameter.from(MacAddress.class, MacAddress.fromString(deviceMacAddress)), + ClassParameter.from(CharSequence.class, displayName), + ClassParameter.from(String.class, deviceProfile), + ClassParameter.from(boolean.class, selfManaged), + ClassParameter.from(boolean.class, notifyOnDeviceNearby), + ClassParameter.from(boolean.class, false /*revoked only supported in aosp*/), + ClassParameter.from(long.class, approvedMs), + ClassParameter.from(long.class, lastTimeConnectedMs)); + } else { + return ReflectionHelpers.callConstructor( + AssociationInfo.class, + ClassParameter.from(int.class, id), + ClassParameter.from(int.class, userId), + ClassParameter.from(String.class, packageName), + ClassParameter.from(MacAddress.class, MacAddress.fromString(deviceMacAddress)), + ClassParameter.from(CharSequence.class, displayName), + ClassParameter.from(String.class, deviceProfile), + ClassParameter.from(boolean.class, selfManaged), + ClassParameter.from(boolean.class, notifyOnDeviceNearby), + ClassParameter.from(long.class, approvedMs), + ClassParameter.from(long.class, lastTimeConnectedMs)); + } } else { return ReflectionHelpers.callConstructor( AssociationInfo.class, diff --git a/shadows/framework/src/main/java/org/robolectric/shadows/CellIdentityNrBuilder.java b/shadows/framework/src/main/java/org/robolectric/shadows/CellIdentityNrBuilder.java new file mode 100644 index 000000000..22a0e75c0 --- /dev/null +++ b/shadows/framework/src/main/java/org/robolectric/shadows/CellIdentityNrBuilder.java @@ -0,0 +1,135 @@ +package org.robolectric.shadows; + +import static org.robolectric.util.reflector.Reflector.reflector; + +import android.os.Build; +import android.telephony.CellIdentityNr; +import android.telephony.CellInfo; +import androidx.annotation.RequiresApi; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import javax.annotation.Nullable; +import org.robolectric.RuntimeEnvironment; +import org.robolectric.util.reflector.Constructor; +import org.robolectric.util.reflector.ForType; + +/** Builder for {@link android.telephony.CellIdentityNr}. */ +@RequiresApi(Build.VERSION_CODES.Q) +public class CellIdentityNrBuilder { + + private int pci = CellInfo.UNAVAILABLE; + private int tac = CellInfo.UNAVAILABLE; + private int nrarfcn = CellInfo.UNAVAILABLE; + private int[] bands = new int[0]; + @Nullable private String mcc = null; + @Nullable private String mnc = null; + private long nci = CellInfo.UNAVAILABLE; + @Nullable private String alphal = null; + @Nullable private String alphas = null; + private List additionalPlmns = new ArrayList<>(); + + private CellIdentityNrBuilder() {} + + public static CellIdentityNrBuilder newBuilder() { + return new CellIdentityNrBuilder(); + } + + // An empty constructor is not available on Q. + @RequiresApi(Build.VERSION_CODES.R) + protected static CellIdentityNr getDefaultInstance() { + return reflector(CellIdentityNrReflector.class).newCellIdentityNr(); + } + + public CellIdentityNrBuilder setNci(long nci) { + this.nci = nci; + return this; + } + + public CellIdentityNrBuilder setPci(int pci) { + this.pci = pci; + return this; + } + + public CellIdentityNrBuilder setTac(int tac) { + this.tac = tac; + return this; + } + + public CellIdentityNrBuilder setNrarfcn(int nrarfcn) { + this.nrarfcn = nrarfcn; + return this; + } + + public CellIdentityNrBuilder setMcc(String mcc) { + this.mcc = mcc; + return this; + } + + public CellIdentityNrBuilder setMnc(String mnc) { + this.mnc = mnc; + return this; + } + + public CellIdentityNrBuilder setBands(int[] bands) { + this.bands = bands; + return this; + } + + public CellIdentityNrBuilder setLongOperatorName(String longOperatorName) { + this.alphal = longOperatorName; + return this; + } + + public CellIdentityNrBuilder setShortOperatorName(String shortOperatorName) { + this.alphas = shortOperatorName; + return this; + } + + public CellIdentityNrBuilder setAdditionalPlmns(List additionalPlmns) { + this.additionalPlmns = additionalPlmns; + return this; + } + + public CellIdentityNr build() { + CellIdentityNrReflector cellIdentityReflector = reflector(CellIdentityNrReflector.class); + if (RuntimeEnvironment.getApiLevel() < Build.VERSION_CODES.R) { + return cellIdentityReflector.newCellIdentityNr( + pci, tac, nrarfcn, mcc, mnc, nci, alphal, alphas); + } else { + return cellIdentityReflector.newCellIdentityNr( + pci, tac, nrarfcn, bands, mcc, mnc, nci, alphal, alphas, additionalPlmns); + } + } + + @ForType(CellIdentityNr.class) + private interface CellIdentityNrReflector { + + @Constructor + CellIdentityNr newCellIdentityNr(); + + @Constructor + CellIdentityNr newCellIdentityNr( + int pci, + int tac, + int nrarfcn, + String mcc, + String mnc, + long nci, + String alphal, + String alphas); + + @Constructor + CellIdentityNr newCellIdentityNr( + int pci, + int tac, + int nrarfcn, + int[] bands, + String mcc, + String mnc, + long nci, + String alphal, + String alphas, + Collection additionalPlmns); + } +} diff --git a/shadows/framework/src/main/java/org/robolectric/shadows/CellInfoLteBuilder.java b/shadows/framework/src/main/java/org/robolectric/shadows/CellInfoLteBuilder.java index 412d8c72f..6f3f93420 100644 --- a/shadows/framework/src/main/java/org/robolectric/shadows/CellInfoLteBuilder.java +++ b/shadows/framework/src/main/java/org/robolectric/shadows/CellInfoLteBuilder.java @@ -57,13 +57,17 @@ public class CellInfoLteBuilder { } public CellInfoLte build() { + int apiLevel = RuntimeEnvironment.getApiLevel(); if (cellIdentity == null) { - cellIdentity = CellIdentityLteBuilder.getDefaultInstance(); + if (apiLevel > Build.VERSION_CODES.Q) { + cellIdentity = CellIdentityLteBuilder.getDefaultInstance(); + } else { + cellIdentity = CellIdentityLteBuilder.newBuilder().build(); + } } if (cellSignalStrength == null) { cellSignalStrength = CellSignalStrengthLteBuilder.getDefaultInstance(); } - int apiLevel = RuntimeEnvironment.getApiLevel(); CellInfoLteReflector cellInfoLteReflector = reflector(CellInfoLteReflector.class); if (apiLevel < Build.VERSION_CODES.TIRAMISU) { CellInfoLte cellInfo = cellInfoLteReflector.newCellInfoLte(); diff --git a/shadows/framework/src/main/java/org/robolectric/shadows/CellInfoNrBuilder.java b/shadows/framework/src/main/java/org/robolectric/shadows/CellInfoNrBuilder.java new file mode 100644 index 000000000..78195246e --- /dev/null +++ b/shadows/framework/src/main/java/org/robolectric/shadows/CellInfoNrBuilder.java @@ -0,0 +1,93 @@ +package org.robolectric.shadows; + +import static org.robolectric.util.reflector.Reflector.reflector; + +import android.os.Build; +import android.os.Parcel; +import android.telephony.CellIdentityNr; +import android.telephony.CellInfoNr; +import android.telephony.CellSignalStrengthNr; +import androidx.annotation.RequiresApi; +import org.robolectric.RuntimeEnvironment; +import org.robolectric.util.reflector.Constructor; +import org.robolectric.util.reflector.ForType; + +/** Builder for {@link android.telephony.CellInfoNr}. */ +@RequiresApi(Build.VERSION_CODES.Q) +public class CellInfoNrBuilder { + + private boolean isRegistered = false; + private long timeStamp = 0L; + private int cellConnectionStatus = 0; + private CellIdentityNr cellIdentity; + private CellSignalStrengthNr cellSignalStrength; + + private CellInfoNrBuilder() {} + + public static CellInfoNrBuilder newBuilder() { + return new CellInfoNrBuilder(); + } + + public CellInfoNrBuilder setRegistered(boolean isRegistered) { + this.isRegistered = isRegistered; + return this; + } + + public CellInfoNrBuilder setTimeStampNanos(long timeStamp) { + this.timeStamp = timeStamp; + return this; + } + + public CellInfoNrBuilder setCellConnectionStatus(int cellConnectionStatus) { + this.cellConnectionStatus = cellConnectionStatus; + return this; + } + + public CellInfoNrBuilder setCellIdentity(CellIdentityNr cellIdentity) { + this.cellIdentity = cellIdentity; + return this; + } + + public CellInfoNrBuilder setCellSignalStrength(CellSignalStrengthNr cellSignalStrength) { + this.cellSignalStrength = cellSignalStrength; + return this; + } + + public CellInfoNr build() { + if (cellIdentity == null) { + cellIdentity = CellIdentityNrBuilder.getDefaultInstance(); + } + if (cellSignalStrength == null) { + cellSignalStrength = CellSignalStrengthNrBuilder.getDefaultInstance(); + } + // CellInfoNr has no default constructor below T so we write it to a Parcel. + if (RuntimeEnvironment.getApiLevel() <= Build.VERSION_CODES.TIRAMISU) { + Parcel p = Parcel.obtain(); + p.writeInt(/* CellInfo#TYPE_NR */ 6); + p.writeInt(isRegistered ? 1 : 0); + p.writeLong(timeStamp); + p.writeInt(cellConnectionStatus); + cellIdentity.writeToParcel(p, 0); + cellSignalStrength.writeToParcel(p, 0); + p.setDataPosition(0); + CellInfoNr cellInfoNr = CellInfoNr.CREATOR.createFromParcel(p); + p.recycle(); + return cellInfoNr; + } else { + return reflector(CellInfoNrReflector.class) + .newCellInfoNr( + cellConnectionStatus, isRegistered, timeStamp, cellIdentity, cellSignalStrength); + } + } + + @ForType(CellInfoNr.class) + private interface CellInfoNrReflector { + @Constructor + CellInfoNr newCellInfoNr( + int cellConnectionStatus, + boolean isRegistered, + long timeStamp, + CellIdentityNr cellIdentity, + CellSignalStrengthNr cellSignalStrength); + } +} diff --git a/shadows/framework/src/main/java/org/robolectric/shadows/CellSignalStrengthNrBuilder.java b/shadows/framework/src/main/java/org/robolectric/shadows/CellSignalStrengthNrBuilder.java new file mode 100644 index 000000000..4f3f859bc --- /dev/null +++ b/shadows/framework/src/main/java/org/robolectric/shadows/CellSignalStrengthNrBuilder.java @@ -0,0 +1,140 @@ +package org.robolectric.shadows; + +import static org.robolectric.util.reflector.Reflector.reflector; + +import android.os.Build; +import android.telephony.CellInfo; +import android.telephony.CellSignalStrengthNr; +import androidx.annotation.RequiresApi; +import java.util.ArrayList; +import java.util.List; +import org.robolectric.RuntimeEnvironment; +import org.robolectric.util.reflector.Constructor; +import org.robolectric.util.reflector.ForType; + +/** Builder for {@link android.telephony.CellSignalStrengthNr} */ +@RequiresApi(Build.VERSION_CODES.Q) +public class CellSignalStrengthNrBuilder { + + private int csiRrsp = CellInfo.UNAVAILABLE; + private int csiRsrq = CellInfo.UNAVAILABLE; + private int csiSinr = CellInfo.UNAVAILABLE; + private int csiCqiTableIndex = CellInfo.UNAVAILABLE; + private List csiCqiReport = new ArrayList<>(); + private int ssRsrp = CellInfo.UNAVAILABLE; + private int ssRsrq = CellInfo.UNAVAILABLE; + private int ssSinr = CellInfo.UNAVAILABLE; + private int timingAdvance = CellInfo.UNAVAILABLE; + + private CellSignalStrengthNrBuilder() {} + + public static CellSignalStrengthNrBuilder newBuilder() { + return new CellSignalStrengthNrBuilder(); + } + + protected static CellSignalStrengthNr getDefaultInstance() { + return reflector(CellSignalStrengthNrReflector.class).newCellSignalStrengthNr(); + } + + public CellSignalStrengthNrBuilder setCsiRsrp(int csiRrsp) { + this.csiRrsp = csiRrsp; + return this; + } + + public CellSignalStrengthNrBuilder setCsiRsrq(int csiRsrq) { + this.csiRsrq = csiRsrq; + return this; + } + + public CellSignalStrengthNrBuilder setCsiSinr(int csiSinr) { + this.csiSinr = csiSinr; + return this; + } + + public CellSignalStrengthNrBuilder setCsiCqiTableIndex(int csiCqiTableIndex) { + this.csiCqiTableIndex = csiCqiTableIndex; + return this; + } + + public CellSignalStrengthNrBuilder setCsiCqiReport(List csiCqiReport) { + this.csiCqiReport = csiCqiReport; + return this; + } + + public CellSignalStrengthNrBuilder setSsRsrp(int ssRsrp) { + this.ssRsrp = ssRsrp; + return this; + } + + public CellSignalStrengthNrBuilder setSsRsrq(int ssRsrq) { + this.ssRsrq = ssRsrq; + return this; + } + + public CellSignalStrengthNrBuilder setSsSinr(int ssSinr) { + this.ssSinr = ssSinr; + return this; + } + + public CellSignalStrengthNrBuilder setTimingAdvance(int timingAdvance) { + this.timingAdvance = timingAdvance; + return this; + } + + public CellSignalStrengthNr build() { + CellSignalStrengthNrReflector cellSignalStrengthReflector = + reflector(CellSignalStrengthNrReflector.class); + if (RuntimeEnvironment.getApiLevel() < Build.VERSION_CODES.TIRAMISU) { + return cellSignalStrengthReflector.newCellSignalStrengthNr( + csiRrsp, csiRsrq, csiSinr, ssRsrp, ssRsrq, ssSinr); + } else if (RuntimeEnvironment.getApiLevel() == Build.VERSION_CODES.TIRAMISU) { + return cellSignalStrengthReflector.newCellSignalStrengthNr( + csiRrsp, csiRsrq, csiSinr, csiCqiTableIndex, csiCqiReport, ssRsrp, ssRsrq, ssSinr); + } else { + return cellSignalStrengthReflector.newCellSignalStrengthNr( + csiRrsp, + csiRsrq, + csiSinr, + csiCqiTableIndex, + csiCqiReport, + ssRsrp, + ssRsrq, + ssSinr, + timingAdvance); + } + } + + @ForType(CellSignalStrengthNr.class) + private interface CellSignalStrengthNrReflector { + + @Constructor + CellSignalStrengthNr newCellSignalStrengthNr(); + + @Constructor + CellSignalStrengthNr newCellSignalStrengthNr( + int csRsrp, int csiRsrq, int csiSinr, int ssRsrp, int ssRsrq, int ssSinr); + + @Constructor + CellSignalStrengthNr newCellSignalStrengthNr( + int csRsrp, + int csiRsrq, + int csiSinr, + int csiCqiTableIndex, + List csiCqiReport, + int ssRsrp, + int ssRsrq, + int ssSinr); + + @Constructor + CellSignalStrengthNr newCellSignalStrengthNr( + int csRsrp, + int csiRsrq, + int csiSinr, + int csiCqiTableIndex, + List csiCqiReport, + int ssRsrp, + int ssRsrq, + int ssSinr, + int timingAdvance); + } +} diff --git a/shadows/framework/src/main/java/org/robolectric/shadows/ShadowArscApkAssets9.java b/shadows/framework/src/main/java/org/robolectric/shadows/ShadowArscApkAssets9.java old mode 100755 new mode 100644 diff --git a/shadows/framework/src/main/java/org/robolectric/shadows/ShadowArscAssetManager.java b/shadows/framework/src/main/java/org/robolectric/shadows/ShadowArscAssetManager.java old mode 100755 new mode 100644 diff --git a/shadows/framework/src/main/java/org/robolectric/shadows/ShadowDisplayManagerGlobal.java b/shadows/framework/src/main/java/org/robolectric/shadows/ShadowDisplayManagerGlobal.java index 3ae8895ae..d0fdab014 100644 --- a/shadows/framework/src/main/java/org/robolectric/shadows/ShadowDisplayManagerGlobal.java +++ b/shadows/framework/src/main/java/org/robolectric/shadows/ShadowDisplayManagerGlobal.java @@ -18,16 +18,12 @@ import android.util.SparseArray; import android.view.Display; import android.view.DisplayInfo; import com.google.common.annotations.VisibleForTesting; - import java.lang.reflect.Field; -import java.sql.Array; -import java.text.Format; import java.util.ArrayList; import java.util.List; import java.util.TreeMap; import java.util.concurrent.CopyOnWriteArrayList; import javax.annotation.Nullable; -import org.robolectric.RuntimeEnvironment; import org.robolectric.android.Bootstrap; import org.robolectric.annotation.HiddenApi; import org.robolectric.annotation.Implementation; @@ -84,18 +80,31 @@ public class ShadowDisplayManagerGlobal { private static DisplayManagerGlobal newDisplayManagerGlobal(IDisplayManager displayManager) { instance = Shadow.newInstanceOf(DisplayManagerGlobal.class); DisplayManagerGlobalReflector displayManagerGlobal = - reflector(DisplayManagerGlobalReflector.class, instance); + reflector(DisplayManagerGlobalReflector.class, instance); displayManagerGlobal.setDm(displayManager); displayManagerGlobal.setLock(new Object()); - List displayListeners = - RuntimeEnvironment.getApiLevel() < ShadowBuild.UPSIDE_DOWN_CAKE - ? new ArrayList<>() - : new CopyOnWriteArrayList<>(); + List displayListeners = createDisplayListeners(); displayManagerGlobal.setDisplayListeners(displayListeners); displayManagerGlobal.setDisplayInfoCache(new SparseArray<>()); return instance; } + private static List createDisplayListeners() { + try { + // The type for mDisplayListeners was changed from ArrayList to CopyOnWriteArrayList + // in some branches of T and U, so we need to reflect on DisplayManagerGlobal class + // to check the type of mDisplayListeners member before initializing appropriately. + Field f = DisplayManagerGlobal.class.getDeclaredField("mDisplayListeners"); + if (f.getType().isAssignableFrom(ArrayList.class)) { + return new ArrayList<>(); + } else { + return new CopyOnWriteArrayList<>(); + } + } catch (NoSuchFieldException e) { + throw new RuntimeException(e); + } + } + @VisibleForTesting static DisplayManagerGlobal getGlobalInstance() { return instance; diff --git a/shadows/framework/src/main/java/org/robolectric/shadows/ShadowLauncherApps.java b/shadows/framework/src/main/java/org/robolectric/shadows/ShadowLauncherApps.java index 96cd2c29b..0cbc9fb21 100644 --- a/shadows/framework/src/main/java/org/robolectric/shadows/ShadowLauncherApps.java +++ b/shadows/framework/src/main/java/org/robolectric/shadows/ShadowLauncherApps.java @@ -49,6 +49,7 @@ import org.robolectric.util.reflector.ForType; public class ShadowLauncherApps { private List shortcuts = new ArrayList<>(); private final Multimap enabledPackages = HashMultimap.create(); + private final Multimap enabledActivities = HashMultimap.create(); private final Multimap shortcutActivityList = HashMultimap.create(); private final Multimap activityList = HashMultimap.create(); @@ -101,6 +102,17 @@ public class ShadowLauncherApps { enabledPackages.put(userHandle, packageName); } + /** + * Sets an activity referenced by ComponentName as enabled, to be checked by {@link + * #isActivityEnabled(ComponentName, UserHandle)}. + * + * @param userHandle the user handle to be set. + * @param componentName the component name of the activity to be enabled. + */ + public void setActivityEnabled(UserHandle userHandle, ComponentName componentName) { + enabledActivities.put(userHandle, componentName); + } + /** * Adds a {@link LauncherActivityInfo} to be retrieved by {@link * #getShortcutConfigActivityList(String, UserHandle)}. @@ -246,10 +258,9 @@ public class ShadowLauncherApps { "This method is not currently supported in Robolectric."); } - @Implementation + @Implementation(minSdk = L) protected boolean isActivityEnabled(ComponentName component, UserHandle user) { - throw new UnsupportedOperationException( - "This method is not currently supported in Robolectric."); + return enabledActivities.containsEntry(user, component); } /** diff --git a/shadows/framework/src/main/java/org/robolectric/shadows/ShadowLoadedApk.java b/shadows/framework/src/main/java/org/robolectric/shadows/ShadowLoadedApk.java index 2a3a61b10..f954ac234 100644 --- a/shadows/framework/src/main/java/org/robolectric/shadows/ShadowLoadedApk.java +++ b/shadows/framework/src/main/java/org/robolectric/shadows/ShadowLoadedApk.java @@ -1,20 +1,38 @@ package org.robolectric.shadows; +import static org.robolectric.shadow.api.Shadow.newInstanceOf; +import static org.robolectric.util.reflector.Reflector.reflector; + import android.app.Application; import android.app.LoadedApk; +import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager.NameNotFoundException; import android.content.res.Resources; import android.os.Build.VERSION_CODES; +import org.robolectric.RuntimeEnvironment; import org.robolectric.annotation.Implementation; import org.robolectric.annotation.Implements; +import org.robolectric.annotation.RealObject; import org.robolectric.util.reflector.Accessor; import org.robolectric.util.reflector.ForType; @Implements(value = LoadedApk.class, isInAndroidSdk = false) public class ShadowLoadedApk { + @RealObject private LoadedApk realLoadedApk; + private boolean isClassLoaderInitialized = false; + private final Object classLoaderLock = new Object(); @Implementation public ClassLoader getClassLoader() { + // The AppComponentFactory was introduced from SDK 28. + if (RuntimeEnvironment.getApiLevel() >= VERSION_CODES.P) { + synchronized (classLoaderLock) { + if (!isClassLoaderInitialized) { + isClassLoaderInitialized = true; + tryInitAppComponentFactory(realLoadedApk); + } + } + } return this.getClass().getClassLoader(); } @@ -23,6 +41,35 @@ public class ShadowLoadedApk { return this.getClass().getClassLoader(); } + private void tryInitAppComponentFactory(LoadedApk realLoadedApk) { + if (RuntimeEnvironment.getApiLevel() >= VERSION_CODES.P) { + ApplicationInfo applicationInfo = realLoadedApk.getApplicationInfo(); + if (applicationInfo == null || applicationInfo.appComponentFactory == null) { + return; + } + _LoadedApk_ loadedApkReflector = reflector(_LoadedApk_.class, realLoadedApk); + if (!loadedApkReflector.getIncludeCode()) { + return; + } + String fullQualifiedClassName = + calculateFullQualifiedClassName( + applicationInfo.appComponentFactory, applicationInfo.packageName); + android.app.AppComponentFactory factory = + (android.app.AppComponentFactory) newInstanceOf(fullQualifiedClassName); + if (factory == null) { + factory = new android.app.AppComponentFactory(); + } + loadedApkReflector.setAppFactory(factory); + } + } + + private String calculateFullQualifiedClassName(String className, String packageName) { + if (packageName == null) { + return className; + } + return className.startsWith(".") ? packageName + className : className; + } + /** Accessor interface for {@link LoadedApk}'s internals. */ @ForType(LoadedApk.class) public interface _LoadedApk_ { @@ -32,5 +79,11 @@ public class ShadowLoadedApk { @Accessor("mResources") void setResources(Resources resources); + + @Accessor("mIncludeCode") + boolean getIncludeCode(); + + @Accessor("mAppComponentFactory") + void setAppFactory(Object appFactory); } } diff --git a/shadows/framework/src/main/java/org/robolectric/shadows/ShadowNativeFontsFontFamily.java b/shadows/framework/src/main/java/org/robolectric/shadows/ShadowNativeFontsFontFamily.java index af3725cb6..b2da82783 100644 --- a/shadows/framework/src/main/java/org/robolectric/shadows/ShadowNativeFontsFontFamily.java +++ b/shadows/framework/src/main/java/org/robolectric/shadows/ShadowNativeFontsFontFamily.java @@ -3,7 +3,6 @@ package org.robolectric.shadows; import static android.os.Build.VERSION_CODES.Q; import static android.os.Build.VERSION_CODES.S; import static android.os.Build.VERSION_CODES.TIRAMISU; -import static android.os.Build.VERSION_CODES.UPSIDE_DOWN_CAKE; import android.graphics.fonts.FontFamily; import org.robolectric.annotation.Implementation; @@ -58,7 +57,7 @@ public class ShadowNativeFontsFontFamily { FontFamilyBuilderNatives.nAddFont(builderPtr, fontPtr); } - @Implementation(maxSdk=TIRAMISU) + @Implementation(maxSdk = TIRAMISU) protected static long nBuild( long builderPtr, String langTags, int variant, boolean isCustomFallback) { return FontFamilyBuilderNatives.nBuild(builderPtr, langTags, variant, isCustomFallback); @@ -66,8 +65,12 @@ public class ShadowNativeFontsFontFamily { @Implementation(minSdk = ShadowBuild.UPSIDE_DOWN_CAKE) protected static long nBuild( - long builderPtr, String langTags, int variant, boolean isCustomFallback, boolean isDefaultFallback) { - return nBuild(builderPtr, langTags, variant, isCustomFallback); + long builderPtr, + String langTags, + int variant, + boolean isCustomFallback, + boolean isDefaultFallback) { + return FontFamilyBuilderNatives.nBuild(builderPtr, langTags, variant, isCustomFallback); } @Implementation diff --git a/shadows/framework/src/main/java/org/robolectric/shadows/ShadowPaint.java b/shadows/framework/src/main/java/org/robolectric/shadows/ShadowPaint.java index 8fec6464a..16a7398f7 100644 --- a/shadows/framework/src/main/java/org/robolectric/shadows/ShadowPaint.java +++ b/shadows/framework/src/main/java/org/robolectric/shadows/ShadowPaint.java @@ -111,6 +111,15 @@ public class ShadowPaint { } } + @Implementation + protected void setStrikeThruText(boolean strikeThruText) { + if (strikeThruText) { + setFlags(flags | Paint.STRIKE_THRU_TEXT_FLAG); + } else { + setFlags(flags & ~Paint.STRIKE_THRU_TEXT_FLAG); + } + } + @Implementation protected Shader setShader(Shader shader) { this.shader = shader; diff --git a/shadows/framework/src/main/java/org/robolectric/shadows/ShadowPausedMessageQueue.java b/shadows/framework/src/main/java/org/robolectric/shadows/ShadowPausedMessageQueue.java index 416033b9b..5caf01642 100644 --- a/shadows/framework/src/main/java/org/robolectric/shadows/ShadowPausedMessageQueue.java +++ b/shadows/framework/src/main/java/org/robolectric/shadows/ShadowPausedMessageQueue.java @@ -59,7 +59,16 @@ public class ShadowPausedMessageQueue extends ShadowMessageQueue { invokeConstructor(MessageQueue.class, realQueue, from(boolean.class, quitAllowed)); int ptr = (int) nativeQueueRegistry.register(this); reflector(MessageQueueReflector.class, realQueue).setPtr(ptr); - clockListener = () -> nativeWake(ptr); + clockListener = + () -> { + synchronized (realQueue) { + // only wake up the Looper thread if queue is non empty to reduce contention if many + // Looper threads are active + if (getMessages() != null) { + nativeWake(ptr); + } + } + }; ShadowPausedSystemClock.addStaticListener(clockListener); } @@ -307,7 +316,9 @@ public class ShadowPausedMessageQueue extends ShadowMessageQueue { return Duration.ZERO; } while (next != null) { - when = shadowOfMsg(next).getWhen(); + if (next.getTarget() != null) { + when = shadowOfMsg(next).getWhen(); + } next = shadowOfMsg(next).internalGetNext(); } } @@ -333,7 +344,9 @@ public class ShadowPausedMessageQueue extends ShadowMessageQueue { synchronized (realQueue) { Message next = getMessages(); while (next != null) { - count++; + if (next.getTarget() != null) { + count++; + } next = shadowOfMsg(next).internalGetNext(); } } @@ -347,12 +360,24 @@ public class ShadowPausedMessageQueue extends ShadowMessageQueue { */ Message getNextIgnoringWhen() { synchronized (realQueue) { - Message head = getMessages(); - if (head != null) { - Message next = shadowOfMsg(head).internalGetNext(); - reflector(MessageQueueReflector.class, realQueue).setMessages(next); + Message prev = null; + Message msg = getMessages(); + // Head is blocked on synchronization barrier, find next asynchronous message. + if (msg != null && msg.getTarget() == null) { + do { + prev = msg; + msg = shadowOfMsg(msg).internalGetNext(); + } while (msg != null && !msg.isAsynchronous()); + } + if (msg != null) { + Message next = shadowOfMsg(msg).internalGetNext(); + if (prev == null) { + reflector(MessageQueueReflector.class, realQueue).setMessages(next); + } else { + ReflectionHelpers.setField(prev, "next", next); + } } - return head; + return msg; } } diff --git a/shadows/framework/src/main/java/org/robolectric/shadows/ShadowSystemVibrator.java b/shadows/framework/src/main/java/org/robolectric/shadows/ShadowSystemVibrator.java index cce1990a0..41fbf4e7d 100644 --- a/shadows/framework/src/main/java/org/robolectric/shadows/ShadowSystemVibrator.java +++ b/shadows/framework/src/main/java/org/robolectric/shadows/ShadowSystemVibrator.java @@ -15,9 +15,9 @@ import android.media.AudioAttributes; import android.os.Handler; import android.os.Looper; import android.os.SystemVibrator; -import android.os.VibrationAttributes; import android.os.VibrationEffect; import android.os.vibrator.VibrationEffectSegment; +import com.google.common.base.Preconditions; import java.util.List; import java.util.Optional; import org.robolectric.RuntimeEnvironment; @@ -25,7 +25,8 @@ import org.robolectric.annotation.Implementation; import org.robolectric.annotation.Implements; import org.robolectric.util.ReflectionHelpers; -@Implements(value = SystemVibrator.class, isInAndroidSdk = false) +/** Shadow for {@link SystemVibrator}. */ +@Implements(value = SystemVibrator.class, isInAndroidSdk = false, looseSignatures = true) public class ShadowSystemVibrator extends ShadowVibrator { private final Handler handler = new Handler(Looper.getMainLooper()); @@ -133,11 +134,14 @@ public class ShadowSystemVibrator extends ShadowVibrator { @Implementation(minSdk = S) protected void vibrate( - int uid, - String opPkg, - VibrationEffect effect, - String reason, - VibrationAttributes attributes) { + Object uid, Object opPkg, Object effect, Object reason, Object attributes) { + Preconditions.checkArgument(uid instanceof Integer); + Preconditions.checkArgument(opPkg == null || opPkg instanceof String); + // The SystemVibrator#vibrate needs effect NonNull. + Preconditions.checkArgument(effect instanceof VibrationEffect); + Preconditions.checkArgument(reason == null || reason instanceof String); + // The SystemVibrator#vibrate needs attributes NonNull. + Preconditions.checkArgument(attributes instanceof android.os.VibrationAttributes); if (effect instanceof VibrationEffect.Composed) { VibrationEffect.Composed composedEffect = (VibrationEffect.Composed) effect; vibrationAttributesFromLastVibration = attributes; diff --git a/shadows/framework/src/main/java/org/robolectric/shadows/ShadowTelephonyManager.java b/shadows/framework/src/main/java/org/robolectric/shadows/ShadowTelephonyManager.java index 0ab373054..048abf03b 100644 --- a/shadows/framework/src/main/java/org/robolectric/shadows/ShadowTelephonyManager.java +++ b/shadows/framework/src/main/java/org/robolectric/shadows/ShadowTelephonyManager.java @@ -52,6 +52,7 @@ import android.telephony.TelephonyDisplayInfo; import android.telephony.TelephonyManager; import android.telephony.TelephonyManager.CellInfoCallback; import android.telephony.VisualVoicemailSmsFilterSettings; +import android.telephony.emergency.EmergencyNumber; import android.text.TextUtils; import android.util.SparseArray; import android.util.SparseIntArray; @@ -59,6 +60,7 @@ import com.google.common.base.Ascii; import com.google.common.base.Preconditions; import com.google.common.base.Predicate; import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableMap; import com.google.common.collect.Iterables; import java.util.ArrayList; import java.util.Collections; @@ -155,6 +157,7 @@ public class ShadowTelephonyManager { private VisualVoicemailSmsParams lastVisualVoicemailSmsParams; private VisualVoicemailSmsFilterSettings visualVoicemailSmsFilterSettings; private boolean emergencyCallbackMode; + private static Map> emergencyNumbersList; /** * Should be {@link TelephonyManager.BootstrapAuthenticationCallback} but this object was @@ -172,6 +175,7 @@ public class ShadowTelephonyManager { @Resetter public static void reset() { callComposerStatus = 0; + emergencyNumbersList = null; } @Implementation(minSdk = S) @@ -1411,4 +1415,25 @@ public class ShadowTelephonyManager { return sentIntent; } } + + /** + * Sets the emergency numbers list returned by {@link TelephonyManager#getEmergencyNumberList}. + */ + public static void setEmergencyNumberList( + Map> emergencyNumbersList) { + ShadowTelephonyManager.emergencyNumbersList = emergencyNumbersList; + } + + /** + * Implementation for {@link TelephonyManager#getEmergencyNumberList}. + * + * @return an immutable map by default, unless set with {@link #setEmergencyNumberList}. + */ + @Implementation(minSdk = R) + protected Map> getEmergencyNumberList() { + if (ShadowTelephonyManager.emergencyNumbersList != null) { + return ShadowTelephonyManager.emergencyNumbersList; + } + return ImmutableMap.of(); + } } diff --git a/shadows/framework/src/main/java/org/robolectric/shadows/ShadowVibrator.java b/shadows/framework/src/main/java/org/robolectric/shadows/ShadowVibrator.java index df299c795..276a31db3 100644 --- a/shadows/framework/src/main/java/org/robolectric/shadows/ShadowVibrator.java +++ b/shadows/framework/src/main/java/org/robolectric/shadows/ShadowVibrator.java @@ -3,11 +3,9 @@ package org.robolectric.shadows; import static android.os.Build.VERSION_CODES.R; import android.media.AudioAttributes; -import android.os.VibrationAttributes; import android.os.VibrationEffect; import android.os.Vibrator; import android.os.vibrator.PrimitiveSegment; -import android.os.vibrator.VibrationEffectSegment; import java.util.ArrayList; import java.util.Collection; import java.util.List; @@ -25,10 +23,10 @@ public class ShadowVibrator { static boolean cancelled; static long milliseconds; protected static long[] pattern; - protected static final List vibrationEffectSegments = new ArrayList<>(); + protected static final List vibrationEffectSegments = new ArrayList<>(); protected static final List primitiveEffects = new ArrayList<>(); protected static final List supportedPrimitives = new ArrayList<>(); - @Nullable protected static VibrationAttributes vibrationAttributesFromLastVibration; + @Nullable protected static Object vibrationAttributesFromLastVibration; @Nullable protected static AudioAttributes audioAttributesFromLastVibration; static int repeat; static boolean hasVibrator = true; @@ -84,11 +82,6 @@ public class ShadowVibrator { return repeat; } - /** Returns the last list of {@link VibrationEffectSegment}. */ - public List getVibrationEffectSegments() { - return vibrationEffectSegments; - } - /** Returns the last list of {@link PrimitiveSegment} vibrations in {@link PrimitiveEffect}. */ @SuppressWarnings("JdkCollectors") // toImmutableList is only supported in Java 8+. public List getPrimitiveSegmentsInPrimitiveEffects() { @@ -125,9 +118,9 @@ public class ShadowVibrator { supportedPrimitives.addAll(primitives); } - /** Returns the {@link VibrationAttributes} from the last vibration. */ + /** Returns the {@link android.os.VibrationAttributes} from the last vibration. */ @Nullable - public VibrationAttributes getVibrationAttributesFromLastVibration() { + public Object getVibrationAttributesFromLastVibration() { return vibrationAttributesFromLastVibration; } diff --git a/shadows/framework/src/main/java/org/robolectric/shadows/ShadowWebView.java b/shadows/framework/src/main/java/org/robolectric/shadows/ShadowWebView.java index 706d86e10..1a8131f01 100644 --- a/shadows/framework/src/main/java/org/robolectric/shadows/ShadowWebView.java +++ b/shadows/framework/src/main/java/org/robolectric/shadows/ShadowWebView.java @@ -569,7 +569,7 @@ public class ShadowWebView extends ShadowViewGroup { * * @param canGoBack Value to return from {@code android.webkit.WebView#canGoBack()} * @deprecated Do not depend on this method as it will be removed in a future update. The - * preferered method is to populate a fake web history to use for going back. + * preferred method is to populate a fake web history to use for going back. */ @Deprecated public void setCanGoBack(boolean canGoBack) { diff --git a/shadows/httpclient/build.gradle b/shadows/httpclient/build.gradle index 332c83e08..4e77a09c3 100644 --- a/shadows/httpclient/build.gradle +++ b/shadows/httpclient/build.gradle @@ -21,17 +21,17 @@ dependencies { api project(":utils") // We should keep httpclient version for low level API compatibility. - earlyRuntime "org.apache.httpcomponents:httpcore:4.0.1" - api "org.apache.httpcomponents:httpclient:4.0.3" - compileOnly(AndroidSdk.LOLLIPOP_MR1.coordinates) { force = true } + earlyRuntime libs.apache.http.core + api libs.apache.http.client + compileOnly(AndroidSdk.LOLLIPOP_MR1.coordinates) testImplementation project(":robolectric") - testImplementation "junit:junit:${junitVersion}" - testImplementation "com.google.truth:truth:${truthVersion}" - testImplementation "org.mockito:mockito-core:${mockitoVersion}" + testImplementation libs.junit4 + testImplementation libs.truth + testImplementation libs.mockito testImplementation "androidx.test.ext:junit:$axtJunitVersion@aar" - testCompileOnly(AndroidSdk.LOLLIPOP_MR1.coordinates) { force = true } + testCompileOnly(AndroidSdk.LOLLIPOP_MR1.coordinates) testRuntimeOnly AndroidSdk.S.coordinates } diff --git a/shadows/playservices/build.gradle b/shadows/playservices/build.gradle index c3abbba05..b00983893 100644 --- a/shadows/playservices/build.gradle +++ b/shadows/playservices/build.gradle @@ -14,25 +14,20 @@ shadows { dependencies { compileOnly project(":shadows:framework") api project(":annotations") - api "com.google.guava:guava:$guavaJREVersion" + api libs.guava - compileOnly "androidx.fragment:fragment:1.2.0" - compileOnly "com.google.android.gms:play-services-base:8.4.0" - compileOnly "com.google.android.gms:play-services-basement:8.4.0" + compileOnly libs.bundles.play.services.base.for.shadows compileOnly AndroidSdk.MAX_SDK.coordinates testCompileOnly AndroidSdk.MAX_SDK.coordinates - testCompileOnly "com.google.android.gms:play-services-base:8.4.0" - testCompileOnly "com.google.android.gms:play-services-basement:8.4.0" + testCompileOnly libs.bundles.play.services.base.for.shadows testImplementation project(":robolectric") - testImplementation "junit:junit:$junitVersion" - testImplementation "com.google.truth:truth:$truthVersion" - testImplementation "org.mockito:mockito-core:$mockitoVersion" - testRuntimeOnly "androidx.fragment:fragment:1.2.0" - testRuntimeOnly "com.google.android.gms:play-services-base:8.4.0" - testRuntimeOnly "com.google.android.gms:play-services-basement:8.4.0" + testImplementation libs.junit4 + testImplementation libs.truth + testImplementation libs.mockito + testRuntimeOnly libs.bundles.play.services.base.for.shadows testRuntimeOnly AndroidSdk.MAX_SDK.coordinates } diff --git a/testapp/build.gradle b/testapp/build.gradle index 651ced0ea..0abf895ae 100644 --- a/testapp/build.gradle +++ b/testapp/build.gradle @@ -2,6 +2,7 @@ apply plugin: 'com.android.library' android { compileSdk 33 + namespace 'org.robolectric.testapp' defaultConfig { minSdk 16 diff --git a/utils/build.gradle b/utils/build.gradle index c10cca279..c31c9a0e0 100644 --- a/utils/build.gradle +++ b/utils/build.gradle @@ -1,3 +1,4 @@ +import org.jetbrains.kotlin.gradle.dsl.JvmTarget import org.robolectric.gradle.DeployedRoboJavaModulePlugin import org.robolectric.gradle.RoboJavaModulePlugin @@ -13,7 +14,7 @@ spotless { } } -tasks.withType(GenerateModuleMetadata) { +tasks.withType(GenerateModuleMetadata).configureEach { // We don't want to release gradle module metadata now to avoid // potential compatibility problems. enabled = false @@ -26,7 +27,7 @@ compileKotlin { // in production. If utils module starts to add Kotlin code in main source // set, we can remove this destinationDirectory modification. destinationDirectory = file("${projectDir}/build/classes/java/main") - compilerOptions.jvmTarget = org.jetbrains.kotlin.gradle.dsl.JvmTarget.JVM_1_8 + compilerOptions.jvmTarget = JvmTarget.JVM_1_8 } afterEvaluate { @@ -48,20 +49,19 @@ afterEvaluate { dependencies { api project(":annotations") api project(":pluginapi") - api "javax.inject:javax.inject:1" - api "javax.annotation:javax.annotation-api:1.3.2" + api libs.javax.inject + api libs.javax.annotation.api // For @VisibleForTesting and ByteStreams - implementation "com.google.guava:guava:$guavaJREVersion" - compileOnly "com.google.code.findbugs:jsr305:3.0.2" + implementation libs.guava + compileOnly libs.findbugs.jsr305 - testCompileOnly "com.google.auto.service:auto-service-annotations:$autoServiceVersion" - testAnnotationProcessor "com.google.auto.service:auto-service:$autoServiceVersion" - testAnnotationProcessor "com.google.errorprone:error_prone_core:$errorproneVersion" - implementation "com.google.errorprone:error_prone_annotations:$errorproneVersion" + testCompileOnly libs.auto.service.annotations + testAnnotationProcessor libs.auto.service + testAnnotationProcessor libs.error.prone.core + implementation libs.error.prone.annotations - testImplementation "junit:junit:${junitVersion}" - testImplementation "com.google.truth:truth:${truthVersion}" - testImplementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlinVersion" - testImplementation "org.mockito:mockito-core:${mockitoVersion}" + testImplementation libs.junit4 + testImplementation libs.truth + testImplementation libs.kotlin.stdlib } diff --git a/utils/reflector/build.gradle b/utils/reflector/build.gradle index 302734505..140e98700 100644 --- a/utils/reflector/build.gradle +++ b/utils/reflector/build.gradle @@ -5,12 +5,12 @@ apply plugin: RoboJavaModulePlugin apply plugin: DeployedRoboJavaModulePlugin dependencies { - api "org.ow2.asm:asm:${asmVersion}" - api "org.ow2.asm:asm-commons:${asmVersion}" - api "org.ow2.asm:asm-util:${asmVersion}" + api libs.asm + api libs.asm.commons + api libs.asm.util api project(":utils") testImplementation project(":shadowapi") - testImplementation "junit:junit:${junitVersion}" - testImplementation "com.google.truth:truth:${truthVersion}" + testImplementation libs.junit4 + testImplementation libs.truth } diff --git a/utils/src/main/java/org/robolectric/util/Util.java b/utils/src/main/java/org/robolectric/util/Util.java old mode 100755 new mode 100644 -- cgit v1.2.3