Add tests for java.util APIs

Bug: 119393918
Bug: 182168282
Test: atest CtsLibcoreTestCases:libcore.java.util
Change-Id: I7c37f7feee751f482570b668d01284b6715799a7
(cherry picked from commit e5fcdc2e9be456da56314742f3c8d4e697c0398b)
Merged-In: I7c37f7feee751f482570b668d01284b6715799a7
diff --git a/luni/src/test/java/libcore/java/util/BitSetTest.java b/luni/src/test/java/libcore/java/util/BitSetTest.java
index 41d4e53..8a38fcb 100644
--- a/luni/src/test/java/libcore/java/util/BitSetTest.java
+++ b/luni/src/test/java/libcore/java/util/BitSetTest.java
@@ -204,6 +204,21 @@
         assertEquals(-1, bs.previousSetBit(-1));
     }
 
+
+    public void test_previousClearBit() {
+        BitSet bs = new BitSet();
+        assertEquals(0, bs.previousClearBit(0));
+        assertEquals(1, bs.previousClearBit(1));
+        assertEquals(5, bs.previousClearBit(5));
+
+        bs.set(0);
+        assertEquals(-1, bs.previousClearBit(0));
+        bs.set(5);
+        assertEquals(4, bs.previousClearBit(5));
+        bs.set(6);
+        assertEquals(4, bs.previousClearBit(6));
+    }
+
     private static BitSet big() {
         BitSet result = new BitSet();
         result.set(1000);
diff --git a/luni/src/test/java/libcore/java/util/CalendarTest.java b/luni/src/test/java/libcore/java/util/CalendarTest.java
index abe5ab8..6c7efc2 100644
--- a/luni/src/test/java/libcore/java/util/CalendarTest.java
+++ b/luni/src/test/java/libcore/java/util/CalendarTest.java
@@ -361,6 +361,11 @@
         } catch (UnsupportedOperationException expected) {}
     }
 
