aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRam Peri <ramperi@google.com>2023-01-11 18:53:19 +0000
committerAutomerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>2023-01-11 18:53:19 +0000
commit686fa50510b1f90838ff431ad8c1fb0d4960f6ba (patch)
treef36377366d74bb4b35e1ac871b35cb72dbaab84b
parent3a8c396d0d84fb66549188cc8e80c0374073a72c (diff)
parent2f74975e5f182ef3aa166e423377e27402829882 (diff)
downloadrobolectric-686fa50510b1f90838ff431ad8c1fb0d4960f6ba.tar.gz
Merge branch 'upstream-google' into rng7 am: 2f74975e5f
Original change: https://googleplex-android-review.googlesource.com/c/platform/external/robolectric/+/20865458 Change-Id: Ic94f775c7833de804379f44941e7a5fb79c70c73 Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
-rw-r--r--.github/workflows/build_native_runtime.yml7
-rw-r--r--.github/workflows/check_aggregateDocs.yml32
-rw-r--r--.github/workflows/gradle_tasks_validation.yml55
-rw-r--r--README.md4
-rw-r--r--build.gradle2
-rw-r--r--buildSrc/build.gradle2
-rw-r--r--buildSrc/src/main/groovy/ShadowsPlugin.groovy1
-rw-r--r--dependencies.gradle18
-rw-r--r--integration_tests/androidx/build.gradle2
-rw-r--r--integration_tests/androidx_test/build.gradle1
-rw-r--r--integration_tests/androidx_test/src/main/java/org/robolectric/integrationtests/axt/EspressoActivity.java5
-rw-r--r--integration_tests/androidx_test/src/main/res/layout/appcompat_activity_with_toolbar_menu.xml10
-rw-r--r--integration_tests/androidx_test/src/main/res/layout/espresso_activity.xml93
-rw-r--r--integration_tests/androidx_test/src/main/res/layout/espresso_scrolling_activity.xml38
-rw-r--r--integration_tests/compat-target28/src/main/AndroidManifest.xml2
-rw-r--r--integration_tests/compat-target28/src/test/java/org/robolectric/integration/compat/target28/NormalCompatibilityTest.kt12
-rw-r--r--integration_tests/ctesque/build.gradle3
-rw-r--r--integration_tests/ctesque/src/sharedTest/java/android/content/res/ResourcesTest.java9
-rw-r--r--nativeruntime/src/main/java/org/robolectric/nativeruntime/DefaultNativeRuntimeLoader.java103
-rw-r--r--nativeruntime/src/test/java/org/robolectric/nativeruntime/DefaultNativeRuntimeLazyLoadTest.java3
-rw-r--r--nativeruntime/src/test/java/org/robolectric/nativeruntime/DefaultNativeRuntimeLoaderTest.java38
-rw-r--r--nativeruntime/src/test/resources/AndroidManifest.xml8
-rw-r--r--nativeruntime/src/test/resources/com/android/tools/test_config.properties5
-rw-r--r--nativeruntime/src/test/resources/resources.ap_bin0 -> 555 bytes
-rw-r--r--preinstrumented/build.gradle11
-rw-r--r--resources/src/main/java/org/robolectric/res/android/ResTable.java12
-rw-r--r--robolectric/build.gradle1
-rw-r--r--robolectric/src/main/java/org/robolectric/RobolectricTestRunner.java7
-rw-r--r--robolectric/src/test/java/org/robolectric/junit/rules/ExpectedLogMessagesRuleTest.java2
-rw-r--r--robolectric/src/test/java/org/robolectric/junit/runner/EnclosedTest.java46
-rw-r--r--robolectric/src/test/java/org/robolectric/shadows/ShadowActivityTest.java283
-rw-r--r--robolectric/src/test/java/org/robolectric/shadows/ShadowLocaleManagerTest.java30
-rw-r--r--robolectric/src/test/java/org/robolectric/shadows/ShadowResourcesTest.java15
-rw-r--r--robolectric/src/test/java/org/robolectric/shadows/ShadowStorageStatsManagerTest.java168
-rw-r--r--robolectric/src/test/java/org/robolectric/shadows/ShadowUIModeManagerTest.java7
-rw-r--r--robolectric/src/test/java/org/robolectric/shadows/ShadowUsbDeviceConnectionTest.java85
-rwxr-xr-xscripts/build-resources.sh9
-rw-r--r--shadowapi/src/test/java/org/robolectric/util/ReflectionHelpersTest.java2
-rw-r--r--shadows/framework/src/main/java/org/robolectric/shadows/ShadowContextHubClient.java6
-rw-r--r--shadows/framework/src/main/java/org/robolectric/shadows/ShadowLocaleManager.java14
-rw-r--r--shadows/framework/src/main/java/org/robolectric/shadows/ShadowSpeechRecognizer.java33
-rw-r--r--shadows/framework/src/main/java/org/robolectric/shadows/ShadowStorageStatsManager.java67
-rw-r--r--shadows/framework/src/main/java/org/robolectric/shadows/ShadowTelephonyManager.java21
-rw-r--r--shadows/framework/src/main/java/org/robolectric/shadows/ShadowUIModeManager.java4
-rw-r--r--shadows/framework/src/main/java/org/robolectric/shadows/ShadowUsbDeviceConnection.java37
-rw-r--r--shadows/framework/src/main/java/org/robolectric/shadows/ShadowUserManager.java13
-rw-r--r--shadows/playservices/build.gradle4
-rw-r--r--shadows/playservices/src/main/java/org/robolectric/shadows/gms/ShadowGooglePlayServicesUtil.java2
-rw-r--r--utils/src/main/java/org/robolectric/util/TempDirectory.java4
49 files changed, 972 insertions, 364 deletions
diff --git a/.github/workflows/build_native_runtime.yml b/.github/workflows/build_native_runtime.yml
index ae94d7c38..0ef105ea2 100644
--- a/.github/workflows/build_native_runtime.yml
+++ b/.github/workflows/build_native_runtime.yml
@@ -30,11 +30,10 @@ jobs:
platform-check-severity: warn
location: D:\
install: >-
- make
- mingw-w64-x86_64-gcc
- mingw-w64-x86_64-ninja
+ base-devel
+ mingw-w64-x86_64-toolchain
mingw-w64-x86_64-cmake
- mingw-w64-x86_64-make
+ mingw-w64-x86_64-ninja
- name: Set up JDK 11
uses: actions/setup-java@v3
diff --git a/.github/workflows/check_aggregateDocs.yml b/.github/workflows/check_aggregateDocs.yml
deleted file mode 100644
index e3251c429..000000000
--- a/.github/workflows/check_aggregateDocs.yml
+++ /dev/null
@@ -1,32 +0,0 @@
-name: Check aggregateDocs
-
-on:
- push:
- branches: [ master ]
-
- pull_request:
- branches: [ master, google ]
-
-permissions:
- contents: read
-
-jobs:
- check_aggregateDocs:
- runs-on: ubuntu-20.04
-
- steps:
- - uses: actions/checkout@v3
- with:
- fetch-depth: 0
- submodules: recursive
-
- - name: Set up JDK
- uses: actions/setup-java@v3
- with:
- distribution: 'zulu' # zulu suports complete JDK list
- java-version: 14
-
- - 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
diff --git a/.github/workflows/gradle_tasks_validation.yml b/.github/workflows/gradle_tasks_validation.yml
new file mode 100644
index 000000000..6a8c2fd12
--- /dev/null
+++ b/.github/workflows/gradle_tasks_validation.yml
@@ -0,0 +1,55 @@
+name: Gradle Tasks Validation
+
+on:
+ push:
+ branches: [ master ]
+
+ pull_request:
+ branches: [ master, google ]
+
+permissions:
+ contents: read
+
+jobs:
+ run_aggregateDocs:
+ runs-on: ubuntu-20.04
+
+ steps:
+ - uses: actions/checkout@v3
+ with:
+ fetch-depth: 0
+ submodules: recursive
+
+ - name: Set up JDK
+ uses: actions/setup-java@v3
+ with:
+ distribution: 'zulu'
+ java-version: 14
+
+ - 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_instrumentAll:
+ runs-on: ubuntu-20.04
+
+ steps:
+ - uses: actions/checkout@v3
+ with:
+ fetch-depth: 0
+ submodules: recursive
+
+ - name: Set up JDK
+ uses: actions/setup-java@v3
+ with:
+ distribution: 'zulu'
+ java-version: 11
+
+ - uses: gradle/gradle-build-action@v2
+
+ - name: Run :preinstrumented:instrumentAll
+ run: SKIP_NATIVERUNTIME_BUILD=true ./gradlew :preinstrumented:instrumentAll
+
+ - name: Run :preinstrumented:instrumentAll with SDK 33
+ run: SKIP_NATIVERUNTIME_BUILD=true PREINSTRUMENTED_SDK_VERSIONS=33 ./gradlew :preinstrumented:instrumentAll \ No newline at end of file
diff --git a/README.md b/README.md
index 71d5d31ee..3ee66862e 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.9"
+testImplementation "org.robolectric:robolectric:4.9.1"
```
## Building And Contributing
@@ -94,6 +94,6 @@ repositories {
}
dependencies {
- testImplementation "org.robolectric:robolectric:4.9-SNAPSHOT"
+ testImplementation "org.robolectric:robolectric:4.10-SNAPSHOT"
}
```
diff --git a/build.gradle b/build.gradle
index ee22a54d1..c224f5b8c 100644
--- a/build.gradle
+++ b/build.gradle
@@ -14,7 +14,7 @@ buildscript {
dependencies {
gradle
- classpath 'com.android.tools.build:gradle:7.3.0'
+ classpath 'com.android.tools.build:gradle:7.3.1'
classpath 'net.ltgt.gradle:gradle-errorprone-plugin:2.0.2'
classpath 'com.netflix.nebula:gradle-aggregate-javadocs-plugin:3.0.1'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlinVersion"
diff --git a/buildSrc/build.gradle b/buildSrc/build.gradle
index a6ba7d79c..082d9dbf3 100644
--- a/buildSrc/build.gradle
+++ b/buildSrc/build.gradle
@@ -14,5 +14,5 @@ dependencies {
api "com.google.guava:guava:31.1-jre"
api 'org.jetbrains:annotations:16.0.2'
implementation "org.ow2.asm:asm-tree:9.2"
- implementation 'com.android.tools.build:gradle:7.3.0'
+ implementation 'com.android.tools.build:gradle:7.3.1'
}
diff --git a/buildSrc/src/main/groovy/ShadowsPlugin.groovy b/buildSrc/src/main/groovy/ShadowsPlugin.groovy
index c41dc8c30..27aa14fa4 100644
--- a/buildSrc/src/main/groovy/ShadowsPlugin.groovy
+++ b/buildSrc/src/main/groovy/ShadowsPlugin.groovy
@@ -32,6 +32,7 @@ class ShadowsPlugin implements Plugin<Project> {
options.compilerArgs.add("-Aorg.robolectric.annotation.processing.jsonDocsDir=${project.buildDir}/docs/json")
options.compilerArgs.add("-Aorg.robolectric.annotation.processing.shadowPackage=${project.shadows.packageName}")
options.compilerArgs.add("-Aorg.robolectric.annotation.processing.sdkCheckMode=${project.shadows.sdkCheckMode}")
+ options.compilerArgs.add("-Aorg.robolectric.annotation.processing.sdks=${project.rootProject.buildDir}/sdks.txt")
}
// include generated sources in javadoc jar
diff --git a/dependencies.gradle b/dependencies.gradle
index 1e93657b3..10a586c1f 100644
--- a/dependencies.gradle
+++ b/dependencies.gradle
@@ -5,20 +5,20 @@ ext {
errorproneJavacVersion='9+181-r4173-1'
// AndroidX test versions
- axtMonitorVersion='1.6.0-beta01'
- axtRunnerVersion='1.5.0-beta01'
- axtRulesVersion='1.4.1-beta01'
- axtCoreVersion='1.5.0-beta01'
- axtTruthVersion='1.5.0-beta01'
- espressoVersion='3.5.0-beta01'
- axtJunitVersion='1.1.4-beta01'
+ axtMonitorVersion='1.6.0'
+ axtRunnerVersion='1.5.0'
+ axtRulesVersion='1.5.0'
+ axtCoreVersion='1.5.0'
+ axtTruthVersion='1.5.0'
+ espressoVersion='3.5.0'
+ axtJunitVersion='1.1.4'
+ axtTestServicesVersion='1.4.2'
// AndroidX versions
coreVersion='1.9.0'
appCompatVersion='1.4.1'
constraintlayoutVersion='2.1.4'
windowVersion='1.0.0'
- lifecycleVersion='2.2.0'
fragmentVersion='1.5.3'
truthVersion='1.1.3'
@@ -29,7 +29,7 @@ ext {
guavaJREVersion='31.1-jre'
- asmVersion='9.3'
+ asmVersion='9.4'
kotlinVersion='1.7.20'
autoServiceVersion='1.0.1'
diff --git a/integration_tests/androidx/build.gradle b/integration_tests/androidx/build.gradle
index 0f2124fcb..96535e1dd 100644
--- a/integration_tests/androidx/build.gradle
+++ b/integration_tests/androidx/build.gradle
@@ -38,8 +38,6 @@ dependencies {
testImplementation("androidx.test:rules:$axtRulesVersion")
testImplementation("androidx.test.espresso:espresso-intents:$espressoVersion")
testImplementation("androidx.test.ext:truth:$axtTruthVersion")
- // TODO: this should be a transitive dependency of core...
- testImplementation("androidx.lifecycle:lifecycle-common:$lifecycleVersion")
testImplementation("androidx.test.ext:junit:$axtJunitVersion")
testImplementation("com.google.truth:truth:$truthVersion")
}
diff --git a/integration_tests/androidx_test/build.gradle b/integration_tests/androidx_test/build.gradle
index 6abd17474..11ac35de6 100644
--- a/integration_tests/androidx_test/build.gradle
+++ b/integration_tests/androidx_test/build.gradle
@@ -67,4 +67,5 @@ dependencies {
androidTestImplementation "androidx.test:core:$axtCoreVersion"
androidTestImplementation "androidx.test.ext:junit:$axtJunitVersion"
androidTestImplementation "com.google.truth:truth:$truthVersion"
+ androidTestUtil "androidx.test.services:test-services:$axtTestServicesVersion"
}
diff --git a/integration_tests/androidx_test/src/main/java/org/robolectric/integrationtests/axt/EspressoActivity.java b/integration_tests/androidx_test/src/main/java/org/robolectric/integrationtests/axt/EspressoActivity.java
index 1964fa066..f3e2550ed 100644
--- a/integration_tests/androidx_test/src/main/java/org/robolectric/integrationtests/axt/EspressoActivity.java
+++ b/integration_tests/androidx_test/src/main/java/org/robolectric/integrationtests/axt/EspressoActivity.java
@@ -2,6 +2,7 @@ package org.robolectric.integrationtests.axt;
import android.app.Activity;
import android.os.Bundle;
+import android.text.InputType;
import android.widget.Button;
import android.widget.EditText;
import org.robolectric.integration.axt.R;
@@ -20,6 +21,10 @@ public class EspressoActivity extends Activity {
setContentView(R.layout.espresso_activity);
editText = findViewById(R.id.edit_text);
+ // Disable auto-correct for EditText to avoid typed text is changed
+ // by these features when running tests.
+ editText.setInputType(editText.getInputType() & (~InputType.TYPE_TEXT_FLAG_AUTO_CORRECT));
+
button = findViewById(R.id.button);
button.setOnClickListener(view -> buttonClicked = true);
}
diff --git a/integration_tests/androidx_test/src/main/res/layout/appcompat_activity_with_toolbar_menu.xml b/integration_tests/androidx_test/src/main/res/layout/appcompat_activity_with_toolbar_menu.xml
index fc61cc564..c30507330 100644
--- a/integration_tests/androidx_test/src/main/res/layout/appcompat_activity_with_toolbar_menu.xml
+++ b/integration_tests/androidx_test/src/main/res/layout/appcompat_activity_with_toolbar_menu.xml
@@ -1,17 +1,15 @@
<?xml version="1.0" encoding="utf-8"?>
-<LinearLayout
- xmlns:android="http://schemas.android.com/apk/res/android"
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/layout"
- android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_horizontal"
- >
+ android:orientation="vertical">
<androidx.appcompat.widget.Toolbar
android:id="@+id/toolbar"
- android:layout_height="wrap_content"
- android:layout_width="match_parent"/>
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content" />
</LinearLayout> \ No newline at end of file
diff --git a/integration_tests/androidx_test/src/main/res/layout/espresso_activity.xml b/integration_tests/androidx_test/src/main/res/layout/espresso_activity.xml
index 716d4e085..1cbc1979d 100644
--- a/integration_tests/androidx_test/src/main/res/layout/espresso_activity.xml
+++ b/integration_tests/androidx_test/src/main/res/layout/espresso_activity.xml
@@ -1,56 +1,53 @@
<?xml version="1.0" encoding="utf-8"?>
-<LinearLayout
- xmlns:android="http://schemas.android.com/apk/res/android"
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/layout"
- android:orientation="vertical"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center_horizontal"
- >
-
- <EditText
- android:id="@+id/edit_text"
- android:layout_height="wrap_content"
- android:layout_width="wrap_content"/>
-
- <Button
- android:id="@+id/button"
- android:layout_height="wrap_content"
- android:layout_width="wrap_content"/>
-
- <TextView
- android:id="@+id/text_view"
- android:layout_height="wrap_content"
- android:layout_width="wrap_content"
- android:text="Text View"/>
-
- <TextView
- android:id="@+id/text_view_positive_scale_x"
- android:layout_height="wrap_content"
- android:layout_width="wrap_content"
- android:textScaleX="1.5"
- android:text="Text View with positive textScaleX"/>
-
- <TextView
- android:id="@+id/text_view_negative_scale_x"
- android:layout_height="wrap_content"
- android:layout_width="wrap_content"
- android:textScaleX="-1.5"
- android:text="Text View with negative textScaleX"/>
-
- <TextView
- android:id="@+id/text_view_letter_spacing"
- android:layout_height="wrap_content"
- android:layout_width="wrap_content"
- android:letterSpacing="0.05"
- android:text="Text View with letterSpacing"/>
-
- <EditText
- android:id="@+id/edit_text_phone"
- android:layout_height="wrap_content"
- android:layout_width="wrap_content"
- android:inputType="phone"
- />
+ android:orientation="vertical">
+
+ <EditText
+ android:id="@+id/edit_text"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content" />
+
+ <Button
+ android:id="@+id/button"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content" />
+
+ <TextView
+ android:id="@+id/text_view"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="Text View" />
+
+ <TextView
+ android:id="@+id/text_view_positive_scale_x"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="Text View with positive textScaleX"
+ android:textScaleX="1.5" />
+
+ <TextView
+ android:id="@+id/text_view_negative_scale_x"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="Text View with negative textScaleX"
+ android:textScaleX="-1.5" />
+
+ <TextView
+ android:id="@+id/text_view_letter_spacing"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:letterSpacing="0.05"
+ android:text="Text View with letterSpacing" />
+
+ <EditText
+ android:id="@+id/edit_text_phone"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:inputType="phone" />
</LinearLayout>
diff --git a/integration_tests/androidx_test/src/main/res/layout/espresso_scrolling_activity.xml b/integration_tests/androidx_test/src/main/res/layout/espresso_scrolling_activity.xml
index 73b6f48a1..1312a244d 100644
--- a/integration_tests/androidx_test/src/main/res/layout/espresso_scrolling_activity.xml
+++ b/integration_tests/androidx_test/src/main/res/layout/espresso_scrolling_activity.xml
@@ -1,23 +1,23 @@
<?xml version="1.0" encoding="utf-8"?>
-<ScrollView
- xmlns:android="http://schemas.android.com/apk/res/android"
+<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/scroll_view"
android:layout_width="match_parent"
- android:layout_height="100dp"
- android:id="@+id/scroll_view" >
- <LinearLayout
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:orientation="vertical">
- <!-- Spacer View -->
- <View
+ android:layout_height="100dp">
+
+ <LinearLayout
android:layout_width="match_parent"
- android:layout_height="60dp"
- android:background="#FF0000FF"/>
- <!-- Button View that is only partially visible -->
- <Button
- android:layout_width="match_parent"
- android:layout_height="60dp"
- android:id="@+id/button"
- android:text="Click me!" />
- </LinearLayout>
+ android:layout_height="wrap_content"
+ android:orientation="vertical">
+ <!-- Spacer View -->
+ <View
+ android:layout_width="match_parent"
+ android:layout_height="60dp"
+ android:background="#FF0000FF" />
+ <!-- Button View that is only partially visible -->
+ <Button
+ android:id="@+id/button"
+ android:layout_width="match_parent"
+ android:layout_height="60dp"
+ android:text="Click me!" />
+ </LinearLayout>
</ScrollView> \ No newline at end of file
diff --git a/integration_tests/compat-target28/src/main/AndroidManifest.xml b/integration_tests/compat-target28/src/main/AndroidManifest.xml
index daca43208..a0c0db960 100644
--- a/integration_tests/compat-target28/src/main/AndroidManifest.xml
+++ b/integration_tests/compat-target28/src/main/AndroidManifest.xml
@@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
-<manifest package="org.robolectric.integrationtests.compattarget29">
+<manifest package="org.robolectric.integrationtests.compattarget28">
<application />
</manifest>
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 4605b9ac6..ee56fc6d2 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
@@ -2,6 +2,7 @@ package org.robolectric.integration.compat.target28
import android.content.Context
import android.os.Build
+import android.speech.SpeechRecognizer
import com.google.common.truth.Truth.assertThat
import org.junit.Test
import org.junit.runner.RunWith
@@ -35,4 +36,15 @@ class NormalCompatibilityTest {
fun `Initialize Activity succeed`() {
Robolectric.setupActivity(TestActivity::class.java)
}
+
+ @Test
+ fun `Initialize TelephonyManager succeed`() {
+ val telephonyManager = application.getSystemService(Context.TELEPHONY_SERVICE)
+ assertThat(telephonyManager).isNotNull()
+ }
+
+ @Test
+ fun `Create speech recognizer succeed`() {
+ assertThat(SpeechRecognizer.createSpeechRecognizer(application)).isNotNull()
+ }
}
diff --git a/integration_tests/ctesque/build.gradle b/integration_tests/ctesque/build.gradle
index 7d88d3d58..11f27e1d6 100644
--- a/integration_tests/ctesque/build.gradle
+++ b/integration_tests/ctesque/build.gradle
@@ -47,7 +47,7 @@ android {
dependencies {
implementation project(':testapp')
- testImplementation project(":robolectric")
+ testImplementation project(':robolectric')
testImplementation "junit:junit:${junitVersion}"
testImplementation("androidx.test:monitor:$axtMonitorVersion")
testImplementation("androidx.test:runner:$axtRunnerVersion")
@@ -67,4 +67,5 @@ dependencies {
androidTestImplementation("androidx.test.ext:truth:$axtTruthVersion")
androidTestImplementation("com.google.truth:truth:${truthVersion}")
androidTestImplementation("com.google.guava:guava:$guavaJREVersion")
+ androidTestUtil "androidx.test.services:test-services:$axtTestServicesVersion"
}
diff --git a/integration_tests/ctesque/src/sharedTest/java/android/content/res/ResourcesTest.java b/integration_tests/ctesque/src/sharedTest/java/android/content/res/ResourcesTest.java
index 131c45e34..c1922b60d 100644
--- a/integration_tests/ctesque/src/sharedTest/java/android/content/res/ResourcesTest.java
+++ b/integration_tests/ctesque/src/sharedTest/java/android/content/res/ResourcesTest.java
@@ -647,9 +647,12 @@ public class ResourcesTest {
}
@Test
- @Ignore("todo: incorrect behavior on robolectric vs framework?")
- public void openRawResourceFd_returnsNull_todo_FIX() {
- assertThat(resources.openRawResourceFd(R.raw.raw_resource)).isNull();
+ public void openRawResourceFd_withNonCompressedFile_returnsNotNull() throws IOException {
+ // This test will run on non-legacy resource mode in Robolectric environment.
+ // To test behavior on legacy mode environment, please see ShadowResourceTest.
+ try (AssetFileDescriptor afd = resources.openRawResourceFd(R.raw.raw_resource)) {
+ assertThat(afd).isNotNull();
+ }
}
@Test
diff --git a/nativeruntime/src/main/java/org/robolectric/nativeruntime/DefaultNativeRuntimeLoader.java b/nativeruntime/src/main/java/org/robolectric/nativeruntime/DefaultNativeRuntimeLoader.java
index 57160522a..5221a4b8a 100644
--- a/nativeruntime/src/main/java/org/robolectric/nativeruntime/DefaultNativeRuntimeLoader.java
+++ b/nativeruntime/src/main/java/org/robolectric/nativeruntime/DefaultNativeRuntimeLoader.java
@@ -1,22 +1,33 @@
package org.robolectric.nativeruntime;
+import static android.os.Build.VERSION_CODES.O;
import static com.google.common.base.StandardSystemProperty.OS_ARCH;
import static com.google.common.base.StandardSystemProperty.OS_NAME;
import android.database.CursorWindow;
+import android.os.Build;
import com.google.auto.service.AutoService;
import com.google.common.annotations.VisibleForTesting;
+import com.google.common.collect.ImmutableMap;
import com.google.common.io.Files;
import com.google.common.io.Resources;
-import java.io.File;
import java.io.IOException;
+import java.net.URI;
+import java.net.URISyntaxException;
import java.net.URL;
+import java.nio.file.FileSystem;
+import java.nio.file.FileSystems;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.Iterator;
import java.util.Locale;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicReference;
+import java.util.stream.Stream;
import javax.annotation.Priority;
import org.robolectric.pluginapi.NativeRuntimeLoader;
import org.robolectric.util.PerfStatsCollector;
+import org.robolectric.util.TempDirectory;
import org.robolectric.util.inject.Injector;
/** Loads the Robolectric native runtime. */
@@ -28,6 +39,8 @@ public class DefaultNativeRuntimeLoader implements NativeRuntimeLoader {
private static final AtomicReference<NativeRuntimeLoader> nativeRuntimeLoader =
new AtomicReference<>();
+ private TempDirectory extractDirectory;
+
public static void injectAndLoad() {
// Ensure a single instance.
synchronized (nativeRuntimeLoader) {
@@ -60,20 +73,86 @@ public class DefaultNativeRuntimeLoader implements NativeRuntimeLoader {
.measure(
"loadNativeRuntime",
() -> {
- String libraryName = System.mapLibraryName("robolectric-nativeruntime");
+ extractDirectory = new TempDirectory("nativeruntime");
System.setProperty(
"robolectric.nativeruntime.languageTag", Locale.getDefault().toLanguageTag());
- File tmpLibraryFile = java.nio.file.Files.createTempFile("", libraryName).toFile();
- tmpLibraryFile.deleteOnExit();
- URL resource = Resources.getResource(nativeLibraryPath());
- Resources.asByteSource(resource).copyTo(Files.asByteSink(tmpLibraryFile));
- System.load(tmpLibraryFile.getAbsolutePath());
+ if (Build.VERSION.SDK_INT >= O) {
+ maybeCopyFonts(extractDirectory);
+ }
+ maybeCopyIcuData(extractDirectory);
+ loadLibrary(extractDirectory);
});
} catch (IOException e) {
throw new AssertionError("Unable to load Robolectric native runtime library", e);
}
}
+ /** Attempts to load the ICU dat file. This is only relevant for native graphics. */
+ private void maybeCopyIcuData(TempDirectory tempDirectory) throws IOException {
+ URL icuDatUrl;
+ try {
+ icuDatUrl = Resources.getResource("icu/icudt68l.dat");
+ } catch (IllegalArgumentException e) {
+ return;
+ }
+ Path icuPath = tempDirectory.create("icu");
+ Path icuDatPath = tempDirectory.getBasePath().resolve("icu/icudt68l.dat");
+ Resources.asByteSource(icuDatUrl).copyTo(Files.asByteSink(icuDatPath.toFile()));
+ System.setProperty("icu.dir", icuPath.toAbsolutePath().toString());
+ }
+
+ /**
+ * Attempts to copy the system fonts to a temporary directory. This is only relevant for native
+ * graphics.
+ */
+ private void maybeCopyFonts(TempDirectory tempDirectory) throws IOException {
+ URI fontsUri = null;
+ try {
+ fontsUri = Resources.getResource("fonts/").toURI();
+ } catch (IllegalArgumentException | URISyntaxException e) {
+ return;
+ }
+
+ FileSystem zipfs = null;
+
+ if ("jar".equals(fontsUri.getScheme())) {
+ zipfs = FileSystems.newFileSystem(fontsUri, ImmutableMap.of("create", "true"));
+ }
+
+ Path fontsInputPath = Paths.get(fontsUri);
+ Path fontsOutputPath = tempDirectory.create("fonts");
+
+ try (Stream<Path> pathStream = java.nio.file.Files.walk(fontsInputPath)) {
+ Iterator<Path> fileIterator = pathStream.iterator();
+ while (fileIterator.hasNext()) {
+ Path path = fileIterator.next();
+ // Avoid copying parent directory.
+ if ("fonts".equals(path.getFileName().toString())) {
+ continue;
+ }
+ String fontPath = "fonts/" + path.getFileName();
+ URL resource = Resources.getResource(fontPath);
+ Path outputPath = tempDirectory.getBasePath().resolve(fontPath);
+ Resources.asByteSource(resource).copyTo(Files.asByteSink(outputPath.toFile()));
+ }
+ }
+ System.setProperty(
+ "robolectric.nativeruntime.fontdir", fontsOutputPath.toAbsolutePath().toString());
+ if (zipfs != null) {
+ zipfs.close();
+ }
+ }
+
+ private void loadLibrary(TempDirectory tempDirectory) throws IOException {
+ String libraryName = System.mapLibraryName("robolectric-nativeruntime");
+ System.setProperty(
+ "robolectric.nativeruntime.languageTag", Locale.getDefault().toLanguageTag());
+ Path libraryPath = tempDirectory.getBasePath().resolve(libraryName);
+ URL libraryResource = Resources.getResource(nativeLibraryPath());
+ Resources.asByteSource(libraryResource).copyTo(Files.asByteSink(libraryPath.toFile()));
+ System.load(libraryPath.toAbsolutePath().toString());
+ }
+
private static boolean isSupported() {
return ("mac".equals(osName()) && ("aarch64".equals(arch()) || "x86_64".equals(arch())))
|| ("linux".equals(osName()) && "x86_64".equals(arch()))
@@ -111,4 +190,14 @@ public class DefaultNativeRuntimeLoader implements NativeRuntimeLoader {
static boolean isLoaded() {
return loaded.get();
}
+
+ @VisibleForTesting
+ Path getDirectory() {
+ return extractDirectory == null ? null : extractDirectory.getBasePath();
+ }
+
+ @VisibleForTesting
+ static void resetLoaded() {
+ loaded.set(false);
+ }
}
diff --git a/nativeruntime/src/test/java/org/robolectric/nativeruntime/DefaultNativeRuntimeLazyLoadTest.java b/nativeruntime/src/test/java/org/robolectric/nativeruntime/DefaultNativeRuntimeLazyLoadTest.java
index ec86818f1..5fa5ad364 100644
--- a/nativeruntime/src/test/java/org/robolectric/nativeruntime/DefaultNativeRuntimeLazyLoadTest.java
+++ b/nativeruntime/src/test/java/org/robolectric/nativeruntime/DefaultNativeRuntimeLazyLoadTest.java
@@ -1,6 +1,7 @@
package org.robolectric.nativeruntime;
import static com.google.common.truth.Truth.assertThat;
+import static org.robolectric.annotation.Config.ALL_SDKS;
import android.app.Application;
import android.database.CursorWindow;
@@ -8,8 +9,10 @@ import org.junit.Test;
import org.junit.runner.RunWith;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
+import org.robolectric.annotation.Config;
@RunWith(RobolectricTestRunner.class)
+@Config(sdk = ALL_SDKS)
public final class DefaultNativeRuntimeLazyLoadTest {
/**
diff --git a/nativeruntime/src/test/java/org/robolectric/nativeruntime/DefaultNativeRuntimeLoaderTest.java b/nativeruntime/src/test/java/org/robolectric/nativeruntime/DefaultNativeRuntimeLoaderTest.java
index cbb9cf1f5..03911593e 100644
--- a/nativeruntime/src/test/java/org/robolectric/nativeruntime/DefaultNativeRuntimeLoaderTest.java
+++ b/nativeruntime/src/test/java/org/robolectric/nativeruntime/DefaultNativeRuntimeLoaderTest.java
@@ -1,21 +1,59 @@
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 android.database.CursorWindow;
import android.database.sqlite.SQLiteDatabase;
+import java.nio.file.Path;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
+import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.robolectric.RobolectricTestRunner;
+import org.robolectric.RuntimeEnvironment;
@RunWith(RobolectricTestRunner.class)
public final class DefaultNativeRuntimeLoaderTest {
ExecutorService executor = Executors.newSingleThreadExecutor();
+ @Before
+ public void setUp() {
+ DefaultNativeRuntimeLoader.resetLoaded();
+ }
+
@Test
public void concurrentLoad() throws Exception {
executor.execute(() -> SQLiteDatabase.create(null));
CursorWindow cursorWindow = new CursorWindow("sdfsdf");
cursorWindow.close();
}
+
+ @Test
+ public void extracts_fontsAndIcuData() {
+ assumeTrue(hasResource("fonts"));
+ assumeTrue(hasResource("icu/icudt68l.dat"));
+ DefaultNativeRuntimeLoader defaultNativeRuntimeLoader = new DefaultNativeRuntimeLoader();
+ defaultNativeRuntimeLoader.ensureLoaded();
+ // Check that extraction of some key files worked.
+ Path root = defaultNativeRuntimeLoader.getDirectory();
+ assertThat(root.resolve("icu/icudt68l.dat").toFile().exists()).isTrue();
+ if (RuntimeEnvironment.getApiLevel() >= O) {
+ assertThat(root.resolve("fonts/fonts.xml").toFile().exists()).isTrue();
+ }
+ }
+
+ @Test
+ public void tempDirectory() {
+ DefaultNativeRuntimeLoader defaultNativeRuntimeLoader = new DefaultNativeRuntimeLoader();
+ assertThat((Object) defaultNativeRuntimeLoader.getDirectory()).isNull();
+ defaultNativeRuntimeLoader.ensureLoaded();
+ assertThat((Object) defaultNativeRuntimeLoader.getDirectory()).isNotNull();
+ }
+
+ private static boolean hasResource(String name) {
+ return Thread.currentThread().getContextClassLoader().getResource(name) != null;
+ }
}
diff --git a/nativeruntime/src/test/resources/AndroidManifest.xml b/nativeruntime/src/test/resources/AndroidManifest.xml
new file mode 100644
index 000000000..efda5aea1
--- /dev/null
+++ b/nativeruntime/src/test/resources/AndroidManifest.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="utf-8"?>
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:tools="http://schemas.android.com/tools"
+ package="org.robolectric.nativeruntime">
+
+ <uses-sdk android:minSdkVersion="16" android:targetSdkVersion="33"/>
+ <application />
+</manifest>
diff --git a/nativeruntime/src/test/resources/com/android/tools/test_config.properties b/nativeruntime/src/test/resources/com/android/tools/test_config.properties
new file mode 100644
index 000000000..1fa076d61
--- /dev/null
+++ b/nativeruntime/src/test/resources/com/android/tools/test_config.properties
@@ -0,0 +1,5 @@
+android_merged_assets=src/test/resources/assets
+android_merged_resources=src/test/resources/res
+android_merged_manifest=src/test/resources/AndroidManifest.xml
+android_custom_package=org.robolectric
+android_resource_apk=src/test/resources/resources.ap_
diff --git a/nativeruntime/src/test/resources/resources.ap_ b/nativeruntime/src/test/resources/resources.ap_
new file mode 100644
index 000000000..bc05da2ad
--- /dev/null
+++ b/nativeruntime/src/test/resources/resources.ap_
Binary files differ
diff --git a/preinstrumented/build.gradle b/preinstrumented/build.gradle
index 438307c42..95d533e4d 100644
--- a/preinstrumented/build.gradle
+++ b/preinstrumented/build.gradle
@@ -116,11 +116,14 @@ if (System.getenv('PUBLISH_PREINSTRUMENTED_JARS') == "true") {
}
}
-def sdksToInstrument() {
+static def sdksToInstrument() {
var result = AndroidSdk.ALL_SDKS
- var sdkFilter = (System.getenv('PREINSTRUMENTED_SDK_VERSIONS') ?: "").split(",").collect { it as Integer }
- if (sdkFilter.size > 0) {
- result = result.findAll { sdkFilter.contains(it.apiLevel) }
+ var preInstrumentedSdkVersions = (System.getenv('PREINSTRUMENTED_SDK_VERSIONS') ?: "")
+ if (preInstrumentedSdkVersions.length() > 0) {
+ var sdkFilter = preInstrumentedSdkVersions.split(",").collect { it as Integer }
+ if (sdkFilter.size > 0) {
+ result = result.findAll { sdkFilter.contains(it.apiLevel) }
+ }
}
return result
}
diff --git a/resources/src/main/java/org/robolectric/res/android/ResTable.java b/resources/src/main/java/org/robolectric/res/android/ResTable.java
index 627a169ba..edc1a0c86 100644
--- a/resources/src/main/java/org/robolectric/res/android/ResTable.java
+++ b/resources/src/main/java/org/robolectric/res/android/ResTable.java
@@ -46,8 +46,10 @@ import org.robolectric.res.android.ResourceTypes.ResTable_type;
import org.robolectric.res.android.ResourceTypes.ResTable_typeSpec;
import org.robolectric.res.android.ResourceTypes.Res_value;
-// transliterated from https://android.googlesource.com/platform/frameworks/base/+/android-9.0.0_r12/libs/androidfw/ResourceTypes.cpp
-// and https://android.googlesource.com/platform/frameworks/base/+/android-9.0.0_r12/include/androidfw/ResourceTypes.h
+// transliterated from
+// https://android.googlesource.com/platform/frameworks/base/+/android-9.0.0_r12/libs/androidfw/ResourceTypes.cpp
+// and
+// https://android.googlesource.com/platform/frameworks/base/+/android-9.0.0_r12/libs/androidfw/include/androidfw/ResourceTypes.h
@SuppressWarnings("NewApi")
public class ResTable {
@@ -2693,11 +2695,7 @@ public class ResTable {
}
public void lock() {
- try {
- mLock.acquire();
- } catch (InterruptedException e) {
- throw new RuntimeException(e);
- }
+ mLock.acquireUninterruptibly();
}
public void unlock() {
diff --git a/robolectric/build.gradle b/robolectric/build.gradle
index 163dfb70f..cf3f7e3d5 100644
--- a/robolectric/build.gradle
+++ b/robolectric/build.gradle
@@ -53,7 +53,6 @@ dependencies {
testImplementation "org.mockito:mockito-core:${mockitoVersion}"
testImplementation "org.hamcrest:hamcrest-junit:2.0.0.0"
testImplementation "androidx.test:core:$axtCoreVersion@aar"
- testImplementation "androidx.lifecycle:lifecycle-common:2.5.1"
testImplementation "androidx.test.ext:junit:$axtJunitVersion@aar"
testImplementation "androidx.test.ext:truth:$axtTruthVersion@aar"
testImplementation "androidx.test:runner:$axtRunnerVersion@aar"
diff --git a/robolectric/src/main/java/org/robolectric/RobolectricTestRunner.java b/robolectric/src/main/java/org/robolectric/RobolectricTestRunner.java
index 3db82c082..affe32afa 100644
--- a/robolectric/src/main/java/org/robolectric/RobolectricTestRunner.java
+++ b/robolectric/src/main/java/org/robolectric/RobolectricTestRunner.java
@@ -273,8 +273,11 @@ public class RobolectricTestRunner extends SandboxTestRunner {
if (resourcesMode == ResourcesMode.LEGACY && sdk.getApiLevel() > Build.VERSION_CODES.P) {
System.err.println(
- "Skip " + method.getName() + " because Robolectric doesn't support legacy mode after P");
- throw new AssumptionViolatedException("Robolectric doesn't support legacy mode after P");
+ "Skip "
+ + method.getName()
+ + " because Robolectric doesn't support legacy resources mode after P");
+ throw new AssumptionViolatedException(
+ "Robolectric doesn't support legacy resources mode after P");
}
LooperMode.Mode looperMode =
roboMethod.configuration == null
diff --git a/robolectric/src/test/java/org/robolectric/junit/rules/ExpectedLogMessagesRuleTest.java b/robolectric/src/test/java/org/robolectric/junit/rules/ExpectedLogMessagesRuleTest.java
index cd0f30191..a84ad0606 100644
--- a/robolectric/src/test/java/org/robolectric/junit/rules/ExpectedLogMessagesRuleTest.java
+++ b/robolectric/src/test/java/org/robolectric/junit/rules/ExpectedLogMessagesRuleTest.java
@@ -179,7 +179,7 @@ public final class ExpectedLogMessagesRuleTest {
+ "\\s+tag='Mytag'"
+ "\\s+msg='message2'"
+ "\\s+throwable=java.lang.IllegalArgumentException"
- + "(\\s+at .*\\)\\n)+"
+ + "(\\s+at .*\\)\\R)+"
+ "\\s+}][\\s\\S]*";
String expectedNotObservedPattern =
"[\\s\\S]*Expected, but not observed:"
diff --git a/robolectric/src/test/java/org/robolectric/junit/runner/EnclosedTest.java b/robolectric/src/test/java/org/robolectric/junit/runner/EnclosedTest.java
new file mode 100644
index 000000000..ee4ee3c07
--- /dev/null
+++ b/robolectric/src/test/java/org/robolectric/junit/runner/EnclosedTest.java
@@ -0,0 +1,46 @@
+package org.robolectric.junit.runner;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.experimental.runners.Enclosed;
+import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
+
+@RunWith(Enclosed.class)
+public class EnclosedTest {
+ public abstract static class BaseTest {
+ protected String foo;
+ }
+
+ @RunWith(RobolectricTestRunner.class)
+ public static class MyFirstTest extends BaseTest {
+ private static final String STRING = "Hello1";
+
+ @Before
+ public void setUp() {
+ foo = STRING;
+ }
+
+ @Test
+ public void testStringInitialization() {
+ assertThat(foo).isEqualTo(STRING);
+ }
+ }
+
+ @RunWith(RobolectricTestRunner.class)
+ public static class MySecondTest extends BaseTest {
+ private static final String STRING = "Hello2";
+
+ @Before
+ public void setUp() {
+ foo = STRING;
+ }
+
+ @Test
+ public void testStringInitialization() {
+ assertThat(foo).isEqualTo(STRING);
+ }
+ }
+}
diff --git a/robolectric/src/test/java/org/robolectric/shadows/ShadowActivityTest.java b/robolectric/src/test/java/org/robolectric/shadows/ShadowActivityTest.java
index c3250a7a8..08b2a2060 100644
--- a/robolectric/src/test/java/org/robolectric/shadows/ShadowActivityTest.java
+++ b/robolectric/src/test/java/org/robolectric/shadows/ShadowActivityTest.java
@@ -118,14 +118,15 @@ public class ShadowActivityTest {
@Test
public void createActivity_noDisplayFinished_shouldFinishActivity() {
- ActivityController<Activity> controller = Robolectric.buildActivity(Activity.class);
- controller.get().setTheme(android.R.style.Theme_NoDisplay);
- controller.create();
- controller.get().finish();
- controller.start().visible().resume();
+ try (ActivityController<Activity> controller = Robolectric.buildActivity(Activity.class)) {
+ controller.get().setTheme(android.R.style.Theme_NoDisplay);
+ controller.create();
+ controller.get().finish();
+ controller.start().visible().resume();
- activity = controller.get();
- assertThat(activity.isFinishing()).isTrue();
+ activity = controller.get();
+ assertThat(activity.isFinishing()).isTrue();
+ }
}
@Config(minSdk = M)
@@ -154,35 +155,38 @@ public class ShadowActivityTest {
public void
shouldNotComplainIfActivityIsDestroyedWhileAnotherActivityHasRegisteredBroadcastReceivers()
throws Exception {
- ActivityController<DialogCreatingActivity> controller =
- Robolectric.buildActivity(DialogCreatingActivity.class);
- activity = controller.get();
+ try (ActivityController<DialogCreatingActivity> controller =
+ Robolectric.buildActivity(DialogCreatingActivity.class)) {
+ activity = controller.get();
- DialogLifeCycleActivity activity2 = Robolectric.setupActivity(DialogLifeCycleActivity.class);
- activity2.registerReceiver(new AppWidgetProvider(), new IntentFilter());
+ DialogLifeCycleActivity activity2 = Robolectric.setupActivity(DialogLifeCycleActivity.class);
+ activity2.registerReceiver(new AppWidgetProvider(), new IntentFilter());
- controller.destroy();
+ controller.destroy();
+ }
}
@Test
public void shouldNotRegisterNullBroadcastReceiver() {
- ActivityController<DialogCreatingActivity> controller =
- Robolectric.buildActivity(DialogCreatingActivity.class);
- activity = controller.get();
- activity.registerReceiver(null, new IntentFilter());
+ try (ActivityController<DialogCreatingActivity> controller =
+ Robolectric.buildActivity(DialogCreatingActivity.class)) {
+ activity = controller.get();
+ activity.registerReceiver(null, new IntentFilter());
- controller.destroy();
+ controller.destroy();
+ }
}
@Test
@Config(minSdk = JELLY_BEAN_MR1)
public void shouldReportDestroyedStatus() {
- ActivityController<DialogCreatingActivity> controller =
- Robolectric.buildActivity(DialogCreatingActivity.class);
- activity = controller.get();
+ try (ActivityController<DialogCreatingActivity> controller =
+ Robolectric.buildActivity(DialogCreatingActivity.class)) {
+ activity = controller.get();
- controller.destroy();
- assertThat(activity.isDestroyed()).isTrue();
+ controller.destroy();
+ assertThat(activity.isDestroyed()).isTrue();
+ }
}
@Test
@@ -259,12 +263,14 @@ public class ShadowActivityTest {
@Test
public void startActivityForResultAndReceiveResult_whenNoIntentMatches_shouldThrowException() {
- ThrowOnResultActivity activity = Robolectric.buildActivity(ThrowOnResultActivity.class).get();
- activity.startActivityForResult(new Intent().setType("audio/*"), 123);
- activity.startActivityForResult(new Intent().setType("image/*"), 456);
-
- Intent requestIntent = new Intent().setType("video/*");
- try {
+ Intent requestIntent = new Intent();
+ try (ActivityController<ThrowOnResultActivity> controller =
+ Robolectric.buildActivity(ThrowOnResultActivity.class)) {
+ ThrowOnResultActivity activity = controller.get();
+ activity.startActivityForResult(new Intent().setType("audio/*"), 123);
+ activity.startActivityForResult(new Intent().setType("image/*"), 456);
+
+ requestIntent.setType("video/*");
shadowOf(activity)
.receiveResult(
requestIntent, Activity.RESULT_OK, new Intent().setData(Uri.parse("content:foo")));
@@ -600,11 +606,13 @@ public class ShadowActivityTest {
@Test // unclear what the correct behavior should be here...
public void shouldPopulateWindowDecorViewWithMergeLayoutContents() {
- Activity activity = Robolectric.buildActivity(Activity.class).create().get();
- activity.setContentView(R.layout.toplevel_merge);
+ try (ActivityController<Activity> controller = Robolectric.buildActivity(Activity.class)) {
+ Activity activity = controller.create().get();
+ activity.setContentView(R.layout.toplevel_merge);
- View contentView = activity.findViewById(android.R.id.content);
- assertThat(((ViewGroup) contentView).getChildCount()).isEqualTo(2);
+ View contentView = activity.findViewById(android.R.id.content);
+ assertThat(((ViewGroup) contentView).getChildCount()).isEqualTo(2);
+ }
}
@Test
@@ -1011,10 +1019,12 @@ public class ShadowActivityTest {
@Test
public void getActionBar_shouldWorkIfActivityHasAnAppropriateTheme() {
- ActionBarThemedActivity myActivity =
- Robolectric.buildActivity(ActionBarThemedActivity.class).create().get();
- ActionBar actionBar = myActivity.getActionBar();
- assertThat(actionBar).isNotNull();
+ try (ActivityController<ActionBarThemedActivity> controller =
+ Robolectric.buildActivity(ActionBarThemedActivity.class)) {
+ ActionBarThemedActivity myActivity = controller.create().get();
+ ActionBar actionBar = myActivity.getActionBar();
+ assertThat(actionBar).isNotNull();
+ }
}
public static class ActionBarThemedActivity extends Activity {
@@ -1330,157 +1340,168 @@ public class ShadowActivityTest {
@Test
@Config(minSdk = O)
public void buildActivity_noOptionsBundle_launchesOnDefaultDisplay() {
- Activity activity = Robolectric.buildActivity(Activity.class, null).setup().get();
+ try (ActivityController<Activity> controller =
+ Robolectric.buildActivity(Activity.class, null)) {
+ Activity activity = controller.setup().get();
- assertThat(activity.getWindowManager().getDefaultDisplay().getDisplayId())
- .isEqualTo(Display.DEFAULT_DISPLAY);
+ assertThat(activity.getWindowManager().getDefaultDisplay().getDisplayId())
+ .isEqualTo(Display.DEFAULT_DISPLAY);
+ }
}
@Test
@Config(minSdk = O)
public void buildActivity_optionBundleWithNoDisplaySet_launchesOnDefaultDisplay() {
- Activity activity =
- Robolectric.buildActivity(Activity.class, null, ActivityOptions.makeBasic().toBundle())
- .setup()
- .get();
+ try (ActivityController<Activity> controller =
+ Robolectric.buildActivity(Activity.class, null, ActivityOptions.makeBasic().toBundle())) {
+ Activity activity = controller.setup().get();
- assertThat(activity.getWindowManager().getDefaultDisplay().getDisplayId())
- .isEqualTo(Display.DEFAULT_DISPLAY);
+ assertThat(activity.getWindowManager().getDefaultDisplay().getDisplayId())
+ .isEqualTo(Display.DEFAULT_DISPLAY);
+ }
}
@Test
@Config(minSdk = O)
public void buildActivity_optionBundleWithDefaultDisplaySet_launchesOnDefaultDisplay() {
- Activity activity =
+ try (ActivityController<Activity> controller =
Robolectric.buildActivity(
- Activity.class,
- null,
- ActivityOptions.makeBasic().setLaunchDisplayId(Display.DEFAULT_DISPLAY).toBundle())
- .setup()
- .get();
-
- assertThat(activity.getWindowManager().getDefaultDisplay().getDisplayId())
- .isEqualTo(Display.DEFAULT_DISPLAY);
+ Activity.class,
+ null,
+ ActivityOptions.makeBasic().setLaunchDisplayId(Display.DEFAULT_DISPLAY).toBundle())) {
+ Activity activity = controller.setup().get();
+ assertThat(activity.getWindowManager().getDefaultDisplay().getDisplayId())
+ .isEqualTo(Display.DEFAULT_DISPLAY);
+ }
}
@Test
@Config(minSdk = O)
public void buildActivity_optionBundleWithValidNonDefaultDisplaySet_launchesOnSpecifiedDisplay() {
int displayId = ShadowDisplayManager.addDisplay("");
-
- Activity activity =
+ try (ActivityController<Activity> controller =
Robolectric.buildActivity(
- Activity.class,
- null,
- ActivityOptions.makeBasic().setLaunchDisplayId(displayId).toBundle())
- .setup()
- .get();
-
- assertThat(activity.getWindowManager().getDefaultDisplay().getDisplayId())
- .isNotEqualTo(Display.DEFAULT_DISPLAY);
- assertThat(activity.getWindowManager().getDefaultDisplay().getDisplayId()).isEqualTo(displayId);
+ Activity.class,
+ null,
+ ActivityOptions.makeBasic().setLaunchDisplayId(displayId).toBundle())) {
+ Activity activity = controller.setup().get();
+ assertThat(activity.getWindowManager().getDefaultDisplay().getDisplayId())
+ .isNotEqualTo(Display.DEFAULT_DISPLAY);
+ assertThat(activity.getWindowManager().getDefaultDisplay().getDisplayId())
+ .isEqualTo(displayId);
+ }
}
@Test
@Config(minSdk = O)
public void buildActivity_optionBundleWithInvalidNonDefaultDisplaySet_launchesOnDefaultDisplay() {
- Activity activity =
+ try (ActivityController<Activity> controller =
Robolectric.buildActivity(
- Activity.class,
- null,
- ActivityOptions.makeBasic().setLaunchDisplayId(123).toBundle())
- .setup()
- .get();
-
- assertThat(activity.getWindowManager().getDefaultDisplay().getDisplayId())
- .isEqualTo(Display.DEFAULT_DISPLAY);
+ Activity.class, null, ActivityOptions.makeBasic().setLaunchDisplayId(123).toBundle())) {
+ Activity activity = controller.setup().get();
+ assertThat(activity.getWindowManager().getDefaultDisplay().getDisplayId())
+ .isEqualTo(Display.DEFAULT_DISPLAY);
+ }
}
@Test
@Config(minSdk = Q)
public void callOnGetDirectActions_succeeds() {
- ActivityController<TestActivity> controller = Robolectric.buildActivity(TestActivity.class);
- TestActivity testActivity = controller.setup().get();
- Consumer<List<DirectAction>> testConsumer =
- (directActions) -> {
- assertThat(directActions.size()).isEqualTo(1);
- DirectAction action = directActions.get(0);
- assertThat(action.getId()).isEqualTo(testActivity.getDirectActionForTesting().getId());
- ComponentName componentName = action.getExtras().getParcelable("componentName");
- assertThat(componentName.compareTo(testActivity.getComponentName())).isEqualTo(0);
- };
- shadowOf(testActivity).callOnGetDirectActions(new CancellationSignal(), testConsumer);
+ try (ActivityController<TestActivity> controller =
+ Robolectric.buildActivity(TestActivity.class)) {
+ TestActivity testActivity = controller.setup().get();
+ Consumer<List<DirectAction>> testConsumer =
+ (directActions) -> {
+ assertThat(directActions.size()).isEqualTo(1);
+ DirectAction action = directActions.get(0);
+ assertThat(action.getId()).isEqualTo(testActivity.getDirectActionForTesting().getId());
+ ComponentName componentName = action.getExtras().getParcelable("componentName");
+ assertThat(componentName.compareTo(testActivity.getComponentName())).isEqualTo(0);
+ };
+ shadowOf(testActivity).callOnGetDirectActions(new CancellationSignal(), testConsumer);
+ }
}
@Test
@Config(minSdk = Q)
public void callOnGetDirectActions_malformedDirectAction_fails() {
- ActivityController<TestActivity> controller = Robolectric.buildActivity(TestActivity.class);
- TestActivity testActivity = controller.setup().get();
- // malformed DirectAction has missing LocusId
- testActivity.setReturnMalformedDirectAction(true);
- assertThrows(
- NullPointerException.class,
- () -> {
- shadowOf(testActivity).callOnGetDirectActions(new CancellationSignal(), (unused) -> {});
- });
+ try (ActivityController<TestActivity> controller =
+ Robolectric.buildActivity(TestActivity.class)) {
+ TestActivity testActivity = controller.setup().get();
+ // malformed DirectAction has missing LocusId
+ testActivity.setReturnMalformedDirectAction(true);
+ assertThrows(
+ NullPointerException.class,
+ () -> {
+ shadowOf(testActivity).callOnGetDirectActions(new CancellationSignal(), (unused) -> {});
+ });
+ }
}
@Test
@Config(minSdk = S)
public void splashScreen_setThemeId_succeeds() {
int splashScreenThemeId = 173;
- Activity activity = Robolectric.buildActivity(Activity.class, null).setup().get();
+ try (ActivityController<Activity> controller = Robolectric.buildActivity(Activity.class)) {
+ Activity activity = controller.setup().get();
- activity.getSplashScreen().setSplashScreenTheme(splashScreenThemeId);
+ activity.getSplashScreen().setSplashScreenTheme(splashScreenThemeId);
- RoboSplashScreen roboSplashScreen = (RoboSplashScreen) activity.getSplashScreen();
- assertThat(roboSplashScreen.getSplashScreenTheme()).isEqualTo(splashScreenThemeId);
+ RoboSplashScreen roboSplashScreen = (RoboSplashScreen) activity.getSplashScreen();
+ assertThat(roboSplashScreen.getSplashScreenTheme()).isEqualTo(splashScreenThemeId);
+ }
}
@Test
@Config(minSdk = S)
public void splashScreen_instanceOfRoboSplashScreen_succeeds() {
- Activity activity = Robolectric.buildActivity(Activity.class, null).setup().get();
-
- assertThat(activity.getSplashScreen()).isInstanceOf(RoboSplashScreen.class);
+ try (ActivityController<Activity> controller = Robolectric.buildActivity(Activity.class)) {
+ Activity activity = controller.setup().get();
+ assertThat(activity.getSplashScreen()).isInstanceOf(RoboSplashScreen.class);
+ }
}
@Test
public void applicationWindow_hasCorrectWindowTokens() {
- Activity activity = Robolectric.buildActivity(TestActivity.class).setup().get();
- View activityView = activity.getWindow().getDecorView();
- WindowManager.LayoutParams activityLp =
- (WindowManager.LayoutParams) activityView.getLayoutParams();
-
- View windowView = new View(activity);
- WindowManager.LayoutParams windowViewLp = new WindowManager.LayoutParams();
- windowViewLp.type = WindowManager.LayoutParams.TYPE_APPLICATION;
- ((WindowManager) activity.getSystemService(Context.WINDOW_SERVICE))
- .addView(windowView, windowViewLp);
- ShadowLooper.idleMainLooper();
-
- assertThat(activityLp.token).isNotNull();
- assertThat(windowViewLp.token).isEqualTo(activityLp.token);
+ try (ActivityController<TestActivity> controller =
+ Robolectric.buildActivity(TestActivity.class)) {
+ Activity activity = controller.setup().get();
+ View activityView = activity.getWindow().getDecorView();
+ WindowManager.LayoutParams activityLp =
+ (WindowManager.LayoutParams) activityView.getLayoutParams();
+
+ View windowView = new View(activity);
+ WindowManager.LayoutParams windowViewLp = new WindowManager.LayoutParams();
+ windowViewLp.type = WindowManager.LayoutParams.TYPE_APPLICATION;
+ ((WindowManager) activity.getSystemService(Context.WINDOW_SERVICE))
+ .addView(windowView, windowViewLp);
+ ShadowLooper.idleMainLooper();
+
+ assertThat(activityLp.token).isNotNull();
+ assertThat(windowViewLp.token).isEqualTo(activityLp.token);
+ }
}
@Test
public void subWindow_hasCorrectWindowTokens() {
- Activity activity = Robolectric.buildActivity(TestActivity.class).setup().get();
- View activityView = activity.getWindow().getDecorView();
- WindowManager.LayoutParams activityLp =
- (WindowManager.LayoutParams) activityView.getLayoutParams();
-
- View windowView = new View(activity);
- WindowManager.LayoutParams windowViewLp = new WindowManager.LayoutParams();
- windowViewLp.type = WindowManager.LayoutParams.TYPE_APPLICATION_PANEL;
- ((WindowManager) activity.getSystemService(Context.WINDOW_SERVICE))
- .addView(windowView, windowViewLp);
- ShadowLooper.idleMainLooper();
-
- assertThat(activityLp.token).isNotNull();
- assertThat(windowViewLp.token).isEqualTo(activityView.getWindowToken());
- assertThat(windowView.getApplicationWindowToken()).isEqualTo(activityView.getWindowToken());
+ try (ActivityController<TestActivity> controller =
+ Robolectric.buildActivity(TestActivity.class)) {
+ Activity activity = controller.setup().get();
+ View activityView = activity.getWindow().getDecorView();
+ WindowManager.LayoutParams activityLp =
+ (WindowManager.LayoutParams) activityView.getLayoutParams();
+
+ View windowView = new View(activity);
+ WindowManager.LayoutParams windowViewLp = new WindowManager.LayoutParams();
+ windowViewLp.type = WindowManager.LayoutParams.TYPE_APPLICATION_PANEL;
+ ((WindowManager) activity.getSystemService(Context.WINDOW_SERVICE))
+ .addView(windowView, windowViewLp);
+ ShadowLooper.idleMainLooper();
+
+ assertThat(activityLp.token).isNotNull();
+ assertThat(windowViewLp.token).isEqualTo(activityView.getWindowToken());
+ assertThat(windowView.getApplicationWindowToken()).isEqualTo(activityView.getWindowToken());
+ }
}
@Test
diff --git a/robolectric/src/test/java/org/robolectric/shadows/ShadowLocaleManagerTest.java b/robolectric/src/test/java/org/robolectric/shadows/ShadowLocaleManagerTest.java
index 6c889e25f..aa7cc4c47 100644
--- a/robolectric/src/test/java/org/robolectric/shadows/ShadowLocaleManagerTest.java
+++ b/robolectric/src/test/java/org/robolectric/shadows/ShadowLocaleManagerTest.java
@@ -22,12 +22,14 @@ public final class ShadowLocaleManagerTest {
private static final LocaleList DEFAULT_LOCALES = LocaleList.forLanguageTags("en-XC,ar-XB");
private Context context;
- private ShadowLocaleManager localeManager;
+ private LocaleManager localeManager;
+ private ShadowLocaleManager shadowLocaleManager;
@Before
public void setUp() {
context = ApplicationProvider.getApplicationContext();
- localeManager = Shadow.extract(context.getSystemService(LocaleManager.class));
+ localeManager = context.getSystemService(LocaleManager.class);
+ shadowLocaleManager = Shadow.extract(localeManager);
}
@Test
@@ -38,7 +40,7 @@ public final class ShadowLocaleManagerTest {
localeManager.setApplicationLocales(DEFAULT_PACKAGE_NAME, DEFAULT_LOCALES);
- localeManager.enforceInstallerCheck(false);
+ shadowLocaleManager.enforceInstallerCheck(false);
assertThat(localeManager.getApplicationLocales(DEFAULT_PACKAGE_NAME))
.isEqualTo(DEFAULT_LOCALES);
}
@@ -46,8 +48,8 @@ public final class ShadowLocaleManagerTest {
@Test
public void getApplicationLocales_fetchAsInstaller_returnsLocales() {
localeManager.setApplicationLocales(DEFAULT_PACKAGE_NAME, DEFAULT_LOCALES);
- localeManager.setCallerAsInstallerForPackage(DEFAULT_PACKAGE_NAME);
- localeManager.enforceInstallerCheck(true);
+ shadowLocaleManager.setCallerAsInstallerForPackage(DEFAULT_PACKAGE_NAME);
+ shadowLocaleManager.enforceInstallerCheck(true);
assertThat(localeManager.getApplicationLocales(DEFAULT_PACKAGE_NAME))
.isEqualTo(DEFAULT_LOCALES);
@@ -56,9 +58,25 @@ public final class ShadowLocaleManagerTest {
@Test
public void getApplicationLocales_fetchAsInstaller_throwsSecurityExceptionIfIncorrectInstaller() {
localeManager.setApplicationLocales(DEFAULT_PACKAGE_NAME, DEFAULT_LOCALES);
- localeManager.enforceInstallerCheck(true);
+ shadowLocaleManager.enforceInstallerCheck(true);
assertThrows(
SecurityException.class, () -> localeManager.getApplicationLocales(DEFAULT_PACKAGE_NAME));
}
+
+ @Test
+ @Config(qualifiers = "en")
+ public void getSystemLocales_en() {
+ LocaleList localeList = localeManager.getSystemLocales();
+ assertThat(localeList.size()).isEqualTo(1);
+ assertThat(localeList.get(0).getLanguage()).isEqualTo("en");
+ }
+
+ @Test
+ @Config(qualifiers = "zh")
+ public void getSystemLocales_zh() {
+ LocaleList localeList = localeManager.getSystemLocales();
+ assertThat(localeList.size()).isEqualTo(1);
+ assertThat(localeList.get(0).getLanguage()).isEqualTo("zh");
+ }
}
diff --git a/robolectric/src/test/java/org/robolectric/shadows/ShadowResourcesTest.java b/robolectric/src/test/java/org/robolectric/shadows/ShadowResourcesTest.java
index 51e7cdfea..da3440139 100644
--- a/robolectric/src/test/java/org/robolectric/shadows/ShadowResourcesTest.java
+++ b/robolectric/src/test/java/org/robolectric/shadows/ShadowResourcesTest.java
@@ -2,6 +2,8 @@ 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 org.robolectric.Shadows.shadowOf;
import static org.robolectric.shadows.ShadowAssetManager.useLegacy;
@@ -82,13 +84,18 @@ public class ShadowResourcesTest {
}
@Test
- public void openRawResourceFd_returnsNull_todo_FIX() throws Exception {
+ public void openRawResourceFd_shouldReturnsNullForLegacyResource() throws Exception {
+ assumeTrue(useLegacy());
try (AssetFileDescriptor afd = resources.openRawResourceFd(R.raw.raw_resource)) {
- if (useLegacy()) {
assertThat(afd).isNull();
- } else {
+ }
+ }
+
+ @Test
+ public void openRawResourceFd_shouldReturnsValidFdForUnCompressFile() throws Exception {
+ assumeFalse(useLegacy());
+ try (AssetFileDescriptor afd = resources.openRawResourceFd(R.raw.raw_resource)) {
assertThat(afd).isNotNull();
- }
}
}
diff --git a/robolectric/src/test/java/org/robolectric/shadows/ShadowStorageStatsManagerTest.java b/robolectric/src/test/java/org/robolectric/shadows/ShadowStorageStatsManagerTest.java
index b3d613909..376529d87 100644
--- a/robolectric/src/test/java/org/robolectric/shadows/ShadowStorageStatsManagerTest.java
+++ b/robolectric/src/test/java/org/robolectric/shadows/ShadowStorageStatsManagerTest.java
@@ -134,7 +134,7 @@ public final class ShadowStorageStatsManagerTest {
}
@Test
- public void queryWithoutSetup_shouldFail() {
+ public void queryPackageWithoutSetup_shouldFail() {
assertThrows(
PackageManager.NameNotFoundException.class,
() ->
@@ -144,7 +144,16 @@ public final class ShadowStorageStatsManagerTest {
}
@Test
- public void queryWithCorrectArguments_shouldReturnSetupValue() throws Exception {
+ public void queryUserWithoutSetup_shouldFail() {
+ assertThrows(
+ PackageManager.NameNotFoundException.class,
+ () ->
+ shadowOf(storageStatsManager)
+ .queryStatsForUser(UUID.randomUUID(), Process.myUserHandle()));
+ }
+
+ @Test
+ public void queryPackageWithCorrectArguments_shouldReturnSetupValue() throws Exception {
// Arrange
StorageStats expected = buildStorageStats();
UUID uuid = UUID.randomUUID();
@@ -161,7 +170,105 @@ public final class ShadowStorageStatsManagerTest {
}
@Test
- public void queryWithWrongArguments_shouldFail() {
+ public void queryUserWithCorrectArguments_shouldReturnSetupValue() throws Exception {
+ // Arrange
+ StorageStats expected = buildStorageStats();
+ UUID uuid = UUID.randomUUID();
+ String packageName = "somePackageName";
+ UserHandle userHandle = Process.myUserHandle();
+ shadowOf(storageStatsManager).addStorageStats(uuid, packageName, userHandle, expected);
+
+ // Act
+ StorageStats actual = shadowOf(storageStatsManager).queryStatsForUser(uuid, userHandle);
+
+ // Assert
+ assertThat(actual).isEqualTo(expected);
+ }
+
+ @Test
+ public void queryUser_shouldReturnAccumulatedStats() throws Exception {
+ // Arrange
+ StorageStats storageStats = buildStorageStats();
+ UUID uuid1 = UUID.randomUUID();
+ UUID uuid2 = UUID.randomUUID();
+ String packageName1 = "somePackageName1";
+ String packageName2 = "somePackageName2";
+ String packageName3 = "somePackageName3";
+ UserHandle userHandle = Process.myUserHandle();
+ shadowOf(storageStatsManager).addStorageStats(uuid1, packageName1, userHandle, storageStats);
+ shadowOf(storageStatsManager).addStorageStats(uuid1, packageName2, userHandle, storageStats);
+ shadowOf(storageStatsManager).addStorageStats(uuid1, packageName3, userHandle, storageStats);
+ shadowOf(storageStatsManager).addStorageStats(uuid2, packageName1, userHandle, storageStats);
+ shadowOf(storageStatsManager).addStorageStats(uuid2, packageName2, userHandle, storageStats);
+
+ // Act
+ StorageStats actual1 = shadowOf(storageStatsManager).queryStatsForUser(uuid1, userHandle);
+ StorageStats actual2 = shadowOf(storageStatsManager).queryStatsForUser(uuid2, userHandle);
+
+ // Assert
+ assertThat(actual1.getAppBytes()).isEqualTo(9000L); // 3000 * 3
+ assertThat(actual1.getDataBytes()).isEqualTo(6000L); // 2000 * 3
+ assertThat(actual1.getCacheBytes()).isEqualTo(3000L); // 1000 * 3
+ assertThat(actual2.getAppBytes()).isEqualTo(6000L); // 3000 * 2
+ assertThat(actual2.getDataBytes()).isEqualTo(4000L); // 2000 * 2
+ assertThat(actual2.getCacheBytes()).isEqualTo(2000L); // 1000 * 2
+ }
+
+ @Test
+ public void queryUser_packageStatsUpdated_shouldUpdateUserStats() throws Exception {
+ // Arrange
+ UUID uuid = UUID.randomUUID();
+ String packageName1 = "somePackageName1";
+ String packageName2 = "somePackageName2";
+ UserHandle userHandle = Process.myUserHandle();
+ shadowOf(storageStatsManager)
+ .addStorageStats(uuid, packageName1, userHandle, buildStorageStats());
+ shadowOf(storageStatsManager)
+ .addStorageStats(uuid, packageName2, userHandle, buildStorageStats());
+ shadowOf(storageStatsManager)
+ .addStorageStats(
+ uuid,
+ packageName2,
+ userHandle,
+ buildStorageStats(
+ /* codeSize= */ 2000L, /* dataSize= */ 1000L, /* cacheSize= */ 3000L));
+
+ // Act
+ StorageStats actual = shadowOf(storageStatsManager).queryStatsForUser(uuid, userHandle);
+
+ // Assert
+ assertThat(actual.getAppBytes()).isEqualTo(5000L); // 3000 + 2000
+ assertThat(actual.getDataBytes()).isEqualTo(3000L); // 2000 + 1000
+ assertThat(actual.getCacheBytes()).isEqualTo(4000L); // 1000 + 3000
+ }
+
+ @Test
+ public void queryUser_packageStatsUpdated_singlePackage_shouldUpdateUserStats() throws Exception {
+ // Arrange
+ UUID uuid = UUID.randomUUID();
+ String packageName = "somePackageName1";
+ UserHandle userHandle = Process.myUserHandle();
+ shadowOf(storageStatsManager)
+ .addStorageStats(uuid, packageName, userHandle, buildStorageStats());
+ shadowOf(storageStatsManager)
+ .addStorageStats(
+ uuid,
+ packageName,
+ userHandle,
+ buildStorageStats(
+ /* codeSize= */ 2000L, /* dataSize= */ 1000L, /* cacheSize= */ 3000L));
+
+ // Act
+ StorageStats actual = shadowOf(storageStatsManager).queryStatsForUser(uuid, userHandle);
+
+ // Assert
+ assertThat(actual.getAppBytes()).isEqualTo(2000L);
+ assertThat(actual.getDataBytes()).isEqualTo(1000L);
+ assertThat(actual.getCacheBytes()).isEqualTo(3000L);
+ }
+
+ @Test
+ public void queryPackageWithWrongArguments_shouldFail() {
// Arrange
StorageStats expected = buildStorageStats();
UUID uuid = UUID.randomUUID();
@@ -199,7 +306,35 @@ public final class ShadowStorageStatsManagerTest {
}
@Test
- public void queryAfterClearSetup_shouldFail() {
+ public void queryUserWithWrongArguments_shouldFail() {
+ // Arrange
+ StorageStats expected = buildStorageStats();
+ UUID uuid = UUID.randomUUID();
+ UUID differentUUID = UUID.randomUUID();
+ UserHandle userHandle = UserHandle.getUserHandleForUid(0);
+ // getUserHandleForUid will divide uid by 100000. Pass in some arbitrary number > 100000 to be
+ // different from system uid 0.
+ UserHandle differentUserHandle = UserHandle.getUserHandleForUid(1200000);
+
+ assertThat(uuid).isNotEqualTo(differentUUID);
+ assertThat(userHandle).isNotEqualTo(differentUserHandle);
+
+ // Act
+ shadowOf(storageStatsManager)
+ .addStorageStats(uuid, /* packageName= */ "somePackageName", userHandle, expected);
+
+ // Assert
+ assertThrows(
+ PackageManager.NameNotFoundException.class,
+ () -> shadowOf(storageStatsManager).queryStatsForUser(differentUUID, userHandle));
+
+ assertThrows(
+ PackageManager.NameNotFoundException.class,
+ () -> shadowOf(storageStatsManager).queryStatsForUser(uuid, differentUserHandle));
+ }
+
+ @Test
+ public void queryPackageAfterClearSetup_shouldFail() {
// Arrange
StorageStats expected = buildStorageStats();
UUID uuid = UUID.randomUUID();
@@ -216,10 +351,29 @@ public final class ShadowStorageStatsManagerTest {
() -> shadowOf(storageStatsManager).queryStatsForPackage(uuid, packageName, userHandle));
}
+ @Test
+ public void queryUserAfterClearSetup_shouldFail() {
+ // Arrange
+ StorageStats expected = buildStorageStats();
+ UUID uuid = UUID.randomUUID();
+ String packageName = "somePackageName";
+ UserHandle userHandle = Process.myUserHandle();
+ shadowOf(storageStatsManager).addStorageStats(uuid, packageName, userHandle, expected);
+
+ // Act
+ shadowOf(storageStatsManager).clearStorageStats();
+
+ // Assert
+ assertThrows(
+ PackageManager.NameNotFoundException.class,
+ () -> shadowOf(storageStatsManager).queryStatsForUser(uuid, userHandle));
+ }
+
private static StorageStats buildStorageStats() {
- long codeSize = 3000L;
- long dataSize = 2000L;
- long cacheSize = 1000L;
+ return buildStorageStats(/* codeSize= */ 3000L, /* dataSize= */ 2000L, /* cacheSize= */ 1000L);
+ }
+
+ private static StorageStats buildStorageStats(long codeSize, long dataSize, long cacheSize) {
Parcel parcel = Parcel.obtain();
parcel.writeLong(codeSize);
parcel.writeLong(dataSize);
diff --git a/robolectric/src/test/java/org/robolectric/shadows/ShadowUIModeManagerTest.java b/robolectric/src/test/java/org/robolectric/shadows/ShadowUIModeManagerTest.java
index 2dff1347c..0fb9bc605 100644
--- a/robolectric/src/test/java/org/robolectric/shadows/ShadowUIModeManagerTest.java
+++ b/robolectric/src/test/java/org/robolectric/shadows/ShadowUIModeManagerTest.java
@@ -53,6 +53,13 @@ public class ShadowUIModeManagerTest {
}
@Test
+ public void testModeType() {
+ assertThat(uiModeManager.getCurrentModeType()).isEqualTo(Configuration.UI_MODE_TYPE_UNDEFINED);
+ shadowOf(uiModeManager).setCurrentModeType(Configuration.UI_MODE_TYPE_DESK);
+ assertThat(uiModeManager.getCurrentModeType()).isEqualTo(Configuration.UI_MODE_TYPE_DESK);
+ }
+
+ @Test
@Config(minSdk = R)
public void testCarModePriority() {
int priority = 9;
diff --git a/robolectric/src/test/java/org/robolectric/shadows/ShadowUsbDeviceConnectionTest.java b/robolectric/src/test/java/org/robolectric/shadows/ShadowUsbDeviceConnectionTest.java
index 09f568c89..52ba0285b 100644
--- a/robolectric/src/test/java/org/robolectric/shadows/ShadowUsbDeviceConnectionTest.java
+++ b/robolectric/src/test/java/org/robolectric/shadows/ShadowUsbDeviceConnectionTest.java
@@ -3,6 +3,7 @@ package org.robolectric.shadows;
import static android.os.Build.VERSION_CODES.LOLLIPOP;
import static com.google.common.truth.Truth.assertThat;
import static java.nio.charset.StandardCharsets.UTF_8;
+import static org.junit.Assert.assertThrows;
import static org.mockito.Mockito.when;
import static org.robolectric.Shadows.shadowOf;
@@ -17,6 +18,9 @@ import android.hardware.usb.UsbManager;
import androidx.annotation.Nullable;
import androidx.test.core.app.ApplicationProvider;
import androidx.test.ext.junit.runners.AndroidJUnit4;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Arrays;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -47,7 +51,6 @@ public class ShadowUsbDeviceConnectionTest {
when(usbDevice.getConfiguration(0)).thenReturn(usbConfiguration);
when(usbConfiguration.getInterfaceCount()).thenReturn(1);
when(usbConfiguration.getInterface(0)).thenReturn(usbInterface);
- when(usbConfiguration.getInterface(0)).thenReturn(usbInterface);
}
@Test
@@ -56,38 +59,47 @@ public class ShadowUsbDeviceConnectionTest {
UsbDeviceConnection usbDeviceConnection = usbManager.openDevice(usbDevice);
UsbInterface usbInterface = selectInterface(usbDevice);
- assertThat(usbDeviceConnection.claimInterface(usbInterface, /*force=*/ false)).isTrue();
+ assertThat(usbDeviceConnection.claimInterface(usbInterface, /* force= */ false)).isTrue();
assertThat(usbDeviceConnection.releaseInterface(usbInterface)).isTrue();
}
@Test
@Config(minSdk = LOLLIPOP)
+ public void setInterface() {
+ UsbDeviceConnection usbDeviceConnection = usbManager.openDevice(usbDevice);
+ UsbInterface usbInterface = selectInterface(usbDevice);
+
+ assertThat(usbDeviceConnection.setInterface(usbInterface)).isTrue();
+ }
+
+ @Test
+ @Config(minSdk = LOLLIPOP)
public void controlTransfer() {
UsbDeviceConnection usbDeviceConnection = usbManager.openDevice(usbDevice);
UsbInterface usbInterface = selectInterface(usbDevice);
- usbDeviceConnection.claimInterface(usbInterface, /*force=*/ false);
+ usbDeviceConnection.claimInterface(usbInterface, /* force= */ false);
int len = 10;
assertThat(
usbDeviceConnection.controlTransfer(
- /*requestType=*/ 0,
- /*request=*/ 0,
- /*value=*/ 0,
- /*index=*/ 0,
- /*buffer=*/ new byte[len],
- /*length=*/ len,
- /*timeout=*/ 0))
+ /* requestType= */ 0,
+ /* request= */ 0,
+ /* value= */ 0,
+ /* index= */ 0,
+ /* buffer= */ new byte[len],
+ /* length= */ len,
+ /* timeout= */ 0))
.isEqualTo(len);
assertThat(
usbDeviceConnection.controlTransfer(
- /*requestType=*/ 0,
- /*request=*/ 0,
- /*value=*/ 0,
- /*index=*/ 0,
- /*buffer=*/ new byte[len],
- /*offset=*/ 0,
- /*length=*/ len,
- /*timeout=*/ 0))
+ /* requestType= */ 0,
+ /* request= */ 0,
+ /* value= */ 0,
+ /* index= */ 0,
+ /* buffer= */ new byte[len],
+ /* offset= */ 0,
+ /* length= */ len,
+ /* timeout= */ 0))
.isEqualTo(len);
}
@@ -97,25 +109,44 @@ public class ShadowUsbDeviceConnectionTest {
UsbDeviceConnection usbDeviceConnection = usbManager.openDevice(usbDevice);
UsbInterface usbInterface = selectInterface(usbDevice);
UsbEndpoint usbEndpointOut = getEndpoint(usbInterface, UsbConstants.USB_DIR_OUT);
- usbDeviceConnection.claimInterface(usbInterface, /*force=*/ false);
+ usbDeviceConnection.claimInterface(usbInterface, /* force= */ false);
+ InputStream outgoingData = shadowOf(usbDeviceConnection).getOutgoingDataStream();
byte[] msg = "Hello World".getBytes(UTF_8);
- assertThat(usbDeviceConnection.bulkTransfer(usbEndpointOut, msg, msg.length, /*timeout=*/ 0))
+ assertThat(usbDeviceConnection.bulkTransfer(usbEndpointOut, msg, msg.length, /* timeout= */ 0))
.isEqualTo(msg.length);
- byte[] buffer = new byte[msg.length];
- shadowOf(usbDeviceConnection).readOutgoingData(buffer);
- assertThat(buffer).isEqualTo(msg);
+ byte[] buffer = new byte[1024];
+ int read = outgoingData.read(buffer);
+ assertThat(Arrays.copyOf(buffer, read)).isEqualTo(msg);
msg = "Goodbye World".getBytes(UTF_8);
assertThat(
usbDeviceConnection.bulkTransfer(
- usbEndpointOut, msg, /*offset=*/ 0, msg.length, /*timeout=*/ 0))
+ usbEndpointOut, msg, /* offset= */ 0, msg.length, /* timeout= */ 0))
+ .isEqualTo(msg.length);
+
+ buffer = new byte[1024];
+ read = outgoingData.read(buffer);
+ assertThat(Arrays.copyOf(buffer, read)).isEqualTo(msg);
+ }
+
+ @Test
+ @Config(minSdk = LOLLIPOP)
+ public void releaseInterface_closesOutgoingDataStream() throws Exception {
+ UsbDeviceConnection usbDeviceConnection = usbManager.openDevice(usbDevice);
+ UsbInterface usbInterface = selectInterface(usbDevice);
+ UsbEndpoint usbEndpointOut = getEndpoint(usbInterface, UsbConstants.USB_DIR_OUT);
+ usbDeviceConnection.claimInterface(usbInterface, /* force= */ false);
+ InputStream outgoingData = shadowOf(usbDeviceConnection).getOutgoingDataStream();
+
+ byte[] msg = "Hello World".getBytes(UTF_8);
+ assertThat(usbDeviceConnection.bulkTransfer(usbEndpointOut, msg, msg.length, /* timeout= */ 0))
.isEqualTo(msg.length);
+ usbDeviceConnection.releaseInterface(usbInterface);
- buffer = new byte[msg.length];
- shadowOf(usbDeviceConnection).readOutgoingData(buffer);
- assertThat(buffer).isEqualTo(msg);
+ byte[] buffer = new byte[1024];
+ assertThrows(IOException.class, () -> outgoingData.read(buffer));
}
@Nullable
diff --git a/scripts/build-resources.sh b/scripts/build-resources.sh
index 8ba26204a..d85b7156c 100755
--- a/scripts/build-resources.sh
+++ b/scripts/build-resources.sh
@@ -2,20 +2,25 @@
set -x
+# Exit the script if ANDROID_HOME is unset
+set -u
+
rootDir=$(dirname $(dirname $0))
-projects=("robolectric")
+projects=("robolectric" "nativeruntime")
for project in "${projects[@]}"
do
androidProjDir="$rootDir/$project"
echo $androidProjDir
- aapts=( $ANDROID_HOME/build-tools/28.0.*/aapt )
+ aapts=( $ANDROID_HOME/build-tools/*/aapt )
aapt=${aapts[-1]}
inDir=$androidProjDir/src/test/resources
outDir=$androidProjDir/src/test/resources
javaSrc=$androidProjDir/src/test/java
+ mkdir -p $inDir/assets
+ mkdir -p $inDir/res
mkdir -p $outDir
mkdir -p $javaSrc
diff --git a/shadowapi/src/test/java/org/robolectric/util/ReflectionHelpersTest.java b/shadowapi/src/test/java/org/robolectric/util/ReflectionHelpersTest.java
index 814efd69d..56c489df1 100644
--- a/shadowapi/src/test/java/org/robolectric/util/ReflectionHelpersTest.java
+++ b/shadowapi/src/test/java/org/robolectric/util/ReflectionHelpersTest.java
@@ -124,8 +124,6 @@ public class ReflectionHelpersTest {
@Test
public void setFinalStaticFieldReflectively_withFieldName_setsStaticFields() {
- int startingValue = ReflectionHelpers.getStaticField(ExampleWithFinalStatic.class, "FIELD");
-
RuntimeException thrown =
assertThrows(
RuntimeException.class,
diff --git a/shadows/framework/src/main/java/org/robolectric/shadows/ShadowContextHubClient.java b/shadows/framework/src/main/java/org/robolectric/shadows/ShadowContextHubClient.java
index 4a99909de..9e871a4c2 100644
--- a/shadows/framework/src/main/java/org/robolectric/shadows/ShadowContextHubClient.java
+++ b/shadows/framework/src/main/java/org/robolectric/shadows/ShadowContextHubClient.java
@@ -12,11 +12,7 @@ import org.robolectric.annotation.Implementation;
import org.robolectric.annotation.Implements;
/** Shadow for {@link ContextHubClient}. */
-@Implements(
- value = ContextHubClient.class,
- minSdk = VERSION_CODES.P,
- isInAndroidSdk = false,
- looseSignatures = true)
+@Implements(value = ContextHubClient.class, minSdk = VERSION_CODES.P, isInAndroidSdk = false)
public class ShadowContextHubClient {
private final List<NanoAppMessage> messages = new ArrayList<>();
diff --git a/shadows/framework/src/main/java/org/robolectric/shadows/ShadowLocaleManager.java b/shadows/framework/src/main/java/org/robolectric/shadows/ShadowLocaleManager.java
index 880c21ade..2244a2be7 100644
--- a/shadows/framework/src/main/java/org/robolectric/shadows/ShadowLocaleManager.java
+++ b/shadows/framework/src/main/java/org/robolectric/shadows/ShadowLocaleManager.java
@@ -1,8 +1,11 @@
package org.robolectric.shadows;
import android.app.LocaleManager;
+import android.content.res.Configuration;
+import android.content.res.Resources;
import android.os.Build.VERSION_CODES;
import android.os.LocaleList;
+import androidx.annotation.RequiresApi;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
@@ -30,6 +33,7 @@ public class ShadowLocaleManager {
* @see #enforceInstallerCheck
* @see #setCallerAsInstallerForPackage
*/
+ @RequiresApi(api = VERSION_CODES.N)
@Implementation
protected LocaleList getApplicationLocales(String packageName) {
if (enforceInstallerCheck) {
@@ -51,6 +55,16 @@ public class ShadowLocaleManager {
appLocales.put(packageName, locales);
}
+ @RequiresApi(api = VERSION_CODES.N)
+ @Implementation
+ protected LocaleList getSystemLocales() {
+ Configuration configuration = Resources.getSystem().getConfiguration();
+ if (configuration != null) {
+ return configuration.getLocales();
+ }
+ return LocaleList.getEmptyLocaleList();
+ }
+
/**
* Sets the value of {@link #enforceInstallerCheck}.
*
diff --git a/shadows/framework/src/main/java/org/robolectric/shadows/ShadowSpeechRecognizer.java b/shadows/framework/src/main/java/org/robolectric/shadows/ShadowSpeechRecognizer.java
index f82e91b25..9287cbb0f 100644
--- a/shadows/framework/src/main/java/org/robolectric/shadows/ShadowSpeechRecognizer.java
+++ b/shadows/framework/src/main/java/org/robolectric/shadows/ShadowSpeechRecognizer.java
@@ -12,11 +12,11 @@ import android.os.Looper;
import android.os.Message;
import android.speech.IRecognitionService;
import android.speech.RecognitionListener;
-import android.speech.RecognitionSupport;
-import android.speech.RecognitionSupportCallback;
import android.speech.SpeechRecognizer;
+import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.RequiresApi;
+import com.google.common.base.Preconditions;
import java.util.Queue;
import java.util.concurrent.Executor;
import org.robolectric.annotation.Implementation;
@@ -30,7 +30,7 @@ import org.robolectric.util.reflector.ForType;
import org.robolectric.util.reflector.Static;
/** Robolectric shadow for SpeechRecognizer. */
-@Implements(SpeechRecognizer.class)
+@Implements(value = SpeechRecognizer.class, looseSignatures = true)
public class ShadowSpeechRecognizer {
@RealObject SpeechRecognizer realSpeechRecognizer;
@@ -39,13 +39,13 @@ public class ShadowSpeechRecognizer {
private RecognitionListener recognitionListener;
private static boolean isOnDeviceRecognitionAvailable = true;
- private RecognitionSupportCallback recognitionSupportCallback;
+ private /*RecognitionSupportCallback*/ Object recognitionSupportCallback;
private Executor recognitionSupportExecutor;
@Nullable private Intent latestModelDownloadIntent;
/**
* Returns the latest SpeechRecognizer. This method can only be called after {@link
- * SpeechRecognizer#createSpeechRecognizer()} is called.
+ * SpeechRecognizer#createSpeechRecognizer(Context)} is called.
*/
public static SpeechRecognizer getLatestSpeechRecognizer() {
return latestSpeechRecognizer;
@@ -138,10 +138,17 @@ public class ShadowSpeechRecognizer {
return isOnDeviceRecognitionAvailable;
}
+ @RequiresApi(api = VERSION_CODES.TIRAMISU)
@Implementation(minSdk = VERSION_CODES.TIRAMISU)
protected void checkRecognitionSupport(
- Intent recognizerIntent, Executor executor, RecognitionSupportCallback supportListener) {
- recognitionSupportExecutor = executor;
+ @NonNull /*Intent*/ Object recognizerIntent,
+ @NonNull /*Executor*/ Object executor,
+ @NonNull /*RecognitionSupportCallback*/ Object supportListener) {
+ Preconditions.checkArgument(recognizerIntent instanceof Intent);
+ Preconditions.checkArgument(executor instanceof Executor);
+ Preconditions.checkArgument(
+ supportListener instanceof android.speech.RecognitionSupportCallback);
+ recognitionSupportExecutor = (Executor) executor;
recognitionSupportCallback = supportListener;
}
@@ -155,14 +162,20 @@ public class ShadowSpeechRecognizer {
}
@RequiresApi(VERSION_CODES.TIRAMISU)
- public void triggerSupportResult(RecognitionSupport recognitionSupport) {
+ public void triggerSupportResult(/*RecognitionSupport*/ Object recognitionSupport) {
+ Preconditions.checkArgument(recognitionSupport instanceof android.speech.RecognitionSupport);
recognitionSupportExecutor.execute(
- () -> recognitionSupportCallback.onSupportResult(recognitionSupport));
+ () ->
+ ((android.speech.RecognitionSupportCallback) recognitionSupportCallback)
+ .onSupportResult((android.speech.RecognitionSupport) recognitionSupport));
}
@RequiresApi(VERSION_CODES.TIRAMISU)
public void triggerSupportError(int error) {
- recognitionSupportExecutor.execute(() -> recognitionSupportCallback.onError(error));
+ recognitionSupportExecutor.execute(
+ () ->
+ ((android.speech.RecognitionSupportCallback) recognitionSupportCallback)
+ .onError(error));
}
@RequiresApi(VERSION_CODES.TIRAMISU)
diff --git a/shadows/framework/src/main/java/org/robolectric/shadows/ShadowStorageStatsManager.java b/shadows/framework/src/main/java/org/robolectric/shadows/ShadowStorageStatsManager.java
index 4720787b9..aee4d14f9 100644
--- a/shadows/framework/src/main/java/org/robolectric/shadows/ShadowStorageStatsManager.java
+++ b/shadows/framework/src/main/java/org/robolectric/shadows/ShadowStorageStatsManager.java
@@ -6,6 +6,7 @@ import android.app.usage.StorageStats;
import android.app.usage.StorageStatsManager;
import android.content.pm.PackageManager;
import android.os.Build;
+import android.os.Parcel;
import android.os.UserHandle;
import android.os.storage.StorageManager;
import com.google.auto.value.AutoValue;
@@ -29,7 +30,10 @@ public class ShadowStorageStatsManager {
private final Map<UUID, FreeAndTotalBytesPair> freeAndTotalBytesMap =
createFreeAndTotalBytesMapWithSingleEntry(
StorageManager.UUID_DEFAULT, DEFAULT_STORAGE_FREE_BYTES, DEFAULT_STORAGE_TOTAL_BYTES);
- private final Map<StorageStatsKey, StorageStats> storageStatsMap = new ConcurrentHashMap<>();
+ private final Map<StorageStatsKey, StorageStats> storageStatsMapForPackage =
+ new ConcurrentHashMap<>();
+ private final Map<StorageStatsKey, StorageStats> storageStatsMapForUser =
+ new ConcurrentHashMap<>();
/**
* Sets the {@code storageUuid} to return the specified {@code freeBytes} and {@code totalBytes}
@@ -53,21 +57,50 @@ public class ShadowStorageStatsManager {
}
/**
- * Sets the {@link StorageStats} to return when queried with matching {@code storageUuid}, {@code
- * packageName} and {@code userHandle}.
+ * Sets the {@link StorageStats} for given {@code storageUuid}, {@code packageName} and {@code
+ * userHandle}. If {@code queryStatsForPackage} is called with matching {@code storageUuid},
+ * {@code packageName} and {@code userHandle}, the {@code storageStatsToReturn} will be returned
+ * directly. If {@code queryStatsForUser} is called with matching {@code storageUuid} and {@code
+ * userHandle}, then an accumulated {@link StorageStats} will be returned.
*/
public void addStorageStats(
UUID storageUuid,
String packageName,
UserHandle userHandle,
StorageStats storageStatsToReturn) {
- storageStatsMap.put(
- StorageStatsKey.create(storageUuid, packageName, userHandle), storageStatsToReturn);
+ StorageStatsKey storageStatsKeyForPackage =
+ StorageStatsKey.create(storageUuid, packageName, userHandle);
+ StorageStats storageStatsForPackage = storageStatsMapForPackage.get(storageStatsKeyForPackage);
+ storageStatsMapForPackage.put(storageStatsKeyForPackage, storageStatsToReturn);
+
+ StorageStatsKey storageStatsKeyForUser =
+ StorageStatsKey.create(storageUuid, /* packageName= */ "", userHandle);
+ StorageStats storageStatsForUser = storageStatsMapForUser.get(storageStatsKeyForUser);
+ if (storageStatsForUser == null) {
+ storageStatsMapForUser.put(storageStatsKeyForUser, storageStatsToReturn);
+ } else {
+ long moreAppBytes = storageStatsToReturn.getAppBytes();
+ long moreDataBytes = storageStatsToReturn.getDataBytes();
+ long moreCacheBytes = storageStatsToReturn.getCacheBytes();
+ if (storageStatsForPackage != null) {
+ moreAppBytes -= storageStatsForPackage.getAppBytes();
+ moreDataBytes -= storageStatsForPackage.getDataBytes();
+ moreCacheBytes -= storageStatsForPackage.getCacheBytes();
+ }
+ Parcel parcel = Parcel.obtain();
+ parcel.writeLong(storageStatsForUser.getAppBytes() + moreAppBytes);
+ parcel.writeLong(storageStatsForUser.getDataBytes() + moreDataBytes);
+ parcel.writeLong(storageStatsForUser.getCacheBytes() + moreCacheBytes);
+ parcel.setDataPosition(0);
+ storageStatsMapForUser.put(
+ storageStatsKeyForUser, StorageStats.CREATOR.createFromParcel(parcel));
+ }
}
/** Clears all {@link StorageStats} set in {@link ShadowStorageStatsManager#addStorageStats}. */
public void clearStorageStats() {
- storageStatsMap.clear();
+ storageStatsMapForPackage.clear();
+ storageStatsMapForUser.clear();
}
/**
@@ -112,7 +145,7 @@ public class ShadowStorageStatsManager {
protected StorageStats queryStatsForPackage(UUID storageUuid, String packageName, UserHandle user)
throws PackageManager.NameNotFoundException, IOException {
StorageStats storageStat =
- storageStatsMap.get(StorageStatsKey.create(storageUuid, packageName, user));
+ storageStatsMapForPackage.get(StorageStatsKey.create(storageUuid, packageName, user));
if (storageStat == null) {
throw new PackageManager.NameNotFoundException(
"queryStatsForPackage with non matching arguments. Did you forget to call"
@@ -121,6 +154,26 @@ public class ShadowStorageStatsManager {
return storageStat;
}
+ /**
+ * Fake implementation of {@link StorageStatsManager#queryStatsForUser} that returns an
+ * accumulated {@link StorageStats} based on the setup values for the user. This fake
+ * implementation does not check for access permission. It only checks for arguments matching
+ * those set in {@link ShadowStorageStatsManager#addStorageStats}.
+ */
+ @Implementation
+ protected StorageStats queryStatsForUser(UUID storageUuid, UserHandle user)
+ throws PackageManager.NameNotFoundException, IOException {
+ StorageStats storageStat =
+ storageStatsMapForUser.get(
+ StorageStatsKey.create(storageUuid, /* packageName= */ "", user));
+ if (storageStat == null) {
+ throw new PackageManager.NameNotFoundException(
+ "queryStatsForUser with non matching arguments. Did you forget to call"
+ + " addStorageStats?");
+ }
+ return storageStat;
+ }
+
private static Map<UUID, FreeAndTotalBytesPair> createFreeAndTotalBytesMapWithSingleEntry(
UUID storageUuid, long freeBytes, long totalBytes) {
Map<UUID, FreeAndTotalBytesPair> currMap = new ConcurrentHashMap<>();
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 dc456f3f6..902669fbc 100644
--- a/shadows/framework/src/main/java/org/robolectric/shadows/ShadowTelephonyManager.java
+++ b/shadows/framework/src/main/java/org/robolectric/shadows/ShadowTelephonyManager.java
@@ -77,7 +77,8 @@ public class ShadowTelephonyManager {
@RealObject protected TelephonyManager realTelephonyManager;
private final Map<PhoneStateListener, Integer> phoneStateRegistrations = new HashMap<>();
- private final List<TelephonyCallback> telephonyCallbackRegistrations = new ArrayList<>();
+ private final /*List<TelephonyCallback>*/ List<Object> telephonyCallbackRegistrations =
+ new ArrayList<>();
private final Map<Integer, String> slotIndexToDeviceId = new HashMap<>();
private final Map<Integer, String> slotIndexToImei = new HashMap<>();
private final Map<Integer, String> slotIndexToMeid = new HashMap<>();
@@ -87,7 +88,7 @@ public class ShadowTelephonyManager {
new HashMap<>();
private PhoneStateListener lastListener;
- private TelephonyCallback lastTelephonyCallback;
+ private /*TelephonyCallback*/ Object lastTelephonyCallback;
private int lastEventFlags;
private String deviceId;
@@ -227,7 +228,10 @@ public class ShadowTelephonyManager {
}
@Implementation(minSdk = S)
- public void registerTelephonyCallback(Executor executor, TelephonyCallback callback) {
+ public void registerTelephonyCallback(
+ /*Executor*/ Object executor, /*TelephonyCallback*/ Object callback) {
+ Preconditions.checkArgument(executor instanceof Executor);
+ Preconditions.checkArgument(callback instanceof TelephonyCallback);
lastTelephonyCallback = callback;
initTelephonyCallback(callback);
telephonyCallbackRegistrations.add(callback);
@@ -235,17 +239,20 @@ public class ShadowTelephonyManager {
@Implementation(minSdk = TIRAMISU)
protected void registerTelephonyCallback(
- int includeLocationData, Executor executor, TelephonyCallback callback) {
+ /*int*/ Object includeLocationData, /*Executor*/
+ Object executor, /*TelephonyCallback*/
+ Object callback) {
+ Preconditions.checkArgument(includeLocationData instanceof Integer);
registerTelephonyCallback(executor, callback);
}
@Implementation(minSdk = S)
- public void unregisterTelephonyCallback(TelephonyCallback callback) {
+ public void unregisterTelephonyCallback(/*TelephonyCallback*/ Object callback) {
telephonyCallbackRegistrations.remove(callback);
}
/** Returns the most recent callback passed to #registerTelephonyCallback(). */
- public TelephonyCallback getLastTelephonyCallback() {
+ public /*TelephonyCallback*/ Object getLastTelephonyCallback() {
return lastTelephonyCallback;
}
@@ -715,7 +722,7 @@ public class ShadowTelephonyManager {
}
@CallSuper
- protected void initTelephonyCallback(TelephonyCallback callback) {
+ protected void initTelephonyCallback(Object callback) {
if (VERSION.SDK_INT < S) {
return;
}
diff --git a/shadows/framework/src/main/java/org/robolectric/shadows/ShadowUIModeManager.java b/shadows/framework/src/main/java/org/robolectric/shadows/ShadowUIModeManager.java
index 44d6ecaef..04c574401 100644
--- a/shadows/framework/src/main/java/org/robolectric/shadows/ShadowUIModeManager.java
+++ b/shadows/framework/src/main/java/org/robolectric/shadows/ShadowUIModeManager.java
@@ -61,6 +61,10 @@ public class ShadowUIModeManager {
return currentModeType;
}
+ public void setCurrentModeType(int modeType) {
+ this.currentModeType = modeType;
+ }
+
@Implementation(maxSdk = VERSION_CODES.Q)
protected void enableCarMode(int flags) {
enableCarMode(DEFAULT_PRIORITY, flags);
diff --git a/shadows/framework/src/main/java/org/robolectric/shadows/ShadowUsbDeviceConnection.java b/shadows/framework/src/main/java/org/robolectric/shadows/ShadowUsbDeviceConnection.java
index af3501a14..45fd5eb13 100644
--- a/shadows/framework/src/main/java/org/robolectric/shadows/ShadowUsbDeviceConnection.java
+++ b/shadows/framework/src/main/java/org/robolectric/shadows/ShadowUsbDeviceConnection.java
@@ -2,13 +2,16 @@ package org.robolectric.shadows;
import static android.os.Build.VERSION_CODES.JELLY_BEAN_MR2;
import static android.os.Build.VERSION_CODES.KITKAT;
+import static android.os.Build.VERSION_CODES.LOLLIPOP;
import static android.os.Build.VERSION_CODES.O;
import android.hardware.usb.UsbDeviceConnection;
import android.hardware.usb.UsbEndpoint;
import android.hardware.usb.UsbInterface;
import android.hardware.usb.UsbRequest;
+import java.io.FilterInputStream;
import java.io.IOException;
+import java.io.InputStream;
import java.io.PipedInputStream;
import java.io.PipedOutputStream;
import java.util.concurrent.TimeoutException;
@@ -52,6 +55,15 @@ public class ShadowUsbDeviceConnection {
return true;
}
+ /**
+ * No-op on Robolectrict. The real implementation would return false on Robolectric and make it
+ * impossible to test callers that expect a successful result. Always returns {@code true}.
+ */
+ @Implementation(minSdk = LOLLIPOP)
+ protected boolean setInterface(UsbInterface intf) {
+ return true;
+ }
+
@Implementation(minSdk = KITKAT)
protected int controlTransfer(
int requestType, int request, int value, int index, byte[] buffer, int length, int timeout) {
@@ -106,9 +118,30 @@ public class ShadowUsbDeviceConnection {
}
}
- /** Fills the buffer with data that was written by UsbDeviceConnection#bulkTransfer. */
+ /**
+ * Fills the buffer with data that was written by UsbDeviceConnection#bulkTransfer.
+ *
+ * @deprecated prefer {@link #getOutgoingDataStream()}, which allows callers to know how much data
+ * has been read and when the {@link UsbDeviceConnection} closes.
+ */
+ @Deprecated
public void readOutgoingData(byte[] buffer) throws IOException {
- outgoingDataPipedInputStream.read(buffer);
+ getOutgoingDataStream().read(buffer);
+ }
+
+ /**
+ * Provides an {@link InputStream} that allows reading data written by
+ * UsbDeviceConnection#bulkTransfer. Closing this stream has no effect. It is effectively closed
+ * during {@link UsbDeviceConnection#releaseInterface(UsbInterface)}.
+ */
+ public InputStream getOutgoingDataStream() {
+ return new FilterInputStream(outgoingDataPipedInputStream) {
+ @Override
+ public void close() throws IOException {
+ // Override close() to prevent clients from closing the piped stream and causing unexpected
+ // side-effects if further writes happen.
+ }
+ };
}
/** Passes data that can then be read by an initialized UsbRequest#queue(ByteBuffer). */
diff --git a/shadows/framework/src/main/java/org/robolectric/shadows/ShadowUserManager.java b/shadows/framework/src/main/java/org/robolectric/shadows/ShadowUserManager.java
index 2c283d6f7..5c8de7314 100644
--- a/shadows/framework/src/main/java/org/robolectric/shadows/ShadowUserManager.java
+++ b/shadows/framework/src/main/java/org/robolectric/shadows/ShadowUserManager.java
@@ -9,6 +9,7 @@ import static android.os.Build.VERSION_CODES.N_MR1;
import static android.os.Build.VERSION_CODES.P;
import static android.os.Build.VERSION_CODES.Q;
import static android.os.Build.VERSION_CODES.R;
+import static android.os.Build.VERSION_CODES.S;
import static android.os.Build.VERSION_CODES.TIRAMISU;
import static android.os.UserManager.USER_TYPE_FULL_GUEST;
import static android.os.UserManager.USER_TYPE_FULL_RESTRICTED;
@@ -75,6 +76,7 @@ public class ShadowUserManager {
private static int maxSupportedUsers = DEFAULT_MAX_SUPPORTED_USERS;
private static boolean isMultiUserSupported = false;
+ private static boolean isHeadlessSystemUserMode = false;
@RealObject private UserManager realObject;
private UserManagerState userManagerState;
@@ -1133,6 +1135,16 @@ public class ShadowUserManager {
return requestQuietModeEnabled(enableQuietMode, userHandle);
}
+ @Implementation(minSdk = S)
+ protected static boolean isHeadlessSystemUserMode() {
+ return isHeadlessSystemUserMode;
+ }
+
+ /** Updates headless system user mode. */
+ public static void setHeadlessSystemUserMode(boolean isEnabled) {
+ ShadowUserManager.isHeadlessSystemUserMode = isEnabled;
+ }
+
@Implementation(minSdk = TIRAMISU)
protected Bundle getUserRestrictions() {
return getUserRestrictions(UserHandle.getUserHandleForUid(Process.myUid()));
@@ -1148,6 +1160,7 @@ public class ShadowUserManager {
public static void reset() {
maxSupportedUsers = DEFAULT_MAX_SUPPORTED_USERS;
isMultiUserSupported = false;
+ isHeadlessSystemUserMode = false;
}
@ForType(UserManager.class)
diff --git a/shadows/playservices/build.gradle b/shadows/playservices/build.gradle
index df8c75321..c3abbba05 100644
--- a/shadows/playservices/build.gradle
+++ b/shadows/playservices/build.gradle
@@ -16,7 +16,7 @@ dependencies {
api project(":annotations")
api "com.google.guava:guava:$guavaJREVersion"
- compileOnly "com.android.support:support-fragment:28.0.0"
+ 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"
@@ -30,7 +30,7 @@ dependencies {
testImplementation "junit:junit:$junitVersion"
testImplementation "com.google.truth:truth:$truthVersion"
testImplementation "org.mockito:mockito-core:$mockitoVersion"
- testRuntimeOnly "com.android.support:support-fragment:28.0.0"
+ 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"
diff --git a/shadows/playservices/src/main/java/org/robolectric/shadows/gms/ShadowGooglePlayServicesUtil.java b/shadows/playservices/src/main/java/org/robolectric/shadows/gms/ShadowGooglePlayServicesUtil.java
index e2f9dcdf9..deb738b27 100644
--- a/shadows/playservices/src/main/java/org/robolectric/shadows/gms/ShadowGooglePlayServicesUtil.java
+++ b/shadows/playservices/src/main/java/org/robolectric/shadows/gms/ShadowGooglePlayServicesUtil.java
@@ -7,7 +7,7 @@ import android.content.Context;
import android.content.DialogInterface.OnCancelListener;
import android.content.Intent;
import android.content.res.Resources;
-import android.support.v4.app.Fragment;
+import androidx.fragment.app.Fragment;
import com.google.android.gms.common.ConnectionResult;
import com.google.android.gms.common.GooglePlayServicesUtil;
import com.google.common.base.Preconditions;
diff --git a/utils/src/main/java/org/robolectric/util/TempDirectory.java b/utils/src/main/java/org/robolectric/util/TempDirectory.java
index b8527a7a1..3ce362065 100644
--- a/utils/src/main/java/org/robolectric/util/TempDirectory.java
+++ b/utils/src/main/java/org/robolectric/util/TempDirectory.java
@@ -52,6 +52,10 @@ public class TempDirectory {
}
}
+ public Path getBasePath() {
+ return basePath;
+ }
+
static void clearAllDirectories() {
ExecutorService deletionExecutorService = Executors.newFixedThreadPool(DELETE_THREAD_POOL_SIZE);
synchronized (tempDirectoriesToDelete) {