This is an automated email from the ASF dual-hosted git repository.

spmallette pushed a commit to branch gvalue
in repository https://gitbox.apache.org/repos/asf/tinkerpop.git


The following commit(s) were added to refs/heads/gvalue by this push:
     new 682cb4b050 wip - refactoring, tests, comments
682cb4b050 is described below

commit 682cb4b0500a25bedac233fa33303daedb15b74c
Author: Stephen Mallette <[email protected]>
AuthorDate: Wed Aug 28 11:29:18 2024 -0400

    wip - refactoring, tests, comments
---
 .../tinkerpop/gremlin/jsr223/JavaTranslator.java   |   5 +
 .../language/grammar/TraversalMethodVisitor.java   |   2 +-
 .../tinkerpop/gremlin/process/traversal/P.java     |   8 +-
 .../gremlin/process/traversal/step/GValue.java     |  73 +++++-------
 .../process/traversal/step/filter/CoinStep.java    |   4 -
 .../process/traversal/step/map/GraphStep.java      |   4 +-
 .../process/traversal/step/map/VertexStep.java     |   2 +-
 .../traversal/step/sideEffect/InjectStep.java      |   2 +-
 .../optimization/InlineFilterStrategy.java         |   2 +-
 .../tinkerpop/gremlin/util/CollectionUtil.java     |  23 +++-
 .../gremlin/process/traversal/step/GValueTest.java | 131 +++++++++++++++++++++
 .../tinkerpop/gremlin/util/CollectionUtilTest.java | 110 +++++++++++++++++
 12 files changed, 306 insertions(+), 60 deletions(-)

diff --git 
a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/jsr223/JavaTranslator.java
 
