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

spmallette pushed a commit to branch GValueManager3.8-wip
in repository https://gitbox.apache.org/repos/asf/tinkerpop.git


The following commit(s) were added to refs/heads/GValueManager3.8-wip by this 
push:
     new 77cc3392cd wip - GValueManager tests
77cc3392cd is described below

commit 77cc3392cd1ce0288e819bb42180beec49092562
Author: Stephen Mallette <[email protected]>
AuthorDate: Mon Jun 16 15:08:43 2025 -0400

    wip - GValueManager tests
---
 .../gremlin/process/traversal/GValueManager.java   | 146 ++--
 .../process/traversal/GValueManagerTest.java       | 749 +++++++++++++++++++++
 .../traversal/strategy/GValueManagerVerifier.java  |  25 +-
 .../optimization/EarlyLimitStrategyTest.java       |   2 +-
 4 files changed, 835 insertions(+), 87 deletions(-)

diff --git 
a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/GValueManager.java
 
b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/GValueManager.java
index 856af3b874..d4781b6220 100644
--- 
a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/GValueManager.java
+++ 
b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/GValueManager.java
@@ -32,14 +32,12 @@ import 
org.apache.tinkerpop.gremlin.process.traversal.step.stepContract.Predicat
 import 
org.apache.tinkerpop.gremlin.process.traversal.step.stepContract.RangeContract;
 import 
org.apache.tinkerpop.gremlin.process.traversal.step.stepContract.StepContract;
 import 
org.apache.tinkerpop.gremlin.process.traversal.step.stepContract.TailContract;
-import org.apache.tinkerpop.gremlin.process.traversal.step.util.HasContainer;
 import org.apache.tinkerpop.gremlin.structure.Vertex;
 
 import java.io.Serializable;
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Collections;
-import java.util.HashMap;
 import java.util.HashSet;
 import java.util.IdentityHashMap;
 import java.util.Map;
@@ -49,7 +47,7 @@ import java.util.stream.Collectors;
 /**
  * The {@code GValueManager} class is responsible for managing the state of 
{@link GValue} instances and their
  * associations with `Step` objects in a traversal. This class ensures that 
`GValue` instances are properly extracted
- * and stored in a registry, allowing for  dynamic query optimizations and 
state management during traversal execution.
+ * and stored in a registry, allowing for dynamic query optimizations and 
state management during traversal execution.
  * Note that the manager can be locked, at which point it becomes immutable, 
and any attempt to modify it will result
  * in an exception.
  */
@@ -104,9 +102,16 @@ public final class GValueManager implements Serializable, 
Cloneable {
         return locked;
     }
 
