aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRam Peri <ramperi@google.com>2024-04-18 20:32:50 +0000
committerGerrit Code Review <noreply-gerritcodereview@google.com>2024-04-18 20:32:50 +0000
commit9201ec4b504ddb37e398e795810f3aa5be946aae (patch)
tree96e08ecd5ae77ac50b2bf021e7a1801c939267d6
parentb3b17db94476c25225589165fbf53ec938f5b6dc (diff)
parent4774468762423e04ef3bb56a477205cc9c192307 (diff)
downloadrobolectric-master.tar.gz
Merge "Merge branch 'upstream-google' into attempt_merge_upstream" into mainHEADmastermain
-rw-r--r--annotations/src/test/java/org/robolectric/versioning/AndroidVersionsTest.java11
-rw-r--r--integration_tests/roborazzi/src/test/java/org/robolectric/integration/roborazzi/RoborazziCaptureTest.kt31
-rw-r--r--integration_tests/versioning/src/test/java/org/robolectric/versioning/AndroidVersionsTest.java9
-rw-r--r--resources/src/main/java/org/robolectric/res/android/AssetDir.java3
-rw-r--r--resources/src/main/java/org/robolectric/res/android/CppApkAssets.java8
-rw-r--r--resources/src/test/java/org/robolectric/res/android/AssetDirTest.java29
-rw-r--r--resources/src/test/java/org/robolectric/res/android/CppApkAssetsTest.java17
-rw-r--r--robolectric/src/main/java/org/robolectric/android/internal/AndroidTestEnvironment.java24
-rw-r--r--robolectric/src/test/java/org/robolectric/RobolectricTestRunnerMultiApiTest.java46
-rw-r--r--robolectric/src/test/java/org/robolectric/RobolectricTestRunnerSelfTest.java7
-rw-r--r--robolectric/src/test/java/org/robolectric/android/BootstrapTest.java2
-rw-r--r--robolectric/src/test/java/org/robolectric/android/controller/ActivityControllerTest.java4
-rw-r--r--robolectric/src/test/java/org/robolectric/shadows/EmergencyNumberBuilderTest.java38
-rw-r--r--robolectric/src/test/java/org/robolectric/shadows/ShadowBitmapTest.java2
-rw-r--r--robolectric/src/test/java/org/robolectric/shadows/ShadowLocationManagerTest.java4
-rw-r--r--robolectric/src/test/java/org/robolectric/shadows/ShadowNetworkTest.java8
-rw-r--r--robolectric/src/test/java/org/robolectric/shadows/ShadowSystemPropertiesTest.java6
-rw-r--r--robolectric/src/test/java/org/robolectric/shadows/ShadowUiAutomationTest.java4
-rw-r--r--robolectric/src/test/java/org/robolectric/shadows/ShadowViewConfigurationTest.java21
-rw-r--r--sandbox/src/main/java/org/robolectric/internal/bytecode/ClassInstrumentor.java7
-rw-r--r--shadows/framework/src/main/java/org/robolectric/shadows/EmergencyNumberBuilder.java71
-rw-r--r--shadows/framework/src/main/java/org/robolectric/shadows/HardwareRenderingScreenshot.java20
-rw-r--r--shadows/framework/src/main/java/org/robolectric/shadows/NativeInput.java133
-rw-r--r--shadows/framework/src/main/java/org/robolectric/shadows/ShadowLocationManager.java4
-rw-r--r--shadows/framework/src/main/java/org/robolectric/shadows/ShadowMotionEvent.java30
-rw-r--r--shadows/framework/src/main/java/org/robolectric/shadows/ShadowPixelCopy.java15
-rw-r--r--shadows/framework/src/main/java/org/robolectric/shadows/ShadowViewConfiguration.java17
27 files changed, 427 insertions, 144 deletions
diff --git a/annotations/src/test/java/org/robolectric/versioning/AndroidVersionsTest.java b/annotations/src/test/java/org/robolectric/versioning/AndroidVersionsTest.java
index 670e695a3..764a7fa2d 100644
--- a/annotations/src/test/java/org/robolectric/versioning/AndroidVersionsTest.java
+++ b/annotations/src/test/java/org/robolectric/versioning/AndroidVersionsTest.java
@@ -155,15 +155,4 @@ public final class AndroidVersionsTest {
assertThat(new AndroidVersions.L().getVersion()).isEqualTo("5.0");
assertThat(new AndroidVersions.L().isReleased()).isEqualTo(true);
}
-
- @Test
- public void testStandardInitializationK() {
- assertThat(AndroidVersions.K.SDK_INT).isEqualTo(19);
- assertThat(AndroidVersions.K.SHORT_CODE).isEqualTo("K");
- assertThat(AndroidVersions.K.VERSION).isEqualTo("4.4");
- assertThat(new AndroidVersions.K().getSdkInt()).isEqualTo(19);
- assertThat(new AndroidVersions.K().getShortCode()).isEqualTo("K");
- assertThat(new AndroidVersions.K().getVersion()).isEqualTo("4.4");
- assertThat(new AndroidVersions.K().isReleased()).isEqualTo(true);
- }
}
diff --git a/integration_tests/roborazzi/src/test/java/org/robolectric/integration/roborazzi/RoborazziCaptureTest.kt b/integration_tests/roborazzi/src/test/java/org/robolectric/integration/roborazzi/RoborazziCaptureTest.kt
index 9adbe305e..6a54739fd 100644
--- a/integration_tests/roborazzi/src/test/java/org/robolectric/integration/roborazzi/RoborazziCaptureTest.kt
+++ b/integration_tests/roborazzi/src/test/java/org/robolectric/integration/roborazzi/RoborazziCaptureTest.kt
@@ -51,12 +51,7 @@ class RoborazziCaptureTest {
RoborazziRule.Options(
outputDirectoryPath = OUTPUT_DIRECTORY_PATH,
roborazziOptions =
- RoborazziOptions(
- recordOptions =
- RoborazziOptions.RecordOptions(
- resizeScale = 0.5,
- )
- )
+ RoborazziOptions(recordOptions = RoborazziOptions.RecordOptions(resizeScale = 0.5)),
)
)
@@ -99,13 +94,16 @@ class RoborazziCaptureTest {
|run `./gradlew integration_tests:roborazzi:recordRoborazziDebug -Drobolectric.alwaysIncludeVariantMarkersInTestName=true` and commit the changes.
|"""
.trimMargin(),
- e
+ e,
)
}
}
companion object {
+ // TODO(hoisie): `robolectric.screenshot.hwrdr.native` is obsolete, remove it after the next
+ // Robolectric point release.
const val USE_HARDWARE_RENDERER_NATIVE_ENV = "robolectric.screenshot.hwrdr.native"
+ const val PIXEL_COPY_RENDER_MODE = "robolectric.pixelCopyRenderMode"
}
}
@@ -113,17 +111,14 @@ private fun registerActivityToPackageManager(activity: String) {
val appContext: Application =
InstrumentationRegistry.getInstrumentation().targetContext.applicationContext as Application
Shadows.shadowOf(appContext.packageManager)
- .addActivityIfNotPresent(
- ComponentName(
- appContext.packageName,
- activity,
- )
- )
+ .addActivityIfNotPresent(ComponentName(appContext.packageName, activity))
}
private fun hardwareRendererEnvironment(block: () -> Unit) {
val originalHwrdrOption =
System.getProperty(RoborazziCaptureTest.USE_HARDWARE_RENDERER_NATIVE_ENV, null)
+ val originalPixelCopyOption =
+ System.getProperty(RoborazziCaptureTest.PIXEL_COPY_RENDER_MODE, null)
// This cause ClassNotFoundException: java.nio.NioUtils
// TODO: Remove comment out after fix this issue
// https://github.com/robolectric/robolectric/issues/8081#issuecomment-1858726896
@@ -133,8 +128,10 @@ private fun hardwareRendererEnvironment(block: () -> Unit) {
} finally {
if (originalHwrdrOption == null) {
System.clearProperty(RoborazziCaptureTest.USE_HARDWARE_RENDERER_NATIVE_ENV)
+ System.clearProperty(RoborazziCaptureTest.PIXEL_COPY_RENDER_MODE)
} else {
System.setProperty(RoborazziCaptureTest.USE_HARDWARE_RENDERER_NATIVE_ENV, originalHwrdrOption)
+ System.setProperty(RoborazziCaptureTest.PIXEL_COPY_RENDER_MODE, originalPixelCopyOption)
}
}
}
@@ -158,9 +155,9 @@ private class RoborazziViewWithElevationTestActivity : Activity() {
},
LinearLayout.LayoutParams(
LinearLayout.LayoutParams.MATCH_PARENT,
- LinearLayout.LayoutParams.MATCH_PARENT
+ LinearLayout.LayoutParams.MATCH_PARENT,
)
- .apply { setMargins(10.toDp(), 10.toDp(), 10.toDp(), 10.toDp()) }
+ .apply { setMargins(10.toDp(), 10.toDp(), 10.toDp(), 10.toDp()) },
)
}
)
@@ -181,9 +178,9 @@ private class RoborazziDialogTestActivity : Activity() {
TextView(this.context).apply { text = "Under the dialog" },
LinearLayout.LayoutParams(
LinearLayout.LayoutParams.WRAP_CONTENT,
- LinearLayout.LayoutParams.WRAP_CONTENT
+ LinearLayout.LayoutParams.WRAP_CONTENT,
)
- .apply { setMargins(10.toDp(), 10.toDp(), 10.toDp(), 10.toDp()) }
+ .apply { setMargins(10.toDp(), 10.toDp(), 10.toDp(), 10.toDp()) },
)
}
)
diff --git a/integration_tests/versioning/src/test/java/org/robolectric/versioning/AndroidVersionsTest.java b/integration_tests/versioning/src/test/java/org/robolectric/versioning/AndroidVersionsTest.java
index 09b394a1a..89bdc6717 100644
--- a/integration_tests/versioning/src/test/java/org/robolectric/versioning/AndroidVersionsTest.java
+++ b/integration_tests/versioning/src/test/java/org/robolectric/versioning/AndroidVersionsTest.java
@@ -139,13 +139,4 @@ public final class AndroidVersionsTest {
assertThat(new AndroidVersions.L().getVersion()).isEqualTo("5.0");
assertThat(AndroidVersions.CURRENT.getShortCode()).isEqualTo("L");
}
-
- @Test
- @Config(sdk = 19)
- public void testStandardInitializationK() {
- assertThat(AndroidVersions.K.SDK_INT).isEqualTo(19);
- assertThat(AndroidVersions.K.SHORT_CODE).isEqualTo("K");
- assertThat(new AndroidVersions.K().getVersion()).isEqualTo("4.4");
- assertThat(AndroidVersions.CURRENT.getShortCode()).isEqualTo("K");
- }
}
diff --git a/resources/src/main/java/org/robolectric/res/android/AssetDir.java b/resources/src/main/java/org/robolectric/res/android/AssetDir.java
index b5a690172..fcf97cb82 100644
--- a/resources/src/main/java/org/robolectric/res/android/AssetDir.java
+++ b/resources/src/main/java/org/robolectric/res/android/AssetDir.java
@@ -20,6 +20,9 @@ public class AssetDir {
* Vector-style access.
*/
public int getFileCount() {
+ if (mFileInfo == null) {
+ return 0;
+ }
return mFileInfo.size();
}
diff --git a/resources/src/main/java/org/robolectric/res/android/CppApkAssets.java b/resources/src/main/java/org/robolectric/res/android/CppApkAssets.java
index 58d436b69..58370c892 100644
--- a/resources/src/main/java/org/robolectric/res/android/CppApkAssets.java
+++ b/resources/src/main/java/org/robolectric/res/android/CppApkAssets.java
@@ -5,7 +5,6 @@ package org.robolectric.res.android;
import static org.robolectric.res.android.CppAssetManager.FileType.kFileTypeDirectory;
import static org.robolectric.res.android.CppAssetManager.FileType.kFileTypeRegular;
-import static org.robolectric.res.android.Util.CHECK;
import static org.robolectric.res.android.ZipFileRO.OpenArchive;
import static org.robolectric.res.android.ZipFileRO.kCompressDeflated;
@@ -57,7 +56,7 @@ public class CppApkAssets {
// bool ForEachFile(const String& path,
// const std::function<void(const StringPiece&, FileType)>& f) const;
- private CppApkAssets() {
+ CppApkAssets() {
this.zipFileRO = null;
}
@@ -357,7 +356,10 @@ public class CppApkAssets {
boolean ForEachFile(String root_path,
ForEachFileCallback f) {
- CHECK(zip_handle_ != null);
+ if (zip_handle_ == null || zipFileRO == null) {
+ // In this case, the ApkAssets was loaded from a pure ARSC, and does not have assets.
+ return false;
+ }
String root_path_full = root_path;
// if (root_path_full.back() != '/') {
diff --git a/resources/src/test/java/org/robolectric/res/android/AssetDirTest.java b/resources/src/test/java/org/robolectric/res/android/AssetDirTest.java
new file mode 100644
index 000000000..4337fde56
--- /dev/null
+++ b/resources/src/test/java/org/robolectric/res/android/AssetDirTest.java
@@ -0,0 +1,29 @@
+package org.robolectric.res.android;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+@RunWith(JUnit4.class)
+public final class AssetDirTest {
+
+ @Test
+ public void getFileCount_returnsZeroIfInitializedTrivially() {
+ assertThat(new AssetDir().getFileCount()).isEqualTo(0);
+ }
+
+ @Test
+ public void getFileCount_returnsCorrectFileCount() {
+ AssetDir.FileInfo fileInfo1 = new AssetDir.FileInfo(new String8("a/a.txt"));
+ AssetDir.FileInfo fileInfo2 = new AssetDir.FileInfo(new String8("b/b.txt"));
+ SortedVector<AssetDir.FileInfo> fileInfos = new SortedVector<>();
+ fileInfos.add(fileInfo1);
+ fileInfos.add(fileInfo2);
+ AssetDir assetDir = new AssetDir();
+ assetDir.setFileList(fileInfos);
+
+ assertThat(assetDir.getFileCount()).isEqualTo(2);
+ }
+}
diff --git a/resources/src/test/java/org/robolectric/res/android/CppApkAssetsTest.java b/resources/src/test/java/org/robolectric/res/android/CppApkAssetsTest.java
new file mode 100644
index 000000000..e4545af7c
--- /dev/null
+++ b/resources/src/test/java/org/robolectric/res/android/CppApkAssetsTest.java
@@ -0,0 +1,17 @@
+package org.robolectric.res.android;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+@RunWith(JUnit4.class)
+public final class CppApkAssetsTest {
+
+ @Test
+ public void forEachFile_returnsFalseIfInitializedTrivially() {
+ boolean runningResult = new CppApkAssets().ForEachFile("a/robo", (string, type) -> {});
+ assertThat(runningResult).isFalse();
+ }
+}
diff --git a/robolectric/src/main/java/org/robolectric/android/internal/AndroidTestEnvironment.java b/robolectric/src/main/java/org/robolectric/android/internal/AndroidTestEnvironment.java
index 976edc52c..20c971652 100644
--- a/robolectric/src/main/java/org/robolectric/android/internal/AndroidTestEnvironment.java
+++ b/robolectric/src/main/java/org/robolectric/android/internal/AndroidTestEnvironment.java
@@ -103,9 +103,11 @@ import org.robolectric.shadows.ShadowView;
import org.robolectric.util.Logger;
import org.robolectric.util.PerfStatsCollector;
import org.robolectric.util.ReflectionHelpers;
+import org.robolectric.util.ReflectionHelpers.ClassParameter;
import org.robolectric.util.Scheduler;
import org.robolectric.util.TempDirectory;
import org.robolectric.versioning.AndroidVersions;
+import org.robolectric.versioning.AndroidVersions.V;
@SuppressLint("NewApi")
public class AndroidTestEnvironment implements TestEnvironment {
@@ -404,20 +406,18 @@ public class AndroidTestEnvironment implements TestEnvironment {
populateAssetPaths(appResources.getAssets(), appManifest);
}
- if (AndroidVersions.CURRENT.getSdkInt() >= AndroidVersions.V.SDK_INT) {
+ // Circumvent the 'No Compatibility callbacks set!' log. See #8509
+ if (apiLevel >= AndroidVersions.V.SDK_INT) {
// Adds loggableChanges parameter.
ReflectionHelpers.callStaticMethod(
AppCompatCallbacks.class,
"install",
- ReflectionHelpers.ClassParameter.from(long[].class, new long[0]),
- ReflectionHelpers.ClassParameter.from(long[].class, new long[0]));
- } else if (AndroidVersions.CURRENT.getSdkInt() >= AndroidVersions.R.SDK_INT) {
- // Invoke the previous version. Circumvents the 'No Compatibility callbacks set!' log. See
- // #8509.
+ ClassParameter.from(long[].class, new long[0]),
+ ClassParameter.from(long[].class, new long[0]));
+ } else if (apiLevel >= AndroidVersions.R.SDK_INT) {
+ // Invoke the previous version.
ReflectionHelpers.callStaticMethod(
- AppCompatCallbacks.class,
- "install",
- ReflectionHelpers.ClassParameter.from(long[].class, new long[0]));
+ AppCompatCallbacks.class, "install", ClassParameter.from(long[].class, new long[0]));
}
PerfStatsCollector.getInstance()
@@ -599,8 +599,12 @@ public class AndroidTestEnvironment implements TestEnvironment {
}
}
+ protected Instrumentation pickInstrumentation() {
+ return new RoboMonitoringInstrumentation();
+ }
+
private Instrumentation createInstrumentation() {
- Instrumentation androidInstrumentation = new RoboMonitoringInstrumentation();
+ Instrumentation androidInstrumentation = pickInstrumentation();
androidInstrumentation.runOnMainSync(
() -> {
ActivityThread activityThread = ReflectionHelpers.callConstructor(ActivityThread.class);
diff --git a/robolectric/src/test/java/org/robolectric/RobolectricTestRunnerMultiApiTest.java b/robolectric/src/test/java/org/robolectric/RobolectricTestRunnerMultiApiTest.java
index d50ca474b..d994d8d92 100644
--- a/robolectric/src/test/java/org/robolectric/RobolectricTestRunnerMultiApiTest.java
+++ b/robolectric/src/test/java/org/robolectric/RobolectricTestRunnerMultiApiTest.java
@@ -1,6 +1,5 @@
package org.robolectric;
-import static android.os.Build.VERSION_CODES.KITKAT;
import static android.os.Build.VERSION_CODES.LOLLIPOP;
import static android.os.Build.VERSION_CODES.LOLLIPOP_MR1;
import static android.os.Build.VERSION_CODES.M;
@@ -39,7 +38,7 @@ import org.robolectric.util.inject.Injector;
@RunWith(JUnit4.class)
public class RobolectricTestRunnerMultiApiTest {
- private static final int[] APIS_FOR_TEST = {KITKAT, LOLLIPOP, LOLLIPOP_MR1, M, N, N_MR1, O};
+ private static final int[] APIS_FOR_TEST = {LOLLIPOP, LOLLIPOP_MR1, M, N, N_MR1, O};
private static SdkPicker delegateSdkPicker;
private static final Injector INJECTOR = defaultInjector()
@@ -82,7 +81,7 @@ public class RobolectricTestRunnerMultiApiTest {
public void createChildrenForEachSupportedApi() throws Throwable {
runner = runnerOf(TestWithNoConfig.class);
assertThat(apisFor(runner.getChildren()))
- .containsExactly(KITKAT, LOLLIPOP, LOLLIPOP_MR1, M, N, N_MR1, O);
+ .containsExactly(LOLLIPOP, LOLLIPOP_MR1, M, N, N_MR1, O);
}
@Test
@@ -101,13 +100,13 @@ public class RobolectricTestRunnerMultiApiTest {
assertThat(e.getMessage())
.contains(
"sdk and minSdk/maxSdk may not be specified together"
- + " (sdk=[19], minSdk=23, maxSdk=24)");
+ + " (sdk=[23], minSdk=23, maxSdk=24)");
}
}
@Test
public void withEnabledSdks_createChildrenForEachSupportedSdk() throws Throwable {
- delegateSdkPicker = new DefaultSdkPicker(new SdkCollection(() -> map(19, 21)), null);
+ delegateSdkPicker = new DefaultSdkPicker(new SdkCollection(() -> map(21, 23)), null);
runner = runnerOf(TestWithNoConfig.class);
assertThat(runner.getChildren()).hasSize(2);
@@ -116,18 +115,17 @@ public class RobolectricTestRunnerMultiApiTest {
@Test
public void shouldAddApiLevelToNameOfAllButHighestNumberedMethodName() throws Throwable {
runner = runnerOf(TestMethodUpToAndIncludingN.class);
- assertThat(runner.getChildren().get(0).getName()).isEqualTo("testSomeApiLevel[19]");
- assertThat(runner.getChildren().get(1).getName()).isEqualTo("testSomeApiLevel[21]");
- assertThat(runner.getChildren().get(2).getName()).isEqualTo("testSomeApiLevel[22]");
- assertThat(runner.getChildren().get(3).getName()).isEqualTo("testSomeApiLevel[23]");
- assertThat(runner.getChildren().get(4).getName()).isEqualTo("testSomeApiLevel");
+ assertThat(runner.getChildren().get(0).getName()).isEqualTo("testSomeApiLevel[21]");
+ assertThat(runner.getChildren().get(1).getName()).isEqualTo("testSomeApiLevel[22]");
+ assertThat(runner.getChildren().get(2).getName()).isEqualTo("testSomeApiLevel[23]");
+ assertThat(runner.getChildren().get(3).getName()).isEqualTo("testSomeApiLevel");
}
@Test
public void noConfig() throws Throwable {
runner = runnerOf(TestWithNoConfig.class);
assertThat(apisFor(runner.getChildren()))
- .containsExactly(KITKAT, LOLLIPOP, LOLLIPOP_MR1, M, N, N_MR1, O);
+ .containsExactly(LOLLIPOP, LOLLIPOP_MR1, M, N, N_MR1, O);
runner.run(runNotifier);
assertThat(runListener.ignored).isEmpty();
@@ -137,24 +135,24 @@ public class RobolectricTestRunnerMultiApiTest {
@Test
public void classConfigWithSdkGroup() throws Throwable {
runner = runnerOf(TestClassConfigWithSdkGroup.class);
- assertThat(apisFor(runner.getChildren())).containsExactly(KITKAT, N);
+ assertThat(apisFor(runner.getChildren())).containsExactly(M, N);
runner.run(runNotifier);
assertThat(runListener.ignored).isEmpty();
- // Test method should be run for KitKat and N
+ // Test method should be run for M and N
assertThat(runListener.finished).hasSize(2);
}
@Test
public void methodConfigWithSdkGroup() throws Throwable {
runner = runnerOf(TestMethodConfigWithSdkGroup.class);
- assertThat(apisFor(runner.getChildren())).containsExactly(KITKAT, N);
+ assertThat(apisFor(runner.getChildren())).containsExactly(M, N);
runner.run(runNotifier);
assertThat(runListener.ignored).isEmpty();
- // Test method should be run for KitKat and N
+ // Test method should be run for M, N
assertThat(runListener.finished).hasSize(2);
}
@@ -173,12 +171,12 @@ public class RobolectricTestRunnerMultiApiTest {
@Test
public void classConfigMaxSdk() throws Throwable {
runner = runnerOf(TestClassUpToAndIncludingN.class);
- assertThat(apisFor(runner.getChildren())).containsExactly(KITKAT, LOLLIPOP, LOLLIPOP_MR1, M, N);
+ assertThat(apisFor(runner.getChildren())).containsExactly(LOLLIPOP, LOLLIPOP_MR1, M, N);
runner.run(runNotifier);
assertThat(runListener.ignored).isEmpty();
- int sdksUpToAndIncludingLollipop = 5;
+ int sdksUpToAndIncludingLollipop = 4;
assertThat(runListener.finished).hasSize(sdksUpToAndIncludingLollipop);
}
@@ -210,12 +208,12 @@ public class RobolectricTestRunnerMultiApiTest {
@Test
public void methodConfigMaxSdk() throws Throwable {
runner = runnerOf(TestMethodUpToAndIncludingN.class);
- assertThat(apisFor(runner.getChildren())).containsExactly(KITKAT, LOLLIPOP, LOLLIPOP_MR1, M, N);
+ assertThat(apisFor(runner.getChildren())).containsExactly(LOLLIPOP, LOLLIPOP_MR1, M, N);
runner.run(runNotifier);
assertThat(runListener.ignored).isEmpty();
- int sdksUpToAndIncludingLollipop = 5;
+ int sdksUpToAndIncludingLollipop = 4;
assertThat(runListener.finished).hasSize(sdksUpToAndIncludingLollipop);
}
@@ -243,19 +241,19 @@ public class RobolectricTestRunnerMultiApiTest {
@Test public void test() {}
}
- @Config(sdk = {KITKAT, N})
+ @Config(sdk = {M, N})
public static class TestClassConfigWithSdkGroup {
@Test public void testShouldRunApi18() {
- assertThat(Build.VERSION.SDK_INT).isIn(Range.closed(KITKAT, N));
+ assertThat(Build.VERSION.SDK_INT).isIn(Range.closed(M, N));
}
}
@Config(sdk = Config.ALL_SDKS)
public static class TestMethodConfigWithSdkGroup {
- @Config(sdk = {KITKAT, N})
+ @Config(sdk = {M, N})
@Test
public void testShouldRunApi16() {
- assertThat(Build.VERSION.SDK_INT).isIn(Range.closed(KITKAT, N));
+ assertThat(Build.VERSION.SDK_INT).isIn(Range.closed(M, N));
}
}
@@ -317,7 +315,7 @@ public class RobolectricTestRunnerMultiApiTest {
@Config(sdk = Config.ALL_SDKS)
public static class TestMethodWithSdkAndMinMax {
- @Config(sdk = KITKAT, minSdk = M, maxSdk = N)
+ @Config(sdk = M, minSdk = M, maxSdk = N)
@Test
public void testWithKitKatAndLollipop() {
assertThat(Build.VERSION.SDK_INT).isIn(Range.closed(M, N));
diff --git a/robolectric/src/test/java/org/robolectric/RobolectricTestRunnerSelfTest.java b/robolectric/src/test/java/org/robolectric/RobolectricTestRunnerSelfTest.java
index f1e71b978..a60af551b 100644
--- a/robolectric/src/test/java/org/robolectric/RobolectricTestRunnerSelfTest.java
+++ b/robolectric/src/test/java/org/robolectric/RobolectricTestRunnerSelfTest.java
@@ -79,11 +79,10 @@ public class RobolectricTestRunnerSelfTest {
}
@Test
- @Config(sdk = Build.VERSION_CODES.KITKAT)
+ @Config(sdk = Build.VERSION_CODES.LOLLIPOP)
public void testVersionConfiguration() {
- assertThat(Build.VERSION.SDK_INT)
- .isEqualTo(Build.VERSION_CODES.KITKAT);
- assertThat(Build.VERSION.RELEASE).isEqualTo("4.4");
+ assertThat(Build.VERSION.SDK_INT).isEqualTo(Build.VERSION_CODES.LOLLIPOP);
+ assertThat(Build.VERSION.RELEASE).isEqualTo("5.0.2");
}
@Test public void hamcrestMatchersDontBlowUpDuringLinking() throws Exception {
diff --git a/robolectric/src/test/java/org/robolectric/android/BootstrapTest.java b/robolectric/src/test/java/org/robolectric/android/BootstrapTest.java
index 3e9eb6a93..6dd5256b9 100644
--- a/robolectric/src/test/java/org/robolectric/android/BootstrapTest.java
+++ b/robolectric/src/test/java/org/robolectric/android/BootstrapTest.java
@@ -32,7 +32,6 @@ import static android.content.res.Configuration.UI_MODE_NIGHT_YES;
import static android.content.res.Configuration.UI_MODE_TYPE_APPLIANCE;
import static android.content.res.Configuration.UI_MODE_TYPE_MASK;
import static android.content.res.Configuration.UI_MODE_TYPE_NORMAL;
-import static android.os.Build.VERSION_CODES.KITKAT;
import static android.os.Build.VERSION_CODES.N;
import static android.os.Build.VERSION_CODES.O;
import static android.view.Surface.ROTATION_0;
@@ -311,7 +310,6 @@ public class BootstrapTest {
}
@Test
- @Config(sdk = KITKAT)
public void applyQualifiers_rtlPseudoLocale_shouldSetLayoutDirection() {
Bootstrap.applyQualifiers(
"ar-rXB", RuntimeEnvironment.getApiLevel(), configuration, displayMetrics);
diff --git a/robolectric/src/test/java/org/robolectric/android/controller/ActivityControllerTest.java b/robolectric/src/test/java/org/robolectric/android/controller/ActivityControllerTest.java
index 09aec11c9..6fe75980f 100644
--- a/robolectric/src/test/java/org/robolectric/android/controller/ActivityControllerTest.java
+++ b/robolectric/src/test/java/org/robolectric/android/controller/ActivityControllerTest.java
@@ -15,7 +15,6 @@ import android.app.WindowConfiguration;
import android.content.ComponentName;
import android.content.Intent;
import android.content.res.Configuration;
-import android.os.Build;
import android.os.Build.VERSION_CODES;
import android.os.Bundle;
import android.os.Handler;
@@ -253,8 +252,7 @@ public class ActivityControllerTest {
}
@Test
- @Config(sdk = Build.VERSION_CODES.KITKAT)
- public void attach_shouldWorkWithAPI19() {
+ public void attach_shouldWork() {
MyActivity activity = Robolectric.buildActivity(MyActivity.class).create().get();
assertThat(activity).isNotNull();
}
diff --git a/robolectric/src/test/java/org/robolectric/shadows/EmergencyNumberBuilderTest.java b/robolectric/src/test/java/org/robolectric/shadows/EmergencyNumberBuilderTest.java
new file mode 100644
index 000000000..f74e5a11c
--- /dev/null
+++ b/robolectric/src/test/java/org/robolectric/shadows/EmergencyNumberBuilderTest.java
@@ -0,0 +1,38 @@
+package org.robolectric.shadows;
+
+import static android.os.Build.VERSION_CODES.Q;
+import static com.google.common.truth.Truth.assertThat;
+
+import android.telephony.emergency.EmergencyNumber;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.annotation.Config;
+
+/** Tests for {@link EmergencyNumberBuilder}. */
+@Config(minSdk = Q)
+@RunWith(AndroidJUnit4.class)
+public final class EmergencyNumberBuilderTest {
+
+ @Test
+ public void testBuildEmergencyNumber() {
+ EmergencyNumber emergencyNumber =
+ EmergencyNumberBuilder.newBuilder("911", "us", "30")
+ .setEmergencyServiceCategories(EmergencyNumber.EMERGENCY_SERVICE_CATEGORY_POLICE)
+ .addEmergencyUrn("urn")
+ .setEmergencyNumberSources(EmergencyNumber.EMERGENCY_NUMBER_SOURCE_DATABASE)
+ .setEmergencyCallRouting(EmergencyNumber.EMERGENCY_CALL_ROUTING_NORMAL)
+ .build();
+
+ assertThat(emergencyNumber.getNumber()).isEqualTo("911");
+ assertThat(emergencyNumber.getCountryIso()).isEqualTo("us");
+ assertThat(emergencyNumber.getMnc()).isEqualTo("30");
+ assertThat(emergencyNumber.getEmergencyServiceCategories())
+ .containsExactly(EmergencyNumber.EMERGENCY_SERVICE_CATEGORY_POLICE);
+ assertThat(emergencyNumber.getEmergencyUrns()).containsExactly("urn");
+ assertThat(emergencyNumber.getEmergencyNumberSources())
+ .containsExactly(EmergencyNumber.EMERGENCY_NUMBER_SOURCE_DATABASE);
+ assertThat(emergencyNumber.getEmergencyCallRouting())
+ .isEqualTo(EmergencyNumber.EMERGENCY_CALL_ROUTING_NORMAL);
+ }
+}
diff --git a/robolectric/src/test/java/org/robolectric/shadows/ShadowBitmapTest.java b/robolectric/src/test/java/org/robolectric/shadows/ShadowBitmapTest.java
index face8d842..ceeb7ef06 100644
--- a/robolectric/src/test/java/org/robolectric/shadows/ShadowBitmapTest.java
+++ b/robolectric/src/test/java/org/robolectric/shadows/ShadowBitmapTest.java
@@ -13,7 +13,6 @@ import android.graphics.ColorMatrixColorFilter;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.Rect;
-import android.os.Build;
import android.os.Parcel;
import android.util.DisplayMetrics;
import androidx.test.ext.junit.runners.AndroidJUnit4;
@@ -624,7 +623,6 @@ public class ShadowBitmapTest {
bitmapOriginal.copyPixelsFromBuffer(buffer);
}
- @Config(sdk = Build.VERSION_CODES.KITKAT)
@Test
public void reconfigure_withArgb8888Bitmap_validDimensionsAndConfig_doesNotThrow() {
Bitmap original = Bitmap.createBitmap(100, 100, Bitmap.Config.ARGB_8888);
diff --git a/robolectric/src/test/java/org/robolectric/shadows/ShadowLocationManagerTest.java b/robolectric/src/test/java/org/robolectric/shadows/ShadowLocationManagerTest.java
index e9e51f909..2c7bb282f 100644
--- a/robolectric/src/test/java/org/robolectric/shadows/ShadowLocationManagerTest.java
+++ b/robolectric/src/test/java/org/robolectric/shadows/ShadowLocationManagerTest.java
@@ -1224,9 +1224,9 @@ public class ShadowLocationManagerTest {
@Test
public void testSimulateLocation_FastestInterval() {
Location loc1 = createLocation(MY_PROVIDER);
- loc1.setTime(1);
+ loc1.setElapsedRealtimeNanos(1000000);
Location loc2 = createLocation(MY_PROVIDER);
- loc2.setTime(10);
+ loc2.setElapsedRealtimeNanos(10000000);
TestLocationListener myListener = new TestLocationListener();
diff --git a/robolectric/src/test/java/org/robolectric/shadows/ShadowNetworkTest.java b/robolectric/src/test/java/org/robolectric/shadows/ShadowNetworkTest.java
index 53d3cbe7b..d7748729e 100644
--- a/robolectric/src/test/java/org/robolectric/shadows/ShadowNetworkTest.java
+++ b/robolectric/src/test/java/org/robolectric/shadows/ShadowNetworkTest.java
@@ -1,11 +1,9 @@
package org.robolectric.shadows;
-import static android.os.Build.VERSION_CODES.KITKAT;
import static android.os.Build.VERSION_CODES.LOLLIPOP;
import static android.os.Build.VERSION_CODES.LOLLIPOP_MR1;
import static android.os.Build.VERSION_CODES.M;
import static com.google.common.truth.Truth.assertThat;
-import static org.junit.Assert.assertThrows;
import android.net.Network;
import androidx.test.ext.junit.runners.AndroidJUnit4;
@@ -30,12 +28,6 @@ public class ShadowNetworkTest {
}
@Test
- @Config(sdk = KITKAT)
- public void shadowNetwork_newInstance_fails_preL() {
- assertThrows(IllegalStateException.class, () -> ShadowNetwork.newInstance(111));
- }
-
- @Test
@Config(minSdk = LOLLIPOP_MR1)
public void bindSocketDatagramSocket_shouldNotCrash() throws Exception {
Network network = ShadowNetwork.newInstance(0);
diff --git a/robolectric/src/test/java/org/robolectric/shadows/ShadowSystemPropertiesTest.java b/robolectric/src/test/java/org/robolectric/shadows/ShadowSystemPropertiesTest.java
index b4562ce7a..602cafa01 100644
--- a/robolectric/src/test/java/org/robolectric/shadows/ShadowSystemPropertiesTest.java
+++ b/robolectric/src/test/java/org/robolectric/shadows/ShadowSystemPropertiesTest.java
@@ -53,12 +53,6 @@ public class ShadowSystemPropertiesTest {
// android-all jar instead of loading build.prop from classpath aka LATEST_SDK.
@Test
- @Config(sdk = 19)
- public void readPropFromJarNotClassPath19() {
- assertThat(SystemProperties.getInt("ro.build.version.sdk", 0)).isEqualTo(19);
- }
-
- @Test
@Config(sdk = 21)
public void readPropFromJarNotClassPath21() {
assertThat(SystemProperties.getInt("ro.build.version.sdk", 0)).isEqualTo(21);
diff --git a/robolectric/src/test/java/org/robolectric/shadows/ShadowUiAutomationTest.java b/robolectric/src/test/java/org/robolectric/shadows/ShadowUiAutomationTest.java
index 4a3d5d527..eb9e124f7 100644
--- a/robolectric/src/test/java/org/robolectric/shadows/ShadowUiAutomationTest.java
+++ b/robolectric/src/test/java/org/robolectric/shadows/ShadowUiAutomationTest.java
@@ -1,6 +1,5 @@
package org.robolectric.shadows;
-import static android.os.Build.VERSION_CODES.KITKAT;
import static com.google.common.truth.Truth.assertThat;
import android.app.UiAutomation;
@@ -14,13 +13,11 @@ import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.platform.app.InstrumentationRegistry;
import org.junit.Test;
import org.junit.runner.RunWith;
-import org.robolectric.annotation.Config;
import org.robolectric.annotation.LooperMode;
/** Test for {@link ShadowUiAutomation}. */
@RunWith(AndroidJUnit4.class)
public class ShadowUiAutomationTest {
- @Config(sdk = KITKAT)
@Test
public void setAnimationScale_zero() throws Exception {
ShadowUiAutomation.setAnimationScaleCompat(0);
@@ -32,7 +29,6 @@ public class ShadowUiAutomationTest {
assertThat(Settings.Global.getFloat(cr, Settings.Global.WINDOW_ANIMATION_SCALE)).isEqualTo(0);
}
- @Config(sdk = KITKAT)
@Test
public void setAnimationScale_one() throws Exception {
ShadowUiAutomation.setAnimationScaleCompat(1);
diff --git a/robolectric/src/test/java/org/robolectric/shadows/ShadowViewConfigurationTest.java b/robolectric/src/test/java/org/robolectric/shadows/ShadowViewConfigurationTest.java
index 41661af4a..d0dfeadfb 100644
--- a/robolectric/src/test/java/org/robolectric/shadows/ShadowViewConfigurationTest.java
+++ b/robolectric/src/test/java/org/robolectric/shadows/ShadowViewConfigurationTest.java
@@ -11,6 +11,7 @@ import androidx.test.ext.junit.runners.AndroidJUnit4;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.robolectric.annotation.Config;
@RunWith(AndroidJUnit4.class)
public class ShadowViewConfigurationTest {
@@ -40,7 +41,7 @@ public class ShadowViewConfigurationTest {
assertEquals(16, ViewConfiguration.getWindowTouchSlop());
assertEquals(50, ViewConfiguration.getMinimumFlingVelocity());
assertEquals(4000, ViewConfiguration.getMaximumFlingVelocity());
- assertEquals(320 * 480 * 4, ViewConfiguration.getMaximumDrawingCacheSize());
+ assertEquals(480 * 800 * 4, ViewConfiguration.getMaximumDrawingCacheSize());
assertEquals(3000, ViewConfiguration.getZoomControlsTimeout());
assertEquals(500, ViewConfiguration.getGlobalActionKeyTimeout());
assertThat(ViewConfiguration.getScrollFriction()).isEqualTo(0.015f);
@@ -56,6 +57,8 @@ public class ShadowViewConfigurationTest {
assertEquals(16, viewConfiguration.getScaledWindowTouchSlop());
assertEquals(50, viewConfiguration.getScaledMinimumFlingVelocity());
assertEquals(4000, viewConfiguration.getScaledMaximumFlingVelocity());
+ // The min value of getScaledMaximumDrawingCacheSize is 480 * 800 * 4.
+ assertEquals(480 * 800 * 4, viewConfiguration.getScaledMaximumDrawingCacheSize());
}
@Test
@@ -83,4 +86,20 @@ public class ShadowViewConfigurationTest {
shadowViewConfiguration.setHasPermanentMenuKey(false);
assertThat(viewConfiguration.hasPermanentMenuKey()).isFalse();
}
+
+ @Config(qualifiers = "w420dp-h800dp-xxxhdpi")
+ @Test
+ public void getScaledMaximumFlingVelocity_scalesWithDisplaySize() {
+ ViewConfiguration viewConfiguration = ViewConfiguration.get(context);
+ int expected = 4 * (4 * 420) * (4 * 800);
+ assertThat(viewConfiguration.getScaledMaximumDrawingCacheSize()).isEqualTo(expected);
+ }
+
+ @Config(qualifiers = "w100dp-h500dp")
+ @Test
+ public void getScaledMaximumFlingVelocity_minValue() {
+ ViewConfiguration viewConfiguration = ViewConfiguration.get(context);
+ int expected = 480 * 800 * 4; // The min value
+ assertThat(viewConfiguration.getScaledMaximumDrawingCacheSize()).isEqualTo(expected);
+ }
}
diff --git a/sandbox/src/main/java/org/robolectric/internal/bytecode/ClassInstrumentor.java b/sandbox/src/main/java/org/robolectric/internal/bytecode/ClassInstrumentor.java
index a9b532a26..9d3d51af9 100644
--- a/sandbox/src/main/java/org/robolectric/internal/bytecode/ClassInstrumentor.java
+++ b/sandbox/src/main/java/org/robolectric/internal/bytecode/ClassInstrumentor.java
@@ -177,8 +177,11 @@ public class ClassInstrumentor {
.visitAnnotation("Lcom/google/errorprone/annotations/DoNotMock;", true)
.visit(
"value",
- "This class is final. Consider using the real thing, or "
- + "adding/enhancing a Robolectric shadow for it.");
+ "This class is final. Consider either:\n"
+ + "1. Using the real class.\n"
+ + "2. If it's a pure data class, adding a Robolectric Builder for it.\n"
+ + "3. If it cannot function on the JVM, adding or enhancing a Robolectric"
+ + " Shadow for it");
}
mutableClass.classNode.access = mutableClass.classNode.access & ~Opcodes.ACC_FINAL;
diff --git a/shadows/framework/src/main/java/org/robolectric/shadows/EmergencyNumberBuilder.java b/shadows/framework/src/main/java/org/robolectric/shadows/EmergencyNumberBuilder.java
new file mode 100644
index 000000000..45ede54c0
--- /dev/null
+++ b/shadows/framework/src/main/java/org/robolectric/shadows/EmergencyNumberBuilder.java
@@ -0,0 +1,71 @@
+package org.robolectric.shadows;
+
+import static android.os.Build.VERSION_CODES.Q;
+
+import android.annotation.RequiresApi;
+import android.telephony.emergency.EmergencyNumber;
+import android.telephony.emergency.EmergencyNumber.EmergencyCallRouting;
+import android.telephony.emergency.EmergencyNumber.EmergencyNumberSources;
+import android.telephony.emergency.EmergencyNumber.EmergencyServiceCategories;
+import java.util.ArrayList;
+import java.util.List;
+import org.robolectric.util.ReflectionHelpers;
+import org.robolectric.util.ReflectionHelpers.ClassParameter;
+
+/** Builder for {@link android.telephony.emergency.EmergencyNumber}. */
+@RequiresApi(Q)
+public class EmergencyNumberBuilder {
+
+ private final String number;
+ private final String countryIso;
+ private final String mnc;
+ private final List<String> emergencyUrns = new ArrayList<String>();
+ private int emergencyServiceCategories = EmergencyNumber.EMERGENCY_SERVICE_CATEGORY_UNSPECIFIED;
+ private int emergencyNumberSources = EmergencyNumber.EMERGENCY_NUMBER_SOURCE_DEFAULT;
+ private int emergencyCallRouting = EmergencyNumber.EMERGENCY_CALL_ROUTING_UNKNOWN;
+
+ private EmergencyNumberBuilder(String number, String countryIso, String mnc) {
+ this.number = number;
+ this.countryIso = countryIso;
+ this.mnc = mnc;
+ }
+
+ public static EmergencyNumberBuilder newBuilder(String number, String countryIso, String mnc) {
+ return new EmergencyNumberBuilder(number, countryIso, mnc);
+ }
+
+ public EmergencyNumberBuilder setEmergencyServiceCategories(
+ @EmergencyServiceCategories int emergencyServiceCategories) {
+ this.emergencyServiceCategories = emergencyServiceCategories;
+ return this;
+ }
+
+ public EmergencyNumberBuilder addEmergencyUrn(String emergencyUrn) {
+ emergencyUrns.add(emergencyUrn);
+ return this;
+ }
+
+ public EmergencyNumberBuilder setEmergencyNumberSources(
+ @EmergencyNumberSources int emergencyNumberSources) {
+ this.emergencyNumberSources = emergencyNumberSources;
+ return this;
+ }
+
+ public EmergencyNumberBuilder setEmergencyCallRouting(
+ @EmergencyCallRouting int emergencyCallRouting) {
+ this.emergencyCallRouting = emergencyCallRouting;
+ return this;
+ }
+
+ public EmergencyNumber build() {
+ return ReflectionHelpers.callConstructor(
+ EmergencyNumber.class,
+ ClassParameter.from(String.class, number),
+ ClassParameter.from(String.class, countryIso),
+ ClassParameter.from(String.class, mnc),
+ ClassParameter.from(int.class, emergencyServiceCategories),
+ ClassParameter.from(List.class, emergencyUrns),
+ ClassParameter.from(int.class, emergencyNumberSources),
+ ClassParameter.from(int.class, emergencyCallRouting));
+ }
+}
diff --git a/shadows/framework/src/main/java/org/robolectric/shadows/HardwareRenderingScreenshot.java b/shadows/framework/src/main/java/org/robolectric/shadows/HardwareRenderingScreenshot.java
index fea0a9a97..63e9d7bd8 100644
--- a/shadows/framework/src/main/java/org/robolectric/shadows/HardwareRenderingScreenshot.java
+++ b/shadows/framework/src/main/java/org/robolectric/shadows/HardwareRenderingScreenshot.java
@@ -16,7 +16,6 @@ import android.util.DisplayMetrics;
import android.view.Surface;
import android.view.View;
import com.android.internal.R;
-import java.nio.IntBuffer;
import org.robolectric.annotation.GraphicsMode;
import org.robolectric.util.ReflectionHelpers;
@@ -26,7 +25,7 @@ import org.robolectric.util.ReflectionHelpers;
*/
public final class HardwareRenderingScreenshot {
- static final String USE_HARDWARE_RENDERER_NATIVE_ENV = "robolectric.screenshot.hwrdr.native";
+ static final String PIXEL_COPY_RENDER_MODE = "robolectric.pixelCopyRenderMode";
private HardwareRenderingScreenshot() {}
@@ -37,7 +36,7 @@ public final class HardwareRenderingScreenshot {
*/
static boolean canTakeScreenshot() {
return VERSION.SDK_INT >= VERSION_CODES.S
- && Boolean.getBoolean(HardwareRenderingScreenshot.USE_HARDWARE_RENDERER_NATIVE_ENV)
+ && "hardware".equalsIgnoreCase(System.getProperty(PIXEL_COPY_RENDER_MODE, ""))
&& ShadowView.useRealGraphics();
}
@@ -71,21 +70,8 @@ public final class HardwareRenderingScreenshot {
renderer.setContentRoot(node);
renderer.createRenderRequest().syncAndDraw();
-
- int[] renderPixels = new int[width * height];
-
Plane[] planes = nativeImage.getPlanes();
- IntBuffer srcBuff = planes[0].getBuffer().asIntBuffer();
- srcBuff.get(renderPixels);
-
- destBitmap.setPixels(
- renderPixels,
- /* offset= */ 0,
- /* stride= */ width,
- /* x= */ 0,
- /* y= */ 0,
- width,
- height);
+ destBitmap.copyPixelsFromBuffer(planes[0].getBuffer());
surface.release();
}
}
diff --git a/shadows/framework/src/main/java/org/robolectric/shadows/NativeInput.java b/shadows/framework/src/main/java/org/robolectric/shadows/NativeInput.java
index d136be83d..cbda24865 100644
--- a/shadows/framework/src/main/java/org/robolectric/shadows/NativeInput.java
+++ b/shadows/framework/src/main/java/org/robolectric/shadows/NativeInput.java
@@ -3,6 +3,7 @@ package org.robolectric.shadows;
import static com.google.common.base.Preconditions.checkState;
import static org.robolectric.shadows.NativeAndroidInput.AINPUT_EVENT_TYPE_MOTION;
import static org.robolectric.shadows.NativeAndroidInput.AINPUT_SOURCE_CLASS_POINTER;
+import static org.robolectric.shadows.NativeAndroidInput.AKEY_EVENT_FLAG_CANCELED;
import static org.robolectric.shadows.NativeAndroidInput.AMOTION_EVENT_ACTION_CANCEL;
import static org.robolectric.shadows.NativeAndroidInput.AMOTION_EVENT_ACTION_DOWN;
import static org.robolectric.shadows.NativeAndroidInput.AMOTION_EVENT_ACTION_MASK;
@@ -28,6 +29,7 @@ import android.view.MotionEvent.PointerProperties;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
+import java.util.Optional;
import org.robolectric.res.android.Ref;
/**
@@ -64,11 +66,11 @@ public class NativeInput {
*/
static class AInputEvent {}
- /*
+ /**
* Pointer coordinate data.
*
- * Deviates from original platform implementation to store axises in simple SparseArray as opposed
- * to complicated bitset + array arrangement.
+ * <p>Deviates from original platform implementation to store axises in simple SparseArray as
+ * opposed to complicated bitset + array arrangement.
*/
static class PointerCoords {
@@ -272,9 +274,7 @@ public class NativeInput {
// nsecs_t mEventTime;
}
- /*
- * Motion events.
- */
+ /** Motion events. */
static class MotionEvent extends InputEvent {
// constants copied from android bionic/libc/include/math.h
@@ -284,6 +284,18 @@ public class NativeInput {
@SuppressWarnings("FloatingPointLiteralPrecision")
private static final double M_PI_2 = 1.57079632679489661923f; /* pi/2 */
+ public static final int ACTION_MASK = 0xff;
+ public static final int ACTION_DOWN = 0;
+ public static final int ACTION_UP = 1;
+ public static final int ACTION_MOVE = 2;
+ public static final int ACTION_CANCEL = 3;
+ public static final int ACTION_POINTER_DOWN = 5;
+ public static final int ACTION_POINTER_UP = 6;
+ private static final int HISTORY_CURRENT = -0x80000000;
+ public static final int FLAG_CANCELED = 0x20;
+ public static final int ACTION_POINTER_INDEX_MASK = 0xff00;
+ public static final int ACTION_POINTER_INDEX_SHIFT = 8;
+
private int mAction;
private int mActionButton;
private int mFlags;
@@ -540,6 +552,115 @@ public class NativeInput {
return getHistoricalAxisValue(AMOTION_EVENT_AXIS_ORIENTATION, pointerIndex, historicalIndex);
}
+ private android.view.MotionEvent.PointerCoords[] getNativePointerCoords() {
+ android.view.MotionEvent.PointerCoords[] nativePointerCoords =
+ new android.view.MotionEvent.PointerCoords[mSamplePointerCoords.size()];
+ for (int i = 0; i < mSamplePointerCoords.size(); i++) {
+ android.view.MotionEvent.PointerCoords newPc = new android.view.MotionEvent.PointerCoords();
+ PointerCoords pc = mSamplePointerCoords.get(i);
+ newPc.x = pc.getX();
+ newPc.y = pc.getY();
+ newPc.setAxisValue(AMOTION_EVENT_AXIS_X, pc.getX());
+ newPc.setAxisValue(AMOTION_EVENT_AXIS_Y, pc.getY());
+ nativePointerCoords[i] = newPc;
+ }
+ return nativePointerCoords;
+ }
+
+ private int resolveActionForSplitMotionEvent(
+ int action,
+ int flags,
+ PointerProperties[] pointerProperties,
+ PointerProperties[] splitPointerProperties) {
+ int maskedAction = getActionMasked();
+ if (maskedAction != AMOTION_EVENT_ACTION_POINTER_DOWN
+ && maskedAction != AMOTION_EVENT_ACTION_POINTER_UP) {
+ // The action is unaffected by splitting this motion event.
+ return action;
+ }
+
+ int actionIndex = getActionIndex();
+
+ int affectedPointerId = pointerProperties[actionIndex].id;
+ Optional<Integer> splitActionIndex = Optional.empty();
+ for (int i = 0; i < splitPointerProperties.length; i++) {
+ if (affectedPointerId == splitPointerProperties[i].id) {
+ splitActionIndex = Optional.of(i);
+ break;
+ }
+ }
+ if (!splitActionIndex.isPresent()) {
+ // The affected pointer is not part of the split motion event.
+ return AMOTION_EVENT_ACTION_MOVE;
+ }
+
+ if (splitPointerProperties.length > 1) {
+ return maskedAction | (splitActionIndex.get() << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT);
+ }
+
+ if (maskedAction == AMOTION_EVENT_ACTION_POINTER_UP) {
+ return ((flags & AKEY_EVENT_FLAG_CANCELED) != 0)
+ ? AMOTION_EVENT_ACTION_CANCEL
+ : AMOTION_EVENT_ACTION_UP;
+ }
+ return AMOTION_EVENT_ACTION_DOWN;
+ }
+
+ public android.view.MotionEvent nativeSplit(int idBits) {
+ final int pointerCount = getPointerCount();
+ List<PointerProperties> pointerProperties = new ArrayList<>(mPointerProperties);
+ final PointerProperties[] pp = pointerProperties.toArray(new PointerProperties[pointerCount]);
+ final android.view.MotionEvent.PointerCoords[] pc = getNativePointerCoords();
+
+ List<PointerProperties> splitPointerProperties = new ArrayList<>();
+ List<android.view.MotionEvent.PointerCoords> splitPointerCoords = new ArrayList<>();
+
+ // Split the matching ids out for the new MotionEvent.
+ for (int i = 0; i < pointerCount; i++) {
+ final int idBit = 1 << pp[i].id;
+ if ((idBit & idBits) != 0) {
+ splitPointerProperties.add(pp[i]);
+ }
+ }
+ for (int i = 0; i < pc.length; i++) {
+ final int idBit = 1 << pp[i % pointerCount].id;
+ if ((idBit & idBits) != 0) {
+ splitPointerCoords.add(pc[i]);
+ }
+ }
+
+ // Convert them to arrays
+ PointerProperties[] splitPointerPropertiesArray =
+ new PointerProperties[splitPointerProperties.size()];
+ splitPointerProperties.toArray(splitPointerPropertiesArray);
+
+ android.view.MotionEvent.PointerCoords[] splitPointerCoordsArray =
+ new android.view.MotionEvent.PointerCoords[splitPointerCoords.size()];
+ splitPointerCoords.toArray(splitPointerCoordsArray);
+
+ int splitAction =
+ resolveActionForSplitMotionEvent(
+ getAction(), getFlags(), pp, splitPointerPropertiesArray);
+
+ android.view.MotionEvent newEvent =
+ android.view.MotionEvent.obtain(
+ getDownTime(),
+ getEventTime(),
+ splitAction,
+ splitPointerProperties.size(),
+ splitPointerPropertiesArray,
+ splitPointerCoordsArray,
+ getMetaState(),
+ getButtonState(),
+ getXPrecision(),
+ getYPrecision(),
+ getDeviceId(),
+ getEdgeFlags(),
+ getSource(),
+ getFlags());
+ return newEvent;
+ }
+
public int findPointerIndex(int pointerId) {
int pointerCount = mPointerProperties.size();
for (int i = 0; i < pointerCount; i++) {
diff --git a/shadows/framework/src/main/java/org/robolectric/shadows/ShadowLocationManager.java b/shadows/framework/src/main/java/org/robolectric/shadows/ShadowLocationManager.java
index d33e639c6..02d6b1776 100644
--- a/shadows/framework/src/main/java/org/robolectric/shadows/ShadowLocationManager.java
+++ b/shadows/framework/src/main/java/org/robolectric/shadows/ShadowLocationManager.java
@@ -1968,7 +1968,9 @@ public class ShadowLocationManager {
ArrayList<Location> deliverableLocations = new ArrayList<>(locations.length);
for (Location location : locations) {
if (lastDeliveredLocation != null) {
- if (location.getTime() - lastDeliveredLocation.getTime()
+ if (NANOSECONDS.toMillis(
+ location.getElapsedRealtimeNanos()
+ - lastDeliveredLocation.getElapsedRealtimeNanos())
< request.getMinUpdateIntervalMillis()) {
Log.w(TAG, "location rejected for simulated delivery - too fast");
continue;
diff --git a/shadows/framework/src/main/java/org/robolectric/shadows/ShadowMotionEvent.java b/shadows/framework/src/main/java/org/robolectric/shadows/ShadowMotionEvent.java
index dc5345ace..371cb6d9e 100644
--- a/shadows/framework/src/main/java/org/robolectric/shadows/ShadowMotionEvent.java
+++ b/shadows/framework/src/main/java/org/robolectric/shadows/ShadowMotionEvent.java
@@ -4,6 +4,7 @@ import static android.os.Build.VERSION_CODES.KITKAT_WATCH;
import static android.os.Build.VERSION_CODES.LOLLIPOP;
import static android.os.Build.VERSION_CODES.M;
import static android.os.Build.VERSION_CODES.P;
+import static android.os.Build.VERSION_CODES.UPSIDE_DOWN_CAKE;
import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.base.Preconditions.checkState;
import static org.robolectric.shadows.NativeAndroidInput.AMOTION_EVENT_AXIS_ORIENTATION;
@@ -33,6 +34,7 @@ import org.robolectric.annotation.RealObject;
import org.robolectric.annotation.Resetter;
import org.robolectric.res.android.NativeObjRegistry;
import org.robolectric.util.ReflectionHelpers;
+import org.robolectric.versioning.AndroidVersions.V;
/**
* Shadow of MotionEvent.
@@ -51,7 +53,7 @@ import org.robolectric.util.ReflectionHelpers;
* the MotionEvent.obtain methods or via MotionEventBuilder.
*/
@SuppressWarnings({"UnusedDeclaration"})
-@Implements(MotionEvent.class)
+@Implements(value = MotionEvent.class)
public class ShadowMotionEvent extends ShadowInputEvent {
private static NativeObjRegistry<NativeInput.MotionEvent> nativeMotionEventRegistry =
@@ -814,7 +816,7 @@ public class ShadowMotionEvent extends ShadowInputEvent {
return nativeGetXOffset((long) nativePtr);
}
- @Implementation(minSdk = LOLLIPOP)
+ @Implementation(minSdk = LOLLIPOP, maxSdk = UPSIDE_DOWN_CAKE)
@HiddenApi
@InDevelopment
protected static float nativeGetXOffset(long nativePtr) {
@@ -828,7 +830,7 @@ public class ShadowMotionEvent extends ShadowInputEvent {
return nativeGetYOffset((long) nativePtr);
}
- @Implementation(minSdk = LOLLIPOP)
+ @Implementation(minSdk = LOLLIPOP, maxSdk = UPSIDE_DOWN_CAKE)
@HiddenApi
@InDevelopment
protected static float nativeGetYOffset(long nativePtr) {
@@ -836,6 +838,24 @@ public class ShadowMotionEvent extends ShadowInputEvent {
return event.getYOffset();
}
+ @Implementation(minSdk = V.SDK_INT)
+ protected final MotionEvent split(int idBits) {
+ NativeInput.MotionEvent event = getNativeMotionEvent();
+ return event.nativeSplit(idBits);
+ }
+
+ @Implementation(minSdk = V.SDK_INT)
+ @HiddenApi
+ protected static float nativeGetRawXOffset(long nativePtr) {
+ return getNativeMotionEvent(nativePtr).getXOffset();
+ }
+
+ @Implementation(minSdk = V.SDK_INT)
+ @HiddenApi
+ protected static float nativeGetRawYOffset(long nativePtr) {
+ return getNativeMotionEvent(nativePtr).getYOffset();
+ }
+
@Implementation(maxSdk = KITKAT_WATCH)
@HiddenApi
protected static float nativeGetXPrecision(int nativePtr) {
@@ -940,7 +960,7 @@ public class ShadowMotionEvent extends ShadowInputEvent {
event.scale(scale);
}
- private static NativeInput.MotionEvent getNativeMotionEvent(long nativePtr) {
+ protected static NativeInput.MotionEvent getNativeMotionEvent(long nativePtr) {
// check that MotionEvent was initialized properly. This can occur if MotionEvent was mocked
checkState(
nativePtr > 0,
@@ -961,7 +981,7 @@ public class ShadowMotionEvent extends ShadowInputEvent {
event.transform(m);
}
- private NativeInput.MotionEvent getNativeMotionEvent() {
+ protected NativeInput.MotionEvent getNativeMotionEvent() {
long nativePtr;
if (RuntimeEnvironment.getApiLevel() <= KITKAT_WATCH) {
Integer nativePtrInt = ReflectionHelpers.getField(realMotionEvent, "mNativePtr");
diff --git a/shadows/framework/src/main/java/org/robolectric/shadows/ShadowPixelCopy.java b/shadows/framework/src/main/java/org/robolectric/shadows/ShadowPixelCopy.java
index a070ce1cb..9039e885e 100644
--- a/shadows/framework/src/main/java/org/robolectric/shadows/ShadowPixelCopy.java
+++ b/shadows/framework/src/main/java/org/robolectric/shadows/ShadowPixelCopy.java
@@ -25,6 +25,7 @@ import org.robolectric.annotation.Implementation;
import org.robolectric.annotation.Implements;
import org.robolectric.shadow.api.Shadow;
import org.robolectric.shadows.ShadowWindowManagerGlobal.WindowManagerGlobalReflector;
+import org.robolectric.util.PerfStatsCollector;
import org.robolectric.util.reflector.Accessor;
import org.robolectric.util.reflector.Constructor;
import org.robolectric.util.reflector.ForType;
@@ -168,10 +169,18 @@ public class ShadowPixelCopy {
Bitmap bitmap = Bitmap.createBitmap(view.getWidth(), view.getHeight(), Bitmap.Config.ARGB_8888);
if (HardwareRenderingScreenshot.canTakeScreenshot()) {
- HardwareRenderingScreenshot.takeScreenshot(view, bitmap);
+ PerfStatsCollector.getInstance()
+ .measure(
+ "ShadowPixelCopy-Hardware",
+ () -> HardwareRenderingScreenshot.takeScreenshot(view, bitmap));
} else {
- Canvas screenshotCanvas = new Canvas(bitmap);
- view.draw(screenshotCanvas);
+ PerfStatsCollector.getInstance()
+ .measure(
+ "ShadowPixelCopy-Software",
+ () -> {
+ Canvas screenshotCanvas = new Canvas(bitmap);
+ view.draw(screenshotCanvas);
+ });
}
Rect dst = new Rect(0, 0, screenshot.getWidth(), screenshot.getHeight());
diff --git a/shadows/framework/src/main/java/org/robolectric/shadows/ShadowViewConfiguration.java b/shadows/framework/src/main/java/org/robolectric/shadows/ShadowViewConfiguration.java
index 33f800f7d..682f53b6a 100644
--- a/shadows/framework/src/main/java/org/robolectric/shadows/ShadowViewConfiguration.java
+++ b/shadows/framework/src/main/java/org/robolectric/shadows/ShadowViewConfiguration.java
@@ -46,7 +46,9 @@ public class ShadowViewConfiguration {
private static final int DOUBLE_TAP_SLOP = 100;
private static final int WINDOW_TOUCH_SLOP = 16;
private static final int MAXIMUM_FLING_VELOCITY = 4000;
- private static final int MAXIMUM_DRAWING_CACHE_SIZE = 320 * 480 * 4;
+
+ // The previous hardcoded value for draw cache size. Some screenshot tests depend on this value.
+ private static final int MIN_MAXIMUM_DRAWING_CACHE_SIZE = 480 * 800 * 4;
private int edgeSlop;
private int fadingEdgeLength;
@@ -57,10 +59,11 @@ public class ShadowViewConfiguration {
private int pagingTouchSlop;
private int doubleTapSlop;
private int windowTouchSlop;
+ private int maximumDrawingCacheSize;
private static boolean hasPermanentMenuKey = true;
private void setup(Context context) {
- DisplayMetrics metrics = context.getResources().getDisplayMetrics();
+ final DisplayMetrics metrics = context.getResources().getDisplayMetrics();
float density = metrics.density;
edgeSlop = (int) (density * ViewConfiguration.getEdgeSlop() + 0.5f);
@@ -72,6 +75,12 @@ public class ShadowViewConfiguration {
pagingTouchSlop = (int) (density * PAGING_TOUCH_SLOP + 0.5f);
doubleTapSlop = (int) (density * DOUBLE_TAP_SLOP + 0.5f);
windowTouchSlop = (int) (density * WINDOW_TOUCH_SLOP + 0.5f);
+ // Some screenshot tests were misconfigured and try to draw very large views onto small
+ // screens using SW rendering. To avoid breaking these tests, we keep the drawing cache a bit
+ // larger when screens are configured to be arbitrarily small.
+ // TODO(hoisie): Investigate removing this Math.max logic.
+ maximumDrawingCacheSize =
+ Math.max(MIN_MAXIMUM_DRAWING_CACHE_SIZE, 4 * metrics.widthPixels * metrics.heightPixels);
}
@Implementation
@@ -174,8 +183,8 @@ public class ShadowViewConfiguration {
}
@Implementation
- protected static int getMaximumDrawingCacheSize() {
- return MAXIMUM_DRAWING_CACHE_SIZE;
+ protected int getScaledMaximumDrawingCacheSize() {
+ return maximumDrawingCacheSize;
}
@Implementation