b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/jsr223/JavaTranslator.java
index 96a4450309..8ac0b507f3 100644
--- 
a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/jsr223/JavaTranslator.java
+++ 
b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/jsr223/JavaTranslator.java
@@ -338,6 +338,11 @@ public final class JavaTranslator<S extends 
TraversalSource, T extends Traversal
     private synchronized static void buildMethodCache(final Object delegate, 
final Map<String, List<ReflectedMethod>> methodCache) {
         if (methodCache.isEmpty()) {
             for (final Method method : delegate.getClass().getMethods()) {
+
+                // skip GValue arguments as they make selecting the right 
method ambiguous for the translator. in any
+                // event a GValue shouldn't get here since bytecode can't 
carry it. also, bytecode is gone in 4.0.0
+                // so this will be deleted anyway. mostly adding this 
exception so the tests can continue to pass until
+                // that removal completes.
                 final boolean freeOfGValue = 
Arrays.stream(method.getParameters()).
                         noneMatch(p -> p.getType() == GValue.class || 
p.getType().getComponentType() == GValue.class);
                 if (freeOfGValue) {
diff --git 
a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/language/grammar/TraversalMethodVisitor.java
 
b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/language/grammar/TraversalMethodVisitor.java
index ce010e326e..4d28a0b8e9 100644
--- 
a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/language/grammar/TraversalMethodVisitor.java
+++ 
b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/language/grammar/TraversalMethodVisitor.java
@@ -765,7 +765,7 @@ public class TraversalMethodVisitor extends 
TraversalRootVisitor<GraphTraversal>
             // if any are GValue then they all need to be GValue to call 
hasLabel
             if (literalOrVar instanceof GValue || 
Arrays.stream(literalOrVars).anyMatch(lov -> lov instanceof GValue)) {
                 literalOrVar = GValue.of(literalOrVar);
-                literalOrVars = 
Arrays.stream(literalOrVars).map(GValue::of).toArray();
+                literalOrVars = GValue.ensureGValues(literalOrVars);
             }
 
             // since we normalized above to gvalue or literal we can just test 
the first arg for gvalue-ness
diff --git 
a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/P.java
 
b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/P.java
index 622a3c3e3a..a878bbb839 100644
--- 
a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/P.java
+++ 
b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/P.java
@@ -82,13 +82,7 @@ public class P<V> implements Predicate<V>, Serializable, 
Cloneable {
         } else {
             // this might be a bunch of GValue that need to be resolved. zomg
             if (this.value instanceof List) {
-                return this.biPredicate.test(testValue, (V) ((List) 
this.value).stream().map(o -> {
-                    if (o instanceof GValue) {
-                        return ((GValue) o).get();
-                    } else {
-                        return o;
-                    }
-                }).collect(Collectors.toList()));
+                return this.biPredicate.test(testValue, (V) ((List) 
this.value).stream().map(GValue::valueOf).collect(Collectors.toList()));
             } else {
                 return this.biPredicate.test(testValue, this.value);
             }
diff --git 
a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/GValue.java
 
b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/GValue.java
index 0b1e88b15c..cdc5aa6972 100644
--- 
a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/GValue.java
+++ 
b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/GValue.java
@@ -57,47 +57,6 @@ public class GValue<V> implements Cloneable, Serializable {
         this.value = value;
     }
 
-    /**
-     * The elements in object array argument are examined to see if they are 
{@link GValue} objects. If they are, they
-     * are preserved as is. If they are not then they are wrapped in a {@link 
GValue} object.
-     */
-    public static <T> GValue<T>[] convertToGValues(final Object[] args) {
-        return Stream.of(args).map(id -> {
-            if (id instanceof GValue)
-                return (GValue<?>) id;
-            else
-                return of(id);
-        }).toArray(GValue[]::new);
-    }
-
-    /**
-     * Converts {@link GValue} objects arguments to their values to prevent 
them from leaking to the Graph API.
-     * Providers extending from this step should use this method to get actual 
values to prevent any {@link GValue}
-     * objects from leaking to the Graph API.
-     */
-    public static Object[] resolveToValues(final List<GValue<?>> gvalues) {
-        // convert gvalues to array
-        final Object[] newIds = new Object[gvalues.size()];
-        int i = 0;
-        for (final GValue<?> gvalue : gvalues) {
-            newIds[i++] = gvalue.get();
-        }
-        return newIds;
-    }
-
-    /**
-     * Converts {@link GValue} objects argument array to their values to 
prevent them from leaking to the Graph API.
-     * Providers extending from this step should use this method to get actual 
values to prevent any {@link GValue}
-     * objects from leaking to the Graph API.
-     */
-    public static Object[] resolveToValues(final GValue<?>[] gvalues) {
-        final Object[] newIds = new Object[gvalues.length];
-        for (int i = 0; i < gvalues.length; i++) {
-            newIds[i] = gvalues[i].get();
-        }
-        return newIds;
-    }
-
     /**
      * Determines if the value held by this object was defined as a variable 
or a literal value. Literal values simply
      * have no name.
@@ -437,4 +396,36 @@ public class GValue<V> implements Cloneable, Serializable {
     public static boolean instanceOfNumber(final Object o) {
         return o instanceof Number || (o instanceof GValue && ((GValue) 
o).getType().isNumeric());
     }
+
+    /**
+     * The elements in object array argument are examined to see if they are 
{@link GValue} objects. If they are, they
+     * are preserved as is. If they are not then they are wrapped in a {@link 
GValue} object.
+     */
+    public static <T> GValue<T>[] ensureGValues(final Object[] args) {
+        return Stream.of(args).map(GValue::of).toArray(GValue[]::new);
+    }
+
+    /**
+     * Converts {@link GValue} objects argument array to their values to 
prevent them from leaking to the Graph API.
+     * Providers extending from this step should use this method to get actual 
values to prevent any {@link GValue}
+     * objects from leaking to the Graph API.
+     */
+    public static Object[] resolveToValues(final GValue<?>[] gvalues) {
+        final Object[] values = new Object[gvalues.length];
+        for (int i = 0; i < gvalues.length; i++) {
+            values[i] = gvalues[i].get();
+        }
+        return values;
+    }
+
+    /**
+     * Takes an argument that is either a {@code GValue} or an object and if 
the former returns the child object and
+     * if the latter returns the object itself.
+     */
+    public static Object valueOf(final Object either) {
+        if (either instanceof GValue)
+            return ((GValue<?>) either).get();
+        else
+            return either;
+    }
 }
diff --git 
a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/filter/CoinStep.java
 
b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/filter/CoinStep.java
index 33b3c80944..5fd63ee3de 100644
--- 
a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/filter/CoinStep.java
+++ 
b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/filter/CoinStep.java
@@ -39,10 +39,6 @@ public final class CoinStep<S> extends FilterStep<S> 
implements Seedable {
     private final Random random = new Random();
     private final GValue<Double> probability;
 
-    /**
-     * @deprecated As of release 3.7.3, replaced by {@link 
#CoinStep(Traversal.Admin, GValue)}
-     */
-    @Deprecated
     public CoinStep(final Traversal.Admin traversal, final double probability) 
{
         super(traversal);
         this.probability = GValue.ofDouble(probability);
diff --git 
a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/GraphStep.java
 
b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/GraphStep.java
index f65ecb7e81..c009588864 100644
--- 
a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/GraphStep.java
+++ 
b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/GraphStep.java
@@ -70,7 +70,7 @@ public class GraphStep<S, E extends Element> extends 
AbstractStep<S, E> implemen
         this.returnClass = returnClass;
 
         // if ids is a single collection like g.V(['a','b','c']), then unroll 
it into an array of ids
-        this.ids = 
GValue.convertToGValues(tryUnrollSingleCollectionArgument(ids));
+        this.ids = 
GValue.ensureGValues(tryUnrollSingleCollectionArgument(ids));
 
         this.isStart = isStart;
 
@@ -169,7 +169,7 @@ public class GraphStep<S, E extends Element> extends 
AbstractStep<S, E> implemen
         this.legacyLogicForPassingNoIds = newIds.length == 1 && ((newIds[0] 
instanceof List && ((List) newIds[0]).isEmpty()) ||
                 (newIds[0] instanceof GValue && ((GValue) 
newIds[0]).getType().isCollection() && ((List) ((GValue) 
newIds[0]).get()).isEmpty()));
 
-        final GValue[] gvalues = 
GValue.convertToGValues(tryUnrollSingleCollectionArgument(newIds));
+        final GValue[] gvalues = 
GValue.ensureGValues(tryUnrollSingleCollectionArgument(newIds));
         this.ids = ArrayUtils.addAll(this.ids, gvalues);
     }
 
diff --git 
a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/VertexStep.java
 
b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/VertexStep.java
index 896934668d..b6230e0538 100644
--- 
a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/VertexStep.java
+++ 
b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/VertexStep.java
@@ -54,7 +54,7 @@ public class VertexStep<E extends Element> extends 
FlatMapStep<Vertex, E> implem
     private final Class<E> returnClass;
 
     public VertexStep(final Traversal.Admin traversal, final Class<E> 
returnClass, final Direction direction, final String... edgeLabels) {
-        this(traversal, returnClass, direction, 
GValue.convertToGValues(edgeLabels));
+        this(traversal, returnClass, direction, 
GValue.ensureGValues(edgeLabels));
     }
 
     public VertexStep(final Traversal.Admin traversal, final Class<E> 
returnClass, final Direction direction, final GValue<String>... edgeLabels) {
diff --git 
a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/sideEffect/InjectStep.java
 
b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/sideEffect/InjectStep.java
index 73bf40b199..fa27d5af92 100644
--- 
a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/sideEffect/InjectStep.java
+++ 
b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/sideEffect/InjectStep.java
@@ -32,7 +32,7 @@ public final class InjectStep<S> extends StartStep<S> {
     @SafeVarargs
     public InjectStep(final Traversal.Admin traversal, final S... injections) {
         super(traversal);
-        this.injections = GValue.convertToGValues(injections);
+        this.injections = GValue.ensureGValues(injections);
         this.start = new 
ArrayIterator<>(GValue.resolveToValues(this.injections));
     }
 
diff --git 
a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/strategy/optimization/InlineFilterStrategy.java
 
b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/strategy/optimization/InlineFilterStrategy.java
index ad0ccf395f..e8b154fef6 100644
--- 
a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/strategy/optimization/InlineFilterStrategy.java
+++ 
b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/strategy/optimization/InlineFilterStrategy.java
@@ -158,7 +158,7 @@ public final class InlineFilterStrategy extends 
AbstractTraversalStrategy<Traver
                 }
             }
             if (!edgeLabels.isEmpty()) {
-                final VertexStep<Edge> newVertexStep = new 
VertexStep<>(traversal, Edge.class, previousStep.getDirection(), 
GValue.convertToGValues(edgeLabels.toArray()));
+                final VertexStep<Edge> newVertexStep = new 
VertexStep<>(traversal, Edge.class, previousStep.getDirection(), 
GValue.ensureGValues(edgeLabels.toArray()));
                 TraversalHelper.replaceStep(previousStep, newVertexStep, 
traversal);
                 TraversalHelper.copyLabels(previousStep, newVertexStep, false);
                 if (step.getHasContainers().isEmpty()) {
diff --git 
a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/util/CollectionUtil.java
 
b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/util/CollectionUtil.java
index 61d9d7573d..27d41d8e18 100644
--- 
a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/util/CollectionUtil.java
+++ 
b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/util/CollectionUtil.java
@@ -32,22 +32,36 @@ import java.util.Map;
 import java.util.Set;
 import java.util.concurrent.ConcurrentHashMap;
 
+/**
+ * Utility class for working with collections and arrays.
+ */
 public final class CollectionUtil {
-    private CollectionUtil() {
-    }
+    private CollectionUtil() { }
 
+    /**
+     * Converts varargs to a {@code List}.
+     */
     public static <E> List<E> asList(final E... elements) {
         return new ArrayList<>(Arrays.asList(elements));
     }
 
+    /**
+     * Converts varargs to a {@code Set}.
+     */
     public static <E> LinkedHashSet<E> asSet(final E... elements) {
         return asSet(Arrays.asList(elements));
     }
 
+    /**
+     * Converts {@code Collection} to a {@code Set}.
+     */
     public static <E> LinkedHashSet<E> asSet(final Collection<E> elements) {
         return new LinkedHashSet<>(elements);
     }
 
+    /**
+     * Converts varargs to a {@code Map} where the elements are key/value 
pairs.
+     */
     public static <K,V> LinkedHashMap<K,V> asMap(final Object... elements) {
         final LinkedHashMap<K,V> map = new LinkedHashMap<>();
         for (int i = 0; i < elements.length; i+=2) {
@@ -58,6 +72,11 @@ public final class CollectionUtil {
         return map;
     }
 
+    /**
+     * Clones a given {@code ConcurrentHashMap} by creating a new map and 
copying all entries from the original map.
+     * If the value of an entry is a {@code Set} or an {@code ArrayList}, a 
deep copy of the value is created.
+     * Otherwise, the value is copied as is.
+     */
     public static <K,V> ConcurrentHashMap<K,V> clone(final 
ConcurrentHashMap<K,V> map) {
         final ConcurrentHashMap<K, V> result = new 
ConcurrentHashMap<>(map.size());
 
diff --git 
a/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/step/GValueTest.java
 
b/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/step/GValueTest.java
index f6aff53c8d..625bfaeb5a 100644
--- 
a/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/step/GValueTest.java
+++ 
b/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/step/GValueTest.java
@@ -34,9 +34,11 @@ import java.util.Set;
 
 import org.junit.Test;
 
+import static org.junit.Assert.assertArrayEquals;
 import static org.junit.Assert.assertEquals;
 import static org.hamcrest.MatcherAssert.assertThat;
 import static org.hamcrest.Matchers.is;
+import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertSame;
 import static org.mockito.Mockito.mock;
 
@@ -475,4 +477,133 @@ public class GValueTest {
     public void valueInstanceOfNumericShouldReturnFalseForNullObject() {
         assertThat(GValue.valueInstanceOfNumeric(null), is(false));
     }
+
+    @Test
+    public void shouldConvertObjectArrayToGValues() {
+        final Object[] input = {1, "string", true};
+        final GValue<?>[] expected = {GValue.of(1), GValue.of("string"), 
GValue.of(true)};
+        final GValue<?>[] result = GValue.ensureGValues(input);
+        assertArrayEquals(expected, result);
+    }
+
+    @Test
+    public void shouldPreserveExistingGValuesInArray() {
+        final GValue<Integer> gValue = GValue.of(123);
+        final Object[] input = {gValue, "string"};
+        final GValue<?>[] expected = {gValue, GValue.of("string")};
+        final GValue<?>[] result = GValue.ensureGValues(input);
+        assertArrayEquals(expected, result);
+    }
+
+    @Test
+    public void shouldHandleEmptyArray() {
+        final Object[] input = {};
+        final GValue<?>[] expected = {};
+        final GValue<?>[] result = GValue.ensureGValues(input);
+        assertArrayEquals(expected, result);
+    }
+
+    @Test
+    public void shouldHandleArrayWithNullValues() {
+        final Object[] input = {null, "string"};
+        final GValue<?>[] expected = {GValue.of(null), GValue.of("string")};
+        final GValue<?>[] result = GValue.ensureGValues(input);
+        assertArrayEquals(expected, result);
+    }
+
+    @Test
+    public void shouldResolveGValuesToValues() {
+        final GValue<?>[] input = {GValue.of(1), GValue.of("string"), 
GValue.of(true)};
+        final Object[] expected = {1, "string", true};
+        final Object[] result = GValue.resolveToValues(input);
+        assertArrayEquals(expected, result);
+    }
+
+    @Test
+    public void shouldHandleEmptyGValuesArray() {
+        final GValue<?>[] input = {};
+        final Object[] expected = {};
+        final Object[] result = GValue.resolveToValues(input);
+        assertArrayEquals(expected, result);
+    }
+
+    @Test
+    public void shouldHandleGValuesArrayWithNullValues() {
+        final GValue<?>[] input = {GValue.of(null), GValue.of("string")};
+        final Object[] expected = {null, "string"};
+        final Object[] result = GValue.resolveToValues(input);
+        assertArrayEquals(expected, result);
+    }
+
+    @Test
+    public void shouldHandleMixedTypeGValuesArray() {
+        final GValue<?>[] input = {GValue.of(1), GValue.of("string"), 
GValue.of(true), GValue.of(123.45)};
+        final Object[] expected = {1, "string", true, 123.45};
+        final Object[] result = GValue.resolveToValues(input);
+        assertArrayEquals(expected, result);
+    }
+
+    @Test
+    public void equalsShouldReturnTrueForSameObject() {
+        final GValue<Integer> gValue = GValue.of(123);
+        assertEquals(true, gValue.equals(gValue));
+    }
+
+    @Test
+    public void equalsShouldReturnFalseForNull() {
+        final GValue<Integer> gValue = GValue.of(123);
+        assertEquals(false, gValue.equals(null));
+    }
+
+    @Test
+    public void equalsShouldReturnFalseForDifferentClass() {
+        final GValue<Integer> gValue = GValue.of(123);
+        assertEquals(false, gValue.equals("string"));
+    }
+
+    @Test
+    public void equalsShouldReturnTrueForEqualGValues() {
+        final GValue<Integer> gValue1 = GValue.of(123);
+        final GValue<Integer> gValue2 = GValue.of(123);
+        assertEquals(true, gValue1.equals(gValue2));
+    }
+
+    @Test
+    public void equalsShouldReturnFalseForDifferentNames() {
+        final GValue<Integer> gValue1 = GValue.of("name1", 123);
+        final GValue<Integer> gValue2 = GValue.of("name2", 123);
+        assertEquals(false, gValue1.equals(gValue2));
+    }
+
+    @Test
+    public void equalsShouldReturnFalseForDifferentTypes() {
+        final GValue<Integer> gValue1 = GValue.of(123);
+        final GValue<String> gValue2 = GValue.of("123");
+        assertEquals(false, gValue1.equals(gValue2));
+    }
+
+    @Test
+    public void equalsShouldReturnFalseForDifferentValues() {
+        final GValue<Integer> gValue1 = GValue.of(123);
+        final GValue<Integer> gValue2 = GValue.of(456);
+        assertEquals(false, gValue1.equals(gValue2));
+    }
+
+    @Test
+    public void valueOfShouldReturnGValueValue() {
+        final GValue<Integer> gValue = GValue.of(123);
+        assertEquals(123, GValue.valueOf(gValue));
+    }
+
+    @Test
+    public void valueOfShouldReturnObjectAsIs() {
+        final String value = "test";
+        assertEquals("test", GValue.valueOf(value));
+    }
+
+    @Test
+    public void valueOfShouldReturnNullForNullInput() {
+        assertNull(GValue.valueOf(null));
+        assertNull(null);
+    }
 }
\ No newline at end of file
diff --git 
a/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/util/CollectionUtilTest.java
 
b/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/util/CollectionUtilTest.java
index 73bd39b415..7b938c1a22 100644
--- 
a/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/util/CollectionUtilTest.java
+++ 
b/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/util/CollectionUtilTest.java
@@ -22,10 +22,20 @@ import org.apache.tinkerpop.gremlin.AssertHelper;
 import org.junit.Test;
 
 import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
 import java.util.HashSet;
+import java.util.LinkedHashMap;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Map;
 import java.util.Set;
 import java.util.concurrent.ConcurrentHashMap;
 
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertThrows;
 import static org.junit.Assert.assertTrue;
 
 public class CollectionUtilTest {
@@ -70,4 +80,104 @@ public class CollectionUtilTest {
         final ConcurrentHashMap<?, ?> cloned = CollectionUtil.clone(source);
         assertTrue(source.equals(cloned));
     }
+
+    @Test
+    public void shouldCloneEmptyConcurrentHashMap() {
+        final ConcurrentHashMap<String, String> source = new 
ConcurrentHashMap<>();
+        final ConcurrentHashMap<?, ?> cloned = CollectionUtil.clone(source);
+        assertTrue(source.equals(cloned));
+    }
+
+    @Test
+    public void shouldCloneConcurrentHashMapWithMixedTypes() {
+        final ConcurrentHashMap<String, Object> source = new 
ConcurrentHashMap<>();
+        source.put("key1", "value1");
+        source.put("key2", new ArrayList<>(Arrays.asList("a", "b")));
+        source.put("key3", new HashSet<>(Arrays.asList("x", "y")));
+
+        final ConcurrentHashMap<?, ?> cloned = CollectionUtil.clone(source);
+        assertTrue(source.equals(cloned));
+    }
+
+    @Test
+    public void shouldAddFirstWhenBothArgumentsNull() {
+        String[] result = CollectionUtil.addFirst(null, null, String.class);
+        assertArrayEquals(new String[]{null}, result);
+    }
+
+    @Test
+    public void shouldAddFirstWhenArrayNull() {
+        String[] result = CollectionUtil.addFirst(null, "element", 
String.class);
+        assertArrayEquals(new String[]{"element"}, result);
+    }
+
+    @Test
+    public void shoulAddFirstWhenNeitherArgumentNull() {
+        Integer[] array = {1, 2, 3};
+        Integer[] result = CollectionUtil.addFirst(array, 0, Integer.class);
+        assertArrayEquals(new Integer[]{0, 1, 2, 3}, result);
+    }
+
+    @Test
+    public void shouldAddFirstWhenEmptyArray() {
+        String[] array = {};
+        String[] result = CollectionUtil.addFirst(array, "element", 
String.class);
+        assertArrayEquals(new String[]{"element"}, result);
+    }
+
+    @Test
+    public void shouldAddFirstWhenIntegers() {
+        Integer[] array = {1, 2, 3};
+        Integer[] result = CollectionUtil.addFirst(array, 0, Integer.class);
+        assertArrayEquals(new Integer[]{0, 1, 2, 3}, result);
+    }
+
+    @Test
+    public void shouldConvertVarargsToList() {
+        List<String> result = CollectionUtil.asList("a", "b", "c");
+        assertEquals(Arrays.asList("a", "b", "c"), result);
+    }
+
+    @Test
+    public void shouldConvertEmptyVarargsToList() {
+        List<String> result = CollectionUtil.asList();
+        assertEquals(Collections.emptyList(), result);
+    }
+
+    @Test
+    public void shouldConvertVarargsToSet() {
+        Set<String> result = CollectionUtil.asSet("a", "b", "c");
+        assertEquals(new LinkedHashSet<>(Arrays.asList("a", "b", "c")), 
result);
+    }
+
+    @Test
+    public void shouldConvertEmptyVarargsToSet() {
+        Set<String> result = CollectionUtil.asSet();
+        assertEquals(new LinkedHashSet<>(), result);
+    }
+
+    @Test
+    public void shouldConvertCollectionToSet() {
+        Collection<String> collection = Arrays.asList("a", "b", "c");
+        Set<String> result = CollectionUtil.asSet(collection);
+        assertEquals(new LinkedHashSet<>(collection), result);
+    }
+
+    @Test
+    public void shouldConvertVarargsToMap() {
+        Map<String, String> result = CollectionUtil.asMap("key1", "value1", 
"key2", "value2");
+        Map<String, String> expected = new LinkedHashMap<>();
+        expected.put("key1", "value1");
+        expected.put("key2", "value2");
+        assertEquals(expected, result);
+    }
+
+    @Test
+    public void shouldConvertVarargsToMapWithOddNumberOfElements() {
+        Map<String, String> result = CollectionUtil.asMap("key1", "value1", 
"key2");
+        Map<String, String> expected = new LinkedHashMap<>();
+        expected.put("key1", "value1");
+        expected.put("key2", null);
+        assertEquals(expected, result);
+    }
 }

Reply via email to