diff options
author | Ram Peri <ramperi@google.com> | 2024-04-18 20:32:50 +0000 |
---|---|---|
committer | Gerrit Code Review <noreply-gerritcodereview@google.com> | 2024-04-18 20:32:50 +0000 |
commit | 9201ec4b504ddb37e398e795810f3aa5be946aae (patch) | |
tree | 96e08ecd5ae77ac50b2bf021e7a1801c939267d6 | |
parent | b3b17db94476c25225589165fbf53ec938f5b6dc (diff) | |
parent | 4774468762423e04ef3bb56a477205cc9c192307 (diff) | |
download | robolectric-master.tar.gz |
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 |