+    /**
+     * Merges the internal registries of the current {@code GValueManager} 
into another {@code GValueManager}.
+     * Transfers the state of the {@code gValueRegistry} and {@code 
stepRegistry} from the current instance to the
+     * specified target instance.
+     *
+     * @param other the target {@code GValueManager} into which the current 
instance's registries will be merged
+     */
     public void mergeInto(final GValueManager other) {
         //TODO, how to proceed when locked? Often end up here with locked 
traversals in OLAP as Traversals are cloned after strategies have finished 
running.
-//        if (this.locked) throw Traversal.Exceptions.traversalIsLocked();
+        // if (this.locked) throw Traversal.Exceptions.traversalIsLocked();
 
         //TODO deal with conflicts
         other.gValueRegistry.putAll(gValueRegistry);
@@ -146,6 +151,11 @@ public final class GValueManager implements Serializable, 
Cloneable {
         return (T) stepRegistry.get(step);
     }
 
+    /**
+     * Retrieves a collection of {@link GValue} instances associated with the 
given {@link Step}.  This method checks
+     * whether the provided step is parameterized and, if so, extracts the 
relevant {@link GValue} instances using the
+     * corresponding {@link StepContract}.
+     */
     public Collection<GValue<?>> getGValues(final Step step) {
         if (!isParameterized(step)) return Collections.emptyList();
         return extractGValues(getStepContract(step));
@@ -159,10 +169,14 @@ public final class GValueManager implements Serializable, 
Cloneable {
      * @return {@code true} if the step is present in the step registry or 
predicate registry,
      *         {@code false} otherwise
      */
-    public <S> boolean isParameterized(final Step step) {
+    public boolean isParameterized(final Step step) {
         return this.stepRegistry.containsKey(step);
     }
 
+    /**
+     * Retrieves an unmodifiable set of all registered steps in the manager. 
The returned set is backed by the step
+     * registry and reflects the current state of registered steps.
+     */
     public Set<Step> getSteps() {
         return Collections.unmodifiableSet(stepRegistry.keySet());
     }
@@ -172,11 +186,11 @@ public final class GValueManager implements Serializable, 
Cloneable {
      * {@link #lock()} is called first. Calls to this method prior to locking 
will force iteration through traversal
      * steps to real-time gather then variable names.
      */
-    public Set<String> variableNames() {
+    public Set<String> getVariableNames() {
         if (locked) {
             return Collections.unmodifiableSet(gValueRegistry.keySet());
         } else {
-            return 
Collections.unmodifiableSet(gValues().stream().map(GValue::getName).collect(Collectors.toSet()));
+            return 
Collections.unmodifiableSet(getGValues().stream().map(GValue::getName).collect(Collectors.toSet()));
         }
     }
 
@@ -185,7 +199,7 @@ public final class GValueManager implements Serializable, 
Cloneable {
      * {@link #lock()} is called first. Calls to this method prior to locking 
will force iteration through traversal
      * steps to real-time gather them.
      */
-    public Set<GValue> gValues() {
+    public Set<GValue> getGValues() {
         if (locked) {
             return Collections.unmodifiableSet(new 
HashSet<>(gValueRegistry.values()));
         } else {
@@ -205,39 +219,11 @@ public final class GValueManager implements Serializable, 
Cloneable {
         return gatherStepGValues(traversal, null);
     }
 
-    private Map<Step, Set<GValue>> gatherStepGValues(final Traversal.Admin<?, 
?> traversal, final Map<Step, Set<GValue>> result) {
-        final GValueManager manager = traversal.getGValueManager();
-        final Map<Step, Set<GValue>> r = null == result ? new 
IdentityHashMap<>() : result;
-
-        for (Step step : traversal.getSteps()) {
-            if (manager.isParameterized(step)) {
-                final StepContract contract = manager.getStepContract(step);
-                r.put(step, new HashSet<>(extractGValues(contract)));
-            }
-
-            if (step instanceof TraversalParent) {
-                TraversalParent parent = (TraversalParent) step;
-
-                // Process global children
-                for (Traversal.Admin<?, ?> child : parent.getGlobalChildren()) 
{
-                    gatherStepGValues(child, r);
-                }
-
-                // Process local children
-                for (Traversal.Admin<?, ?> child : parent.getLocalChildren()) {
-                    gatherStepGValues(child, r);
-                }
-            }
-        }
-
-        return r;
-    }
-
     /**
      * Determines whether the manager has step registrations.
      */
     public boolean hasStepRegistrations() {
-        return stepRegistry.isEmpty();
+        return !stepRegistry.isEmpty();
     }
 
     /**
@@ -245,11 +231,11 @@ public final class GValueManager implements Serializable, 
Cloneable {
      * called.
      */
     public boolean hasVariables() {
-        return variableNames().isEmpty();
+        return !getVariableNames().isEmpty();
     }
 
     /**
-     * Delete all data
+     * Delete all data.
      */
     public void reset() {
         stepRegistry.clear();
@@ -257,49 +243,27 @@ public final class GValueManager implements Serializable, 
Cloneable {
     }
 
     /**
-     * Removes the given {@link Step} from the registry.
-     * If the manager is locked, the operation is not permitted and an 
exception is thrown.
+     * Removes the given {@link Step} from the registry. If the manager is 
locked, the operation is not permitted and
+     * an exception is thrown.
      *
      * @param step the {@link Step} to be removed from the manager
+     * @return the removed contract or null if it was no mapping for the key
      * @throws IllegalStateException if the manager is locked
      */
-    public void remove(final Step step) {
+    public StepContract remove(final Step step) {
         if (this.locked) throw Traversal.Exceptions.traversalIsLocked();
         final StepContract removedContract = stepRegistry.remove(step);
 
         if (removedContract != null) {
             removeGValues(extractGValues(removedContract));
         }
-    }
 
-    /**
-     * Removes all potential step contracts for a particular traversal. This 
is not a recursive operation, so use
-     * {@link #removeAllRecursively(Traversal.Admin)} for that case.
-     */
-    public void removeAll(final Traversal.Admin<?, ?> traversal) {
-        if (this.locked) throw Traversal.Exceptions.traversalIsLocked();
-        traversal.getSteps().forEach(this::remove);
+        return removedContract;
     }
 
-    /**
-     * Removes all potential step contracts for a particular traversal. This 
is a recursive operation and will examine
-     * all steps in this traversal as well as associated child traversals.
-     */
-    public void removeAllRecursively(final Traversal.Admin<?, ?> traversal) {
-        if (this.locked) throw Traversal.Exceptions.traversalIsLocked();
-
-        for (Step step : traversal.getSteps()) {
-            this.remove(step);
-            if (step instanceof TraversalParent) {
-                TraversalParent parent = (TraversalParent) step;
-                for (Traversal.Admin<?, ?> child : parent.getGlobalChildren()) 
{
-                    removeAllRecursively(child);
-                }
-                for (Traversal.Admin<?, ?> child : parent.getLocalChildren()) {
-                    removeAllRecursively(child);
-                }
-            }
-        }
+    @Override
+    public GValueManager clone() {
+        return new GValueManager(gValueRegistry, stepRegistry);
     }
 
     private Collection<GValue<?>> extractGValues(final StepContract contract) {
@@ -490,6 +454,10 @@ public final class GValueManager implements Serializable, 
Cloneable {
         return results;
     }
 
+    /**
+     * Registers a collection of {@link GValue} instances into the internal 
registry. This method only
+     * adds {@link GValue} instances that are identified as variables to the 
registry.
+     */
     private void registerGValues(final Collection<GValue<?>> gValues) {
         for (GValue<?> gValue : gValues) {
             if (gValue.isVariable()) {
@@ -498,15 +466,47 @@ public final class GValueManager implements Serializable, 
Cloneable {
         }
     }
 
+    /**
+     * Removes a collection of {@link GValue} instances from the internal 
registry. Iteratively processes each
+     * {@link GValue} to delete its association by name from the registry.
+     */
     private void removeGValues(final Collection<GValue<?>> gValues) {
         for (GValue<?> gValue : gValues) {
             gValueRegistry.remove(gValue.getName(), gValue);
-            // TODO:: cascade to other steps
         }
     }
 
-    @Override
-    public GValueManager clone() {
-        return new GValueManager(gValueRegistry, stepRegistry);
+    /**
+     * Recursively collects mappings of {@link Step} to associated {@link 
GValue} instances from the given traversal
+     * and its child traversals. If a {@link Step} is parameterized, its 
associated {@link GValue}s are extracted
+     * based on its {@link StepContract} and added to the result map.
+     *
+     * @param traversal the traversal from which {@link Step} to {@link 
GValue} mappings are collected
+     * @param result an optional pre-existing map to store the collected 
mappings (nullable, creates a new map if null)
+     * @return a map containing {@link Step} to {@link GValue} mappings for 
the traversal and its children
+     */
+    private Map<Step, Set<GValue>> gatherStepGValues(final Traversal.Admin<?, 
?> traversal, final Map<Step, Set<GValue>> result) {
+        final GValueManager manager = traversal.getGValueManager();
+        final Map<Step, Set<GValue>> r = null == result ? new 
IdentityHashMap<>() : result;
+
+        for (Step step : traversal.getSteps()) {
+            if (manager.isParameterized(step)) {
+                final StepContract contract = manager.getStepContract(step);
+                r.put(step, new HashSet<>(extractGValues(contract)));
+            }
+
+            if (step instanceof TraversalParent) {
+                TraversalParent parent = (TraversalParent) step;
+                for (Traversal.Admin<?, ?> child : parent.getGlobalChildren()) 
{
+                    gatherStepGValues(child, r);
+                }
+
+                for (Traversal.Admin<?, ?> child : parent.getLocalChildren()) {
+                    gatherStepGValues(child, r);
+                }
+            }
+        }
+
+        return r;
     }
 }
diff --git 
a/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/GValueManagerTest.java
 
b/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/GValueManagerTest.java
new file mode 100644
index 0000000000..e4b77fe111
--- /dev/null
+++ 
b/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/GValueManagerTest.java
@@ -0,0 +1,749 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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 org.apache.tinkerpop.gremlin.process.traversal;
+
+import org.apache.tinkerpop.gremlin.process.traversal.step.GValue;
+import org.apache.tinkerpop.gremlin.process.traversal.step.HasContainerHolder;
+import org.apache.tinkerpop.gremlin.process.traversal.step.TraversalParent;
+import 
org.apache.tinkerpop.gremlin.process.traversal.step.stepContract.AddEdgeContract;
+import 
org.apache.tinkerpop.gremlin.process.traversal.step.stepContract.AddPropertyContract;
+import 
org.apache.tinkerpop.gremlin.process.traversal.step.stepContract.AddVertexContract;
+import 
org.apache.tinkerpop.gremlin.process.traversal.step.stepContract.CallContract;
+import 
org.apache.tinkerpop.gremlin.process.traversal.step.stepContract.EdgeLabelContract;
+import 
org.apache.tinkerpop.gremlin.process.traversal.step.stepContract.ElementIdsContract;
+import 
org.apache.tinkerpop.gremlin.process.traversal.step.stepContract.MergeElementContract;
+import 
org.apache.tinkerpop.gremlin.process.traversal.step.stepContract.PredicateContract;
+import 
org.apache.tinkerpop.gremlin.process.traversal.step.stepContract.RangeContract;
+import 
org.apache.tinkerpop.gremlin.process.traversal.step.stepContract.StepContract;
+import 
org.apache.tinkerpop.gremlin.process.traversal.step.stepContract.TailContract;
+import org.apache.tinkerpop.gremlin.process.traversal.step.util.HasContainer;
+import org.apache.tinkerpop.gremlin.structure.Vertex;
+import org.junit.Test;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import static org.mockito.Mockito.withSettings;
+import static org.hamcrest.CoreMatchers.is;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertThat;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+public class GValueManagerTest {
+
+    @Test
+    public void shouldRegisterStep() {
+        final GValueManager manager = new GValueManager();
+        final Step step = mock(Step.class);
+        final StepContract contract = mock(StepContract.class);
+
+        manager.register(step, contract);
+
+        assertThat(manager.isParameterized(step), is(true));
+        assertEquals(contract, manager.getStepContract(step));
+    }
+
+    @Test(expected = IllegalStateException.class)
+    public void shouldThrowExceptionWhenRegisteringStepAfterLock() {
+        final GValueManager manager = new GValueManager();
+        final Step step = mock(Step.class);
+        final StepContract contract = mock(StepContract.class);
+        manager.lock();
+
+        manager.register(step, contract);
+    }
+
+    @Test
+    public void shouldLockManager() {
+        final GValueManager manager = new GValueManager();
+
+        manager.lock();
+
+        assertThat(manager.isLocked(), is(true));
+    }
+
+    @Test
+    public void shouldNotAffectStateWhenLockingTwice() {
+        final GValueManager manager = new GValueManager();
+        manager.lock();
+
+        // second lock should have no effect
+        manager.lock();
+
+        assertThat(manager.isLocked(), is(true));
+    }
+
+    @Test
+    public void shouldMergeIntoAnotherManager() {
+        final GValueManager sourceManager = new GValueManager();
+        final GValueManager targetManager = new GValueManager();
+
+        final Step step = mock(Step.class);
+        final StepContract contract = mock(StepContract.class);
+        sourceManager.register(step, contract);
+
+        sourceManager.mergeInto(targetManager);
+
+        assertThat(targetManager.isParameterized(step), is(true));
+        assertEquals(contract, targetManager.getStepContract(step));
+    }
+
+    @Test
+    public void shouldCopyRegistryState() {
+        final GValueManager manager = new GValueManager();
+        final Step sourceStep = mock(Step.class);
+        final Step targetStep = mock(Step.class);
+        final StepContract contract = mock(StepContract.class);
+
+        manager.register(sourceStep, contract);
+
+        manager.copyRegistryState(sourceStep, targetStep);
+
+        assertThat(manager.isParameterized(targetStep), is(true));
+        assertEquals(contract, manager.getStepContract(targetStep));
+    }
+
+    @Test(expected = IllegalStateException.class)
+    public void shouldThrowExceptionWhenCopyingRegistryStateAfterLock() {
+        final GValueManager manager = new GValueManager();
+        final Step sourceStep = mock(Step.class);
+        final Step targetStep = mock(Step.class);
+
+        final TailContract<GValue<Long>> contract = mock(TailContract.class);
+        when(contract.getLimit()).thenReturn(GValue.ofLong(5L));
+
+        manager.register(sourceStep, contract);
+        manager.lock();
+
+        manager.copyRegistryState(sourceStep, targetStep);
+    }
+
+    @Test
+    public void shouldGetStepContract() {
+        final GValueManager manager = new GValueManager();
+        final Step step = mock(Step.class);
+        final StepContract contract = mock(StepContract.class);
+
+        manager.register(step, contract);
+
+        final StepContract result = manager.getStepContract(step);
+
+        assertEquals(contract, result);
+    }
+
+    @Test
+    public void shouldGetGValuesForStep() {
+        final GValueManager manager = new GValueManager();
+        final Step step = mock(Step.class);
+        final RangeContract<GValue<Long>> contract = mock(RangeContract.class);
+
+        final GValue<Long> lowRange = GValue.ofLong("low", 1L);
+        final GValue<Long> highRange = GValue.ofLong("high", 10L);
+
+        when(contract.getLowRange()).thenReturn(lowRange);
+        when(contract.getHighRange()).thenReturn(highRange);
+
+        manager.register(step, contract);
+
+        final Collection<GValue<?>> result = manager.getGValues(step);
+
+        assertEquals(2, result.size());
+        assertThat(result.contains(lowRange), is(true));
+        assertThat(result.contains(highRange), is(true));
+    }
+
+    @Test
+    public void shouldReturnEmptyCollectionForNonParameterizedStep() {
+        final GValueManager manager = new GValueManager();
+        final Step step = mock(Step.class);
+
+        final Collection<GValue<?>> result = manager.getGValues(step);
+
+        assertThat(result.isEmpty(), is(true));
+    }
+
+    @Test
+    public void shouldCheckIfStepIsParameterized() {
+        final GValueManager manager = new GValueManager();
+        final Step step = mock(Step.class);
+        final StepContract contract = mock(StepContract.class);
+
+        assertThat(manager.isParameterized(step), is(false));
+
+        manager.register(step, contract);
+        assertThat(manager.isParameterized(step), is(true));
+    }
+
+    @Test
+    public void shouldGetAllRegisteredSteps() {
+        final GValueManager manager = new GValueManager();
+        final Step step1 = mock(Step.class);
+        final Step step2 = mock(Step.class);
+        final StepContract contract = mock(StepContract.class);
+
+        manager.register(step1, contract);
+        manager.register(step2, contract);
+
+        final Set<Step> steps = manager.getSteps();
+
+        assertEquals(2, steps.size());
+        assertThat(steps.contains(step1), is(true));
+        assertThat(steps.contains(step2), is(true));
+    }
+
+    @Test
+    public void shouldGetVariableNames() {
+        final GValueManager manager = new GValueManager();
+        final Step step = mock(Step.class);
+        final TailContract<GValue<Long>> contract = mock(TailContract.class);
+
+        final GValue<Long> limit = GValue.ofLong("limit", 5L);
+        when(contract.getLimit()).thenReturn(limit);
+
+        manager.register(step, contract);
+        manager.lock();
+
+        final Set<String> variableNames = manager.getVariableNames();
+
+        assertEquals(1, variableNames.size());
+        assertThat(variableNames.contains("limit"), is(true));
+    }
+
+    @Test
+    public void shouldGetAllGValues() {
+        final GValueManager manager = new GValueManager();
+        final Step step = mock(Step.class);
+        final TailContract<GValue<Long>> contract = mock(TailContract.class);
+
+        final GValue<Long> limit = GValue.ofLong("limit", 5L);
+        when(contract.getLimit()).thenReturn(limit);
+
+        manager.register(step, contract);
+        manager.lock();
+
+        final Set<GValue> gValues = manager.getGValues();
+
+        assertEquals(1, gValues.size());
+        assertThat(gValues.contains(limit), is(true));
+    }
+
+    @Test
+    public void shouldGatherStepGValues() {
+        final GValueManager manager = new GValueManager();
+        final Traversal.Admin<?, ?> traversal = mock(Traversal.Admin.class);
+        final Step step = mock(Step.class);
+        final TailContract<GValue<Long>> contract = mock(TailContract.class);
+
+        final GValue<Long> limit = GValue.ofLong("limit", 5L);
+        when(contract.getLimit()).thenReturn(limit);
+
+        manager.register(step, contract);
+        when(traversal.getGValueManager()).thenReturn(manager);
+        when(traversal.getSteps()).thenReturn(Collections.singletonList(step));
+
+        final Map<Step, Set<GValue>> result = 
manager.gatherStepGValues(traversal);
+
+        assertEquals(1, result.size());
+        assertThat(result.containsKey(step), is(true));
+        assertEquals(1, result.get(step).size());
+        assertThat(result.get(step).contains(limit), is(true));
+    }
+
+    @Test
+    public void shouldGatherStepGValuesRecursivelyFromGlobalChildren() {
+        final GValueManager manager = new GValueManager();
+        final Traversal.Admin<?, ?> parentTraversal = 
mock(Traversal.Admin.class);
+        when(parentTraversal.getGValueManager()).thenReturn(manager);
+
+        final TraversalParent parentStep = mock(TraversalParent.class, 
withSettings().extraInterfaces(Step.class));
+        final Step parentStepAsStep = (Step) parentStep;
+
+        // create global child traversal
+        final Traversal.Admin<?, ?> globalChildTraversal = 
mock(Traversal.Admin.class);
+        when(globalChildTraversal.getGValueManager()).thenReturn(manager);
+
+        // create a step for the global child traversal
+        final Step globalChildStep = mock(Step.class);
+        final TailContract<GValue<Long>> globalChildContract = 
mock(TailContract.class);
+        final GValue<Long> globalChildLimit = GValue.ofLong("globalLimit", 
10L);
+        when(globalChildContract.getLimit()).thenReturn(globalChildLimit);
+
+        // register the global child step
+        manager.register(globalChildStep, globalChildContract);
+
+        // set up the global child traversal
+        
when(globalChildTraversal.getSteps()).thenReturn(Collections.singletonList(globalChildStep));
+
+        // set up the parent step to return the global child traversal
+        when(parentStep.getGlobalChildren()).thenReturn((List) 
Collections.singletonList(globalChildTraversal));
+        when(parentStep.getLocalChildren()).thenReturn((List) 
Collections.emptyList());
+
+        // set up the parent traversal
+        
when(parentTraversal.getSteps()).thenReturn(Collections.singletonList(parentStepAsStep));
+
+        final Map<Step, Set<GValue>> result = 
manager.gatherStepGValues(parentTraversal);
+
+        // Verify the result contains the global child step and its GValue
+        assertEquals(1, result.size());
+        assertThat(result.containsKey(globalChildStep), is(true));
+        assertEquals(1, result.get(globalChildStep).size());
+        assertThat(result.get(globalChildStep).contains(globalChildLimit), 
is(true));
+    }
+
+    @Test
+    public void shouldGatherStepGValuesRecursivelyFromLocalChildren() {
+        final GValueManager manager = new GValueManager();
+        final Traversal.Admin<?, ?> parentTraversal = 
mock(Traversal.Admin.class);
+        when(parentTraversal.getGValueManager()).thenReturn(manager);
+
+        // create a step that implements TraversalParent
+        final TraversalParent parentStep = mock(TraversalParent.class, 
withSettings().extraInterfaces(Step.class));
+        final Step parentStepAsStep = (Step) parentStep;
+
+        // create local child traversal
+        final Traversal.Admin<?, ?> localChildTraversal = 
mock(Traversal.Admin.class);
+        when(localChildTraversal.getGValueManager()).thenReturn(manager);
+
+        // create a step for the local child traversal
+        final Step localChildStep = mock(Step.class);
+        final TailContract<GValue<Long>> localChildContract = 
mock(TailContract.class);
+        final GValue<Long> localChildLimit = GValue.ofLong("localLimit", 20L);
+        when(localChildContract.getLimit()).thenReturn(localChildLimit);
+
+        // register the local child step
+        manager.register(localChildStep, localChildContract);
+
+        // set up the local child traversal
+        
when(localChildTraversal.getSteps()).thenReturn(Collections.singletonList(localChildStep));
+
+        // set up the parent step to return the local child traversal
+        when(parentStep.getGlobalChildren()).thenReturn((List) 
Collections.emptyList());
+        when(parentStep.getLocalChildren()).thenReturn((List) 
Collections.singletonList(localChildTraversal));
+
+        // set up the parent traversal
+        
when(parentTraversal.getSteps()).thenReturn(Collections.singletonList(parentStepAsStep));
+
+        final Map<Step, Set<GValue>> result = 
manager.gatherStepGValues(parentTraversal);
+
+        // Verify the result contains the local child step and its GValue
+        assertEquals(1, result.size());
+        assertThat(result.containsKey(localChildStep), is(true));
+        assertEquals(1, result.get(localChildStep).size());
+        assertThat(result.get(localChildStep).contains(localChildLimit), 
is(true));
+    }
+
+    @Test
+    public void 
shouldGatherStepGValuesRecursivelyFromBothGlobalAndLocalChildren() {
+        final GValueManager manager = new GValueManager();
+        final Traversal.Admin<?, ?> parentTraversal = 
mock(Traversal.Admin.class);
+        when(parentTraversal.getGValueManager()).thenReturn(manager);
+
+        // create a step that implements TraversalParent
+        final TraversalParent parentStep = mock(TraversalParent.class, 
withSettings().extraInterfaces(Step.class));
+        final Step parentStepAsStep = (Step) parentStep;
+
+        // create global child traversal
+        final Traversal.Admin<?, ?> globalChildTraversal = 
mock(Traversal.Admin.class);
+        when(globalChildTraversal.getGValueManager()).thenReturn(manager);
+
+        // create a step for the global child traversal
+        final Step globalChildStep = mock(Step.class);
+        final TailContract<GValue<Long>> globalChildContract = 
mock(TailContract.class);
+        final GValue<Long> globalChildLimit = GValue.ofLong("globalLimit", 
10L);
+        when(globalChildContract.getLimit()).thenReturn(globalChildLimit);
+
+        // register the global child step
+        manager.register(globalChildStep, globalChildContract);
+
+        // set up the global child traversal
+        
when(globalChildTraversal.getSteps()).thenReturn(Collections.singletonList(globalChildStep));
+
+        // create local child traversal
+        final Traversal.Admin<?, ?> localChildTraversal = 
mock(Traversal.Admin.class);
+        when(localChildTraversal.getGValueManager()).thenReturn(manager);
+
+        // create a step for the local child traversal
+        final Step localChildStep = mock(Step.class);
+        final TailContract<GValue<Long>> localChildContract = 
mock(TailContract.class);
+        final GValue<Long> localChildLimit = GValue.ofLong("localLimit", 20L);
+        when(localChildContract.getLimit()).thenReturn(localChildLimit);
+
+        // register the local child step
+        manager.register(localChildStep, localChildContract);
+
+        // set up the local child traversal
+        
when(localChildTraversal.getSteps()).thenReturn(Collections.singletonList(localChildStep));
+
+        // set up the parent step to return both global and local child 
traversals
+        when(parentStep.getGlobalChildren()).thenReturn((List) 
Collections.singletonList(globalChildTraversal));
+        when(parentStep.getLocalChildren()).thenReturn((List) 
Collections.singletonList(localChildTraversal));
+
+        // set up the parent traversal
+        
when(parentTraversal.getSteps()).thenReturn(Collections.singletonList(parentStepAsStep));
+
+        final Map<Step, Set<GValue>> result = 
manager.gatherStepGValues(parentTraversal);
+
+        // verify the result contains both child steps and their GValues
+        assertEquals(2, result.size());
+        assertThat(result.containsKey(globalChildStep), is(true));
+        assertEquals(1, result.get(globalChildStep).size());
+        assertThat(result.get(globalChildStep).contains(globalChildLimit), 
is(true));
+        assertThat(result.containsKey(localChildStep), is(true));
+        assertEquals(1, result.get(localChildStep).size());
+        assertThat(result.get(localChildStep).contains(localChildLimit), 
is(true));
+    }
+
+    @Test
+    public void shouldCheckIfManagerHasStepRegistrations() {
+        final GValueManager manager = new GValueManager();
+
+        assertThat(manager.hasStepRegistrations(), is(false));
+
+        final Step step = mock(Step.class);
+        final StepContract contract = mock(StepContract.class);
+        manager.register(step, contract);
+
+        assertThat(manager.hasStepRegistrations(), is(true));
+    }
+
+    @Test
+    public void shouldCheckIfManagerHasVariables() {
+        final GValueManager manager = new GValueManager();
+
+        assertThat(manager.hasVariables(), is(false));
+
+        final Step step = mock(Step.class);
+        final TailContract<GValue<Long>> contract = mock(TailContract.class);
+        final GValue<Long> limit = GValue.ofLong("limit", 5L);
+        when(contract.getLimit()).thenReturn(limit);
+
+        manager.register(step, contract);
+        manager.lock();
+
+        assertThat(manager.hasVariables(), is(true));
+    }
+
+    @Test
+    public void shouldResetManager() {
+        final GValueManager manager = new GValueManager();
+        final Step step = mock(Step.class);
+        final StepContract contract = mock(StepContract.class);
+
+        manager.register(step, contract);
+
+        manager.reset();
+
+        assertThat(manager.isParameterized(step), is(false));
+        assertThat(manager.getSteps().isEmpty(), is(true));
+    }
+
+    @Test
+    public void shouldRemoveStep() {
+        final GValueManager manager = new GValueManager();
+        final Step step = mock(Step.class);
+
+        final TailContract<GValue<Long>> contract = mock(TailContract.class);
+        when(contract.getLimit()).thenReturn(GValue.ofLong(5L)); // 
Non-variable GValue
+
+        manager.register(step, contract);
+
+        final StepContract removedContract = manager.remove(step);
+
+        assertEquals(contract, removedContract);
+        assertThat(manager.isParameterized(step), is(false));
+    }
+
+    @Test(expected = IllegalStateException.class)
+    public void shouldThrowExceptionWhenRemovingStepAfterLock() {
+        final GValueManager manager = new GValueManager();
+        final Step step = mock(Step.class);
+
+        final TailContract<GValue<Long>> contract = mock(TailContract.class);
+        when(contract.getLimit()).thenReturn(GValue.ofLong(5L));
+
+        manager.register(step, contract);
+        manager.lock();
+
+        manager.remove(step);
+    }
+
+    @Test
+    public void shouldCloneManager() {
+        final GValueManager manager = new GValueManager();
+        final Step step = mock(Step.class);
+        final StepContract contract = mock(StepContract.class);
+
+        manager.register(step, contract);
+
+        final GValueManager clonedManager = manager.clone();
+
+        assertThat(clonedManager.isParameterized(step), is(true));
+        assertEquals(contract, clonedManager.getStepContract(step));
+    }
+
+    @Test
+    public void shouldMergeHasContainerHolders() {
+        final GValueManager manager = new GValueManager();
+        final HasContainerHolderStep<Object, Object> sourceStep = 
mock(HasContainerHolderStep.class);
+        final HasContainerHolderStep<Object, Object> targetStep = 
mock(HasContainerHolderStep.class);
+
+        final HasContainer hasContainer1 = mock(HasContainer.class);
+        final HasContainer hasContainer2 = mock(HasContainer.class);
+
+        final List<HasContainer> sourceHasContainers = new ArrayList<>();
+        sourceHasContainers.add(hasContainer1);
+        sourceHasContainers.add(hasContainer2);
+
+        when(sourceStep.getHasContainers()).thenReturn(sourceHasContainers);
+        manager.register(sourceStep, sourceStep);
+        manager.register(targetStep, targetStep);
+
+        manager.copyRegistryState(sourceStep, targetStep);
+
+        verify(targetStep).addHasContainer(hasContainer1);
+        verify(targetStep).addHasContainer(hasContainer2);
+    }
+
+    @Test
+    public void shouldGetGValuesFromElementIdsContract() {
+        final GValueManager manager = new GValueManager();
+        final Step step = mock(Step.class);
+        final ElementIdsContract<GValue<?>> contract = 
mock(ElementIdsContract.class);
+
+        final GValue<String> id1 = GValue.ofString("var1", "id1");
+        final GValue<String> id2 = GValue.ofString("var2", "id2");
+        final GValue<String> id3 = GValue.ofString(null, "id3"); // 
Non-variable GValue
+        when(contract.getIds()).thenReturn(new GValue[]{id1, id2, id3});
+
+        manager.register(step, contract);
+
+        final Collection<GValue<?>> result = manager.getGValues(step);
+
+        // Only variable GValues should be extracted
+        assertEquals(2, result.size());
+        assertThat(result.contains(id1), is(true));
+        assertThat(result.contains(id2), is(true));
+        assertThat(result.contains(id3), is(false));
+    }
+
+    @Test
+    public void shouldGetGValuesFromAddVertexContract() {
+        final GValueManager manager = new GValueManager();
+        final Step step = mock(Step.class);
+        final AddVertexContract<GValue<String>, String, GValue<?>> contract = 
mock(AddVertexContract.class);
+
+        final GValue<String> label = GValue.ofString("x", "person");
+        final GValue<String> name = GValue.ofString("y", "John");
+        final GValue<Integer> age = GValue.ofInteger("z", 30);
+
+        final Map<String, GValue<?>> properties = new HashMap<>();
+        properties.put("name", name);
+        properties.put("age", age);
+
+        when(contract.getLabel()).thenReturn(label);
+        when(contract.getProperties()).thenReturn(properties);
+
+        manager.register(step, contract);
+
+        final Collection<GValue<?>> result = manager.getGValues(step);
+
+        assertEquals(3, result.size());
+        assertThat(result.contains(label), is(true));
+        assertThat(result.contains(name), is(true));
+        assertThat(result.contains(age), is(true));
+    }
+
+    @Test
+    public void shouldGetGValuesFromAddEdgeContract() {
+        final GValueManager manager = new GValueManager();
+        final Step step = mock(Step.class);
+        final AddEdgeContract<GValue<String>, GValue<Vertex>, GValue<?>, 
GValue<?>> contract = mock(AddEdgeContract.class);
+
+        final GValue<String> edgeLabel = GValue.ofString("labelVar", "knows");
+        final GValue<Vertex> fromVertex = GValue.ofVertex("fromVar", 
mock(Vertex.class));
+        final GValue<Vertex> toVertex = GValue.ofVertex("toVar", 
mock(Vertex.class));
+        final GValue<String> propertyKey = GValue.ofString("keyVar", "core");
+
+        final Map properties = new HashMap<>();
+        properties.put("key", propertyKey);
+
+        when(contract.getLabel()).thenReturn(edgeLabel);
+        when(contract.getFrom()).thenReturn(fromVertex);
+        when(contract.getTo()).thenReturn(toVertex);
+        when(contract.getProperties()).thenReturn(properties);
+
+        manager.register(step, contract);
+
+        final Collection<GValue<?>> result = manager.getGValues(step);
+
+        assertEquals(4, result.size());
+        assertThat(result.contains(edgeLabel), is(true));
+        assertThat(result.contains(fromVertex), is(true));
+        assertThat(result.contains(toVertex), is(true));
+        assertThat(result.contains(propertyKey), is(true));
+    }
+
+    @Test
+    public void shouldGetGValuesFromAddPropertyContract() {
+        final GValueManager manager = new GValueManager();
+        final Step step = mock(Step.class);
+        final AddPropertyContract<GValue<String>, GValue<?>> contract = 
mock(AddPropertyContract.class);
+
+        final GValue propertyValue = GValue.ofString("valueVar", 
"propertyValue");
+        final GValue<Integer> extraProperty = GValue.ofInteger("extraVar", 
100);
+
+        final Map extraProperties = new HashMap<>();
+        extraProperties.put("extraKey", extraProperty);
+
+        when(contract.getValue()).thenReturn(propertyValue);
+        when(contract.getProperties()).thenReturn(extraProperties);
+
+        manager.register(step, contract);
+
+        final Collection<GValue<?>> result = manager.getGValues(step);
+
+        assertEquals(2, result.size());
+        assertThat(result.contains(propertyValue), is(true));
+        assertThat(result.contains(extraProperty), is(true));
+    }
+
+    @Test
+    public void shouldGetGValuesFromMergeElementContract() {
+        final GValueManager manager = new GValueManager();
+        final Step step = mock(Step.class);
+        final MergeElementContract<GValue<Map<?, ?>>> contract = 
mock(MergeElementContract.class);
+
+        final GValue<Map<?, ?>> mergeMap = GValue.ofMap("mergeVar", new 
HashMap<>());
+        final GValue<Map<?, ?>> onCreateMap = GValue.ofMap("createVar", new 
HashMap<>());
+        final GValue<Map<?, ?>> onMatchMap = GValue.ofMap("matchVar", new 
HashMap<>());
+
+        when(contract.getMergeMap()).thenReturn(mergeMap);
+        when(contract.getOnCreateMap()).thenReturn(onCreateMap);
+        when(contract.getOnMatchMap()).thenReturn(onMatchMap);
+
+        manager.register(step, contract);
+
+        final Collection<GValue<?>> result = manager.getGValues(step);
+
+        assertEquals(3, result.size());
+        assertThat(result.contains(mergeMap), is(true));
+        assertThat(result.contains(onCreateMap), is(true));
+        assertThat(result.contains(onMatchMap), is(true));
+    }
+
+    @Test
+    public void shouldGetGValuesFromEdgeLabelContract() {
+        final GValueManager manager = new GValueManager();
+        final Step step = mock(Step.class);
+        final EdgeLabelContract<GValue<String>> contract = 
mock(EdgeLabelContract.class);
+
+        final GValue<String> namedLabel = GValue.ofString("labelVar", 
"relationship");
+        final GValue<String> unnamedLabel = GValue.ofString(null, "meta");
+
+        when(contract.getEdgeLabels()).thenReturn(new GValue[]{namedLabel, 
unnamedLabel});
+
+        manager.register(step, contract);
+
+        final Collection<GValue<?>> result = manager.getGValues(step);
+
+        assertEquals(1, result.size());
+        assertThat(result.contains(namedLabel), is(true));
+        assertThat(result.contains(unnamedLabel), is(false));
+    }
+
+    @Test
+    public void shouldGetGValuesFromCallContract() {
+        final GValueManager manager = new GValueManager();
+        final Step step = mock(Step.class);
+        final CallContract<GValue<Map<?, ?>>> contract = 
mock(CallContract.class);
+
+        final GValue<Map<?, ?>> staticParams = GValue.ofMap("paramsVar", new 
HashMap<>());
+
+        when(contract.getStaticParams()).thenReturn(staticParams);
+
+        manager.register(step, contract);
+
+        final Collection<GValue<?>> result = manager.getGValues(step);
+
+        assertEquals(1, result.size());
+        assertThat(result.contains(staticParams), is(true));
+    }
+
+    @Test
+    public void shouldGetGValuesFromPredicateContract() {
+        final GValueManager manager = new GValueManager();
+        final Step step = mock(Step.class);
+        final GValue<String> predicateValue = GValue.ofString("predVar", 
"test");
+        final PredicateContract contract = () -> new P(Compare.eq, 
predicateValue);
+
+        manager.register(step, contract);
+
+        final Collection<GValue<?>> result = manager.getGValues(step);
+
+        assertEquals(1, result.size());
+        assertThat(result.contains(predicateValue), is(true));
+    }
+
+    @Test
+    public void shouldGetGValuesFromHasContainerHolder() {
+        final GValueManager manager = new GValueManager();
+        final Step step = mock(Step.class);
+
+        // create a real HasContainerHolder implementation
+        final GValue<String> predicateValue = GValue.ofString("predVar", 
"test");
+        final P<?> predicate = new P(Compare.eq, predicateValue);
+
+        final HasContainerHolder contract = new HasContainerHolder() {
+            @Override
+            public List<HasContainer> getHasContainers() {
+                return Collections.emptyList();
+            }
+
+            @Override
+            public void addHasContainer(final HasContainer hasContainer) {
+                // Not needed for this test
+            }
+
+            @Override
+            public Collection<P<?>> getPredicates() {
+                return Collections.singletonList(predicate);
+            }
+        };
+
+        manager.register(step, contract);
+
+        final Collection<GValue<?>> result = manager.getGValues(step);
+
+        assertEquals(1, result.size());
+        assertThat(result.contains(predicateValue), is(true));
+    }
+
+    /**
+     * Interface that combines Step and HasContainerHolder for testing.
+     */
+    private interface HasContainerHolderStep<S, E> extends Step<S, E>, 
HasContainerHolder {
+    }
+}
diff --git 
a/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/strategy/GValueManagerVerifier.java
 
b/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/strategy/GValueManagerVerifier.java
index a6cdad3ad8..90f06edeba 100644
--- 
a/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/strategy/GValueManagerVerifier.java
+++ 
b/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/strategy/GValueManagerVerifier.java
@@ -109,8 +109,8 @@ public class GValueManagerVerifier {
             // Capture pre-strategy state
             final GValueManager manager = traversal.getGValueManager();
             final Map<Step, Set<GValue>> preStepGValues = 
manager.gatherStepGValues(traversal);
-            final Set<String> preVariables = manager.variableNames();
-            final Set<GValue> preGValues = manager.gValues();
+            final Set<String> preVariables = manager.getVariableNames();
+            final Set<GValue> preGValues = manager.getGValues();
 
             // Apply strategies
             final TraversalStrategies traversalStrategies = new 
DefaultTraversalStrategies();
@@ -187,13 +187,12 @@ public class GValueManagerVerifier {
 
             // compute the preStepVariables from the preStepGValues by 
converting the GValue to their name if
             // their isVariable is true
-            this.preStepVariables = preStepGValues.entrySet().stream()
-                    .collect(java.util.stream.Collectors.toMap(
-                            Map.Entry::getKey,
-                            e -> e.getValue().stream()
-                                    .filter(GValue::isVariable)
-                                    .map(GValue::getName)
-                                    
.collect(java.util.stream.Collectors.toSet())
+            this.preStepVariables = preStepGValues.entrySet().stream().
+                    collect(Collectors.toMap(Map.Entry::getKey,
+                            e -> e.getValue().stream().
+                                    filter(GValue::isVariable).
+                                    map(GValue::getName).
+                                    collect(Collectors.toSet())
                     ));
         }
 
@@ -206,7 +205,7 @@ public class GValueManagerVerifier {
          * Verifies that all variables are preserved
          */
         public AfterVerifier<S, E> variablesArePreserved() {
-            final Set<String> currentVariables = manager.variableNames();
+            final Set<String> currentVariables = manager.getVariableNames();
             assertEquals("All variables should be preserved", preVariables, 
currentVariables);
             return this;
         }
@@ -262,7 +261,7 @@ public class GValueManagerVerifier {
             }
 
             // Verify that the same GValues are in the manager
-            final Set<GValue> currentGValues = manager.gValues();
+            final Set<GValue> currentGValues = manager.getGValues();
             assertEquals("GValues in GValueManager should be unchanged", 
preGValues, currentGValues);
 
             return this;
@@ -293,7 +292,7 @@ public class GValueManagerVerifier {
          * {@link GValue} you can have scenarios where there is a mix.
          */
         public T managerIsEmpty() {
-            assertThat(String.format("GValueManager should be empty but 
contains [%s]", manager.gValues()), manager.hasVariables(), is(true));
+            assertThat(String.format("GValueManager should be empty but 
contains [%s]", manager.getGValues()), manager.hasVariables(), is(false));
             return self();
         }
 
@@ -370,7 +369,7 @@ public class GValueManagerVerifier {
          */
         public T hasVariables(final Set<String> variables) {
             // Get variables from the current traversal and all child 
traversals
-            final Set<String> currentVariables = manager.variableNames();
+            final Set<String> currentVariables = manager.getVariableNames();
             assertEquals("Variables should match expected set", variables, 
currentVariables);
             return self();
         }
diff --git 
a/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/strategy/optimization/EarlyLimitStrategyTest.java
 
b/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/strategy/optimization/EarlyLimitStrategyTest.java
index f4415b42eb..6c3add16db 100644
--- 
a/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/strategy/optimization/EarlyLimitStrategyTest.java
+++ 
b/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/strategy/optimization/EarlyLimitStrategyTest.java
@@ -124,7 +124,7 @@ public class EarlyLimitStrategyTest {
             this.original.asAdmin().applyStrategies();
             assertEquals(repr, this.optimized, this.original);
 
-            assertThat(original.asAdmin().getGValueManager().hasVariables(), 
is(true));
+            assertThat(original.asAdmin().getGValueManager().hasVariables(), 
is(false));
         }
     }
 

Reply via email to