+    public void testGetCalendarType() {
+        Calendar cal = new FakeCalendar();
+        assertEquals(FakeCalendar.class.getName(), cal.getCalendarType());
+    }
+
     public static class FakeCalendar extends Calendar {
 
         private int[] subclassFields;
diff --git a/luni/src/test/java/libcore/java/util/IllformedLocaleExceptionTest.java b/luni/src/test/java/libcore/java/util/IllformedLocaleExceptionTest.java
new file mode 100644
index 0000000..ac010ae
--- /dev/null
+++ b/luni/src/test/java/libcore/java/util/IllformedLocaleExceptionTest.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2022 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.
+ */
+
+package libcore.java.util;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+
+import java.util.IllformedLocaleException;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+@RunWith(JUnit4.class)
+public class IllformedLocaleExceptionTest {
+
+    @Test
+    public void testConstructor() {
+        IllformedLocaleException exception = new IllformedLocaleException();
+        assertNull(exception.getMessage());
+        assertEquals(-1, exception.getErrorIndex());
+    }
+
+    @Test
+    public void testGetErrorIndex() {
+        IllformedLocaleException exception = new IllformedLocaleException("message", 6);
+        assertEquals(6, exception.getErrorIndex());
+    }
+}
diff --git a/luni/src/test/java/libcore/java/util/ResourceBundleTest.java b/luni/src/test/java/libcore/java/util/ResourceBundleTest.java
new file mode 100644
index 0000000..d8bcc99
--- /dev/null
+++ b/luni/src/test/java/libcore/java/util/ResourceBundleTest.java
@@ -0,0 +1,108 @@
+/*
+ * Copyright (C) 2022 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.
+ */
+
+package libcore.java.util;
+
+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 java.util.Locale;
+import java.util.MissingResourceException;
+import java.util.ResourceBundle;
+import java.util.ResourceBundle.Control;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+import tests.support.resource.Support_Resources;
+
+@RunWith(JUnit4.class)
+public class ResourceBundleTest {
+
+    private static final String PROP_RESOURCE_NAME = Support_Resources.RESOURCE_PACKAGE_NAME
+            + ".hyts_resource";
+
+    @Before
+    public void setUp() {
+        // Due to http://b/231440892, clear global cache before each test
+        ResourceBundle.clearCache();
+    }
+
+    @After
+    public void tearDown() {
+        // Due to http://b/231440892, clear global cache after each test
+        ResourceBundle.clearCache();
+    }
+
+    @Test
+    public void testGetBundle_withControl() {
+        Control propControl = Control.getControl(Control.FORMAT_PROPERTIES);
+        Control classControl = Control.getControl(Control.FORMAT_CLASS);
+        ClassLoader resClassLoader = Support_Resources.class.getClassLoader();
+        Locale locale = Locale.getDefault();
+
+        // Test for getBundle(String, Control)
+        ResourceBundle bundle = ResourceBundle.getBundle(PROP_RESOURCE_NAME, propControl);
+        assertEquals("parent", bundle.getString("property"));
+        ResourceBundle.clearCache(resClassLoader);
+        try {
+            ResourceBundle.getBundle(PROP_RESOURCE_NAME, classControl);
+            fail("ResourceBundle.getBundle() is expected to throw MissingResourceException");
+        } catch (MissingResourceException e) {
+            // expected
+        }
+
+        // clearCache() must be called after the previous lookup failure. See http://b/231440892.
+        ResourceBundle.clearCache(resClassLoader);
+        // Test for getBundle(String, Locale, Control)
+        bundle = ResourceBundle.getBundle(PROP_RESOURCE_NAME, locale, propControl);
+        assertEquals("parent", bundle.getString("property"));
+        ResourceBundle.clearCache(resClassLoader);
+        try {
+            ResourceBundle.getBundle(PROP_RESOURCE_NAME, locale, classControl);
+            fail("ResourceBundle.getBundle() is expected to throw MissingResourceException");
+        } catch (MissingResourceException e) {
+            // expected
+        }
+
+        // clearCache() must be called after the previous lookup failure. See http://b/231440892.
+        ResourceBundle.clearCache(resClassLoader);
+        // Test for getBundle(String, Locale, ClassLoader, Control)
+        bundle = ResourceBundle.getBundle(PROP_RESOURCE_NAME, locale, resClassLoader, propControl);
+        assertEquals("parent", bundle.getString("property"));
+
+        // clearCache() and clearCache(resClassLoader) should have the same effect, because the
+        // classes are in the same class loader.
+        assertEquals(resClassLoader, ResourceBundleTest.class.getClassLoader());
+        ResourceBundle.clearCache();
+        try {
+            ResourceBundle.getBundle(PROP_RESOURCE_NAME, locale, resClassLoader, classControl);
+            fail("ResourceBundle.getBundle() is expected to throw MissingResourceException");
+        } catch (MissingResourceException e) {
+            // expected
+        }
+    }
+
+    @Test
+    public void testContainsKey() {
+        ResourceBundle bundle = ResourceBundle.getBundle(PROP_RESOURCE_NAME);
+        assertTrue(bundle.containsKey("property"));
+        assertFalse(bundle.containsKey("anotherProperty"));
+    }
+}
diff --git a/luni/src/test/java/libcore/java/util/ScannerTest.java b/luni/src/test/java/libcore/java/util/ScannerTest.java
new file mode 100644
index 0000000..f84dac1
--- /dev/null
+++ b/luni/src/test/java/libcore/java/util/ScannerTest.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2022 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.
+ */
+
+package libcore.java.util;
+
+import static org.junit.Assert.assertEquals;
+
+import java.util.Locale;
+import java.util.Scanner;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+@RunWith(JUnit4.class)
+public class ScannerTest {
+    @Test
+    public void testReset() {
+        Scanner scanner = new Scanner("123 45");
+        Locale locale = Locale.forLanguageTag("zh");
+        scanner.useDelimiter("123");
+        scanner.useLocale(locale);
+        scanner.useRadix(8);
+        assertEquals("123", scanner.delimiter().pattern());
+        assertEquals(locale, scanner.locale());
+        assertEquals(8, scanner.radix());
+
+        scanner.reset();
+        assertEquals("\\p{javaWhitespace}+", scanner.delimiter().pattern());
+        assertEquals(Locale.getDefault(Locale.Category.FORMAT), scanner.locale());
+        assertEquals(10, scanner.radix());
+    }
+}
diff --git a/luni/src/test/java/libcore/java/util/ServiceLoaderTest.java b/luni/src/test/java/libcore/java/util/ServiceLoaderTest.java
index f17eb22..4be9007 100644
--- a/luni/src/test/java/libcore/java/util/ServiceLoaderTest.java
+++ b/luni/src/test/java/libcore/java/util/ServiceLoaderTest.java
@@ -38,6 +38,17 @@
     assertFalse(it.hasNext());
   }
 
