diff options
31 files changed, 355 insertions, 131 deletions
diff --git a/JavaLibrary.bp b/JavaLibrary.bp index 015f3c844f4..dde6d243e75 100644 --- a/JavaLibrary.bp +++ b/JavaLibrary.bp @@ -951,11 +951,19 @@ droidstubs { // libcore/luni/annotations/flagged_api. droidstubs { name: "libart-sdk-stubs-no-javadoc", - srcs: [":core_libart_api_files"], + srcs: [ + ":core_libart_api_files", + // The definition of @SystemApi is needed to generate module-lib stubs. + ":framework-api-annotations", + ], installable: false, sdk_version: "none", system_modules: "none", - args: "--exclude-documentation-from-stubs", + api_levels_sdk_type: "module-lib", + flags: [ + "--exclude-documentation-from-stubs", + "--show-for-stub-purposes-annotation android.annotation.SystemApi\\(client=android.annotation.SystemApi.Client.MODULE_LIBRARIES\\)", + ], } // Separate @FlaggedApi annotations for luni/ to avoid a circular dependency between @@ -1003,7 +1011,7 @@ java_library { aconfig_declarations { name: "libcore_aconfig_flags", package: "com.android.libcore", - container: "system", + container: "com.android.art", srcs: ["libcore.aconfig"], } diff --git a/NativeCode.bp b/NativeCode.bp index defc30698a0..373761f8751 100644 --- a/NativeCode.bp +++ b/NativeCode.bp @@ -20,7 +20,6 @@ cc_defaults { name: "core_native_default_flags", - defaults: ["art_module_source_build_defaults"], host_supported: true, cflags: [ "-Wall", @@ -33,6 +32,9 @@ cc_defaults { darwin: { enabled: false, }, + windows: { + enabled: false, + }, }, min_sdk_version: "S", } @@ -75,6 +77,7 @@ cc_library_shared { static_libs: [ "libziparchive", ], + version_script: "libjavacore.map", } cc_library_shared { diff --git a/expectations/skippedCtsTest.txt b/expectations/skippedCtsTest.txt index 4ee04feb36c..ef24a987e00 100644 --- a/expectations/skippedCtsTest.txt +++ b/expectations/skippedCtsTest.txt @@ -127,5 +127,24 @@ "names": [ "org.apache.harmony.tests.java.util.PriorityQueueTest#test_spliterator_CME" ] + }, + { + "bug": 338503591, + "description": "Test for internal APIs.", + "names": [ + "libcore.java.util.LinkedHashMapTest#test_entryCompatibility_runtime", + "libcore.java.util.zip.ZipFileTest#testZipFileOffsetNeverChangesAfterInit", + "libcore.jdk.internal.misc.SharedSecretsTest#testGetJavaIOFileDescriptorAccess_notNull" + ] + }, + { + "bug": 338503591, + "description": "The test asserts buggy or non-breaking behaviors, but the behavior has been fixed in a new mainline module version.", + "names": [ + "libcore.java.lang.OldClassTest#test_getGenericInterfaces", + "libcore.java.lang.reflect.OldAndroidClassTest#testClassGetMethodsNoDupes", + "libcore.java.util.zip.ZipFileTest#test_FileNotFound", + "org.apache.harmony.tests.java.util.zip.DeflaterTest#test_finalize" + ] } ]
\ No newline at end of file diff --git a/expectations/skippedCtsTest_manual_base.txt b/expectations/skippedCtsTest_manual_base.txt index 9bda1f44787..45bc39c3f03 100644 --- a/expectations/skippedCtsTest_manual_base.txt +++ b/expectations/skippedCtsTest_manual_base.txt @@ -26,5 +26,25 @@ "bug": 316502724, "description": "SharedSecrets class is at different location in newer ART versions", "name": "libcore.sun.misc.SharedSecretsTest#testGetJavaIOFileDescriptorAccess_notNull" + }, + { + "bug": 338503591, + "description": "Test for internal APIs.", + "name": "libcore.java.util.LinkedHashMapTest#test_entryCompatibility_runtime" + }, + { + "bug": 338503591, + "description": "Test for internal APIs.", + "name": "libcore.java.util.zip.ZipFileTest#testZipFileOffsetNeverChangesAfterInit" + }, + { + "bug": 338503591, + "description": "The test asserts buggy or non-breaking behaviors, but the behavior has been fixed in a new mainline module version.", + "name": "org.apache.harmony.tests.java.util.zip.DeflaterTest#test_finalize" + }, + { + "bug": 338503591, + "description": "Test for internal APIs.", + "name": "libcore.jdk.internal.misc.SharedSecretsTest#testGetJavaIOFileDescriptorAccess_notNull" } ] diff --git a/harmony-tests/src/test/java/org/apache/harmony/tests/java/util/FormatterTest.java b/harmony-tests/src/test/java/org/apache/harmony/tests/java/util/FormatterTest.java index f59451d5034..51e4b4cfacc 100644 --- a/harmony-tests/src/test/java/org/apache/harmony/tests/java/util/FormatterTest.java +++ b/harmony-tests/src/test/java/org/apache/harmony/tests/java/util/FormatterTest.java @@ -740,7 +740,7 @@ public class FormatterTest extends TestCase { assertEquals("1234567891110", formatter.toString()); formatter = new Formatter(Locale.JAPAN); - formatter.format("%0$s", "hello"); + formatter.format("%1$s", "hello"); assertEquals("hello", formatter.toString()); try { @@ -797,8 +797,10 @@ public class FormatterTest extends TestCase { try { // 2147483648 is the value of Integer.MAX_VALUE + 1 formatter.format("%2147483648$s", "hello"); - fail("should throw MissingFormatArgumentException"); - } catch (MissingFormatArgumentException e) { + // Starting from V the line above throws IllegalFormatArgumentIndexException, but + // that class is package-private. + fail("should throw IllegalFormatException"); + } catch (IllegalFormatException e) { // expected } @@ -839,11 +841,6 @@ public class FormatterTest extends TestCase { f.format("%1$-1%", "string"); assertEquals("%", f.toString()); - f = new Formatter(Locale.ITALY); - // 2147483648 is the value of Integer.MAX_VALUE + 1 - f.format("%2147483648s", "string"); - assertEquals("string", f.toString()); - // the value of Integer.MAX_VALUE will allocate about 4G bytes of // memory. // It may cause OutOfMemoryError, so this value is not tested @@ -857,11 +854,6 @@ public class FormatterTest extends TestCase { f.format("%.5s", "123456"); assertEquals("12345", f.toString()); - f = new Formatter(Locale.US); - // 2147483648 is the value of Integer.MAX_VALUE + 1 - f.format("%.2147483648s", "..."); - assertEquals("...", f.toString()); - // the value of Integer.MAX_VALUE will allocate about 4G bytes of // memory. // It may cause OutOfMemoryError, so this value is not tested @@ -2659,10 +2651,14 @@ public class FormatterTest extends TestCase { try { f = new Formatter(); + // Here the first 0 is a padding element and the rest is the width. It is expected + // that the value is an int, so the correct behavior is to throw + // IllegalFormatWidthException, but prior to V the exception was + // MissingFormatWidthException. Once V is finalized the catch could be simplified. f.format("%010000000000000000000000000000000001d", new BigInteger( "1")); - fail("should throw MissingFormatWidthException"); - } catch (MissingFormatWidthException e) { + fail("should throw IllegalFormatWidthException or MissingFormatWidthException"); + } catch (IllegalFormatWidthException | MissingFormatWidthException e) { // expected } } diff --git a/libcore.aconfig b/libcore.aconfig index ca9f851df5c..4fb3092a72c 100644 --- a/libcore.aconfig +++ b/libcore.aconfig @@ -16,7 +16,7 @@ # com.android is chosen as the java_aconfig_library is normally exposed to the platform, # but not public apps. package: "com.android.libcore" -container: "system" +container: "com.android.art" flag { namespace: "core_libraries" diff --git a/libjavacore.map b/libjavacore.map new file mode 100644 index 00000000000..27bc1f0e47a --- /dev/null +++ b/libjavacore.map @@ -0,0 +1,23 @@ +# +# Copyright (C) 2024 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +{ + global: + JNI_OnLoad; + JNI_OnUnload; + local: + *; +}; diff --git a/luni/src/main/java/android/compat/Compatibility.java b/luni/src/main/java/android/compat/Compatibility.java index 31c1fe18e24..5340c87ef0d 100644 --- a/luni/src/main/java/android/compat/Compatibility.java +++ b/luni/src/main/java/android/compat/Compatibility.java @@ -114,6 +114,17 @@ public final class Compatibility { } /** + * Return the behavior change delegate + * + * @hide + */ + // VisibleForTesting + @NonNull + public static BehaviorChangeDelegate getBehaviorChangeDelegate() { + return sCallbacks; + } + + /** * For use by tests only. Causes values from {@code overrides} to be returned instead of the * real value. * diff --git a/luni/src/test/java/libcore/android/compat/CompatibilityTest.java b/luni/src/test/java/libcore/android/compat/CompatibilityTest.java index e990dae9407..22be3db7abc 100644 --- a/luni/src/test/java/libcore/android/compat/CompatibilityTest.java +++ b/luni/src/test/java/libcore/android/compat/CompatibilityTest.java @@ -32,6 +32,7 @@ import android.compat.Compatibility.ChangeConfig; import android.compat.Compatibility.BehaviorChangeDelegate; import org.junit.After; +import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; @@ -39,9 +40,16 @@ import org.junit.runners.JUnit4; @RunWith(JUnit4.class) public class CompatibilityTest { + private BehaviorChangeDelegate mDelegate; + + @Before + public void setUp() { + mDelegate = Compatibility.getBehaviorChangeDelegate(); + } + @After public void reset() { - Compatibility.clearBehaviorChangeDelegate(); + Compatibility.setBehaviorChangeDelegate(mDelegate); } @Test diff --git a/luni/src/test/java/libcore/highmemorytest/java/util/CurrencyTest.java b/luni/src/test/java/libcore/highmemorytest/java/util/CurrencyTest.java index e31030205dc..3467ced0583 100644 --- a/luni/src/test/java/libcore/highmemorytest/java/util/CurrencyTest.java +++ b/luni/src/test/java/libcore/highmemorytest/java/util/CurrencyTest.java @@ -45,14 +45,23 @@ public class CurrencyTest { @Test public void test_currencyCodeIcuConsistency() { + String countryCode = locale.getCountry(); // java.util.Currency.getCurrency is time-sensitive. And Croatia doesn't use Euro until // 2023/1/1. https://unicode-org.atlassian.net/browse/CLDR-16061 // We skip the test until Feb 2023. - if ("HR".equals(locale.getCountry()) && + if ("HR".equals(countryCode) && LocalDateTime.of(2023, 2, 1, 0, 0).atZone(ZoneId.of("GMT")).toInstant() .isAfter(Instant.now())) { return; } + // https://unicode-org.atlassian.net/browse/CLDR-17274 + // Effective 2025-03-31, CuraƧao and Sint Maarten are replacing ANG (Netherlands + // Antillean Guilder) with XCG (Caribbean Guilder). + if (("CW".equals(countryCode) || "SX".equals(countryCode)) && + LocalDateTime.of(2025, 5, 1, 0, 0).atZone(ZoneId.of("GMT")).toInstant() + .isAfter(Instant.now())) { + return; + } Currency javaCurrency = getCurrency(locale); if (javaCurrency == null) { return; diff --git a/luni/src/test/java/libcore/java/io/FileTest.java b/luni/src/test/java/libcore/java/io/FileTest.java index e61f572e8e3..e57e38b8563 100644 --- a/luni/src/test/java/libcore/java/io/FileTest.java +++ b/luni/src/test/java/libcore/java/io/FileTest.java @@ -28,18 +28,41 @@ import java.nio.file.InvalidPathException; import java.nio.file.Path; import java.nio.file.Paths; import java.util.UUID; +import java.util.concurrent.atomic.AtomicReference; + import libcore.io.Libcore; +import libcore.junit.util.compat.CoreCompatChangeRule; import static android.system.Os.stat; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; + +import static org.junit.Assume.assumeTrue; + +import dalvik.annotation.compat.VersionCodes; +import dalvik.system.VMRuntime; + import org.junit.Assert; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.TestRule; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; -public class FileTest extends junit.framework.TestCase { +@RunWith(JUnit4.class) +public class FileTest { static { System.loadLibrary("javacoretests"); } + + @Rule + public final TestRule compatChangeRule = new CoreCompatChangeRule(); + private static File createTemporaryDirectory() throws Exception { String base = System.getProperty("java.io.tmpdir"); File directory = new File(base, UUID.randomUUID().toString()); @@ -71,6 +94,7 @@ public class FileTest extends junit.framework.TestCase { // Rather than test all methods, assume that if createTempFile creates a long path and // exists can see it, the code for coping with long paths (shared by all methods) works. + @Test public void test_longPath() throws Exception { File base = createTemporaryDirectory(); assertTrue(createDeepStructure(base).exists()); @@ -83,6 +107,7 @@ public class FileTest extends junit.framework.TestCase { * isn't true on Android if you're using /sdcard (which is used if this test is * run using vogar). It will work in /data/data/ though. */ + @Test public void test_longReadlink() throws Exception { File base = createTemporaryDirectory(); File target = createDeepStructure(base); @@ -98,6 +123,7 @@ public class FileTest extends junit.framework.TestCase { // TODO: File.list is a special case too, but I haven't fixed it yet, and the new code, // like the old code, will die of a native buffer overrun if we exercise it. + @Test public void test_emptyFilename() throws Exception { // The behavior of the empty filename is an odd mixture. File f = new File(""); @@ -149,6 +175,7 @@ public class FileTest extends junit.framework.TestCase { // http://b/2486943 - between eclair and froyo, we added a call to // isAbsolute from the File constructor, potentially breaking subclasses. + @Test public void test_subclassing() throws Exception { class MyFile extends File { private String field; @@ -171,6 +198,7 @@ public class FileTest extends junit.framework.TestCase { * isn't true on Android if you're using /sdcard (which is used if this test is * run using vogar). It will work in /data/data/ though. */ + @Test public void test_getCanonicalPath() throws Exception { File base = createTemporaryDirectory(); File target = new File(base, "target"); @@ -207,6 +235,7 @@ public class FileTest extends junit.framework.TestCase { Libcore.os.symlink(target, linkName); } + @Test public void test_createNewFile() throws Exception { File f = File.createTempFile("FileTest", "tmp"); assertFalse(f.createNewFile()); // EEXIST -> false @@ -223,6 +252,7 @@ public class FileTest extends junit.framework.TestCase { } } + @Test public void test_rename() throws Exception { File f = File.createTempFile("FileTest", "tmp"); assertFalse(f.renameTo(new File(""))); @@ -231,6 +261,7 @@ public class FileTest extends junit.framework.TestCase { assertTrue(f.renameTo(f)); } + @Test public void test_getAbsolutePath() throws Exception { String userDir = System.getProperty("user.dir"); if (!userDir.endsWith(File.separator)) { @@ -241,12 +272,14 @@ public class FileTest extends junit.framework.TestCase { assertEquals(userDir + "poop", f.getAbsolutePath()); } + @Test public void test_getSpace() throws Exception { assertTrue(new File("/").getFreeSpace() >= 0); assertTrue(new File("/").getTotalSpace() >= 0); assertTrue(new File("/").getUsableSpace() >= 0); } + @Test public void test_mkdirs() throws Exception { // Set up a directory to test in. File base = createTemporaryDirectory(); @@ -290,6 +323,7 @@ public class FileTest extends junit.framework.TestCase { // http://b/23523042 : Test that creating a directory and file with // a surrogate pair in the name works as expected and roundtrips correctly. + @Test public void testFilesWithSurrogatePairs() throws Exception { File base = createTemporaryDirectory(); // U+13000 encodes to the surrogate pair D80C + DC00 in UTF16 @@ -319,20 +353,37 @@ public class FileTest extends junit.framework.TestCase { // SECCOMP filter. The SECCOMP filter is designed to not allow stat(fstatat64/newfstatat) calls // and whenever a thread makes the system call, android.system.ErrnoException // (EPERM - Operation not permitted) will be raised. - public void testExistsOnSystem() throws ErrnoException, IOException { + @Test + public void testExistsOnSystem() throws IOException, InterruptedException { File tmpFile = File.createTempFile("testExistsOnSystem", ".tmp"); - try { - assertEquals("SECCOMP filter is not installed.", 0, installSeccompFilter()); + AtomicReference<Throwable> pendingThrowable = new AtomicReference<>(); + // Start a new Thread to install seccomp filter on the thread. + // It avoids the seccomp filter affecting other test cases. + // The alternative is to clean-up the seccomp filter in a teardown / finally {} block, + // but it would be difficult to reset if another seccomp filter was pre-set on the test + // thread. + Thread thread = new Thread(() -> { try { - // Verify that SECCOMP filter obstructs stat. - stat(tmpFile.getAbsolutePath()); - fail(); - } catch (ErrnoException expected) { - assertEquals(OsConstants.EPERM, expected.errno); + assertEquals("SECCOMP filter is not installed.", 0, installSeccompFilter()); + try { + // Verify that SECCOMP filter obstructs stat. + stat(tmpFile.getAbsolutePath()); + fail(); + } catch (ErrnoException expected) { + assertEquals(OsConstants.EPERM, expected.errno); + } + assertTrue(tmpFile.exists()); + } finally { + tmpFile.delete(); } - assertTrue(tmpFile.exists()); - } finally { - tmpFile.delete(); + }); + thread.setUncaughtExceptionHandler((t, throwable) -> pendingThrowable.set(throwable)); + thread.start(); + thread.join(); + + Throwable t = pendingThrowable.get(); + if (t != null) { + throw new AssertionError(t); } } @@ -343,6 +394,7 @@ public class FileTest extends junit.framework.TestCase { // OpenJdk is treating empty parent string as a special case, // it substitutes parent string with the filesystem default parent value "/" // This wasn't the case before the switch to openJdk. + @Test public void testEmptyParentString() { File f1 = new File("", "foo.bar"); File f2 = new File((String)null, "foo.bar"); @@ -351,6 +403,7 @@ public class FileTest extends junit.framework.TestCase { } // http://b/27273930 + @Test public void testJavaIoTmpdirMutable() throws Exception { final String oldTmpDir = System.getProperty("java.io.tmpdir"); final String directoryName = "/newTemp" + Integer.toString(Math.randomIntInternal()); @@ -369,12 +422,14 @@ public class FileTest extends junit.framework.TestCase { private static native void nativeTestFilesWithSurrogatePairs(String base); // http://b/27731686 + @Test public void testBug27731686() { File file = new File("/test1", "/"); assertEquals("/test1", file.getPath()); assertEquals("test1", file.getName()); } + @Test public void testFileNameNormalization() { assertEquals("/", new File("/", "/").getPath()); assertEquals("/", new File("/", "").getPath()); @@ -385,6 +440,7 @@ public class FileTest extends junit.framework.TestCase { assertEquals("/foo/bar", new File("/foo", "/bar//").getPath()); } + @Test public void test_toPath() { File file = new File("testPath"); Path filePath = file.toPath(); @@ -398,6 +454,7 @@ public class FileTest extends junit.framework.TestCase { } // http://b/62301183 + @Test public void test_canonicalCachesAreOff() throws Exception { File tempDir = createTemporaryDirectory(); File f1 = new File(tempDir, "testCannonCachesOff1"); @@ -418,6 +475,7 @@ public class FileTest extends junit.framework.TestCase { assertEquals(symlinkFile.getCanonicalPath(), f2.getCanonicalPath()); } + @Test public void testGetCanonicalPath_duplicatePathSeparator() throws Exception { assertCanonicalPath("/a//./b", "/a/b"); assertCanonicalPath("//a//b", "/a/b"); @@ -434,8 +492,53 @@ public class FileTest extends junit.framework.TestCase { assertEquals(expected, file.getCanonicalFile().getPath()); } + @Test public void testGetCanonicalPath_longPath() { String p = "/a".repeat(2048); Assert.assertThrows(IOException.class, () -> new File(p).getCanonicalFile()); } + + @Test + @CoreCompatChangeRule.EnableCompatChanges({File.CANONICALIZE_PARENT_OF_ROOT_DIR}) + public void testGetCanonicalPath_parentOfRootPath_afterU_whenChecksAreEnabled() + throws IOException { + assertGetCanonicalPath_commonTestCases(); + + var msg = "CANONICALIZE_PARENT_OF_ROOT_DIR change is are done only starting from V. " + + "Current SDK=" + VMRuntime.getSdkVersion(); + assumeTrue(msg, VMRuntime.getSdkVersion() >= VersionCodes.VANILLA_ICE_CREAM); + assertGetCanonicalPath("/non_existing/directory/../../../new_file", "/new_file"); + assertGetCanonicalPath("/non/existing/directory/../../../../new_file", "/new_file"); + } + + @Test + @CoreCompatChangeRule.DisableCompatChanges({File.CANONICALIZE_PARENT_OF_ROOT_DIR}) + public void testGetCanonicalPath_parentOfRootPath_whenChecksAreDisabled() throws IOException { + assertGetCanonicalPath("/non_existing/directory/../../../new_file", "/../new_file"); + assertGetCanonicalPath("/non/existing/directory/../../../../new_file", "/../new_file"); + + assertGetCanonicalPath_commonTestCases(); + } + + private void assertGetCanonicalPath_commonTestCases() throws IOException { + // "/.." is normally resolved by libc's realpath(3) regardless + // CANONICALIZE_PARENT_OF_ROOT_DIR is enabled or not. + assertGetCanonicalPath("/..", "/"); + assertGetCanonicalPath("/../..", "/"); + assertGetCanonicalPath("/../../abc", "/abc"); + assertGetCanonicalPath("/../../abc/..", "/"); + assertGetCanonicalPath("/../../abc/../def", "/def"); + // getCanonicalPath treats relative path as absolute path in most cases. + assertGetCanonicalPath("..", "/"); + assertGetCanonicalPath("../..", "/"); + assertGetCanonicalPath("../../abc", "/abc"); + assertGetCanonicalPath("../../abc/..", "/"); + assertGetCanonicalPath("../../abc/../def", "/def"); + } + + private void assertGetCanonicalPath(String input, String expected) throws IOException { + File file = new File(input); + assertEquals(expected, file.getCanonicalPath()); + } + } diff --git a/luni/src/test/java/libcore/java/lang/OldClassTest.java b/luni/src/test/java/libcore/java/lang/OldClassTest.java index daf32552d35..3a28398d0c0 100644 --- a/luni/src/test/java/libcore/java/lang/OldClassTest.java +++ b/luni/src/test/java/libcore/java/lang/OldClassTest.java @@ -16,6 +16,9 @@ package libcore.java.lang; +import libcore.test.annotation.NonCts; +import libcore.test.reasons.NonCtsReasons; + import java.io.File; import java.io.IOException; import java.io.InputStream; @@ -424,6 +427,7 @@ public class OldClassTest extends junit.framework.TestCase { public enum TestEmptyEnum { } + @NonCts(bug = 338503591, reason = NonCtsReasons.NON_BREAKING_BEHAVIOR_FIX) public void test_getGenericInterfaces() { Type [] types = ExtendTestClass1.class.getGenericInterfaces(); assertEquals(0, types.length); diff --git a/luni/src/test/java/libcore/java/lang/reflect/OldAndroidClassTest.java b/luni/src/test/java/libcore/java/lang/reflect/OldAndroidClassTest.java index 7ec843d3acb..23d1b26aba3 100644 --- a/luni/src/test/java/libcore/java/lang/reflect/OldAndroidClassTest.java +++ b/luni/src/test/java/libcore/java/lang/reflect/OldAndroidClassTest.java @@ -16,6 +16,9 @@ package libcore.java.lang.reflect; +import libcore.test.annotation.NonCts; +import libcore.test.reasons.NonCtsReasons; + import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.lang.reflect.Method; @@ -137,6 +140,7 @@ public final class OldAndroidClassTest extends TestCase { // Regression for 1018067: Class.getMethods() returns the same method over // and over again from all base classes + @NonCts(bug = 338503591, reason = NonCtsReasons.NON_BREAKING_BEHAVIOR_FIX) public void testClassGetMethodsNoDupes() { Method[] methods = ArrayList.class.getMethods(); Set<String> set = new HashSet<String>(); diff --git a/luni/src/test/java/libcore/java/util/FormatterTest.java b/luni/src/test/java/libcore/java/util/FormatterTest.java index 4e4c4d5f741..abca282a564 100644 --- a/luni/src/test/java/libcore/java/util/FormatterTest.java +++ b/luni/src/test/java/libcore/java/util/FormatterTest.java @@ -30,6 +30,7 @@ import static java.nio.charset.StandardCharsets.UTF_8; import dalvik.annotation.compat.VersionCodes; import dalvik.system.VMRuntime; +import libcore.junit.util.compat.CoreCompatChangeRule; import libcore.junit.util.compat.CoreCompatChangeRule.DisableCompatChanges; import libcore.junit.util.compat.CoreCompatChangeRule.EnableCompatChanges; import libcore.test.annotation.NonMts; @@ -37,7 +38,9 @@ import libcore.test.reasons.NonMtsReasons; import org.junit.After; import org.junit.Before; +import org.junit.Rule; import org.junit.Test; +import org.junit.rules.TestRule; import java.io.File; import java.io.FileOutputStream; @@ -60,6 +63,9 @@ import java.util.TimeZone; @SuppressWarnings("FormatString") public class FormatterTest { + @Rule + public final TestRule compatChangeRule = new CoreCompatChangeRule(); + private File aFile; @Before diff --git a/luni/src/test/java/libcore/java/util/RandomTest.java b/luni/src/test/java/libcore/java/util/RandomTest.java index ead9f69b5c6..5b2c3634d9b 100644 --- a/luni/src/test/java/libcore/java/util/RandomTest.java +++ b/luni/src/test/java/libcore/java/util/RandomTest.java @@ -22,9 +22,12 @@ import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; +import libcore.junit.util.compat.CoreCompatChangeRule; import libcore.junit.util.compat.CoreCompatChangeRule.DisableCompatChanges; +import org.junit.Rule; import org.junit.Test; +import org.junit.rules.TestRule; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; @@ -34,6 +37,9 @@ import java.util.Random; @RunWith(JUnit4.class) public class RandomTest { + @Rule + public final TestRule compatChangeRule = new CoreCompatChangeRule(); + @Test public void test_subclassing() throws Exception { // http://b/2502231 diff --git a/luni/src/test/java/libcore/java/util/zip/ZipFileTest.java b/luni/src/test/java/libcore/java/util/zip/ZipFileTest.java index b9e942eca0d..1511471cf24 100644 --- a/luni/src/test/java/libcore/java/util/zip/ZipFileTest.java +++ b/luni/src/test/java/libcore/java/util/zip/ZipFileTest.java @@ -18,6 +18,8 @@ package libcore.java.util.zip; import android.system.OsConstants; import libcore.io.Libcore; +import libcore.test.annotation.NonCts; +import libcore.test.reasons.NonCtsReasons; import java.io.BufferedOutputStream; import java.io.File; @@ -41,6 +43,7 @@ public final class ZipFileTest extends AbstractZipFileTest { } // b/31077136 + @NonCts(bug = 338503591, reason = NonCtsReasons.NON_BREAKING_BEHAVIOR_FIX) public void test_FileNotFound() throws Exception { File nonExistentFile = new File("fileThatDefinitelyDoesntExist.zip"); assertFalse(nonExistentFile.exists()); diff --git a/ojluni/src/main/java/java/awt/font/TEST_MAPPING b/ojluni/src/main/java/java/awt/font/TEST_MAPPING index 8bc46cc8702..76ff4049ee8 100644 --- a/ojluni/src/main/java/java/awt/font/TEST_MAPPING +++ b/ojluni/src/main/java/java/awt/font/TEST_MAPPING @@ -9,12 +9,7 @@ ] }, { - "name": "CtsLibcoreOjTestCases", - "options": [ - { - "include-filter": "test.java.awt.font" - } - ] + "name": "CtsLibcoreOjTestCases_awt_font" } ] -}
\ No newline at end of file +} diff --git a/ojluni/src/main/java/java/io/File.java b/ojluni/src/main/java/java/io/File.java index 00375341184..ba90a3f6095 100644 --- a/ojluni/src/main/java/java/io/File.java +++ b/ojluni/src/main/java/java/io/File.java @@ -26,6 +26,11 @@ package java.io; +import android.compat.annotation.ChangeId; +import android.compat.annotation.EnabledSince; + +import dalvik.annotation.compat.VersionCodes; + import java.net.URI; import java.net.URL; import java.net.MalformedURLException; @@ -611,6 +616,17 @@ public class File return fs.canonicalize(fs.resolve(this)); } + // Android-added: Remove parent directory /.. at the rootfs. http://b/312399441 + /** + * Canonicalize the parent directory of the root directory when app targets SDK level 35 + * (Android 15) or higher. "/.." can be canonicalized into "/" according to POSIX. + * + * @hide + */ + @ChangeId + @EnabledSince(targetSdkVersion = VersionCodes.VANILLA_ICE_CREAM) + public static final long CANONICALIZE_PARENT_OF_ROOT_DIR = 312399441L; + /** * Returns the canonical form of this abstract pathname. Equivalent to * <code>new File(this.{@link #getCanonicalPath})</code>. diff --git a/ojluni/src/main/java/java/io/UnixFileSystem.java b/ojluni/src/main/java/java/io/UnixFileSystem.java index 1bf876b1a20..32840973328 100644 --- a/ojluni/src/main/java/java/io/UnixFileSystem.java +++ b/ojluni/src/main/java/java/io/UnixFileSystem.java @@ -25,10 +25,17 @@ package java.io; +import static java.io.File.CANONICALIZE_PARENT_OF_ROOT_DIR; + +import android.compat.Compatibility; +import android.compat.annotation.ChangeId; +import android.compat.annotation.EnabledSince; import android.system.ErrnoException; import android.system.OsConstants; +import dalvik.annotation.compat.VersionCodes; import dalvik.system.BlockGuard; +import dalvik.system.VMRuntime; import libcore.io.Libcore; @@ -229,7 +236,19 @@ class UnixFileSystem extends FileSystem { return res; } } - private native String canonicalize0(String path) throws IOException; + + // BEGIN Android-changed: Remove parent directory /.. at the rootfs. http://b/312399441 + private String canonicalize0(String path) throws IOException { + boolean isAtLeastTargetSdk35 = + VMRuntime.getSdkVersion() >= VersionCodes.VANILLA_ICE_CREAM && + Compatibility.isChangeEnabled(CANONICALIZE_PARENT_OF_ROOT_DIR); + return canonicalize0(path, isAtLeastTargetSdk35); + } + + private native String canonicalize0(String path, boolean isAtLeastTargetSdk35) + throws IOException; + // END Android-changed: Remove parent directory /.. at the rootfs. http://b/312399441 + // Best-effort attempt to get parent of this path; used for // optimization of filename canonicalization. This must return null for // any cases where the code in canonicalize_md.c would throw an diff --git a/ojluni/src/main/java/java/nio/file/attribute/TEST_MAPPING b/ojluni/src/main/java/java/nio/file/attribute/TEST_MAPPING index b6c90d44536..6c130c4f350 100644 --- a/ojluni/src/main/java/java/nio/file/attribute/TEST_MAPPING +++ b/ojluni/src/main/java/java/nio/file/attribute/TEST_MAPPING @@ -9,12 +9,7 @@ ] }, { - "name": "CtsLibcoreOjTestCases", - "options": [ - { - "include-filter": "test.java.nio.file.attribute" - } - ] + "name": "CtsLibcoreOjTestCases_nio_file_attribute" } ] -}
\ No newline at end of file +} diff --git a/ojluni/src/main/java/java/security/cert/TEST_MAPPING b/ojluni/src/main/java/java/security/cert/TEST_MAPPING index 267b914face..0c96cc48208 100644 --- a/ojluni/src/main/java/java/security/cert/TEST_MAPPING +++ b/ojluni/src/main/java/java/security/cert/TEST_MAPPING @@ -18,12 +18,7 @@ ] }, { - "name": "CtsLibcoreOjTestCases", - "options": [ - { - "include-filter": "test.java.security.cert" - } - ] + "name": "CtsLibcoreOjTestCases_security_cert" } ] -}
\ No newline at end of file +} diff --git a/ojluni/src/main/java/java/time/TEST_MAPPING b/ojluni/src/main/java/java/time/TEST_MAPPING index 54d2ea6bf20..6956e2fd933 100644 --- a/ojluni/src/main/java/java/time/TEST_MAPPING +++ b/ojluni/src/main/java/java/time/TEST_MAPPING @@ -11,18 +11,7 @@ ], "presubmit-large": [ { - "name": "CtsLibcoreOjTestCases", - "options": [ - { - "include-filter": "tck.java.time" - }, - { - "include-filter": "tck.java.time.serial" - }, - { - "include-filter": "test.java.time" - } - ] + "name": "CtsLibcoreOjTestCases_time" } ] } diff --git a/ojluni/src/main/java/java/time/chrono/TEST_MAPPING b/ojluni/src/main/java/java/time/chrono/TEST_MAPPING index 461c15d4d55..0e310657f59 100644 --- a/ojluni/src/main/java/java/time/chrono/TEST_MAPPING +++ b/ojluni/src/main/java/java/time/chrono/TEST_MAPPING @@ -9,18 +9,7 @@ ] }, { - "name": "CtsLibcoreOjTestCases", - "options": [ - { - "include-filter": "tck.java.time.chrono.serial" - }, - { - "include-filter": "tck.java.time.chrono" - }, - { - "include-filter": "test.java.time.chrono" - } - ] + "name": "CtsLibcoreOjTestCases_time_chrono" } ] -}
\ No newline at end of file +} diff --git a/ojluni/src/main/java/java/time/format/TEST_MAPPING b/ojluni/src/main/java/java/time/format/TEST_MAPPING index 42286e44667..9de70982a0f 100644 --- a/ojluni/src/main/java/java/time/format/TEST_MAPPING +++ b/ojluni/src/main/java/java/time/format/TEST_MAPPING @@ -9,15 +9,7 @@ ] }, { - "name": "CtsLibcoreOjTestCases", - "options": [ - { - "include-filter": "tck.java.time.format" - }, - { - "include-filter": "test.java.time.format" - } - ] + "name": "CtsLibcoreOjTestCases_time_format" } ] -}
\ No newline at end of file +} diff --git a/ojluni/src/main/java/java/time/temporal/TEST_MAPPING b/ojluni/src/main/java/java/time/temporal/TEST_MAPPING index deac48a2378..f40206fdefd 100644 --- a/ojluni/src/main/java/java/time/temporal/TEST_MAPPING +++ b/ojluni/src/main/java/java/time/temporal/TEST_MAPPING @@ -9,18 +9,7 @@ ] }, { - "name": "CtsLibcoreOjTestCases", - "options": [ - { - "include-filter": "test.java.time.temporal" - }, - { - "include-filter": "tck.java.time.temporal" - }, - { - "include-filter": "tck.java.time.temporal.serial" - } - ] + "name": "CtsLibcoreOjTestCases_time_temporal" } ] -}
\ No newline at end of file +} diff --git a/ojluni/src/main/java/java/time/zone/TEST_MAPPING b/ojluni/src/main/java/java/time/zone/TEST_MAPPING index 1c8c89e1255..00aefafbf02 100644 --- a/ojluni/src/main/java/java/time/zone/TEST_MAPPING +++ b/ojluni/src/main/java/java/time/zone/TEST_MAPPING @@ -9,18 +9,7 @@ ] }, { - "name": "CtsLibcoreOjTestCases", - "options": [ - { - "include-filter": "tck.java.time.zone" - }, - { - "include-filter": "tck.java.time.zone.serial" - }, - { - "include-filter": "test.java.time.zone" - } - ] + "name": "CtsLibcoreOjTestCases_time_zone" } ] -}
\ No newline at end of file +} diff --git a/ojluni/src/main/native/UnixFileSystem_md.c b/ojluni/src/main/native/UnixFileSystem_md.c index 6f432fddf1d..02dd29794bc 100644 --- a/ojluni/src/main/native/UnixFileSystem_md.c +++ b/ojluni/src/main/native/UnixFileSystem_md.c @@ -41,6 +41,7 @@ #include <errno.h> #include <fcntl.h> #include <dirent.h> +#include <stdbool.h> #include "jni.h" #include "jni_util.h" @@ -99,18 +100,22 @@ Java_java_io_UnixFileSystem_initIDs(JNIEnv *env, jclass cls) // Android-changed: hidden to avoid conflict with libm (b/135018555) __attribute__((visibility("hidden"))) -extern int canonicalize(char *path, const char *out, int len); +extern int canonicalize(char *path, const char *out, int len, + // Android-added: Remove parent directory /.. at the rootfs. http://b/312399441 + bool isAtLeastTargetSdk35); JNIEXPORT jstring JNICALL Java_java_io_UnixFileSystem_canonicalize0(JNIEnv *env, jobject this, - jstring pathname) + jstring pathname, + // Android-added: Remove parent directory /.. at the rootfs. http://b/312399441 + jboolean isAtLeastTargetSdk35) { jstring rv = NULL; WITH_PLATFORM_STRING(env, pathname, path) { char canonicalPath[PATH_MAX]; if (canonicalize((char *)path, - canonicalPath, PATH_MAX) < 0) { + canonicalPath, PATH_MAX, isAtLeastTargetSdk35) < 0) { JNU_ThrowIOExceptionWithLastError(env, "Bad pathname"); } else { #ifdef MACOSX @@ -554,7 +559,7 @@ Java_java_io_UnixFileSystem_getNameMax0(JNIEnv *env, jobject this, static JNINativeMethod gMethods[] = { NATIVE_METHOD(UnixFileSystem, initIDs, "()V"), - NATIVE_METHOD(UnixFileSystem, canonicalize0, "(Ljava/lang/String;)Ljava/lang/String;"), + NATIVE_METHOD(UnixFileSystem, canonicalize0, "(Ljava/lang/String;Z)Ljava/lang/String;"), NATIVE_METHOD(UnixFileSystem, getBooleanAttributes0, "(Ljava/lang/String;)I"), NATIVE_METHOD(UnixFileSystem, getNameMax0, "(Ljava/lang/String;)J"), NATIVE_METHOD(UnixFileSystem, setPermission0, "(Ljava/io/File;IZZ)Z"), diff --git a/ojluni/src/main/native/canonicalize_md.c b/ojluni/src/main/native/canonicalize_md.c index ed926191193..a012dbf3510 100644 --- a/ojluni/src/main/native/canonicalize_md.c +++ b/ojluni/src/main/native/canonicalize_md.c @@ -27,6 +27,7 @@ * Pathname canonicalization for Unix file systems */ +#include <stdbool.h> #include <stdio.h> #include <stdlib.h> #include <string.h> @@ -165,12 +166,16 @@ joinNames(char *names, int nc, char **ix) after invoking the realpath() procedure. */ static void -collapse(char *path) +collapse(char *path, + // Android-added: Remove parent directory /.. at the rootfs. http://b/312399441 + bool isAtLeastTargetSdk35) { // Android-changed: Remove consecutive duplicate path separators "//". b/267617531 removeDupSeparator(path); - char *names = (path[0] == '/') ? path + 1 : path; /* Preserve first '/' */ + // Android-changed: Remove parent directory /.. at the rootfs. http://b/312399441 + bool isPathAbsolute = (path[0] == '/'); + char *names = isPathAbsolute ? path + 1 : path; /* Preserve first '/' */ int nc; char **ix; int i, j; @@ -211,12 +216,18 @@ collapse(char *path) ix[i] = 0; } else { - /* If there is a preceding name, remove both that name and this + /* If there is a preceding name and at the rootfs, remove both that name and this instance of ".."; otherwise, leave the ".." as is */ for (j = i - 1; j >= 0; j--) { if (ix[j]) break; } - if (j < 0) continue; + if (j < 0) { + // Android-added: Remove parent directory /.. at the rootfs. http://b/312399441 + if (isPathAbsolute && isAtLeastTargetSdk35) { + ix[i] = 0; + } + continue; + } ix[j] = 0; ix[i] = 0; } @@ -235,7 +246,9 @@ collapse(char *path) // Android-changed: hidden to avoid conflict with libm (b/135018555) __attribute__((visibility("hidden"))) int -canonicalize(char *original, char *resolved, int len) +canonicalize(char *original, char *resolved, int len, + // Android-added: Remove parent directory /.. at the rootfs. http://b/312399441 + bool isAtLeastTargetSdk35) { if (len < PATH_MAX) { errno = EINVAL; @@ -252,7 +265,9 @@ canonicalize(char *original, char *resolved, int len) /* First try realpath() on the entire path */ if (realpath(original, resolved)) { /* That worked, so return it */ - collapse(resolved); + // Android-changed: Remove parent directory /.. at the rootfs. http://b/312399441 + // collapse(resolved); + collapse(resolved, isAtLeastTargetSdk35); return 0; } else { @@ -326,14 +341,18 @@ canonicalize(char *original, char *resolved, int len) p++; } strcpy(r + rn, p); - collapse(r); + // Android-changed: Remove parent directory /.. at the rootfs. http://b/312399441 + // collapse(r); + collapse(r, isAtLeastTargetSdk35); } else { /* Nothing resolved, so just return the original path */ // Android-changed: Avoid crash in getCanonicalPath() due to a long path. b/266432364 nameMax = pathconf("/", _PC_NAME_MAX); strcpy(resolved, path); - collapse(resolved); + // Android-changed: Remove parent directory /.. at the rootfs. http://b/312399441 + // collapse(resolved); + collapse(resolved, isAtLeastTargetSdk35); } // BEGIN Android-added: Avoid crash in getCanonicalPath() due to a long path. b/266432364 diff --git a/ojluni/src/main/native/java_io_UnixFileSystem.h b/ojluni/src/main/native/java_io_UnixFileSystem.h index 87187b416c7..aa324634a27 100644 --- a/ojluni/src/main/native/java_io_UnixFileSystem.h +++ b/ojluni/src/main/native/java_io_UnixFileSystem.h @@ -60,7 +60,9 @@ extern "C" { * Signature: (Ljava/lang/String;)Ljava/lang/String; */ JNIEXPORT jstring JNICALL Java_java_io_UnixFileSystem_canonicalize0 - (JNIEnv *, jobject, jstring); + (JNIEnv *, jobject, jstring, + // Android-added: Remove parent directory /.. at the rootfs. http://b/312399441 + jboolean); /* * Class: java_io_UnixFileSystem diff --git a/ojluni/src/main/native/jni_util.c b/ojluni/src/main/native/jni_util.c index 6c6e5ba1629..1f7ae7d1cd2 100644 --- a/ojluni/src/main/native/jni_util.c +++ b/ojluni/src/main/native/jni_util.c @@ -839,6 +839,8 @@ JNU_ReleaseStringPlatformChars(JNIEnv *env, jstring jstr, const char *str) * VM can find it when loading system classes. * */ +// Android-removed: Remove unused Canonicalize(). +/* // Android-changed: hidden to avoid conflict with libm (b/135018555) __attribute__((visibility("hidden"))) extern int canonicalize(char *path, const char *out, int len); @@ -846,9 +848,10 @@ extern int canonicalize(char *path, const char *out, int len); JNIEXPORT int Canonicalize(JNIEnv *env, char *orig, char *out, int len) { - /* canonicalize an already natived path */ + * canonicalize an already natived path * return canonicalize(orig, out, len); } +*/ JNIEXPORT jclass JNICALL JNU_ClassString(JNIEnv *env) diff --git a/test-rules/src/main/java/libcore/test/annotation/NonCts.java b/test-rules/src/main/java/libcore/test/annotation/NonCts.java index f028ee47d43..c280bd48766 100644 --- a/test-rules/src/main/java/libcore/test/annotation/NonCts.java +++ b/test-rules/src/main/java/libcore/test/annotation/NonCts.java @@ -27,9 +27,13 @@ import java.lang.annotation.Target; * Note that every annotation element below should be associated to a field in * {@link vogar.expect.Expectation}, because it will be de- and serialized by * {@link vogar.expect.ExpectationStore} for back-porting to an older branch. + * + * @deprecated All CTS modules supporting @NonCts annotations are expected to migrate to MCTS. + * Please use {@link NonMts} to skip test in the MCTS instead. */ @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.TYPE, ElementType.METHOD}) +@Deprecated public @interface NonCts { /** * Optional bug id showing why this test fails / shouldn't run in MTS. |