This is an automated email from the ASF dual-hosted git repository.
chaokunyang pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/fory.git
The following commit(s) were added to refs/heads/main by this push:
new 14f9f4d6a fix(java): preserve ConcurrentSkipListSet comparator on copy
(#3520)
14f9f4d6a is described below
commit 14f9f4d6aef5ef75c248c19e0e9de671b95a8c0f
Author: Sebastian Mandrean <[email protected]>
AuthorDate: Fri Mar 27 13:22:40 2026 +0100
fix(java): preserve ConcurrentSkipListSet comparator on copy (#3520)
## Why?
As a follow-up to #3342 and #3344, I wanted to make sure there is test
coverage for all the public constructors of the different Map/Set
classes supported by the serializers.
That exposed a copy-path bug in `ConcurrentSkipListSetSerializer`
(reported as #3519) which this PR addresses.
The deserialize path already preserves the comparator, but
`fory.copy(...)` rebuilt `ConcurrentSkipListSet` with the default
constructor path, which dropped the comparator and silently changed
ordering semantics. This is easy to miss because `Set.equals(...)` still
passes even when iteration order changes.
## What does this PR do?
Its kind-of two part: fix the bug, and add more test coverage.
Specifically:
- Preserve the comparator when copying `ConcurrentSkipListSet` by
overriding the collection copy construction path in
`ConcurrentSkipListSetSerializer`.
- Add constructor-matrix coverage for:
- `TreeSet`
- `TreeMap`
- `ConcurrentSkipListSet`
- `ConcurrentSkipListMap`
- `PriorityQueue`
- Exercise both round-trip deserialize and `fory.copy(...)` behavior for
the relevant public constructors.
- Assert concrete runtime type, comparator nullability, and observable
ordering semantics.
- Use queue-specific assertions for `PriorityQueue` by comparing drained
poll order instead of relying on `equals(...)`.
## Related issues
- Fixes #3519
- Related to #3337
- Related to #3343
## AI Contribution Checklist
- [x] Substantial AI assistance was used in this PR: `yes` / `no`
- [x] If `yes`, I included a completed [AI Contribution
Checklist](https://github.com/apache/fory/blob/main/AI_POLICY.md#9-contributor-checklist-for-ai-assisted-prs)
in this PR description and the required `AI Usage Disclosure`.
- [x] If `yes`, I can explain and defend all important changes without
AI help.
- [x] If `yes`, I reviewed AI-assisted code changes line by line before
submission.
- [x] If `yes`, I ran adequate human verification and recorded evidence
(checks run locally or in CI, pass/fail summary, and confirmation I
reviewed results).
- [x] If `yes`, I added/updated tests and specs where required.
- [x] If `yes`, I validated protocol/performance impacts with evidence
when applicable.
- [x] If `yes`, I verified licensing and provenance compliance.
AI Usage Disclosure
- substantial_ai_assistance: yes
- scope: code drafting, test design
- affected_files_or_subsystems: java/fory-core collection serializers
and java/fory-core collection/map serializer tests
- human_verification: local verification evidence available: `cd java &&
ENABLE_FORY_DEBUG_OUTPUT=1 mvn -T16 -pl fory-core
-Dtest=org.apache.fory.serializer.collection.CollectionSerializersTest,org.apache.fory.serializer.collection.MapSerializersTest
test` -> pass (237 tests, 0 failures)
- performance_verification: no dedicated benchmark run; runtime change
is limited to the `fory.copy(...)` path for `ConcurrentSkipListSet`,
while serialize/deserialize hot paths are unchanged; the added
comparator copy matches existing `PriorityQueue` and
`ConcurrentSkipListMap` copy behavior
- provenance_license_confirmation: repository-local
Apache-2.0-compatible changes only; no third-party code introduced
## Does this PR introduce any user-facing change?
- [ ] Does this PR introduce any public API change?
- [ ] Does this PR introduce any binary protocol compatibility change?
## Benchmark
No dedicated benchmark was run.
This PR is a correctness fix plus test coverage expansion, not a
performance optimization:
- The runtime code change only affects the `fory.copy(...)` path for
`ConcurrentSkipListSet`.
- Serialization and deserialization hot paths are unchanged.
- The added work is one comparator copy per copied
`ConcurrentSkipListSet`, which is expected to be negligible relative to
element copy and skip-list insertion work, and is consistent with
existing `PriorityQueue` and `ConcurrentSkipListMap` copy behavior.
Co-authored-by: Shawn Yang <[email protected]>
---
.../collection/CollectionSerializers.java | 7 +
.../collection/CollectionSerializersTest.java | 322 +++++++++++++++++++++
.../serializer/collection/MapSerializersTest.java | 195 +++++++++++++
3 files changed, 524 insertions(+)
diff --git
a/java/fory-core/src/main/java/org/apache/fory/serializer/collection/CollectionSerializers.java
b/java/fory-core/src/main/java/org/apache/fory/serializer/collection/CollectionSerializers.java
index 92e0a8b93..53a115d0d 100644
---
a/java/fory-core/src/main/java/org/apache/fory/serializer/collection/CollectionSerializers.java
+++
b/java/fory-core/src/main/java/org/apache/fory/serializer/collection/CollectionSerializers.java
@@ -476,6 +476,13 @@ public class CollectionSerializers {
refResolver.setReadObject(refId, skipListSet);
return skipListSet;
}
+
+ @Override
+ public Collection newCollection(Collection originCollection) {
+ Comparator comparator =
+ fory.copyObject(((ConcurrentSkipListSet)
originCollection).comparator());
+ return new ConcurrentSkipListSet(comparator);
+ }
}
public static final class SetFromMapSerializer extends
CollectionSerializer<Set<?>> {
diff --git
a/java/fory-core/src/test/java/org/apache/fory/serializer/collection/CollectionSerializersTest.java
b/java/fory-core/src/test/java/org/apache/fory/serializer/collection/CollectionSerializersTest.java
index fb292f33e..cd56bdd5c 100644
---
a/java/fory-core/src/test/java/org/apache/fory/serializer/collection/CollectionSerializersTest.java
+++
b/java/fory-core/src/test/java/org/apache/fory/serializer/collection/CollectionSerializersTest.java
@@ -59,6 +59,7 @@ import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.CopyOnWriteArraySet;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.atomic.AtomicInteger;
+import java.util.function.Supplier;
import java.util.stream.LongStream;
import lombok.AllArgsConstructor;
import lombok.Data;
@@ -78,6 +79,57 @@ import org.testng.collections.Maps;
@SuppressWarnings("rawtypes")
public class CollectionSerializersTest extends ForyTestBase {
+ private static final List<String> SORTED_COLLECTION_INPUT =
ImmutableList.of("bbb", "a", "cc");
+ private static final List<String> NATURAL_SORT_ORDER = ImmutableList.of("a",
"bbb", "cc");
+ private static final List<String> LENGTH_SORT_ORDER = ImmutableList.of("a",
"cc", "bbb");
+
+ private static final class LengthThenNaturalComparator
+ implements Comparator<String>, Serializable {
+ @Override
+ public int compare(String left, String right) {
+ int delta = left.length() - right.length();
+ return delta != 0 ? delta : left.compareTo(right);
+ }
+ }
+
+ private static final class SortedSetConstructorCase {
+ private final String name;
+ private final Class<?> expectedType;
+ private final boolean comparatorExpected;
+ private final List<String> expectedOrder;
+ private final Supplier<SortedSet<String>> factory;
+
+ private SortedSetConstructorCase(
+ String name,
+ Class<?> expectedType,
+ boolean comparatorExpected,
+ List<String> expectedOrder,
+ Supplier<SortedSet<String>> factory) {
+ this.name = name;
+ this.expectedType = expectedType;
+ this.comparatorExpected = comparatorExpected;
+ this.expectedOrder = expectedOrder;
+ this.factory = factory;
+ }
+ }
+
+ private static final class PriorityQueueConstructorCase {
+ private final String name;
+ private final boolean comparatorExpected;
+ private final List<String> expectedOrder;
+ private final Supplier<PriorityQueue<String>> factory;
+
+ private PriorityQueueConstructorCase(
+ String name,
+ boolean comparatorExpected,
+ List<String> expectedOrder,
+ Supplier<PriorityQueue<String>> factory) {
+ this.name = name;
+ this.comparatorExpected = comparatorExpected;
+ this.expectedOrder = expectedOrder;
+ this.factory = factory;
+ }
+ }
@Test(dataProvider = "referenceTrackingConfig")
public void testBasicList(boolean referenceTrackingConfig) {
@@ -243,6 +295,97 @@ public class CollectionSerializersTest extends
ForyTestBase {
}
}
+ @Test(dataProvider = "referenceTrackingConfig")
+ public void testTreeSetConstructorMatrix(boolean referenceTrackingConfig) {
+ Fory fory =
+ Fory.builder()
+ .withLanguage(Language.JAVA)
+ .withRefTracking(referenceTrackingConfig)
+ .requireClassRegistration(false)
+ .build();
+ for (SortedSetConstructorCase testCase : treeSetConstructorCases()) {
+ SortedSet<String> original = testCase.factory.get();
+ assertSortedSetState(testCase, original);
+ SortedSet<String> deserialized = serDe(fory, original);
+ assertSortedSetState(testCase, deserialized);
+ Assert.assertEquals(deserialized, original, testCase.name);
+ Assert.assertNotSame(deserialized, original, testCase.name);
+ }
+ }
+
+ @Test(dataProvider = "foryCopyConfig")
+ public void testTreeSetConstructorMatrix(Fory fory) {
+ for (SortedSetConstructorCase testCase : treeSetConstructorCases()) {
+ SortedSet<String> original = testCase.factory.get();
+ assertSortedSetState(testCase, original);
+ SortedSet<String> copy = fory.copy(original);
+ assertSortedSetState(testCase, copy);
+ Assert.assertEquals(copy, original, testCase.name);
+ Assert.assertNotSame(copy, original, testCase.name);
+ }
+ }
+
+ @Test(dataProvider = "referenceTrackingConfig")
+ public void testConcurrentSkipListSetConstructorMatrix(boolean
referenceTrackingConfig) {
+ Fory fory =
+ Fory.builder()
+ .withLanguage(Language.JAVA)
+ .withRefTracking(referenceTrackingConfig)
+ .requireClassRegistration(false)
+ .build();
+ for (SortedSetConstructorCase testCase :
concurrentSkipListSetConstructorCases()) {
+ SortedSet<String> original = testCase.factory.get();
+ assertSortedSetState(testCase, original);
+ SortedSet<String> deserialized = serDe(fory, original);
+ assertSortedSetState(testCase, deserialized);
+ Assert.assertEquals(deserialized, original, testCase.name);
+ Assert.assertNotSame(deserialized, original, testCase.name);
+ }
+ }
+
+ @Test(dataProvider = "foryCopyConfig")
+ public void testConcurrentSkipListSetConstructorMatrix(Fory fory) {
+ for (SortedSetConstructorCase testCase :
concurrentSkipListSetConstructorCases()) {
+ SortedSet<String> original = testCase.factory.get();
+ assertSortedSetState(testCase, original);
+ SortedSet<String> copy = fory.copy(original);
+ assertSortedSetState(testCase, copy);
+ Assert.assertEquals(copy, original, testCase.name);
+ Assert.assertNotSame(copy, original, testCase.name);
+ }
+ }
+
+ @Test(dataProvider = "referenceTrackingConfig")
+ public void testPriorityQueueConstructorMatrix(boolean
referenceTrackingConfig) {
+ Fory fory =
+ Fory.builder()
+ .withLanguage(Language.JAVA)
+ .withRefTracking(referenceTrackingConfig)
+ .requireClassRegistration(false)
+ .build();
+ for (PriorityQueueConstructorCase testCase :
priorityQueueConstructorCases()) {
+ PriorityQueue<String> original = testCase.factory.get();
+ assertPriorityQueueState(testCase, original);
+ PriorityQueue<String> deserialized = serDe(fory, original);
+ assertPriorityQueueState(testCase, deserialized);
+ Assert.assertEquals(
+ drainPriorityQueue(deserialized), drainPriorityQueue(original),
testCase.name);
+ Assert.assertNotSame(deserialized, original, testCase.name);
+ }
+ }
+
+ @Test(dataProvider = "foryCopyConfig")
+ public void testPriorityQueueConstructorMatrix(Fory fory) {
+ for (PriorityQueueConstructorCase testCase :
priorityQueueConstructorCases()) {
+ PriorityQueue<String> original = testCase.factory.get();
+ assertPriorityQueueState(testCase, original);
+ PriorityQueue<String> copy = fory.copy(original);
+ assertPriorityQueueState(testCase, copy);
+ Assert.assertEquals(drainPriorityQueue(copy),
drainPriorityQueue(original), testCase.name);
+ Assert.assertNotSame(copy, original, testCase.name);
+ }
+ }
+
// TreeSet subclass without a Comparator constructor (natural ordering only)
public static class ChildTreeSet extends TreeSet<String> {
public ChildTreeSet() {
@@ -505,6 +648,185 @@ public class CollectionSerializersTest extends
ForyTestBase {
Assert.assertEquals(ImmutableList.sortedCopyOf(copy), list);
}
+ private List<SortedSetConstructorCase> treeSetConstructorCases() {
+ return Arrays.asList(
+ new SortedSetConstructorCase(
+ "TreeSet()",
+ TreeSet.class,
+ false,
+ NATURAL_SORT_ORDER,
+ () -> {
+ TreeSet<String> set = new TreeSet<>();
+ set.addAll(sortedCollectionInput());
+ return set;
+ }),
+ new SortedSetConstructorCase(
+ "TreeSet(Comparator)",
+ TreeSet.class,
+ true,
+ LENGTH_SORT_ORDER,
+ () -> {
+ TreeSet<String> set = new TreeSet<>(new
LengthThenNaturalComparator());
+ set.addAll(sortedCollectionInput());
+ return set;
+ }),
+ new SortedSetConstructorCase(
+ "TreeSet(Collection)",
+ TreeSet.class,
+ false,
+ NATURAL_SORT_ORDER,
+ () -> new TreeSet<>(new ArrayList<>(sortedCollectionInput()))),
+ new SortedSetConstructorCase(
+ "TreeSet(SortedSet)",
+ TreeSet.class,
+ true,
+ LENGTH_SORT_ORDER,
+ () -> new TreeSet<>(newComparatorSortedSetSource())));
+ }
+
+ private List<SortedSetConstructorCase>
concurrentSkipListSetConstructorCases() {
+ return Arrays.asList(
+ new SortedSetConstructorCase(
+ "ConcurrentSkipListSet()",
+ ConcurrentSkipListSet.class,
+ false,
+ NATURAL_SORT_ORDER,
+ () -> {
+ ConcurrentSkipListSet<String> set = new
ConcurrentSkipListSet<>();
+ set.addAll(sortedCollectionInput());
+ return set;
+ }),
+ new SortedSetConstructorCase(
+ "ConcurrentSkipListSet(Comparator)",
+ ConcurrentSkipListSet.class,
+ true,
+ LENGTH_SORT_ORDER,
+ () -> {
+ ConcurrentSkipListSet<String> set =
+ new ConcurrentSkipListSet<>(new
LengthThenNaturalComparator());
+ set.addAll(sortedCollectionInput());
+ return set;
+ }),
+ new SortedSetConstructorCase(
+ "ConcurrentSkipListSet(Collection)",
+ ConcurrentSkipListSet.class,
+ false,
+ NATURAL_SORT_ORDER,
+ () -> new ConcurrentSkipListSet<>(new
ArrayList<>(sortedCollectionInput()))),
+ new SortedSetConstructorCase(
+ "ConcurrentSkipListSet(SortedSet)",
+ ConcurrentSkipListSet.class,
+ true,
+ LENGTH_SORT_ORDER,
+ () -> new
ConcurrentSkipListSet<>(newComparatorSortedSetSource())));
+ }
+
+ private List<PriorityQueueConstructorCase> priorityQueueConstructorCases() {
+ return Arrays.asList(
+ new PriorityQueueConstructorCase(
+ "PriorityQueue()",
+ false,
+ NATURAL_SORT_ORDER,
+ () -> {
+ PriorityQueue<String> queue = new PriorityQueue<>();
+ queue.addAll(sortedCollectionInput());
+ return queue;
+ }),
+ new PriorityQueueConstructorCase(
+ "PriorityQueue(int)",
+ false,
+ NATURAL_SORT_ORDER,
+ () -> {
+ PriorityQueue<String> queue = new PriorityQueue<>(32);
+ queue.addAll(sortedCollectionInput());
+ return queue;
+ }),
+ new PriorityQueueConstructorCase(
+ "PriorityQueue(Comparator)",
+ true,
+ LENGTH_SORT_ORDER,
+ () -> {
+ PriorityQueue<String> queue = new PriorityQueue<>(new
LengthThenNaturalComparator());
+ queue.addAll(sortedCollectionInput());
+ return queue;
+ }),
+ new PriorityQueueConstructorCase(
+ "PriorityQueue(int, Comparator)",
+ true,
+ LENGTH_SORT_ORDER,
+ () -> {
+ PriorityQueue<String> queue =
+ new PriorityQueue<>(32, new LengthThenNaturalComparator());
+ queue.addAll(sortedCollectionInput());
+ return queue;
+ }),
+ new PriorityQueueConstructorCase(
+ "PriorityQueue(Collection)",
+ false,
+ NATURAL_SORT_ORDER,
+ () -> new PriorityQueue<>(new
ArrayList<>(sortedCollectionInput()))),
+ new PriorityQueueConstructorCase(
+ "PriorityQueue(PriorityQueue)",
+ true,
+ LENGTH_SORT_ORDER,
+ () -> new PriorityQueue<>(newComparatorPriorityQueueSource())),
+ new PriorityQueueConstructorCase(
+ "PriorityQueue(SortedSet)",
+ true,
+ LENGTH_SORT_ORDER,
+ () -> new PriorityQueue<>(newComparatorSortedSetSource())));
+ }
+
+ private void assertSortedSetState(SortedSetConstructorCase testCase,
SortedSet<String> set) {
+ Assert.assertEquals(set.getClass(), testCase.expectedType, testCase.name);
+ Assert.assertEquals(new ArrayList<>(set), testCase.expectedOrder,
testCase.name);
+ if (testCase.comparatorExpected) {
+ Assert.assertNotNull(set.comparator(), testCase.name);
+ Assert.assertEquals(
+ set.comparator().getClass(), LengthThenNaturalComparator.class,
testCase.name);
+ } else {
+ Assert.assertNull(set.comparator(), testCase.name);
+ }
+ }
+
+ private void assertPriorityQueueState(
+ PriorityQueueConstructorCase testCase, PriorityQueue<String> queue) {
+ Assert.assertEquals(queue.getClass(), PriorityQueue.class, testCase.name);
+ Assert.assertEquals(drainPriorityQueue(queue), testCase.expectedOrder,
testCase.name);
+ if (testCase.comparatorExpected) {
+ Assert.assertNotNull(queue.comparator(), testCase.name);
+ Assert.assertEquals(
+ queue.comparator().getClass(), LengthThenNaturalComparator.class,
testCase.name);
+ } else {
+ Assert.assertNull(queue.comparator(), testCase.name);
+ }
+ }
+
+ private static PriorityQueue<String> newComparatorPriorityQueueSource() {
+ PriorityQueue<String> queue = new PriorityQueue<>(new
LengthThenNaturalComparator());
+ queue.addAll(sortedCollectionInput());
+ return queue;
+ }
+
+ private static SortedSet<String> newComparatorSortedSetSource() {
+ SortedSet<String> set = new TreeSet<>(new LengthThenNaturalComparator());
+ set.addAll(sortedCollectionInput());
+ return set;
+ }
+
+ private static List<String> drainPriorityQueue(PriorityQueue<String> queue) {
+ PriorityQueue<String> copy = new PriorityQueue<>(queue);
+ List<String> ordered = new ArrayList<>(copy.size());
+ while (!copy.isEmpty()) {
+ ordered.add(copy.poll());
+ }
+ return ordered;
+ }
+
+ private static List<String> sortedCollectionInput() {
+ return new ArrayList<>(SORTED_COLLECTION_INPUT);
+ }
+
@Test
public void testCopyOnWriteArrayList() {
final CopyOnWriteArrayList<String> list =
diff --git
a/java/fory-core/src/test/java/org/apache/fory/serializer/collection/MapSerializersTest.java
b/java/fory-core/src/test/java/org/apache/fory/serializer/collection/MapSerializersTest.java
index ca52e0e47..ed909fe6e 100644
---
a/java/fory-core/src/test/java/org/apache/fory/serializer/collection/MapSerializersTest.java
+++
b/java/fory-core/src/test/java/org/apache/fory/serializer/collection/MapSerializersTest.java
@@ -41,9 +41,11 @@ import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
+import java.util.SortedMap;
import java.util.TreeMap;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentSkipListMap;
+import java.util.function.Supplier;
import lombok.AllArgsConstructor;
import lombok.Data;
import org.apache.fory.Fory;
@@ -64,6 +66,39 @@ import org.testng.Assert;
import org.testng.annotations.Test;
public class MapSerializersTest extends ForyTestBase {
+ private static final List<String> SORTED_MAP_INPUT_KEYS =
Arrays.asList("bbb", "a", "cc");
+ private static final List<String> NATURAL_KEY_ORDER = Arrays.asList("a",
"bbb", "cc");
+ private static final List<String> LENGTH_KEY_ORDER = Arrays.asList("a",
"cc", "bbb");
+
+ private static final class LengthThenNaturalComparator
+ implements Comparator<String>, Serializable {
+ @Override
+ public int compare(String left, String right) {
+ int delta = left.length() - right.length();
+ return delta != 0 ? delta : left.compareTo(right);
+ }
+ }
+
+ private static final class SortedMapConstructorCase {
+ private final String name;
+ private final Class<?> expectedType;
+ private final boolean comparatorExpected;
+ private final List<String> expectedKeyOrder;
+ private final Supplier<SortedMap<String, Integer>> factory;
+
+ private SortedMapConstructorCase(
+ String name,
+ Class<?> expectedType,
+ boolean comparatorExpected,
+ List<String> expectedKeyOrder,
+ Supplier<SortedMap<String, Integer>> factory) {
+ this.name = name;
+ this.expectedType = expectedType;
+ this.comparatorExpected = comparatorExpected;
+ this.expectedKeyOrder = expectedKeyOrder;
+ this.factory = factory;
+ }
+ }
@Test(dataProvider = "basicMultiConfigFory")
public void basicTestCaseWithMultiConfig(
@@ -341,6 +376,66 @@ public class MapSerializersTest extends ForyTestBase {
copyCheck(fory, beanForMap);
}
+ @Test(dataProvider = "referenceTrackingConfig")
+ public void testTreeMapConstructorMatrix(boolean referenceTrackingConfig) {
+ Fory fory =
+ builder()
+ .withLanguage(Language.JAVA)
+ .withRefTracking(referenceTrackingConfig)
+ .requireClassRegistration(false)
+ .build();
+ for (SortedMapConstructorCase testCase : treeMapConstructorCases()) {
+ SortedMap<String, Integer> original = testCase.factory.get();
+ assertSortedMapState(testCase, original);
+ SortedMap<String, Integer> deserialized = serDe(fory, original);
+ assertSortedMapState(testCase, deserialized);
+ Assert.assertEquals(deserialized, original, testCase.name);
+ Assert.assertNotSame(deserialized, original, testCase.name);
+ }
+ }
+
+ @Test(dataProvider = "foryCopyConfig")
+ public void testTreeMapConstructorMatrix(Fory fory) {
+ for (SortedMapConstructorCase testCase : treeMapConstructorCases()) {
+ SortedMap<String, Integer> original = testCase.factory.get();
+ assertSortedMapState(testCase, original);
+ SortedMap<String, Integer> copy = fory.copy(original);
+ assertSortedMapState(testCase, copy);
+ Assert.assertEquals(copy, original, testCase.name);
+ Assert.assertNotSame(copy, original, testCase.name);
+ }
+ }
+
+ @Test(dataProvider = "referenceTrackingConfig")
+ public void testConcurrentSkipListMapConstructorMatrix(boolean
referenceTrackingConfig) {
+ Fory fory =
+ builder()
+ .withLanguage(Language.JAVA)
+ .withRefTracking(referenceTrackingConfig)
+ .requireClassRegistration(false)
+ .build();
+ for (SortedMapConstructorCase testCase :
concurrentSkipListMapConstructorCases()) {
+ SortedMap<String, Integer> original = testCase.factory.get();
+ assertSortedMapState(testCase, original);
+ SortedMap<String, Integer> deserialized = serDe(fory, original);
+ assertSortedMapState(testCase, deserialized);
+ Assert.assertEquals(deserialized, original, testCase.name);
+ Assert.assertNotSame(deserialized, original, testCase.name);
+ }
+ }
+
+ @Test(dataProvider = "foryCopyConfig")
+ public void testConcurrentSkipListMapConstructorMatrix(Fory fory) {
+ for (SortedMapConstructorCase testCase :
concurrentSkipListMapConstructorCases()) {
+ SortedMap<String, Integer> original = testCase.factory.get();
+ assertSortedMapState(testCase, original);
+ SortedMap<String, Integer> copy = fory.copy(original);
+ assertSortedMapState(testCase, copy);
+ Assert.assertEquals(copy, original, testCase.name);
+ Assert.assertNotSame(copy, original, testCase.name);
+ }
+ }
+
// TreeMap subclass without a Comparator constructor (natural ordering only)
public static class ChildTreeMap extends TreeMap<String, String> {
public ChildTreeMap() {
@@ -470,6 +565,106 @@ public class MapSerializersTest extends ForyTestBase {
copyCheck(fory, new ConcurrentSkipListMap<>(data));
}
+ private List<SortedMapConstructorCase> treeMapConstructorCases() {
+ return Arrays.asList(
+ new SortedMapConstructorCase(
+ "TreeMap()",
+ TreeMap.class,
+ false,
+ NATURAL_KEY_ORDER,
+ () -> {
+ TreeMap<String, Integer> map = new TreeMap<>();
+ map.putAll(sortedMapInput());
+ return map;
+ }),
+ new SortedMapConstructorCase(
+ "TreeMap(Comparator)",
+ TreeMap.class,
+ true,
+ LENGTH_KEY_ORDER,
+ () -> {
+ TreeMap<String, Integer> map = new TreeMap<>(new
LengthThenNaturalComparator());
+ map.putAll(sortedMapInput());
+ return map;
+ }),
+ new SortedMapConstructorCase(
+ "TreeMap(Map)",
+ TreeMap.class,
+ false,
+ NATURAL_KEY_ORDER,
+ () -> new TreeMap<>(new LinkedHashMap<>(sortedMapInput()))),
+ new SortedMapConstructorCase(
+ "TreeMap(SortedMap)",
+ TreeMap.class,
+ true,
+ LENGTH_KEY_ORDER,
+ () -> new TreeMap<>(newComparatorSortedMapSource())));
+ }
+
+ private List<SortedMapConstructorCase>
concurrentSkipListMapConstructorCases() {
+ return Arrays.asList(
+ new SortedMapConstructorCase(
+ "ConcurrentSkipListMap()",
+ ConcurrentSkipListMap.class,
+ false,
+ NATURAL_KEY_ORDER,
+ () -> {
+ ConcurrentSkipListMap<String, Integer> map = new
ConcurrentSkipListMap<>();
+ map.putAll(sortedMapInput());
+ return map;
+ }),
+ new SortedMapConstructorCase(
+ "ConcurrentSkipListMap(Comparator)",
+ ConcurrentSkipListMap.class,
+ true,
+ LENGTH_KEY_ORDER,
+ () -> {
+ ConcurrentSkipListMap<String, Integer> map =
+ new ConcurrentSkipListMap<>(new
LengthThenNaturalComparator());
+ map.putAll(sortedMapInput());
+ return map;
+ }),
+ new SortedMapConstructorCase(
+ "ConcurrentSkipListMap(Map)",
+ ConcurrentSkipListMap.class,
+ false,
+ NATURAL_KEY_ORDER,
+ () -> new ConcurrentSkipListMap<>(new
LinkedHashMap<>(sortedMapInput()))),
+ new SortedMapConstructorCase(
+ "ConcurrentSkipListMap(SortedMap)",
+ ConcurrentSkipListMap.class,
+ true,
+ LENGTH_KEY_ORDER,
+ () -> new
ConcurrentSkipListMap<>(newComparatorSortedMapSource())));
+ }
+
+ private void assertSortedMapState(
+ SortedMapConstructorCase testCase, SortedMap<String, Integer> map) {
+ Assert.assertEquals(map.getClass(), testCase.expectedType, testCase.name);
+ Assert.assertEquals(new ArrayList<>(map.keySet()),
testCase.expectedKeyOrder, testCase.name);
+ if (testCase.comparatorExpected) {
+ Assert.assertNotNull(map.comparator(), testCase.name);
+ Assert.assertEquals(
+ map.comparator().getClass(), LengthThenNaturalComparator.class,
testCase.name);
+ } else {
+ Assert.assertNull(map.comparator(), testCase.name);
+ }
+ }
+
+ private static SortedMap<String, Integer> newComparatorSortedMapSource() {
+ SortedMap<String, Integer> map = new TreeMap<>(new
LengthThenNaturalComparator());
+ map.putAll(sortedMapInput());
+ return map;
+ }
+
+ private static Map<String, Integer> sortedMapInput() {
+ LinkedHashMap<String, Integer> map = new LinkedHashMap<>();
+ for (String key : SORTED_MAP_INPUT_KEYS) {
+ map.put(key, key.length());
+ }
+ return map;
+ }
+
@Test
public void testEnumMap() {
EnumMap<TestEnum, Object> enumMap = new EnumMap<>(TestEnum.class);
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]