diff options
author | Android Build Coastguard Worker <android-build-coastguard-worker@google.com> | 2023-07-07 01:01:56 +0000 |
---|---|---|
committer | Android Build Coastguard Worker <android-build-coastguard-worker@google.com> | 2023-07-07 01:01:56 +0000 |
commit | e6603d13b1ced9fa093927daed2aea8d1600a405 (patch) | |
tree | b525d4e63285a47329bac37c594a6b9eb3980bfa | |
parent | dc10306162ecb0c009b02aa679ea3f22d68dbdb8 (diff) | |
parent | f0abff70c16cd0a8f16a68cf6b24adb083fa7ab9 (diff) | |
download | robolectric-shadows-e6603d13b1ced9fa093927daed2aea8d1600a405.tar.gz |
Snap for 10447354 from f0abff70c16cd0a8f16a68cf6b24adb083fa7ab9 to mainline-cellbroadcast-releaseaml_cbr_341311010aml_cbr_341110000aml_cbr_341011000aml_cbr_340914000
Change-Id: I63a263f2adaaacbb4a746a9124c35d7e819e647b
47 files changed, 500 insertions, 123 deletions
diff --git a/Android.bp b/Android.bp index 33d2ea864..c12f01202 100644 --- a/Android.bp +++ b/Android.bp @@ -54,27 +54,6 @@ robolectric_build_props { name: "robolectric_build_props", } -// package the framework raw/uncompiled resources and assets into a jar -// This logic can be removed once the transition to binary resources is complete -java_library { - name: "robolectric_framework_raw_res_orig", - java_resources: [":robolectric_framework_raw_res_files"], -} - -// Move the raw/uncompiled resources and assets into raw-res/ -// This logic can be removed once the transition to binary resources is complete -java_genrule_host { - name: "robolectric_framework_raw_res", - out: ["robolectric_framework_raw_res.jar"], - srcs: [":robolectric_framework_raw_res_orig"], - tools: ["zip2zip"], - cmd: "$(location zip2zip) " + - "-i $(location :robolectric_framework_raw_res_orig) " + - "-o $(location robolectric_framework_raw_res.jar) " + - "-x META-INF/**/* " + - "**/*:raw-res/", -} - java_genrule_host { name: "robolectric_framework_res", tools: ["zip2zip"], @@ -115,7 +94,6 @@ java_library_host { "robolectric_android-all-device-deps", "robolectric_tzdata", "robolectric_framework_res", - "robolectric_framework_raw_res", ], dist: { targets: [ @@ -155,11 +133,11 @@ java_library_host { "Robolectric_sandbox", "Robolectric_junit", "Robolectric_utils", - "asm-9.2", + "ow2-asm", "junit", - "asm-tree-9.2", + "ow2-asm-tree", "guava", - "asm-commons-9.2", + "ow2-asm-commons", "bouncycastle-unbundled", "robolectric-sqlite4java-0.282", "hamcrest", diff --git a/junit/Android.bp b/junit/Android.bp index 38ab52978..bee751423 100644 --- a/junit/Android.bp +++ b/junit/Android.bp @@ -18,12 +18,12 @@ java_library_host { "Robolectric_shadowapi", "Robolectric_sandbox", "Robolectric_utils", - "asm-commons-9.2", + "ow2-asm-commons", "guava", - "asm-tree-9.2", + "ow2-asm-tree", "hamcrest", "junit", - "asm-9.2", + "ow2-asm", "jsr305", ], srcs: ["src/main/java/**/*.java"], diff --git a/processor/Android.bp b/processor/Android.bp index 7dbdbc02e..a61af5000 100644 --- a/processor/Android.bp +++ b/processor/Android.bp @@ -21,16 +21,17 @@ java_library_host { static_libs: [ "Robolectric_annotations", "Robolectric_shadowapi", - "asm-commons-9.2", + "ow2-asm-commons", "guava", - "asm-tree-9.2", - "gson-prebuilt-jar", - "asm-9.2", + "ow2-asm-tree", + "gson", + "ow2-asm", "jsr305", ], openjdk9: { javacflags: [ + "--add-exports=jdk.compiler/com.sun.tools.javac.api=ALL-UNNAMED", "--add-exports=jdk.compiler/com.sun.tools.javac.code=ALL-UNNAMED", "--add-exports=jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED", ], @@ -64,7 +65,7 @@ java_test_host { "objenesis", "junit", "truth-prebuilt", - "gson-prebuilt-jar", + "gson", "jsr305", ], diff --git a/processor/AndroidTest.xml b/processor/AndroidTest.xml index 96fc65801..7f31410b0 100644 --- a/processor/AndroidTest.xml +++ b/processor/AndroidTest.xml @@ -18,6 +18,8 @@ <option name="jar" value="Robolectric_processor_tests.jar" /> <option name="java-flags" value="-Drobolectric.offline=true" /> <option name="java-flags" value="-Drobolectric.resourcesMode=binary" /> + <option name="java-flags" value="--add-modules=jdk.compiler"/> + <option name="java-flags" value="--add-exports=jdk.compiler/com.sun.tools.javac.api=ALL-UNNAMED"/> <option name="exclude-paths" value="java/" /> </test> </configuration> diff --git a/processor/sdks.txt b/processor/sdks.txt index 5444e46e9..3b4b80de4 100644 --- a/processor/sdks.txt +++ b/processor/sdks.txt @@ -10,7 +10,8 @@ prebuilts/misc/common/robolectric/android-all/android-all-7.1.0_r7-robolectric-r prebuilts/misc/common/robolectric/android-all/android-all-8.0.0_r4-robolectric-r1.jar prebuilts/misc/common/robolectric/android-all/android-all-8.1.0-robolectric-4611349.jar prebuilts/misc/common/robolectric/android-all/android-all-9-robolectric-4913185-2.jar -prebuilts/misc/common/robolectric/android-all/android-all-9plus-robolectric-5616371.jar prebuilts/misc/common/robolectric/android-all/android-all-10-robolectric-5803371.jar -prebuilts/misc/common/robolectric/android-all/android-all-R-beta2-robolectric-6625208.jar -prebuilts/misc/common/robolectric/android-all/android-all-S-beta3-robolectric-7541949.jar +prebuilts/misc/common/robolectric/android-all/android-all-11-robolectric-6757853.jar +prebuilts/misc/common/robolectric/android-all/android-all-12-robolectric-7732740.jar +prebuilts/misc/common/robolectric/android-all/android-all-12.1-robolectric-8229987.jar +prebuilts/misc/common/robolectric/android-all/android-all-13-robolectric-9030017.jar diff --git a/processor/src/main/java/org/robolectric/annotation/processing/validator/SdkStore.java b/processor/src/main/java/org/robolectric/annotation/processing/validator/SdkStore.java index 1f11dbb3a..b1d8cb826 100644 --- a/processor/src/main/java/org/robolectric/annotation/processing/validator/SdkStore.java +++ b/processor/src/main/java/org/robolectric/annotation/processing/validator/SdkStore.java @@ -5,8 +5,6 @@ import static org.robolectric.annotation.processing.validator.ImplementsValidato import static org.robolectric.annotation.processing.validator.ImplementsValidator.STATIC_INITIALIZER_METHOD_NAME; import static org.robolectric.annotation.processing.validator.ImplementsValidator.getClassFQName; -import com.sun.tools.javac.code.Type.ArrayType; -import com.sun.tools.javac.code.Type.TypeVar; import java.io.BufferedReader; import java.io.File; import java.io.FileOutputStream; @@ -29,7 +27,10 @@ import javax.lang.model.element.ExecutableElement; import javax.lang.model.element.Modifier; import javax.lang.model.element.TypeElement; import javax.lang.model.element.VariableElement; +import javax.lang.model.type.ArrayType; +import javax.lang.model.type.TypeKind; import javax.lang.model.type.TypeMirror; +import javax.lang.model.type.TypeVariable; import org.objectweb.asm.ClassReader; import org.objectweb.asm.Opcodes; import org.objectweb.asm.Type; @@ -100,10 +101,10 @@ class SdkStore { } private static String canonicalize(TypeMirror typeMirror) { - if (typeMirror instanceof TypeVar) { - return ((TypeVar) typeMirror).getUpperBound().toString(); - } else if (typeMirror instanceof ArrayType) { - return canonicalize(((ArrayType) typeMirror).elemtype) + "[]"; + if (typeMirror.getKind() == TypeKind.TYPEVAR) { + return ((TypeVariable) typeMirror).getUpperBound().toString(); + } else if (typeMirror.getKind() == TypeKind.ARRAY) { + return canonicalize(((ArrayType) typeMirror).getComponentType()) + "[]"; } else { return typeMirror.toString(); } diff --git a/resources/Android.bp b/resources/Android.bp index 76c2c052b..3d08f907f 100644 --- a/resources/Android.bp +++ b/resources/Android.bp @@ -20,6 +20,11 @@ java_library_host { "guava", "jsr305", ], + errorprone: { + javacflags: [ + "-Xep:EmptyTopLevelDeclaration:WARN", + ], + }, } //############################################# diff --git a/resources/src/main/java/org/robolectric/res/android/LoadedArsc.java b/resources/src/main/java/org/robolectric/res/android/LoadedArsc.java index 09f79572f..0c41714f6 100644 --- a/resources/src/main/java/org/robolectric/res/android/LoadedArsc.java +++ b/resources/src/main/java/org/robolectric/res/android/LoadedArsc.java @@ -400,15 +400,17 @@ public class LoadedArsc { // }); ResTable_sparseTypeEntry result = null; for (int i = 0; i < entry_count; i++) { - ResTable_sparseTypeEntry entry = new ResTable_sparseTypeEntry(type_chunk.myBuf(), - type_chunk.myOffset() + offsets_offset); - if (entry.idxOrOffset >= entry_index) { + ResTable_sparseTypeEntry entry = + new ResTable_sparseTypeEntry( + type_chunk.myBuf(), + type_chunk.myOffset() + offsets_offset + i * ResTable_sparseTypeEntry.SIZEOF); + if (entry.idx >= entry_index) { result = entry; break; } } - if (result == null || dtohs(result.idxOrOffset) != entry_index) { + if (result == null || dtohs(result.idx) != entry_index) { // No entry found. return ResTable_type.NO_ENTRY; } @@ -416,7 +418,7 @@ public class LoadedArsc { // Extract the offset from the entry. Each offset must be a multiple of 4 so we store it as // the real offset divided by 4. // return int{dtohs(result.offset)} * 4u; - return dtohs(result.idxOrOffset) * 4; + return dtohs(result.offset) * 4; } // This type is encoded as a dense array. diff --git a/resources/src/main/java/org/robolectric/res/android/ResTable.java b/resources/src/main/java/org/robolectric/res/android/ResTable.java index dbb2c6fc1..2176cafab 100644 --- a/resources/src/main/java/org/robolectric/res/android/ResTable.java +++ b/resources/src/main/java/org/robolectric/res/android/ResTable.java @@ -630,18 +630,18 @@ public class ResTable { sparseIndices, new ResTable_sparseTypeEntry(buf, sparseIndices.myOffset() + dtohl(thisType.entryCount)), new ResTable_sparseTypeEntry(buf, realEntryIndex), - (a, b) -> dtohs(a.idxOrOffset) < dtohs(b.idxOrOffset)); + (a, b) -> dtohs(a.idx) < dtohs(b.idx)); // if (result == sparseIndices + dtohl(thisType.entryCount) // || dtohs(result.idx) != realEntryIndex) { if (result.myOffset() == sparseIndices.myOffset() + dtohl(thisType.entryCount) - || dtohs(result.idxOrOffset) != realEntryIndex) { + || dtohs(result.idx) != realEntryIndex) { // No entry found. continue; } // Extract the offset from the entry. Each offset must be a multiple of 4 // so we store it as the real offset divided by 4. // thisOffset = dtohs(result->offset) * 4u; - thisOffset = dtohs(result.idxOrOffset) * 4; + thisOffset = dtohs(result.offset) * 4; } else { if (realEntryIndex >= dtohl(thisType.entryCount)) { // Entry does not exist. @@ -986,11 +986,6 @@ public class ResTable { } Type t = typeList.get(typeList.size() - 1); - if (newEntryCount != t.entryCount) { - ALOGE("ResTable_type entry count inconsistent: given %d, previously %d", - (int) newEntryCount, (int) t.entryCount); - return (mError = BAD_TYPE); - } if (t._package_ != _package) { ALOGE("No TypeSpec for type %d", type.id); diff --git a/resources/src/main/java/org/robolectric/res/android/ResourceTypes.java b/resources/src/main/java/org/robolectric/res/android/ResourceTypes.java index ce12e4b3a..5438f33cb 100644 --- a/resources/src/main/java/org/robolectric/res/android/ResourceTypes.java +++ b/resources/src/main/java/org/robolectric/res/android/ResourceTypes.java @@ -1259,12 +1259,13 @@ public static class ResTable_ref * An entry in a ResTable_type with the flag `FLAG_SPARSE` set. */ static class ResTable_sparseTypeEntry extends WithOffset { - public static final int SIZEOF = 6; + public static final int SIZEOF = 4; // Holds the raw uint32_t encoded value. Do not read this. int entry; - short idxOrOffset; + short idx; + short offset; // struct { // The index of the entry. // uint16_t idx; @@ -1276,8 +1277,8 @@ public static class ResTable_ref public ResTable_sparseTypeEntry(ByteBuffer buf, int offset) { super(buf, offset); - entry = buf.getInt(offset); - idxOrOffset = buf.getShort(offset + 4); + this.idx = buf.getShort(offset); + this.offset = buf.getShort(offset + 2); } }; diff --git a/robolectric/Android.bp b/robolectric/Android.bp index ab08b47ff..4d2639d15 100644 --- a/robolectric/Android.bp +++ b/robolectric/Android.bp @@ -25,13 +25,13 @@ java_library_host { "robolectric-host-androidx-test-monitor", "robolectric-maven-ant-tasks-2.1.3", "bouncycastle-unbundled", - "asm-commons-9.2", + "ow2-asm-commons", "guava", "robolectric-xstream-1.4.8", - "asm-tree-9.2", + "ow2-asm-tree", "junit", "robolectric-ant-1.8.0", - "asm-9.2", + "ow2-asm", "jsr305", "robolectric-host-android_all", ], @@ -70,17 +70,17 @@ java_test_host { "bouncycastle-unbundled", "hamcrest", "robolectric-sqlite4java-0.282", - "asm-commons-9.2", + "ow2-asm-commons", "robolectric-diffutils-1.3.0", "guava", "objenesis", "robolectric-xstream-1.4.8", - "asm-tree-9.2", + "ow2-asm-tree", "junit", "icu4j", "truth-prebuilt", "robolectric-ant-1.8.0", - "asm-9.2", + "ow2-asm", "jsr305", ], libs: ["robolectric-host-android_all"], @@ -88,4 +88,10 @@ java_test_host { test_options: { unit_test: false, }, + + errorprone: { + javacflags: [ + "-Xep:ReturnValueIgnored:WARN", + ], + }, } diff --git a/robolectric/src/main/java/org/robolectric/android/internal/LocalActivityInvoker.java b/robolectric/src/main/java/org/robolectric/android/internal/LocalActivityInvoker.java index a3119e021..d44a05f42 100644 --- a/robolectric/src/main/java/org/robolectric/android/internal/LocalActivityInvoker.java +++ b/robolectric/src/main/java/org/robolectric/android/internal/LocalActivityInvoker.java @@ -29,6 +29,8 @@ public class LocalActivityInvoker implements ActivityInvoker { @Nullable private ActivityController<? extends Activity> controller; + private boolean isActivityLaunchedForResult = false; + @Override public void startActivity(Intent intent, @Nullable Bundle activityOptions) { startActivity(intent); @@ -54,7 +56,23 @@ public class LocalActivityInvoker implements ActivityInvoker { } @Override + public void startActivityForResult(Intent intent, @Nullable Bundle activityOptions) { + startActivityForResult(intent); + } + + @Override + public void startActivityForResult(Intent intent) { + isActivityLaunchedForResult = true; + startActivity(intent); + } + + @Override public ActivityResult getActivityResult() { + if (!isActivityLaunchedForResult) { + throw new IllegalStateException( + "You must start Activity first. Make sure you are using launchActivityForResult() to" + + " launch an Activity."); + } checkNotNull(controller); checkState(controller.get().isFinishing(), "You must finish your Activity first"); ShadowActivity shadowActivity = Shadow.extract(controller.get()); diff --git a/robolectric/src/main/java/org/robolectric/android/internal/ParallelUniverse.java b/robolectric/src/main/java/org/robolectric/android/internal/ParallelUniverse.java index e74a03cdb..5de048893 100644 --- a/robolectric/src/main/java/org/robolectric/android/internal/ParallelUniverse.java +++ b/robolectric/src/main/java/org/robolectric/android/internal/ParallelUniverse.java @@ -420,7 +420,8 @@ public class ParallelUniverse implements ParallelUniverseInterface { * Create a file system safe directory path name for the current test. */ private String createTestDataDirRootPath(Method method) { - return method.getClass().getSimpleName() + "_" + method.getName().replaceAll("[^a-zA-Z0-9.-]", "_"); + return method.getDeclaringClass().getSimpleName() + "_" + + method.getName().replaceAll("[^a-zA-Z0-9.-]", "_"); } @Override diff --git a/robolectric/src/test/java/org/robolectric/shadows/ShadowDateIntervalFormatTest.java b/robolectric/src/test/java/org/robolectric/shadows/ShadowDateIntervalFormatTest.java index bde0e7263..f9721e464 100644 --- a/robolectric/src/test/java/org/robolectric/shadows/ShadowDateIntervalFormatTest.java +++ b/robolectric/src/test/java/org/robolectric/shadows/ShadowDateIntervalFormatTest.java @@ -1,6 +1,6 @@ package org.robolectric.shadows; -import static android.os.Build.VERSION_CODES.M; +import static android.os.Build.VERSION_CODES.UPSIDE_DOWN_CAKE; import static com.google.common.truth.Truth.assertThat; import android.icu.text.DateFormat; @@ -12,13 +12,13 @@ import androidx.test.ext.junit.runners.AndroidJUnit4; import java.text.ParseException; import java.util.Calendar; import java.util.Date; -import libcore.icu.DateIntervalFormat; +import android.text.format.DateIntervalFormat; import org.junit.Test; import org.junit.runner.RunWith; import org.robolectric.annotation.Config; @RunWith(AndroidJUnit4.class) -@Config(minSdk = M) +@Config(minSdk = UPSIDE_DOWN_CAKE) public class ShadowDateIntervalFormatTest { @Test public void testDateInterval_FormatDateRange() throws ParseException { diff --git a/robotest-internal.mk b/robotest-internal.mk index 77e173024..34db668c7 100644 --- a/robotest-internal.mk +++ b/robotest-internal.mk @@ -25,7 +25,7 @@ $(my_target_output): PRIVATE_MODULE := $(LOCAL_MODULE) $(my_target_output): PRIVATE_TESTS := $(my_tests) $(my_target_output): PRIVATE_JARS := $(my_jars) $(my_target_output): PRIVATE_JAVA_ARGS := $(my_java_args) -$(my_target_output): PRIVATE_JAVA_PATH := $(ANDROID_JAVA11_HOME)/bin: +$(my_target_output): PRIVATE_JAVA_PATH := $(ANDROID_JAVA17_HOME)/bin: $(my_target_output): PRIVATE_ROBOLECTRIC_PATH := $(my_robolectric_path) $(my_target_output): PRIVATE_ROBOLECTRIC_SCRIPT_PATH := $(my_robolectric_script_path) $(my_target_output): PRIVATE_TARGET_MESSAGE := $(my_target_message) diff --git a/robotest.sh b/robotest.sh index e95f09df3..df5570bcf 100755 --- a/robotest.sh +++ b/robotest.sh @@ -73,6 +73,8 @@ function junit() { # Remove the timeout so Robolectric doesn't get killed while debugging local debug_timeout="0" fi + # For --add-opens jdk.internal.util.random see b/238100560. + # For --add-opens java.io see b/251387255. local command=( "${PRIVATE_ROBOLECTRIC_SCRIPT_PATH}/java-timeout" "${debug_timeout:-${PRIVATE_TIMEOUT}}" @@ -84,6 +86,10 @@ function junit() { -Drobolectric.conscryptMode=OFF -Drobolectric.logging.enabled=true -cp "$classpath" + --add-opens=java.base/java.lang=ALL-UNNAMED + --add-opens=java.base/java.lang.reflect=ALL-UNNAMED + --add-opens=java.base/jdk.internal.util.random=ALL-UNNAMED + --add-opens=java.base/java.io=ALL-UNNAMED com.android.junitxml.JUnitXmlRunner ) echo "${command[@]}" "$@" diff --git a/run_robolectric_module_tests.mk b/run_robolectric_module_tests.mk index 96f1cb59b..0050c7326 100644 --- a/run_robolectric_module_tests.mk +++ b/run_robolectric_module_tests.mk @@ -51,9 +51,11 @@ copy_android_all_jar_pairs := \ $(android_all_source_dir)/android-all-8.0.0_r4-robolectric-r1.jar:$(android_all_target_dir)/android-all-8.0.0_r4-robolectric-r1.jar \ $(android_all_source_dir)/android-all-8.1.0-robolectric-4611349.jar:$(android_all_target_dir)/android-all-8.1.0-robolectric-4611349.jar \ $(android_all_source_dir)/android-all-9-robolectric-4913185-2.jar:$(android_all_target_dir)/android-all-9-robolectric-4913185-2.jar \ - $(android_all_source_dir)/android-all-9plus-robolectric-5616371.jar:$(android_all_target_dir)/android-all-9plus-robolectric-5616371.jar \ - $(android_all_source_dir)/android-all-R-beta2-robolectric-6625208.jar:$(android_all_target_dir)/android-all-R-beta2-robolectric-6625208.jar \ - $(android_all_source_dir)/android-all-S-beta3-robolectric-7541949.jar:$(android_all_target_dir)/android-all-S-beta3-robolectric-7541949.jar \ + $(android_all_source_dir)/android-all-10-robolectric-5803371.jar:$(android_all_target_dir)/android-all-10-robolectric-5803371.jar \ + $(android_all_source_dir)/android-all-11-robolectric-6757853.jar:$(android_all_target_dir)/android-all-11-robolectric-6757853.jar \ + $(android_all_source_dir)/android-all-12-robolectric-7732740.jar:$(android_all_target_dir)/android-all-12-robolectric-7732740.jar \ + $(android_all_source_dir)/android-all-12.1-robolectric-8229987.jar:$(android_all_target_dir)/android-all-12.1-robolectric-8229987.jar \ + $(android_all_source_dir)/android-all-13-robolectric-9030017.jar:$(android_all_target_dir)/android-all-13-robolectric-9030017.jar \ $(local_android_all_source_jar):$(android_all_target_dir)/android-all-current-robolectric-r0.jar copy_android_all_jars := $(call copy-many-files, $(copy_android_all_jar_pairs)) @@ -91,6 +93,9 @@ $(LOCAL_BUILT_MODULE): $(copy_test_resource_files) $(copy_android_all_jars) $(cl -Drobolectric.resourcesMode=binary \ -Drobolectric-tests.base-dir=$(private_test_base_dir) \ -Drobolectric.dependency.dir=$(private_android_all_dir) \ + --add-exports=jdk.compiler/com.sun.tools.javac.api=ALL-UNNAMED \ + --add-exports=jdk.compiler/com.sun.tools.javac.code=ALL-UNNAMED \ + --add-exports=jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED \ $(private_debug_test_args) \ -cp $(private_host_jdk_tools_jar):$(private_test_base_dir):$(private_classpath_jars) \ org.junit.runner.JUnitCore \ diff --git a/run_robotests.mk b/run_robotests.mk index b5a6ee44e..cb0db1b74 100644 --- a/run_robotests.mk +++ b/run_robotests.mk @@ -135,8 +135,10 @@ ifneq ($(DISABLE_ROBO_RUN_TESTS),true) $(android_all_lib_path)/android-all-8.1.0-robolectric-4611349.jar:$(my_robolectric_path)/android-all-8.1.0-robolectric-4611349.jar \ $(android_all_lib_path)/android-all-9-robolectric-4913185-2.jar:$(my_robolectric_path)/android-all-9-robolectric-4913185-2.jar \ $(android_all_lib_path)/android-all-10-robolectric-5803371.jar:$(my_robolectric_path)/android-all-10-robolectric-5803371.jar \ - $(android_all_lib_path)/android-all-R-beta2-robolectric-6625208.jar:$(my_robolectric_path)/android-all-R-beta2-robolectric-6625208.jar \ - $(android_all_lib_path)/android-all-S-beta3-robolectric-7541949.jar:$(my_robolectric_path)/android-all-S-beta3-robolectric-7541949.jar \ + $(android_all_lib_path)/android-all-11-robolectric-6757853.jar:$(my_robolectric_path)/android-all-11-robolectric-6757853.jar \ + $(android_all_lib_path)/android-all-12-robolectric-7732740.jar:$(my_robolectric_path)/android-all-12-robolectric-7732740.jar \ + $(android_all_lib_path)/android-all-12.1-robolectric-8229987.jar:$(my_robolectric_path)/android-all-12.1-robolectric-8229987.jar \ + $(android_all_lib_path)/android-all-13-robolectric-9030017.jar:$(my_robolectric_path)/android-all-13-robolectric-9030017.jar \ $(local_android_all_source_jar):$(my_robolectric_path)/android-all-current-robolectric-r0.jar copy_android_all_jars := $(call copy-many-files, $(copy_android_all_jar_pairs)) diff --git a/sandbox/Android.bp b/sandbox/Android.bp index ae6aa36cf..fafcdef53 100644 --- a/sandbox/Android.bp +++ b/sandbox/Android.bp @@ -18,10 +18,10 @@ java_library_host { "Robolectric_annotations", "Robolectric_shadowapi", "Robolectric_utils", - "asm-commons-9.2", + "ow2-asm-commons", "guava", - "asm-tree-9.2", - "asm-9.2", + "ow2-asm-tree", + "ow2-asm", "jsr305", ], } @@ -41,14 +41,19 @@ java_test_host { "Robolectric_utils", "mockito", "hamcrest", - "asm-commons-9.2", + "ow2-asm-commons", "guava", "objenesis", - "asm-tree-9.2", + "ow2-asm-tree", "junit", "truth-prebuilt", - "asm-9.2", + "ow2-asm", "jsr305", ], test_suites: ["general-tests"], + errorprone: { + javacflags: [ + "-Xep:ReturnValueIgnored:WARN", + ], + }, } diff --git a/sandbox/AndroidTest.xml b/sandbox/AndroidTest.xml index 932f08385..f8f3c8780 100644 --- a/sandbox/AndroidTest.xml +++ b/sandbox/AndroidTest.xml @@ -18,6 +18,7 @@ <option name="jar" value="Robolectric_sandbox_tests.jar" /> <option name="java-flags" value="-Drobolectric.offline=true" /> <option name="java-flags" value="-Drobolectric.resourcesMode=binary" /> + <option name="java-flags" value="--add-opens=java.base/java.lang=ALL-UNNAMED"/> <option name="exclude-paths" value="java/" /> </test> </configuration> 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 8997be4b2..ebe36c66f 100644 --- a/sandbox/src/main/java/org/robolectric/internal/bytecode/ClassInstrumentor.java +++ b/sandbox/src/main/java/org/robolectric/internal/bytecode/ClassInstrumentor.java @@ -42,7 +42,7 @@ public abstract class ClassInstrumentor { final InstrumentationConfiguration config, ClassNodeProvider classNodeProvider) { ClassNode classNode = - new ClassNode(Opcodes.ASM4) { + new ClassNode(Opcodes.ASM9) { @Override public FieldVisitor visitField( int access, String name, String desc, String signature, Object value) { @@ -366,7 +366,7 @@ public abstract class ClassInstrumentor { //todo rename private MethodNode redirectorMethod(MutableClass mutableClass, MethodNode method, String newName) { - MethodNode redirector = new MethodNode(Opcodes.ASM4, newName, method.desc, method.signature, exceptionArray(method)); + MethodNode redirector = new MethodNode(Opcodes.ASM9, newName, method.desc, method.signature, exceptionArray(method)); redirector.access = method.access & ~(Opcodes.ACC_NATIVE | Opcodes.ACC_ABSTRACT | Opcodes.ACC_FINAL); makeMethodPrivate(redirector); RobolectricGeneratorAdapter generator = new RobolectricGeneratorAdapter(redirector); diff --git a/sandbox/src/main/java/org/robolectric/internal/bytecode/ProxyMaker.java b/sandbox/src/main/java/org/robolectric/internal/bytecode/ProxyMaker.java index 9cf0c829d..67dbdf2d7 100644 --- a/sandbox/src/main/java/org/robolectric/internal/bytecode/ProxyMaker.java +++ b/sandbox/src/main/java/org/robolectric/internal/bytecode/ProxyMaker.java @@ -6,6 +6,9 @@ import static org.objectweb.asm.Opcodes.ACC_SUPER; import static org.objectweb.asm.Opcodes.INVOKEVIRTUAL; import static org.objectweb.asm.Opcodes.V1_7; +import java.lang.invoke.MethodHandles; +import java.lang.invoke.MethodHandles.Lookup; +import java.lang.reflect.Array; import java.lang.reflect.Field; import java.lang.reflect.Modifier; import org.objectweb.asm.ClassWriter; @@ -16,18 +19,68 @@ import sun.misc.Unsafe; public class ProxyMaker { private static final String TARGET_FIELD = "__proxy__"; + private static final Class UNSAFE_CLASS = Unsafe.class; + private static final Class LOOKUP_CLASS = MethodHandles.Lookup.class; private static final Unsafe UNSAFE; + private static final java.lang.reflect.Method DEFINE_ANONYMOUS_CLASS; + + private static final MethodHandles.Lookup LOOKUP; + private static final java.lang.reflect.Method HIDDEN_DEFINE_METHOD; + private static final Object HIDDEN_CLASS_OPTIONS; + static { try { - Field unsafeField = Unsafe.class.getDeclaredField("theUnsafe"); + Field unsafeField = UNSAFE_CLASS.getDeclaredField("theUnsafe"); unsafeField.setAccessible(true); UNSAFE = (Unsafe) unsafeField.get(null); - } catch (NoSuchFieldException | IllegalAccessException e) { + + // Unsafe.defineAnonymousClass() has been deprecated in Java 15 and removed in Java 17. Its + // usage should be replace by MethodHandles.Lookup.defineHiddenClass() which was introduced in + // Java 15. For now, try and support both older and newer Java versions. + DEFINE_ANONYMOUS_CLASS = getDefineAnonymousClass(); + if (DEFINE_ANONYMOUS_CLASS == null) { + LOOKUP = getTrustedLookup(); + + Class classOptionClass = Class.forName(LOOKUP_CLASS.getName() + "$ClassOption"); + HIDDEN_CLASS_OPTIONS = Array.newInstance(classOptionClass, 1); + Array.set(HIDDEN_CLASS_OPTIONS, 0, Enum.valueOf(classOptionClass, "NESTMATE")); + HIDDEN_DEFINE_METHOD = LOOKUP_CLASS.getMethod("defineHiddenClass", byte[].class, + boolean.class, + HIDDEN_CLASS_OPTIONS.getClass()); + } else { + LOOKUP = null; + HIDDEN_DEFINE_METHOD = null; + HIDDEN_CLASS_OPTIONS = null; + } + } catch (ReflectiveOperationException e) { throw new AssertionError(e); } } + private static java.lang.reflect.Method getDefineAnonymousClass() { + try { + return UNSAFE_CLASS.getMethod("defineAnonymousClass", Class.class, + byte[].class, Object[].class); + } catch (NoSuchMethodException e) { + return null; + } + } + + private static MethodHandles.Lookup getTrustedLookup() throws ReflectiveOperationException { + Field trustedLookupField = LOOKUP_CLASS.getDeclaredField("IMPL_LOOKUP"); + java.lang.reflect.Method baseMethod = UNSAFE_CLASS.getMethod("staticFieldBase", + Field.class); + Object lookupBase = baseMethod.invoke(UNSAFE, trustedLookupField); + java.lang.reflect.Method offsetMethod = UNSAFE_CLASS.getMethod("staticFieldOffset", + Field.class); + Object lookupOffset = offsetMethod.invoke(UNSAFE, trustedLookupField); + + java.lang.reflect.Method getObjectMethod = UNSAFE_CLASS.getMethod("getObject", + Object.class, long.class); + return (MethodHandles.Lookup) getObjectMethod.invoke(UNSAFE, lookupBase, lookupOffset); + } + private final MethodMapper methodMapper; private final ClassValueMap<Factory> factories; @@ -74,9 +127,8 @@ public class ProxyMaker { writer.visitEnd(); - final Class<?> proxyClass = UNSAFE.defineAnonymousClass(targetClass, writer.toByteArray(), null); - try { + final Class<?> proxyClass = defineHiddenClass(targetClass, writer.toByteArray()); final Field field = proxyClass.getDeclaredField(TARGET_FIELD); return new Factory() { @Override public <E> E createProxy(Class<E> targetClass, E target) { @@ -91,11 +143,24 @@ public class ProxyMaker { } } }; - } catch (NoSuchFieldException e) { + } catch (ReflectiveOperationException e) { throw new AssertionError(e); } } + private static Class<?> defineHiddenClass(Class<?> targetClass, + byte[] bytes) throws ReflectiveOperationException { + if (DEFINE_ANONYMOUS_CLASS != null) { + return (Class<?>) DEFINE_ANONYMOUS_CLASS.invoke(UNSAFE, targetClass, + bytes, (Object[]) null); + } else { + MethodHandles.Lookup lookup = (MethodHandles.Lookup) LOOKUP.in(targetClass); + MethodHandles.Lookup definedLookup = (MethodHandles.Lookup) + HIDDEN_DEFINE_METHOD.invoke(lookup, bytes, false, HIDDEN_CLASS_OPTIONS); + return definedLookup.lookupClass(); + } + } + private static boolean shouldProxy(java.lang.reflect.Method method) { int modifiers = method.getModifiers(); return !Modifier.isAbstract(modifiers) && !Modifier.isFinal(modifiers) && !Modifier.isPrivate( diff --git a/sandbox/src/main/java/org/robolectric/internal/bytecode/RobolectricGeneratorAdapter.java b/sandbox/src/main/java/org/robolectric/internal/bytecode/RobolectricGeneratorAdapter.java index 1569c379c..14ab0415d 100644 --- a/sandbox/src/main/java/org/robolectric/internal/bytecode/RobolectricGeneratorAdapter.java +++ b/sandbox/src/main/java/org/robolectric/internal/bytecode/RobolectricGeneratorAdapter.java @@ -18,7 +18,7 @@ class RobolectricGeneratorAdapter extends GeneratorAdapter { private final String desc; public RobolectricGeneratorAdapter(MethodNode methodNode) { - super(Opcodes.ASM4, methodNode, methodNode.access, methodNode.name, methodNode.desc); + super(Opcodes.ASM9, methodNode, methodNode.access, methodNode.name, methodNode.desc); this.isStatic = Modifier.isStatic(methodNode.access); this.desc = methodNode.desc; } diff --git a/shadowapi/Android.bp b/shadowapi/Android.bp index ad3841e2f..a46f8ca03 100644 --- a/shadowapi/Android.bp +++ b/shadowapi/Android.bp @@ -15,6 +15,11 @@ java_library_host { name: "Robolectric_shadowapi", libs: ["jsr305"], srcs: ["src/main/java/**/*.java"], + openjdk9: { + javacflags: [ + "--add-opens=java.base/java.lang=ALL-UNNAMED", + ], + }, } //############################################# diff --git a/shadowapi/AndroidTest.xml b/shadowapi/AndroidTest.xml index 16368dd43..c0ef7cad5 100644 --- a/shadowapi/AndroidTest.xml +++ b/shadowapi/AndroidTest.xml @@ -18,6 +18,8 @@ <option name="jar" value="Robolectric_shadowapi_tests.jar" /> <option name="java-flags" value="-Drobolectric.offline=true" /> <option name="java-flags" value="-Drobolectric.resourcesMode=binary" /> + <option name="java-flags" value="--add-opens=java.base/java.lang=ALL-UNNAMED"/> + <option name="java-flags" value="--add-opens=java.base/java.lang.reflect=ALL-UNNAMED"/> <option name="exclude-paths" value="java/" /> </test> </configuration> diff --git a/shadowapi/src/main/java/org/robolectric/shadow/api/Shadow.java b/shadowapi/src/main/java/org/robolectric/shadow/api/Shadow.java index 0ae7c85a1..0d54e52d3 100644 --- a/shadowapi/src/main/java/org/robolectric/shadow/api/Shadow.java +++ b/shadowapi/src/main/java/org/robolectric/shadow/api/Shadow.java @@ -5,7 +5,7 @@ import org.robolectric.util.ReflectionHelpers.ClassParameter; public class Shadow { @SuppressWarnings("unused") - private final static IShadow SHADOW_IMPL; + private static IShadow SHADOW_IMPL; static { try { diff --git a/shadowapi/src/main/java/org/robolectric/util/ReflectionHelpers.java b/shadowapi/src/main/java/org/robolectric/util/ReflectionHelpers.java index 8fdfdcb37..1c2e52dab 100644 --- a/shadowapi/src/main/java/org/robolectric/util/ReflectionHelpers.java +++ b/shadowapi/src/main/java/org/robolectric/util/ReflectionHelpers.java @@ -411,18 +411,37 @@ public class ReflectionHelpers { private static void makeFieldVeryAccessible(Field field) { field.setAccessible(true); - - try { - Field modifiersField = Field.class.getDeclaredField("modifiers"); + // remove 'final' modifier if present + if ((field.getModifiers() & Modifier.FINAL) == Modifier.FINAL) { + Field modifiersField = getModifiersField(); modifiersField.setAccessible(true); try { modifiersField.setInt(field, field.getModifiers() & ~Modifier.FINAL); } catch (IllegalAccessException e) { - throw new RuntimeException(e); + + throw new AssertionError(e); } + } + } + + private static Field getModifiersField() { + try { + return Field.class.getDeclaredField("modifiers"); } catch (NoSuchFieldException e) { - // ignore missing fields + try { + Method getFieldsMethod = Class.class.getDeclaredMethod("getDeclaredFields0", boolean.class); + getFieldsMethod.setAccessible(true); + Field[] fields = (Field[]) getFieldsMethod.invoke(Field.class, false); + for (Field modifiersField : fields) { + if ("modifiers".equals(modifiersField.getName())) { + return modifiersField; + } + } + } catch (ReflectiveOperationException innerE) { + throw new AssertionError(innerE); + } } + throw new AssertionError(); } public static Object defaultValueForType(String returnType) { diff --git a/shadowapi/src/test/java/org/robolectric/util/ReflectionHelpersTest.java b/shadowapi/src/test/java/org/robolectric/util/ReflectionHelpersTest.java index 40c007f5b..96306d43d 100644 --- a/shadowapi/src/test/java/org/robolectric/util/ReflectionHelpersTest.java +++ b/shadowapi/src/test/java/org/robolectric/util/ReflectionHelpersTest.java @@ -84,6 +84,19 @@ public class ReflectionHelpersTest { } @Test + public void getFinalStaticFieldReflectively_withField_getsStaticField() throws Exception { + Field field = ExampleBase.class.getDeclaredField("BASE"); + + int result = ReflectionHelpers.getStaticField(field); + assertThat(result).isEqualTo(8); + } + + @Test + public void getFinalStaticFieldReflectively_withFieldName_getsStaticField() throws Exception { + assertThat((int) ReflectionHelpers.getStaticField(ExampleBase.class, "BASE")).isEqualTo(8); + } + + @Test public void setStaticFieldReflectively_withField_setsStaticFields() throws Exception { Field field = ExampleDescendant.class.getDeclaredField("DESCENDANT"); int startingValue = ReflectionHelpers.getStaticField(field); @@ -109,6 +122,20 @@ public class ReflectionHelpersTest { } @Test + public void setFinalStaticFieldReflectively_withFieldName_setsStaticFields() { + int startingValue = ReflectionHelpers.getStaticField(ExampleWithFinalStatic.class, "FIELD"); + + ReflectionHelpers.setStaticField(ExampleWithFinalStatic.class, "FIELD", 101); + assertWithMessage("startingValue").that(startingValue).isEqualTo(100); + assertWithMessage("BASE") + .that((int) ReflectionHelpers.getStaticField(ExampleWithFinalStatic.class, "FIELD")) + .isEqualTo(101); + + // Reset the value to avoid test pollution + ReflectionHelpers.setStaticField(ExampleWithFinalStatic.class, "FIELD", startingValue); + } + + @Test public void callInstanceMethodReflectively_callsPrivateMethods() { ExampleDescendant example = new ExampleDescendant(); assertThat((int) ReflectionHelpers.callInstanceMethod(example, "returnNumber")).isEqualTo(1337); @@ -339,6 +366,11 @@ public class ReflectionHelpersTest { } } + @SuppressWarnings("unused") + private static class ExampleWithFinalStatic { + private static final int FIELD = 100; + } + private static class ThrowsError { @SuppressWarnings("unused") public ThrowsError() { diff --git a/shadows/framework/Android.bp b/shadows/framework/Android.bp index 28a7108ba..2eddf8b28 100644 --- a/shadows/framework/Android.bp +++ b/shadows/framework/Android.bp @@ -32,4 +32,11 @@ java_library_host { javacflags: ["-Aorg.robolectric.annotation.processing.shadowPackage=org.robolectric"], srcs: ["src/main/java/**/*.java"], java_resource_dirs: ["src/main/resources"], + errorprone: { + javacflags: [ + "-Xep:XorPower:WARN", + "-Xep:EmptyTopLevelDeclaration:WARN", + "-Xep:ReturnValueIgnored:WARN", + ], + }, } diff --git a/shadows/framework/src/main/java/org/robolectric/android/controller/ActivityController.java b/shadows/framework/src/main/java/org/robolectric/android/controller/ActivityController.java index 3f173dcb8..b41ca9cee 100644 --- a/shadows/framework/src/main/java/org/robolectric/android/controller/ActivityController.java +++ b/shadows/framework/src/main/java/org/robolectric/android/controller/ActivityController.java @@ -16,6 +16,7 @@ import android.content.Intent; import android.content.pm.ActivityInfo; import android.content.pm.PackageManager; import android.content.res.Configuration; +import android.os.Build; import android.os.Bundle; import android.view.ViewRootImpl; import org.robolectric.RuntimeEnvironment; @@ -160,9 +161,14 @@ public class ActivityController<T extends Activity> extends ComponentController< callDispatchResized(root); } - ReflectionHelpers.callInstanceMethod(root, "windowFocusChanged", + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { + ReflectionHelpers.callInstanceMethod(root, "windowFocusChanged", + from(boolean.class, hasFocus)); + } else { + ReflectionHelpers.callInstanceMethod(root, "windowFocusChanged", from(boolean.class, hasFocus), /* hasFocus */ from(boolean.class, false) /* inTouchMode */); + } return this; } diff --git a/shadows/framework/src/main/java/org/robolectric/shadows/ImageUtil.java b/shadows/framework/src/main/java/org/robolectric/shadows/ImageUtil.java index 534a0eebc..83ed5a7b9 100644 --- a/shadows/framework/src/main/java/org/robolectric/shadows/ImageUtil.java +++ b/shadows/framework/src/main/java/org/robolectric/shadows/ImageUtil.java @@ -21,7 +21,25 @@ import javax.imageio.stream.ImageOutputStream; public class ImageUtil { private static boolean initialized; + public static class ImageInfo { + + public final int width; + public final int height; + public final String mimeType; + + ImageInfo(int width, int height, String mimeType) { + this.width = width; + this.height = height; + this.mimeType = mimeType; + } + } + public static Point getImageSizeFromStream(InputStream is) { + ImageInfo info = getImageInfoFromStream(is); + return new Point(info.width, info.height); + } + + public static ImageInfo getImageInfoFromStream(InputStream is) { if (!initialized) { // Stops ImageIO from creating temp files when reading images // from input stream. @@ -37,7 +55,7 @@ public class ImageUtil { ImageReader reader = readers.next(); try { reader.setInput(imageStream); - return new Point(reader.getWidth(0), reader.getHeight(0)); + return new ImageInfo(reader.getWidth(0), reader.getHeight(0), "image/" + reader.getFormatName()); } finally { reader.dispose(); } diff --git a/shadows/framework/src/main/java/org/robolectric/shadows/ShadowAlarmManager.java b/shadows/framework/src/main/java/org/robolectric/shadows/ShadowAlarmManager.java index bea306d2e..0ec2bc838 100644 --- a/shadows/framework/src/main/java/org/robolectric/shadows/ShadowAlarmManager.java +++ b/shadows/framework/src/main/java/org/robolectric/shadows/ShadowAlarmManager.java @@ -5,6 +5,7 @@ import static android.os.Build.VERSION_CODES.KITKAT; import static android.os.Build.VERSION_CODES.LOLLIPOP; import static android.os.Build.VERSION_CODES.M; import static android.os.Build.VERSION_CODES.N; +import static android.os.Build.VERSION_CODES.S; import android.annotation.TargetApi; import android.app.AlarmManager; @@ -29,6 +30,7 @@ public class ShadowAlarmManager { private static final TimeZone DEFAULT_TIMEZONE = TimeZone.getDefault(); + private static boolean canScheduleExactAlarms; private final List<ScheduledAlarm> scheduledAlarms = new ArrayList<>(); @RealObject private AlarmManager realObject; @@ -36,6 +38,7 @@ public class ShadowAlarmManager { @Resetter public static void reset() { TimeZone.setDefault(DEFAULT_TIMEZONE); + canScheduleExactAlarms = false; } @Implementation @@ -196,6 +199,20 @@ public class ShadowAlarmManager { } } + /** Returns the schedule exact alarm state set by {@link #setCanScheduleExactAlarms}. */ + @Implementation(minSdk = S) + protected boolean canScheduleExactAlarms() { + return canScheduleExactAlarms; + } + + /** + * Sets the schedule exact alarm state reported by {@link AlarmManager#canScheduleExactAlarms}, + * but has no effect otherwise. + */ + public static void setCanScheduleExactAlarms(boolean scheduleExactAlarms) { + canScheduleExactAlarms = scheduleExactAlarms; + } + /** * Container object to hold a PendingIntent and parameters describing when to send it. */ diff --git a/shadows/framework/src/main/java/org/robolectric/shadows/ShadowDateIntervalFormat.java b/shadows/framework/src/main/java/org/robolectric/shadows/ShadowDateIntervalFormat.java index abbed64fb..83d5235c0 100644 --- a/shadows/framework/src/main/java/org/robolectric/shadows/ShadowDateIntervalFormat.java +++ b/shadows/framework/src/main/java/org/robolectric/shadows/ShadowDateIntervalFormat.java @@ -2,16 +2,17 @@ package org.robolectric.shadows; import static android.os.Build.VERSION_CODES.KITKAT; import static android.os.Build.VERSION_CODES.LOLLIPOP_MR1; +import static android.os.Build.VERSION_CODES.TIRAMISU; import java.text.FieldPosition; import java.util.HashMap; import java.util.Locale; import java.util.Map; -import libcore.icu.DateIntervalFormat; import org.robolectric.annotation.Implementation; import org.robolectric.annotation.Implements; -@Implements(value = DateIntervalFormat.class, isInAndroidSdk = false, minSdk = KITKAT) +@Implements(className = "libcore.icu.DateIntervalFormat", isInAndroidSdk = false, minSdk = KITKAT, + maxSdk = TIRAMISU) public class ShadowDateIntervalFormat { private static long address; diff --git a/shadows/framework/src/main/java/org/robolectric/shadows/ShadowDateIntervalFormatU.java b/shadows/framework/src/main/java/org/robolectric/shadows/ShadowDateIntervalFormatU.java new file mode 100644 index 000000000..ac435c46f --- /dev/null +++ b/shadows/framework/src/main/java/org/robolectric/shadows/ShadowDateIntervalFormatU.java @@ -0,0 +1,42 @@ +package org.robolectric.shadows; + +import static android.os.Build.VERSION_CODES.LOLLIPOP_MR1; +import static android.os.Build.VERSION_CODES.UPSIDE_DOWN_CAKE; + +import java.text.FieldPosition; +import java.util.HashMap; +import java.util.Locale; +import java.util.Map; +import android.text.format.DateIntervalFormat; +import org.robolectric.annotation.Implementation; +import org.robolectric.annotation.Implements; + +@Implements(value = DateIntervalFormat.class, isInAndroidSdk = false, minSdk = UPSIDE_DOWN_CAKE) +public class ShadowDateIntervalFormatU { + + private static long address; + private static Map<Long, com.ibm.icu.text.DateIntervalFormat> INTERVAL_CACHE = new HashMap<>(); + + @Implementation + public static long createDateIntervalFormat(String skeleton, String localeName, String tzName) { + address++; + INTERVAL_CACHE.put(address, com.ibm.icu.text.DateIntervalFormat.getInstance(skeleton, new Locale(localeName))); + return address; + } + + @Implementation + public static void destroyDateIntervalFormat(long address) { + INTERVAL_CACHE.remove(address); + } + + @Implementation + @SuppressWarnings("JdkObsolete") + public static String formatDateInterval(long address, long fromDate, long toDate) { + StringBuffer buffer = new StringBuffer(); + + FieldPosition pos = new FieldPosition(0); + INTERVAL_CACHE.get(address).format(new com.ibm.icu.util.DateInterval(fromDate, toDate), buffer, pos); + + return buffer.toString(); + } +} diff --git a/shadows/framework/src/main/java/org/robolectric/shadows/ShadowImageDecoder.java b/shadows/framework/src/main/java/org/robolectric/shadows/ShadowImageDecoder.java index fbe48ac8b..b7efad575 100644 --- a/shadows/framework/src/main/java/org/robolectric/shadows/ShadowImageDecoder.java +++ b/shadows/framework/src/main/java/org/robolectric/shadows/ShadowImageDecoder.java @@ -39,12 +39,14 @@ public class ShadowImageDecoder { private final int height; private final boolean animated = false; private final boolean ninePatch; + private final String mimeType; ImgStream() { InputStream inputStream = getInputStream(); - final Point size = ImageUtil.getImageSizeFromStream(inputStream); - this.width = size == null ? 10 : size.x; - this.height = size == null ? 10 : size.y; + final ImageUtil.ImageInfo info = ImageUtil.getImageInfoFromStream(inputStream); + this.width = info == null ? 10 : info.width; + this.height = info == null ? 10 : info.height; + this.mimeType = info == null ? "" : info.mimeType; if (inputStream instanceof AssetManager.AssetInputStream) { ShadowAssetInputStream sis = Shadow.extract(inputStream); this.ninePatch = sis.isNinePatch(); @@ -70,6 +72,10 @@ public class ShadowImageDecoder { boolean isNinePatch() { return ninePatch; } + + String getMimeType() { + return mimeType; + } } private static final class CppImageDecoder { @@ -80,6 +86,9 @@ public class ShadowImageDecoder { this.imgStream = imgStream; } + public String getMimeType() { + return imgStream.getMimeType(); + } } private static final NativeObjRegistry<CppImageDecoder> NATIVE_IMAGE_DECODER_REGISTRY = @@ -238,8 +247,7 @@ public class ShadowImageDecoder { static String ImageDecoder_nGetMimeType(long nativePtr) { CppImageDecoder decoder = NATIVE_IMAGE_DECODER_REGISTRY.getNativeObject(nativePtr); - // return encodedFormatToString(decoder.mCodec.getEncodedFormat()); - throw new UnsupportedOperationException(); + return decoder.getMimeType(); } static ColorSpace ImageDecoder_nGetColorSpace(long nativePtr) { @@ -247,7 +255,7 @@ public class ShadowImageDecoder { // auto colorType = codec.computeOutputColorType(codec.getInfo().colorType()); // sk_sp<SkColorSpace> colorSpace = codec.computeOutputColorSpace(colorType); // return GraphicsJNI.getColorSpace(colorSpace, colorType); - throw new UnsupportedOperationException(); + return null; } diff --git a/shadows/framework/src/main/java/org/robolectric/shadows/ShadowInputManager.java b/shadows/framework/src/main/java/org/robolectric/shadows/ShadowInputManager.java index 4c8ebf509..867c72033 100644 --- a/shadows/framework/src/main/java/org/robolectric/shadows/ShadowInputManager.java +++ b/shadows/framework/src/main/java/org/robolectric/shadows/ShadowInputManager.java @@ -3,6 +3,7 @@ package org.robolectric.shadows; import static android.os.Build.VERSION_CODES.KITKAT; import android.hardware.input.InputManager; +import android.hardware.input.InputManagerGlobal; import android.view.InputEvent; import org.robolectric.annotation.Implementation; import org.robolectric.annotation.Implements; @@ -25,6 +26,7 @@ public class ShadowInputManager { @Resetter public static void reset() { - ReflectionHelpers.setStaticField(InputManager.class, "sInstance", null); + ReflectionHelpers.setStaticField(InputManagerGlobal.class, + "sInstance", null); } } diff --git a/shadows/framework/src/main/java/org/robolectric/shadows/ShadowSQLiteConnection.java b/shadows/framework/src/main/java/org/robolectric/shadows/ShadowSQLiteConnection.java index d4b8a1db7..ce65e8ef6 100644 --- a/shadows/framework/src/main/java/org/robolectric/shadows/ShadowSQLiteConnection.java +++ b/shadows/framework/src/main/java/org/robolectric/shadows/ShadowSQLiteConnection.java @@ -1,5 +1,6 @@ package org.robolectric.shadows; +import static android.os.Build.VERSION_CODES.CUR_DEVELOPMENT; import static android.os.Build.VERSION_CODES.KITKAT_WATCH; import static android.os.Build.VERSION_CODES.LOLLIPOP; import static android.os.Build.VERSION_CODES.O; @@ -158,11 +159,16 @@ public class ShadowSQLiteConnection { nativeExecute((long) connectionPtr, (long) statementPtr); } - @Implementation(minSdk = LOLLIPOP) + @Implementation(minSdk = LOLLIPOP, maxSdk = 32) public static void nativeExecute(final long connectionPtr, final long statementPtr) { CONNECTIONS.executeStatement(connectionPtr, statementPtr); } + @Implementation(minSdk = CUR_DEVELOPMENT) + public static void nativeExecute(final long connectionPtr, final long statementPtr, boolean isPragmaStmt){ + CONNECTIONS.executeStatement(connectionPtr, statementPtr); + } + @Implementation(maxSdk = KITKAT_WATCH) public static String nativeExecuteForString(int connectionPtr, int statementPtr) { return nativeExecuteForString((long) connectionPtr, (long) statementPtr); @@ -668,7 +674,7 @@ static class Connections { @Override public Long call() throws Exception { statement.stepThrough(); - return connection.getLastInsertId(); + return connection.getChanges() > 0 ? connection.getLastInsertId() : -1L; } }); } diff --git a/shadows/framework/src/main/java/org/robolectric/shadows/ShadowSensor.java b/shadows/framework/src/main/java/org/robolectric/shadows/ShadowSensor.java index c37ca3a3d..00fea9483 100644 --- a/shadows/framework/src/main/java/org/robolectric/shadows/ShadowSensor.java +++ b/shadows/framework/src/main/java/org/robolectric/shadows/ShadowSensor.java @@ -1,27 +1,51 @@ package org.robolectric.shadows; - import android.hardware.Sensor; import android.os.Build.VERSION_CODES; import org.robolectric.RuntimeEnvironment; import org.robolectric.annotation.Implements; +import org.robolectric.annotation.RealObject; import org.robolectric.shadow.api.Shadow; import org.robolectric.util.ReflectionHelpers; -import org.robolectric.util.ReflectionHelpers.ClassParameter; @Implements(Sensor.class) public class ShadowSensor { - /** - * Constructs a {@link Sensor} with a given type. - */ + @RealObject private Sensor realSensor; + + /** Constructs a {@link Sensor} with a given type. */ public static Sensor newInstance(int type) { Sensor sensor = Shadow.newInstanceOf(Sensor.class); if (RuntimeEnvironment.getApiLevel() >= VERSION_CODES.M) { - Shadow.directlyOn(sensor, Sensor.class, "setType", ClassParameter.from(int.class, type)); + Shadow.directlyOn(sensor, Sensor.class, "setType", ReflectionHelpers.ClassParameter.from(int.class, type)); } else { ReflectionHelpers.setField(Sensor.class, sensor, "mType", type); } return sensor; } + + /** Controls the return value of {@link Sensor#isWakeUpSensor()}. */ + public void setWakeUpFlag(boolean wakeup) { + int wakeUpSensorFlag = getWakeUpSensorFlag(); + + if(wakeup) { + setMask(wakeUpSensorFlag); + } else { + clearMask(wakeUpSensorFlag); + } + } + + private void setMask(int mask) { + int value = ReflectionHelpers.getField(realSensor, "mFlags"); + ReflectionHelpers.setField(realSensor, "mFlags", (value | mask)); + } + + private void clearMask(int mask) { + int value = ReflectionHelpers.getField(realSensor, "mFlags"); + ReflectionHelpers.setField(realSensor, "mFlags", (value & ~mask)); + } + + private int getWakeUpSensorFlag() { + return ReflectionHelpers.getStaticField(Sensor.class, "SENSOR_FLAG_WAKE_UP_SENSOR"); + } } diff --git a/shadows/framework/src/main/java/org/robolectric/shadows/ShadowSensorManager.java b/shadows/framework/src/main/java/org/robolectric/shadows/ShadowSensorManager.java index 68a80cd43..05a4b9574 100644 --- a/shadows/framework/src/main/java/org/robolectric/shadows/ShadowSensorManager.java +++ b/shadows/framework/src/main/java/org/robolectric/shadows/ShadowSensorManager.java @@ -53,6 +53,12 @@ public class ShadowSensorManager { return sensorMap.get(type); } + @Implementation + protected Sensor getDefaultSensor(int type, boolean wakeUp) { + Sensor typedSensor = sensorMap.get(type); + return (typedSensor != null && typedSensor.isWakeUpSensor() == wakeUp) ? typedSensor : null; + } + /** @param handler is ignored. */ @Implementation protected boolean registerListener( diff --git a/shadows/framework/src/main/java/org/robolectric/shadows/ShadowServiceManager.java b/shadows/framework/src/main/java/org/robolectric/shadows/ShadowServiceManager.java index 3c4afdab6..facd5a5b6 100644 --- a/shadows/framework/src/main/java/org/robolectric/shadows/ShadowServiceManager.java +++ b/shadows/framework/src/main/java/org/robolectric/shadows/ShadowServiceManager.java @@ -55,6 +55,7 @@ import android.os.ServiceManager; import android.os.storage.IStorageManager; import android.permission.IPermissionManager; import android.service.persistentdata.IPersistentDataBlockService; +import android.view.IWindowManager; import com.android.internal.app.IAppOpsService; import com.android.internal.app.IBatteryStats; @@ -133,6 +134,9 @@ public class ShadowServiceManager { map.put( Context.APPWIDGET_SERVICE, createBinder(IAppWidgetService.class, "com.android.internal.appwidget.IAppWidgetService")); + map.put( + Context.WINDOW_SERVICE, + createBinder(IWindowManager.class, "android.view.IWindowManager")); if (RuntimeEnvironment.getApiLevel() >= JELLY_BEAN_MR1) { map.put(Context.USER_SERVICE, createBinder(IUserManager.class, "android.os.IUserManager")); diff --git a/shadows/framework/src/main/java/org/robolectric/shadows/ShadowSettings.java b/shadows/framework/src/main/java/org/robolectric/shadows/ShadowSettings.java index b925f4317..3de5456a9 100644 --- a/shadows/framework/src/main/java/org/robolectric/shadows/ShadowSettings.java +++ b/shadows/framework/src/main/java/org/robolectric/shadows/ShadowSettings.java @@ -6,6 +6,7 @@ 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 android.app.ActivityThread; import android.content.ContentResolver; import android.content.Context; import android.os.Build; @@ -265,11 +266,21 @@ public class ShadowSettings { return true; } + @Implementation(minSdk = Build.VERSION_CODES.UPSIDE_DOWN_CAKE) + protected static boolean putString(String name, String value) { + return putString(ActivityThread.currentApplication().getContentResolver(), name, value); + } + @Implementation protected static String getString(ContentResolver cr, String name) { return get(cr).get(name); } + @Implementation(minSdk = Build.VERSION_CODES.UPSIDE_DOWN_CAKE) + protected static String getString(String name) { + return getString(ActivityThread.currentApplication().getContentResolver(), name); + } + // BEGIN-INTERNAL @Implementation(minSdk = Build.VERSION_CODES.R) protected static Map<String, String> getStrings(ContentResolver cr, String prefix, @@ -288,6 +299,12 @@ public class ShadowSettings { } return arrayMap; } + + @Implementation(minSdk = Build.VERSION_CODES.UPSIDE_DOWN_CAKE) + protected static Map<String, String> getStrings(String prefix, + List<String> names) { + return getStrings(ActivityThread.currentApplication().getContentResolver(), prefix, names); + } // END-INTERNAL private static Map<String, String> get(ContentResolver cr) { diff --git a/shadows/framework/src/main/java/org/robolectric/shadows/ShadowUsbManager.java b/shadows/framework/src/main/java/org/robolectric/shadows/ShadowUsbManager.java index d5dd186b4..52a83e9f7 100644 --- a/shadows/framework/src/main/java/org/robolectric/shadows/ShadowUsbManager.java +++ b/shadows/framework/src/main/java/org/robolectric/shadows/ShadowUsbManager.java @@ -4,6 +4,7 @@ import static android.os.Build.VERSION_CODES.M; import static android.os.Build.VERSION_CODES.N; import static android.os.Build.VERSION_CODES.N_MR1; import static android.os.Build.VERSION_CODES.P; +import static android.os.Build.VERSION_CODES.TIRAMISU; import static org.robolectric.RuntimeEnvironment.application; import static org.robolectric.util.ReflectionHelpers.ClassParameter.from; import static org.robolectric.util.ReflectionHelpers.callConstructor; @@ -261,6 +262,15 @@ public class ShadowUsbManager { from(UsbManager.class, usbManager), from(String.class, id), from(int.class, supportedModes)); + } else if (RuntimeEnvironment.getApiLevel() <= TIRAMISU) { + return new UsbPort( + usbManager, + id, + supportedModes, + 0, + false, + false + ); } // BEGIN-INTERNAL return new UsbPort( @@ -269,7 +279,9 @@ public class ShadowUsbManager { supportedModes, 0, false, - false + false, + false, + 0 ); // END-INTERNAL } diff --git a/shadows/framework/src/main/java/org/robolectric/shadows/ShadowUserManager.java b/shadows/framework/src/main/java/org/robolectric/shadows/ShadowUserManager.java index e1625d159..f4598aaa3 100644 --- a/shadows/framework/src/main/java/org/robolectric/shadows/ShadowUserManager.java +++ b/shadows/framework/src/main/java/org/robolectric/shadows/ShadowUserManager.java @@ -8,6 +8,7 @@ import static android.os.Build.VERSION_CODES.N; import static android.os.Build.VERSION_CODES.N_MR1; import static android.os.Build.VERSION_CODES.R; import static android.os.Build.VERSION_CODES.TIRAMISU; +import static android.os.Build.VERSION_CODES.UPSIDE_DOWN_CAKE; import static org.robolectric.shadow.api.Shadow.directlyOn; @@ -17,6 +18,7 @@ import android.annotation.UserIdInt; import android.content.Context; import android.content.pm.PackageManager; import android.content.pm.UserInfo; +import android.content.pm.UserProperties; import android.os.Bundle; import android.os.IUserManager; import android.os.Process; @@ -64,6 +66,10 @@ public class ShadowUserManager { private boolean userUnlocked = true; private boolean managedProfile = false; private boolean isSystemUser = true; + private static boolean isHeadlessSystemUserMode = false; + private static boolean isMultipleAdminEnabled = false; + + private Map<Integer, Bundle> userRestrictions = new HashMap<>(); private BiMap<UserHandle, Long> userProfiles = HashBiMap.create(); private Map<String, Bundle> applicationRestrictions = new HashMap<>(); @@ -72,6 +78,7 @@ public class ShadowUserManager { private Map<Integer, UserInfo> userInfoMap = new HashMap<>(); private Map<Integer, List<UserInfo>> profiles = new HashMap<>(); private Map<Integer, Integer> profileToParent = new HashMap<>(); + private Map<Integer, UserProperties> mUserPropertiesMap = new HashMap<>(); private Context context; private boolean enforcePermissions; @@ -341,6 +348,24 @@ public class ShadowUserManager { return context.getPackageManager().checkPermission(permission.MANAGE_USERS, context.getPackageName()) == PackageManager.PERMISSION_GRANTED; } + public static void setIsMultipleAdminEnabled(boolean enableMultipleAdmin) { + isMultipleAdminEnabled = enableMultipleAdmin; + } + + @Implementation(minSdk = UPSIDE_DOWN_CAKE) + protected static boolean isMultipleAdminEnabled() { + return isMultipleAdminEnabled; + } + + public static void setIsHeadlessSystemUserMode(boolean isHSUM) { + isHeadlessSystemUserMode = isHSUM; + } + + @Implementation + protected static boolean isHeadlessSystemUserMode() { + return isHeadlessSystemUserMode; + } + private void checkPermissions() { // TODO Ensure permisions // throw new SecurityException("You need INTERACT_ACROSS_USERS or MANAGE_USERS @@ -636,5 +661,18 @@ public class ShadowUserManager { userPidMap.clear(); userPidMap.put(UserHandle.USER_SYSTEM, Process.myUid()); } + isMultipleAdminEnabled = false; + } + + public void setupUserProperty(int userId, int showInSettings) { + UserProperties userProperties = new UserProperties(new UserProperties.Builder() + .setShowInSettings(showInSettings).build()); + mUserPropertiesMap.putIfAbsent(userId, userProperties); + } + + @Implementation(minSdk = UPSIDE_DOWN_CAKE) + protected UserProperties getUserProperties(UserHandle user) { + return mUserPropertiesMap.getOrDefault(user.getIdentifier(), + new UserProperties(new UserProperties.Builder().build())); } } diff --git a/shadows/framework/src/main/java/org/robolectric/shadows/ShadowViewRootImpl.java b/shadows/framework/src/main/java/org/robolectric/shadows/ShadowViewRootImpl.java index ad17119d4..5e6719c24 100644 --- a/shadows/framework/src/main/java/org/robolectric/shadows/ShadowViewRootImpl.java +++ b/shadows/framework/src/main/java/org/robolectric/shadows/ShadowViewRootImpl.java @@ -163,7 +163,7 @@ public class ShadowViewRootImpl { ClassParameter.from(boolean.class, false), ClassParameter.from(int.class, 0)); // END-INTERNAL - } else if (apiLevel >= Build.VERSION_CODES.TIRAMISU) { + } else if (apiLevel <= Build.VERSION_CODES.TIRAMISU) { // BEGIN-INTERNAL ReflectionHelpers.callInstanceMethod(ViewRootImpl.class, component, "dispatchResized", ClassParameter.from(ClientWindowFrames.class, clientWindowFrame), @@ -176,6 +176,19 @@ public class ShadowViewRootImpl { ClassParameter.from(int.class, 0), ClassParameter.from(int.class, 0)); // END-INTERNAL + } else if (apiLevel >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) { + // BEGIN-INTERNAL + ReflectionHelpers.callInstanceMethod(ViewRootImpl.class, component, "dispatchResized", + ClassParameter.from(ClientWindowFrames.class, clientWindowFrame), + ClassParameter.from(boolean.class, true), + ClassParameter.from(MergedConfiguration.class, new MergedConfiguration()), + ClassParameter.from(InsetsState.class, new InsetsState()), + ClassParameter.from(boolean.class, false), + ClassParameter.from(boolean.class, false), + ClassParameter.from(int.class, 0), + ClassParameter.from(int.class, 0), + ClassParameter.from(boolean.class, false)); + // END-INTERNAL } else { throw new RuntimeException("Could not find AndroidRuntimeAdapter for API level: " + apiLevel); } diff --git a/shadows/framework/src/main/java/org/robolectric/shadows/ShadowWindowManagerGlobal.java b/shadows/framework/src/main/java/org/robolectric/shadows/ShadowWindowManagerGlobal.java index a6aeaf2a9..a23dab947 100644 --- a/shadows/framework/src/main/java/org/robolectric/shadows/ShadowWindowManagerGlobal.java +++ b/shadows/framework/src/main/java/org/robolectric/shadows/ShadowWindowManagerGlobal.java @@ -3,7 +3,10 @@ package org.robolectric.shadows; import static android.os.Build.VERSION_CODES.JELLY_BEAN_MR1; import static android.os.Build.VERSION_CODES.JELLY_BEAN_MR2; +import android.content.Context; import android.os.Looper; +import android.os.ServiceManager; +import android.view.IWindowManager; import android.view.WindowManagerGlobal; import org.robolectric.annotation.Implementation; import org.robolectric.annotation.Implements; @@ -32,7 +35,7 @@ public class ShadowWindowManagerGlobal { @Implementation public static Object getWindowManagerService() { - return null; + return IWindowManager.Stub.asInterface(ServiceManager.getService(Context.WINDOW_SERVICE)); } }
\ No newline at end of file diff --git a/shadows/httpclient/Android.bp b/shadows/httpclient/Android.bp index 39d57a8be..d3ca43f56 100644 --- a/shadows/httpclient/Android.bp +++ b/shadows/httpclient/Android.bp @@ -55,14 +55,14 @@ java_test_host { "bouncycastle-unbundled", "hamcrest", "robolectric-httpclient-4.0.3", - "asm-commons-9.2", + "ow2-asm-commons", "robolectric-httpcore-4.0.1", "guava", - "asm-tree-9.2", + "ow2-asm-tree", "junit", "truth-prebuilt", "robolectric-ant-1.8.0", - "asm-9.2", + "ow2-asm", "jsr305", "robolectric-host-android_all", "robolectric-host-org_apache_http_legacy", diff --git a/shadows/supportv4/Android.bp b/shadows/supportv4/Android.bp index 04714e520..c2fd48036 100644 --- a/shadows/supportv4/Android.bp +++ b/shadows/supportv4/Android.bp @@ -53,14 +53,14 @@ java_test_host { "mockito", "bouncycastle-unbundled", "hamcrest", - "asm-commons-9.2", + "ow2-asm-commons", "guava", "objenesis", - "asm-tree-9.2", + "ow2-asm-tree", "junit", "truth-prebuilt", "robolectric-ant-1.8.0", - "asm-9.2", + "ow2-asm", "jsr305", ], libs: ["robolectric-host-android_all"], |