Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
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
@@ -0,0 +1,166 @@
/*
* 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 static com.google.common.collect.testing.Helpers.getMethod;

import com.google.common.annotations.GwtCompatible;
import com.google.common.annotations.GwtIncompatible;
import com.google.common.annotations.J2ktIncompatible;
import com.google.common.collect.testing.AbstractMapTester;
import java.lang.reflect.Method;
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, and that no spliterator reports both {@code CONCURRENT} and {@code SIZED}
* characteristics (which are mutually incompatible per the Java specification).
*
* <p>Can't be invoked directly; please see {@link
* com.google.common.collect.testing.ConcurrentMapTestSuiteBuilder}.
*
* @author Guava Authors
*/
@GwtCompatible(emulated = true)
@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.

}

/**
* Tests that the spliterator returned by {@code entrySet().spliterator()} does not have both
* {@code CONCURRENT} and {@code SIZED} characteristics, which are mutually incompatible per the
* Java specification.
*/
public void testEntrySetSpliteratorNotConcurrentAndSized() {
Spliterator<?> spliterator = getMap().entrySet().spliterator();
assertFalse(
"entrySet().spliterator() should not have both CONCURRENT and SIZED characteristics",
spliterator.hasCharacteristics(Spliterator.CONCURRENT)
&& spliterator.hasCharacteristics(Spliterator.SIZED));
}

/**
* Tests that the spliterator returned by {@code keySet().spliterator()} does not have both {@code
* CONCURRENT} and {@code SIZED} characteristics, which are mutually incompatible per the Java
* specification.
*/
public void testKeySetSpliteratorNotConcurrentAndSized() {
Spliterator<?> spliterator = getMap().keySet().spliterator();
assertFalse(
"keySet().spliterator() should not have both CONCURRENT and SIZED characteristics",
spliterator.hasCharacteristics(Spliterator.CONCURRENT)
&& spliterator.hasCharacteristics(Spliterator.SIZED));
}

/**
* Tests that the spliterator returned by {@code values().spliterator()} does not have both {@code
* CONCURRENT} and {@code SIZED} characteristics, which are mutually incompatible per the Java
* specification.
*/
public void testValuesSpliteratorNotConcurrentAndSized() {
Spliterator<?> spliterator = getMap().values().spliterator();
assertFalse(
"values().spliterator() should not have both CONCURRENT and SIZED characteristics",
spliterator.hasCharacteristics(Spliterator.CONCURRENT)
&& spliterator.hasCharacteristics(Spliterator.SIZED));
}

// Reflection methods for test suppression

@J2ktIncompatible
@GwtIncompatible // reflection
public static Method getEntrySetSpliteratorHasConcurrentMethod() {
return getMethod(ConcurrentMapSpliteratorTester.class, "testEntrySetSpliteratorHasConcurrent");
}

@J2ktIncompatible
@GwtIncompatible // reflection
public static Method getKeySetSpliteratorHasConcurrentMethod() {
return getMethod(ConcurrentMapSpliteratorTester.class, "testKeySetSpliteratorHasConcurrent");
}

@J2ktIncompatible
@GwtIncompatible // reflection
public static Method getValuesSpliteratorHasConcurrentMethod() {
return getMethod(ConcurrentMapSpliteratorTester.class, "testValuesSpliteratorHasConcurrent");
}

@J2ktIncompatible
@GwtIncompatible // reflection
public static Method getEntrySetSpliteratorNotConcurrentAndSizedMethod() {
return getMethod(
ConcurrentMapSpliteratorTester.class, "testEntrySetSpliteratorNotConcurrentAndSized");
}

@J2ktIncompatible
@GwtIncompatible // reflection
public static Method getKeySetSpliteratorNotConcurrentAndSizedMethod() {
return getMethod(
ConcurrentMapSpliteratorTester.class, "testKeySetSpliteratorNotConcurrentAndSized");
}

@J2ktIncompatible
@GwtIncompatible // reflection
public static Method getValuesSpliteratorNotConcurrentAndSizedMethod() {
return getMethod(
ConcurrentMapSpliteratorTester.class, "testValuesSpliteratorNotConcurrentAndSized");
}
}
Loading