+  public void testLoadInstalled() {
+    // We need to register a service in the System or Boot class loader in order to test this
+    // properly. We only test the negative case here.
+    ServiceLoader<ServiceLoaderTestInterface> loader = ServiceLoader.loadInstalled(
+            ServiceLoaderTestInterface.class);
+    Iterator<ServiceLoaderTestInterface> it = loader.iterator();
+    // Impl1 and Impl2 are not in the System or Boot class loader.
+    // Thus, ServiceLoader.loadInstalled shouldn't load any of them.
+    assertFalse(it.hasNext());
+  }
+
   // Something like "does.not.Exist", that is a well-formed class name, but just doesn't exist.
   public void test_missingRegisteredClass() throws Exception {
     try {
diff --git a/luni/src/test/java/libcore/java/util/SpliteratorTest.java b/luni/src/test/java/libcore/java/util/SpliteratorTest.java
new file mode 100644
index 0000000..37b1d7a
--- /dev/null
+++ b/luni/src/test/java/libcore/java/util/SpliteratorTest.java
@@ -0,0 +1,91 @@
+/*
+ * Copyright (C) 2022 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.
+ */
+
+package libcore.java.util;
+
+import static org.junit.Assert.assertEquals;
+
+import java.util.Spliterator.OfPrimitive;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.function.Consumer;
+import java.util.function.IntConsumer;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+@RunWith(JUnit4.class)
+public class SpliteratorTest {
+
+
+    /**
+     * Class used to test {@link OfPrimitive#forEachRemaining(Object)}
+     */
+    private static class PrimitiveIntegerSpliterator
+            implements OfPrimitive<Integer, IntConsumer, PrimitiveIntegerSpliterator> {
+        private int current = 1;
+        private final int size;
+
+        PrimitiveIntegerSpliterator(int size) {
+            this.size = size;
+        }
+
+        @Override
+        public boolean tryAdvance(IntConsumer action) {
+            if (current <= size) {
+                action.accept(current++);
+                return true;
+            } else {
+                return false;
+            }
+        }
+
+        @Override
+        public long estimateSize() {
+            return size;
+        }
+
+        @Override
+        public int characteristics() {
+            return 0;
+        }
+
+        @Override
+        public boolean tryAdvance(Consumer<? super Integer> action) {
+            if (action instanceof IntConsumer) {
+                return tryAdvance(action);
+            } else {
+                return tryAdvance((IntConsumer) action::accept);
+            }
+        }
+
+        @Override
+        public PrimitiveIntegerSpliterator trySplit() {
+            // null implying that this spliterator cannot be split
+            return null;
+        }
+    }
+
+    @Test
+    public void testOfPrimitiveForEachRemaining() {
+        OfPrimitive<Integer, IntConsumer, PrimitiveIntegerSpliterator> spliterator =
+                new PrimitiveIntegerSpliterator(10);
+        AtomicInteger sum = new AtomicInteger();
+        IntConsumer action = (i) -> sum.addAndGet(i);
+        spliterator.forEachRemaining(action);
+
+        assertEquals(55, sum.get());
+    }
+}
diff --git a/luni/src/test/java/libcore/java/util/SplittableRandomTest.java b/luni/src/test/java/libcore/java/util/SplittableRandomTest.java
new file mode 100644
index 0000000..f3f394d
--- /dev/null
+++ b/luni/src/test/java/libcore/java/util/SplittableRandomTest.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2022 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.
+ */
+
+package libcore.java.util;
+
+import static org.junit.Assert.assertEquals;
+
+import java.util.SplittableRandom;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+@RunWith(JUnit4.class)
+public class SplittableRandomTest {
+
+    @Test
+    public void testNextBoolean() {
+        long seed = 0x1234567890L;
+        SplittableRandom random1 = new SplittableRandom(seed);
+        SplittableRandom random2 = new SplittableRandom(seed);
+        for (int i = 0; i < 1000; i++) {
+            assertEquals(random1.nextBoolean(), random2.nextBoolean());
+        }
+    }
+}
diff --git a/luni/src/test/java/libcore/java/util/TreeSetTest.java b/luni/src/test/java/libcore/java/util/TreeSetTest.java
index 11cb708..958bdb9 100644
--- a/luni/src/test/java/libcore/java/util/TreeSetTest.java
+++ b/luni/src/test/java/libcore/java/util/TreeSetTest.java
@@ -16,7 +16,9 @@
 
 package libcore.java.util;
 
+import java.util.Arrays;
 import java.util.NavigableSet;
+import java.util.Set;
 import java.util.SortedSet;
 import java.util.TreeSet;
 import junit.framework.TestCase;
@@ -133,4 +135,22 @@
             }
         }.test();
     }
+
+    public void testHeadSet() {
+        TreeSet<Integer> set = new TreeSet<>();
+        for (int i = -5; i < 5; i++) {
+            set.add(i);
+            // Adding twice should make no difference to the set.
+            set.add(i);
+        }
+
+        Set<Integer> headset = set.headSet(-1, true);
+        assertTrue(headset.containsAll(Arrays.asList(-5, -4, -3, -2, -1)));
+        assertEquals(5, headset.size());
+
+
+        headset = set.headSet(-1, false);
+        assertTrue(headset.containsAll(Arrays.asList(-5, -4, -3, -2)));
+        assertEquals(4, headset.size());
+    }
 }