Skip to content
Open
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
import com.google.common.collect.testing.testers.ConcurrentMapRemoveTester;
import com.google.common.collect.testing.testers.ConcurrentMapReplaceEntryTester;
import com.google.common.collect.testing.testers.ConcurrentMapReplaceTester;
import com.google.common.collect.testing.testers.ConcurrentMapSpliteratorTester;
import java.util.List;

/**
Expand All @@ -44,7 +45,8 @@ public static <K, V> ConcurrentMapTestSuiteBuilder<K, V> using(TestMapGenerator<
ConcurrentMapPutIfAbsentTester.class,
ConcurrentMapRemoveTester.class,
ConcurrentMapReplaceTester.class,
ConcurrentMapReplaceEntryTester.class);
ConcurrentMapReplaceEntryTester.class,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Instead of adding a new tester here, we can maybe override the

  • createDerivedEntrySetSuite
  • createDerivedKeySetSuite
  • createDerivedValueCollectionSuite

methods to append a ConcurrentCollectionSpliteratorTester, so it could be reused for arbitrary concurrent collections.

ConcurrentMapSpliteratorTester.class);

@SuppressWarnings("rawtypes") // class literals
@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,56 @@ public void testSpliteratorNotImmutable_collectionAllowsRemove() {
assertFalse(collection.spliterator().hasCharacteristics(Spliterator.IMMUTABLE));
}

/**
* Tests that the spliterator does not report both {@code CONCURRENT} and {@code SIZED}
* characteristics, which are incompatible per the {@link Spliterator} specification.
*/
public void testSpliteratorNotConcurrentAndSized() {
Spliterator<E> spliterator = collection.spliterator();
assertFalse(
"spliterator should not have both CONCURRENT and SIZED characteristics",
spliterator.hasCharacteristics(Spliterator.CONCURRENT)
&& spliterator.hasCharacteristics(Spliterator.SIZED));
}

/**
* Tests that the spliterator does not report both {@code CONCURRENT} and {@code IMMUTABLE}
* characteristics, which are incompatible per the {@link Spliterator} specification.
*/
public void testSpliteratorNotConcurrentAndImmutable() {
Spliterator<E> spliterator = collection.spliterator();
assertFalse(
"spliterator should not have both CONCURRENT and IMMUTABLE characteristics",
spliterator.hasCharacteristics(Spliterator.CONCURRENT)
&& spliterator.hasCharacteristics(Spliterator.IMMUTABLE));
}

/**
* Tests that if the spliterator reports {@code SORTED}, it also reports {@code ORDERED}, as
* required by the {@link Spliterator} specification.
*/
public void testSpliteratorSortedRequiresOrdered() {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch! Thanks for adding this as well.

Spliterator<E> spliterator = collection.spliterator();
if (spliterator.hasCharacteristics(Spliterator.SORTED)) {
assertTrue(
"spliterator reporting SORTED must also report ORDERED",
spliterator.hasCharacteristics(Spliterator.ORDERED));
}
}

/**
* Tests that if the spliterator reports {@code SUBSIZED}, it also reports {@code SIZED}, as
* required by the {@link Spliterator} specification.
*/
public void testSpliteratorSubsizedRequiresSized() {
Spliterator<E> spliterator = collection.spliterator();
if (spliterator.hasCharacteristics(Spliterator.SUBSIZED)) {
assertTrue(
"spliterator reporting SUBSIZED must also report SIZED",
spliterator.hasCharacteristics(Spliterator.SIZED));
}
}

@J2ktIncompatible
@GwtIncompatible // reflection
public static Method getSpliteratorNotImmutableCollectionAllowsAddMethod() {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
/*
* Copyright (C) 2025 The Guava Authors
*
* 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 com.google.common.collect.testing.testers;

import com.google.common.annotations.GwtCompatible;
import com.google.common.collect.testing.AbstractMapTester;
import java.util.Spliterator;
import java.util.concurrent.ConcurrentMap;
import org.jspecify.annotations.NullMarked;
import org.junit.Ignore;

/**
* A generic JUnit test which tests spliterator characteristics on concurrent map views. Verifies
* that {@link ConcurrentMap} implementations return spliterators with the {@link
* Spliterator#CONCURRENT} characteristic on their {@code entrySet()}, {@code keySet()}, and {@code
* values()} views.
*
* <p>Can't be invoked directly; please see {@link
* com.google.common.collect.testing.ConcurrentMapTestSuiteBuilder}.
*
* @author Guava Authors
*/
@GwtCompatible
@Ignore("test runners must not instantiate and run this directly, only via suites we build")
// @Ignore affects the Android test runner, which respects JUnit 4 annotations on JUnit 3 tests.
@SuppressWarnings("JUnit4ClassUsedInJUnit3")
@NullMarked
public class ConcurrentMapSpliteratorTester<K, V> extends AbstractMapTester<K, V> {
@Override
protected ConcurrentMap<K, V> getMap() {
return (ConcurrentMap<K, V>) super.getMap();
}

/**
* Tests that the spliterator returned by {@code entrySet().spliterator()} has the {@code
* CONCURRENT} characteristic.
*/
public void testEntrySetSpliteratorHasConcurrent() {
Spliterator<?> spliterator = getMap().entrySet().spliterator();
assertTrue(
"entrySet().spliterator() should have CONCURRENT characteristic",
spliterator.hasCharacteristics(Spliterator.CONCURRENT));
}

/**
* Tests that the spliterator returned by {@code keySet().spliterator()} has the {@code
* CONCURRENT} characteristic.
*/
public void testKeySetSpliteratorHasConcurrent() {
Spliterator<?> spliterator = getMap().keySet().spliterator();
assertTrue(
"keySet().spliterator() should have CONCURRENT characteristic",
spliterator.hasCharacteristics(Spliterator.CONCURRENT));
}

/**
* Tests that the spliterator returned by {@code values().spliterator()} has the {@code
* CONCURRENT} characteristic.
*/
public void testValuesSpliteratorHasConcurrent() {
Spliterator<?> spliterator = getMap().values().spliterator();
assertTrue(
"values().spliterator() should have CONCURRENT characteristic",
spliterator.hasCharacteristics(Spliterator.CONCURRENT));
Copy link
Member

@chaoren chaoren Feb 2, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we can move the testEntrySetSpliteratorNotConcurrentAndSized, testKeySetSpliteratorNotConcurrentAndSized, and testValuesSpliteratorNotConcurrentAndSized tests to CollectionSpliteratorTester instead.

Per https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/util/Spliterator.html#CONCURRENT,

A top-level Spliterator should not report both CONCURRENT and SIZED
A top-level Spliterator should not report both CONCURRENT and IMMUTABLE

and that should apply to all top-level Spliterators, not just concurrent ones.

Also, https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/util/Spliterator.html#SUBSIZED says

A Spliterator that does not report SIZED as required by SUBSIZED is inconsistent

We can check that as well.

}
}
Loading