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

Cole-Greer pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/tinkerpop.git


The following commit(s) were added to refs/heads/master by this push:
     new 795594a107 Update GraphBinary/GraphSON model test coverage (#3526)
795594a107 is described below

commit 795594a1070ec35672a4096b81a2ebb589b72efa
Author: Ken Hu <[email protected]>
AuthorDate: Mon Jul 20 10:25:02 2026 -0700

    Update GraphBinary/GraphSON model test coverage (#3526)
    
    These changes update the model tests by removing stale tests, adding more 
tests for existing types and new tests for newly supported types.
    
    * Fix empty_bulklist typo in test_graphbinaryv4model.py
    
    * Remove stale model test references to removed Traverser type
    
    * Update GraphBinary/GraphSON model round-trip coverage.
    
    Assisted-by: Codex:gpt-5.5
---
 docs/src/reference/gremlin-variants.asciidoc       |  22 +-
 .../io/graphson/PdtGraphSONSerializersV4.java      |  14 +-
 .../io/graphson/PdtGraphSONSerializersV4Test.java  |  27 +-
 .../Structure/IO/GraphBinary4/Model.cs             | 162 ++++++++++-
 .../Structure/IO/GraphBinary4/RoundTripTests.cs    | 262 +++++++++++++++++-
 .../structure/io/binary/internals/MapSerializer.js |   2 +-
 .../test/unit/graphbinary/model-test.js            | 157 ++++++++---
 .../test/unit/graphbinary/model.js                 | 140 +++++++++-
 .../gremlin_python/structure/io/graphbinaryV4.py   |   2 +-
 .../main/python/tests/unit/structure/io/model.py   | 135 ++++++++-
 .../unit/structure/io/test_graphbinaryv4model.py   | 192 +++++++++++--
 .../GremlinServerSerializationIntegrateTest.java   |   7 +-
 .../io/graphbinary/empty-label-vertex-v4.gbin      | Bin 0 -> 18 bytes
 .../structure/io/graphbinary/empty-string-v4.gbin  | Bin 0 -> 6 bytes
 .../structure/io/graphbinary/empty-tree-v4.gbin    | Bin 0 -> 6 bytes
 .../io/graphbinary/four-byte-char-v4.gbin          | Bin 0 -> 6 bytes
 .../structure/io/graphbinary/merge-in-v-v4.gbin    | Bin 0 -> 11 bytes
 .../io/graphbinary/merge-on-create-v4.gbin         | Bin 0 -> 16 bytes
 .../io/graphbinary/merge-on-match-v4.gbin          | Bin 0 -> 15 bytes
 .../structure/io/graphbinary/merge-out-v-v4.gbin   | Bin 0 -> 12 bytes
 .../io/graphbinary/multi-label-vertex-v4.gbin      | Bin 0 -> 44 bytes
 .../io/graphbinary/nanos-duration-v4.gbin          | Bin 0 -> 14 bytes
 .../io/graphbinary/negative-duration-v4.gbin       | Bin 0 -> 14 bytes
 .../graphbinary/negative-scale-bigdecimal-v4.gbin  | Bin 0 -> 12 bytes
 .../structure/io/graphbinary/null-int-v4.gbin      |   1 +
 .../structure/io/graphbinary/null-list-v4.gbin     |   1 +
 .../structure/io/graphbinary/null-long-v4.gbin     |   1 +
 .../structure/io/graphbinary/null-map-v4.gbin      |   2 +
 .../structure/io/graphbinary/null-set-v4.gbin      |   1 +
 .../structure/io/graphbinary/null-string-v4.gbin   |   1 +
 .../io/graphbinary/ordered-string-int-map-v4.gbin  | Bin 0 -> 111 bytes
 .../io/graphbinary/path-multiple-labels-v4.gbin    | Bin 0 -> 45 bytes
 .../io/graphbinary/path-zero-labels-v4.gbin        | Bin 0 -> 31 bytes
 .../io/graphbinary/point-composite-pdt-v4.gbin     | Bin 0 -> 45 bytes
 .../io/graphbinary/positive-duration-v4.gbin       | Bin 0 -> 14 bytes
 .../io/graphbinary/scale-zero-bigdecimal-v4.gbin   | Bin 0 -> 12 bytes
 .../sign-boundary-neg-biginteger-v4.gbin           | Bin 0 -> 8 bytes
 .../sign-boundary-pos-biginteger-v4.gbin           | Bin 0 -> 8 bytes
 .../graphbinary/small-decimal-bigdecimal-v4.gbin   | Bin 0 -> 12 bytes
 .../io/graphbinary/three-byte-char-v4.gbin         | Bin 0 -> 5 bytes
 .../io/graphbinary/tree-deep-nesting-v4.gbin       | Bin 0 -> 50 bytes
 .../io/graphbinary/tree-mixed-key-types-v4.gbin    | Bin 0 -> 30 bytes
 .../structure/io/graphbinary/tree-null-key-v4.gbin | Bin 0 -> 12 bytes
 ...lti-byte-char-v4.gbin => two-byte-char-v4.gbin} | Bin
 .../io/graphbinary/uint8-primitive-pdt-v4.gbin     | Bin 0 -> 21 bytes
 .../io/graphbinary/zero-bigdecimal-v4.gbin         | Bin 0 -> 11 bytes
 .../io/graphbinary/zero-biginteger-v4.gbin         | Bin 0 -> 7 bytes
 .../io/graphson/empty-label-vertex-v4.json         |  10 +
 .../structure/io/graphson/empty-string-v4.json     |   1 +
 .../structure/io/graphson/merge-in-v-v4.json       |   4 +
 .../structure/io/graphson/merge-on-create-v4.json  |   4 +
 .../structure/io/graphson/merge-on-match-v4.json   |   4 +
 .../structure/io/graphson/merge-out-v-v4.json      |   4 +
 .../io/graphson/multi-label-vertex-v4.json         |  10 +
 .../structure/io/graphson/nanos-duration-v4.json   |   4 +
 .../io/graphson/negative-duration-v4.json          |   4 +
 .../io/graphson/negative-scale-bigdecimal-v4.json  |   4 +
 .../io/graphson/ordered-string-int-map-v4.json     |  22 ++
 .../io/graphson/path-multiple-labels-v4.json       |  16 ++
 .../structure/io/graphson/path-zero-labels-v4.json |  16 ++
 .../io/graphson/point-composite-pdt-v4.json        |  16 ++
 .../io/graphson/positive-duration-v4.json          |   4 +
 .../io/graphson/scale-zero-bigdecimal-v4.json      |   4 +
 .../graphson/sign-boundary-neg-biginteger-v4.json  |   4 +
 .../graphson/sign-boundary-pos-biginteger-v4.json  |   4 +
 .../io/graphson/small-decimal-bigdecimal-v4.json   |   4 +
 ...i-byte-char-v4.json => three-byte-char-v4.json} |   2 +-
 ...lti-byte-char-v4.json => two-byte-char-v4.json} |   0
 .../io/graphson/uint8-primitive-pdt-v4.json        |   7 +
 .../structure/io/graphson/zero-bigdecimal-v4.json  |   4 +
 .../structure/io/graphson/zero-biginteger-v4.json  |   4 +
 .../io/AbstractTypedCompatibilityTest.java         | 304 ++++++++++++++++++++-
 .../tinkerpop/gremlin/structure/io/Model.java      |  81 +++++-
 .../graphbinary/GraphBinaryCompatibilityTest.java  | 104 +++++++
 74 files changed, 1650 insertions(+), 124 deletions(-)

diff --git a/docs/src/reference/gremlin-variants.asciidoc 
b/docs/src/reference/gremlin-variants.asciidoc
index eab512a14f..885e4866c4 100644
--- a/docs/src/reference/gremlin-variants.asciidoc
+++ b/docs/src/reference/gremlin-variants.asciidoc
@@ -737,6 +737,11 @@ that can be used to fulfill the `gremlingo.Set` interface 
if desired.
 * Go does not support ordered maps natively as the built-in `map` type does 
not guarantee iteration order. Traversal
 results which contain maps may not preserve original ordering when 
deserialized into Go's native map types.
 
+* GraphBinary `Char` values are not supported.
+
+* GraphBinary permits map keys of any supported type, including composite 
values such as lists. Go's native `map` type
+only supports comparable keys, so GraphBinary maps with non-comparable keys 
are not supported.
+
 * The `subgraph()`-step returns a detached `*Graph` data container exposing
 `Vertices map[interface{}]*Vertex` and `Edges map[interface{}]*Edge`. The 
result is not a live `Graph` instance:
 mutating the maps has no effect on the source graph, and it cannot be passed 
to `traversal().with(...)`. To
@@ -1265,6 +1270,12 @@ The driver streams and deserializes a response 
incrementally as it arrives only
 is configured, which is the default. Any other `MessageSerializer`, such as a 
GraphSON serializer, causes the driver to
 buffer the full response body before deserializing it.
 
+[[gremlin-java-limitations]]
+=== Limitations
+
+* Java `Character` values do not support values outside the Basic Multilingual 
Plane, which are mainly 4-byte UTF-8
+characters. Those GraphBinary `Char` values are not supported.
+
 [[gremlin-java-gvalue]]
 === GValue Parameterization
 
@@ -2507,12 +2518,15 @@ unwrap('hello');    // 'hello'
 
 * JavaScript's `Number` type is an IEEE 754 double-precision float. `Float`, 
`Byte`, and `Short` values from the server
 are deserialized as `Number` and lose their original type information. Use 
`preciseNumbers: true` to preserve the
-original types — see <<gremlin-javascript-numeric-types>>.
+original types. See <<gremlin-javascript-numeric-types>>.
 * `Long` values outside the safe integer range (|n| > 2^53 - 1) are 
deserialized as `BigInt` to preserve precision.
 Values within the safe range are deserialized as `Number`. The same server 
type may produce different JavaScript types.
 * `Number.isInteger(1.0)` is `true` in JavaScript, so the driver cannot 
distinguish integer values from whole-number
-doubles. `BigDecimal` is not implemented. Typed wrappers (e.g. `toInt()`, 
`toDouble()`) can be used to control the
-exact type sent to the server — see <<gremlin-javascript-numeric-types>>.
+doubles. Typed wrappers (e.g. `toInt()`, `toDouble()`) can be used to control 
the exact type sent to the server. See
+<<gremlin-javascript-numeric-types>>.
+* GraphBinary `BigDecimal`, `Char`, and `Duration` values are not implemented.
+* JavaScript `Date` values do not support the extreme values of GraphBinary's 
`DateTime` maximum and minimum. Those
+GraphBinary `DateTime` values are not supported.
 * The driver applies GremlinLang type suffixes automatically based on value 
characteristics: integers within the 32-bit
 signed range are unsuffixed (Int), integers beyond that up to 
`Number.MAX_SAFE_INTEGER` use the `L` suffix (Long),
 non-integer numbers and integers beyond the safe range use the `D` suffix 
(Double), and `BigInt` values use the `N`
@@ -3144,6 +3158,8 @@ so offset date-time values at those boundaries will fail 
to deserialize.
 `Duration.FOREVER`) that exceed `TimeSpan.MaxValue` or `TimeSpan.MinValue` 
will fail to deserialize.
 * Gremlin's `BigDecimal` supports up to 33 digits of precision while C#'s 
`decimal` type is limited to 28-29
 significant digits, so high-precision values may lose accuracy or fail to 
deserialize.
+* C# `char` values do not support values outside the Basic Multilingual Plane, 
which are mainly 4-byte UTF-8
+characters. Those GraphBinary `Char` values are not supported.
 * C# `Dictionary` does not allow `null` keys, so `Map` results with `null` 
keys (e.g. from `group()` or
 `groupCount()` on a missing property) will fail during deserialization.
 
diff --git 
a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/graphson/PdtGraphSONSerializersV4.java
 
b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/graphson/PdtGraphSONSerializersV4.java
index a4bcd391ad..f9652b2591 100644
--- 
a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/graphson/PdtGraphSONSerializersV4.java
+++ 
b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/graphson/PdtGraphSONSerializersV4.java
@@ -57,12 +57,7 @@ final class PdtGraphSONSerializersV4 {
             jsonGenerator.writeStartObject();
             jsonGenerator.writeStringField("type", pdt.getName());
             jsonGenerator.writeFieldName("fields");
-            jsonGenerator.writeStartObject();
-            for (final Map.Entry<String, Object> entry : 
pdt.getFields().entrySet()) {
-                jsonGenerator.writeFieldName(entry.getKey());
-                jsonGenerator.writeObject(entry.getValue());
-            }
-            jsonGenerator.writeEndObject();
+            jsonGenerator.writeObject(pdt.getFields());
             jsonGenerator.writeEndObject();
         }
     }
@@ -92,12 +87,7 @@ final class PdtGraphSONSerializersV4 {
                     typeName = jsonParser.getText();
                 } else if ("fields".equals(fieldName)) {
                     jsonParser.nextToken();
-                    while (jsonParser.nextToken() != JsonToken.END_OBJECT) {
-                        final String key = jsonParser.getCurrentName();
-                        jsonParser.nextToken();
-                        final Object value = 
deserializationContext.readValue(jsonParser, Object.class);
-                        fields.put(key, value);
-                    }
+                    fields = deserializationContext.readValue(jsonParser, 
Map.class);
                 }
             }
 
diff --git 
a/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/structure/io/graphson/PdtGraphSONSerializersV4Test.java
 
b/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/structure/io/graphson/PdtGraphSONSerializersV4Test.java
index 6da520d0e0..f153e95ce4 100644
--- 
a/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/structure/io/graphson/PdtGraphSONSerializersV4Test.java
+++ 
b/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/structure/io/graphson/PdtGraphSONSerializersV4Test.java
@@ -70,15 +70,18 @@ public class PdtGraphSONSerializersV4Test extends 
AbstractGraphSONTest {
         assertEquals("Point", value.get("type").asText());
 
         final JsonNode fields = value.get("fields");
-        assertEquals("g:Int32", fields.get("x").get("@type").asText());
-        assertEquals(1, fields.get("x").get("@value").asInt());
-        assertEquals("g:Int32", fields.get("y").get("@type").asText());
-        assertEquals(2, fields.get("y").get("@value").asInt());
+        assertEquals("g:Map", fields.get("@type").asText());
+        final JsonNode x = getMapValue(fields, "x");
+        assertEquals("g:Int32", x.get("@type").asText());
+        assertEquals(1, x.get("@value").asInt());
+        final JsonNode y = getMapValue(fields, "y");
+        assertEquals("g:Int32", y.get("@type").asText());
+        assertEquals(2, y.get("@value").asInt());
     }
 
     @Test
     public void shouldDeserializeValidJson() throws Exception {
-        final String json = 
"{\"@type\":\"g:CompositePdt\",\"@value\":{\"type\":\"Point\",\"fields\":{\"x\":{\"@type\":\"g:Int32\",\"@value\":1},\"y\":{\"@type\":\"g:Int32\",\"@value\":2}}}}";
+        final String json = 
"{\"@type\":\"g:CompositePdt\",\"@value\":{\"type\":\"Point\",\"fields\":{\"@type\":\"g:Map\",\"@value\":[\"x\",{\"@type\":\"g:Int32\",\"@value\":1},\"y\",{\"@type\":\"g:Int32\",\"@value\":2}]}}}";
         final CompositePDT pdt = mapper.readValue(json, CompositePDT.class);
 
         assertEquals("Point", pdt.getName());
@@ -117,7 +120,7 @@ public class PdtGraphSONSerializersV4Test extends 
AbstractGraphSONTest {
 
         assertEquals("g:CompositePdt", node.get("@type").asText());
         final JsonNode fields = node.get("@value").get("fields");
-        final JsonNode locationNode = fields.get("location");
+        final JsonNode locationNode = getMapValue(fields, "location");
         assertEquals("g:CompositePdt", locationNode.get("@type").asText());
         assertEquals("Point", locationNode.get("@value").get("type").asText());
 
@@ -318,7 +321,7 @@ public class PdtGraphSONSerializersV4Test extends 
AbstractGraphSONTest {
         final JsonNode node = plainMapper.readTree(json);
 
         assertEquals("g:CompositePdt", node.get("@type").asText());
-        final JsonNode durNode = node.get("@value").get("fields").get("dur");
+        final JsonNode durNode = getMapValue(node.get("@value").get("fields"), 
"dur");
         assertEquals("g:PrimitivePdt", durNode.get("@type").asText());
         assertEquals("Duration", durNode.get("@value").get("type").asText());
         assertEquals("PT1H", durNode.get("@value").get("value").asText());
@@ -332,6 +335,16 @@ public class PdtGraphSONSerializersV4Test extends 
AbstractGraphSONTest {
         assertEquals("PT1H", nestedResult.getValue());
     }
 
+    private JsonNode getMapValue(final JsonNode graphsonMap, final String key) 
{
+        final JsonNode values = graphsonMap.get("@value");
+        for (int ix = 0; ix < values.size(); ix = ix + 2) {
+            if (key.equals(values.get(ix).asText()))
+                return values.get(ix + 1);
+        }
+
+        throw new IllegalArgumentException("Could not find key in GraphSON 
map: " + key);
+    }
+
     // helper types for primitive PDT hydration tests
 
     static class MyDuration {
diff --git 
a/gremlin-dotnet/test/Gremlin.Net.UnitTest/Structure/IO/GraphBinary4/Model.cs 
b/gremlin-dotnet/test/Gremlin.Net.UnitTest/Structure/IO/GraphBinary4/Model.cs
index bc92428e9d..b161044af2 100644
--- 
a/gremlin-dotnet/test/Gremlin.Net.UnitTest/Structure/IO/GraphBinary4/Model.cs
+++ 
b/gremlin-dotnet/test/Gremlin.Net.UnitTest/Structure/IO/GraphBinary4/Model.cs
@@ -23,6 +23,7 @@
 
 using System;
 using System.Collections.Generic;
+using System.Linq;
 using System.Numerics;
 using Gremlin.Net.Process.Traversal;
 using Gremlin.Net.Structure;
@@ -33,12 +34,12 @@ namespace Gremlin.Net.UnitTest.Structure.IO.GraphBinary4
     /// Defines the supported types for GraphBinary 4.0 IO and provides test 
entries for round-trip testing.
     /// 
     /// The following models aren't supported:
-    /// tinker-graph              Graph type not implemented
     /// max-offsetdatetime        DateTimeOffset.MaxValue exceeds 
serialization range
     /// min-offsetdatetime        DateTimeOffset.MinValue exceeds 
serialization range
     /// forever-duration          TimeSpan cannot represent Duration.FOREVER
     /// pos-bigdecimal            Java BigDecimal precision (33 digits) 
exceeds C# decimal (28-29 digits)
     /// neg-bigdecimal            Java BigDecimal precision (33 digits) 
exceeds C# decimal (28-29 digits)
+    /// four-byte-char            C# char cannot represent a supplementary 
Unicode code point as a single scalar value
     /// var-type-map              Dictionary doesn't support null key
     /// </summary>
     public static class Model
@@ -70,11 +71,50 @@ namespace Gremlin.Net.UnitTest.Structure.IO.GraphBinary4
             return tree;
         }
 
+        private static Tree BuildNullKeyTree()
+        {
+            var tree = new Tree();
+            tree.GetOrCreateChild(null);
+            return tree;
+        }
+
+        private static Tree BuildMixedKeyTypesTree()
+        {
+            var tree = new Tree();
+            tree.GetOrCreateChild("name");
+            tree.GetOrCreateChild(123);
+            return tree;
+        }
+
+        private static Tree BuildDeepNestingTree()
+        {
+            var tree = new Tree();
+            
tree.GetOrCreateChild("root").GetOrCreateChild("branch").GetOrCreateChild("leaf");
+            return tree;
+        }
+
         public static Dictionary<string, object?> Entries { get; } = new()
         {
+            // BigDecimal
+            ["zero-bigdecimal"] = 0m,
+            ["scale-zero-bigdecimal"] = 1234m,
+            ["negative-scale-bigdecimal"] = 123400m,
+            ["small-decimal-bigdecimal"] = 12.34m,
+
             // BigInteger
             ["pos-biginteger"] = 
BigInteger.Parse("123456789987654321123456789987654321"),
             ["neg-biginteger"] = 
BigInteger.Parse("-123456789987654321123456789987654321"),
+            ["zero-biginteger"] = BigInteger.Zero,
+            ["sign-boundary-pos-biginteger"] = new BigInteger(128),
+            ["sign-boundary-neg-biginteger"] = new BigInteger(-129),
+
+            // Provider-defined types
+            ["uint8-primitive-pdt"] = new PrimitivePDT("Uint8", "10"),
+            ["point-composite-pdt"] = new CompositePDT("Point", new 
Dictionary<string, object?>
+            {
+                { "x", 1 },
+                { "y", 2 },
+            }),
             
             // Byte (sbyte in C#)
             ["min-byte"] = sbyte.MinValue,  // -128
@@ -108,10 +148,17 @@ namespace Gremlin.Net.UnitTest.Structure.IO.GraphBinary4
             
             // Char
             ["single-byte-char"] = 'a',
-            ["multi-byte-char"] = '\u03A9',  // Greek capital letter Omega
+            ["two-byte-char"] = '\u03A9',  // Greek capital letter Omega
+            ["three-byte-char"] = '\u20AC',
             
             // Null
             ["unspecified-null"] = null,
+            ["null-int"] = null,
+            ["null-long"] = null,
+            ["null-string"] = null,
+            ["null-list"] = null,
+            ["null-map"] = null,
+            ["null-set"] = null,
             
             // Boolean
             ["true-boolean"] = true,
@@ -120,6 +167,7 @@ namespace Gremlin.Net.UnitTest.Structure.IO.GraphBinary4
             // String
             ["single-byte-string"] = "abc",
             ["mixed-string"] = "abc\u0391\u0392\u0393",  // abc + Greek 
letters Alpha, Beta, Gamma
+            ["empty-string"] = "",
             
             // BulkSet (represented as List with duplicates)
             ["var-bulklist"] = new List<object?> { "marko", "josh", "josh" },
@@ -127,11 +175,15 @@ namespace Gremlin.Net.UnitTest.Structure.IO.GraphBinary4
             
             // Duration (TimeSpan)
             ["zero-duration"] = TimeSpan.Zero,
+            ["positive-duration"] = TimeSpan.FromSeconds(123),
+            ["negative-duration"] = TimeSpan.FromSeconds(-123),
+            ["nanos-duration"] = TimeSpan.FromSeconds(123) + 
TimeSpan.FromTicks(4567),
             
             // Edge
             ["traversal-edge"] = new Edge(13, new Vertex(1, "person"), 
"develops", new Vertex(10, "software"),
                 new dynamic[] { SinceProperty }),
             ["no-prop-edge"] = new Edge(13, new Vertex(1, "person"), 
"develops", new Vertex(10, "software")),
+            ["tinker-graph"] = CrewGraphFactory.Create(),
             
             // Int
             ["max-int"] = int.MaxValue,
@@ -154,16 +206,34 @@ namespace Gremlin.Net.UnitTest.Structure.IO.GraphBinary4
                 // Note: null key not supported in C# Dictionary
             },
             ["empty-map"] = new Dictionary<object, object?>(),
+            ["ordered-string-int-map"] = new Dictionary<object, object?>
+            {
+                { "delta", 4 },
+                { "alpha", 1 },
+                { "charlie", 3 },
+                { "bravo", 2 },
+                { "echo", 5 },
+                { "foxtrot", 6 },
+            },
             
             // Path
             ["traversal-path"] = new Path(
                 new List<ISet<string>> { new HashSet<string>(), new 
HashSet<string>(), new HashSet<string>() },
                 new List<object?> { new Vertex(1, "person"), new Vertex(10, 
"software"), new Vertex(11, "software") }),
             ["empty-path"] = new Path(new List<ISet<string>>(), new 
List<object?>()),
+            ["path-zero-labels"] = new Path(
+                new List<ISet<string>> { new HashSet<string>() },
+                new List<object?> { "marko" }),
+            ["path-multiple-labels"] = new Path(
+                new List<ISet<string>> { new HashSet<string> { "a", "b" } },
+                new List<object?> { "marko" }),
 
             // Tree
             ["traversal-tree"] = BuildTraversalTree(),
             ["empty-tree"] = new Tree(),
+            ["tree-null-key"] = BuildNullKeyTree(),
+            ["tree-mixed-key-types"] = BuildMixedKeyTypesTree(),
+            ["tree-deep-nesting"] = BuildDeepNestingTree(),
 
             ["prop-path"] = new Path(
                 new List<ISet<string>> { new HashSet<string>(), new 
HashSet<string>(), new HashSet<string>() },
@@ -199,6 +269,8 @@ namespace Gremlin.Net.UnitTest.Structure.IO.GraphBinary4
             
             // Vertex
             ["no-prop-vertex"] = new Vertex(1, "person"),
+            ["multi-label-vertex"] = new Vertex(1, "person", labels: new[] { 
"person", "employee" }),
+            ["empty-label-vertex"] = new Vertex(1, "", labels: 
Array.Empty<string>()),
             ["traversal-vertex"] = new Vertex(1, "person", new dynamic[]
             {
                 new Property("name", NameMarko, null),
@@ -217,6 +289,92 @@ namespace Gremlin.Net.UnitTest.Structure.IO.GraphBinary4
             
             // Direction enum
             ["out-direction"] = Direction.Out,
+
+            // Merge enum
+            ["merge-on-create"] = Merge.OnCreate,
+            ["merge-on-match"] = Merge.OnMatch,
+            ["merge-out-v"] = Merge.OutV,
+            ["merge-in-v"] = Merge.InV,
         };
+
+        private static class CrewGraphFactory
+        {
+            public static Graph Create()
+            {
+                var graph = new Graph();
+                var v1 = AddVertex(graph, 1, "person",
+                    Vp(0L, "name", "marko"),
+                    Vp(6L, "location", "san diego", new Property("startTime", 
1997, null),
+                        new Property("endTime", 2001, null)),
+                    Vp(7L, "location", "santa cruz", new Property("startTime", 
2001, null),
+                        new Property("endTime", 2004, null)),
+                    Vp(8L, "location", "brussels", new Property("startTime", 
2004, null),
+                        new Property("endTime", 2005, null)),
+                    Vp(9L, "location", "santa fe", new Property("startTime", 
2005, null)));
+                var v7 = AddVertex(graph, 7, "person",
+                    Vp(1L, "name", "stephen"),
+                    Vp(10L, "location", "centreville", new 
Property("startTime", 1990, null),
+                        new Property("endTime", 2000, null)),
+                    Vp(11L, "location", "dulles", new Property("startTime", 
2000, null),
+                        new Property("endTime", 2006, null)),
+                    Vp(12L, "location", "purcellville", new 
Property("startTime", 2006, null)));
+                var v8 = AddVertex(graph, 8, "person",
+                    Vp(2L, "name", "matthias"),
+                    Vp(13L, "location", "bremen", new Property("startTime", 
2004, null),
+                        new Property("endTime", 2007, null)),
+                    Vp(14L, "location", "baltimore", new Property("startTime", 
2007, null),
+                        new Property("endTime", 2011, null)),
+                    Vp(15L, "location", "oakland", new Property("startTime", 
2011, null),
+                        new Property("endTime", 2014, null)),
+                    Vp(16L, "location", "seattle", new Property("startTime", 
2014, null)));
+                var v9 = AddVertex(graph, 9, "person",
+                    Vp(3L, "name", "daniel"),
+                    Vp(17L, "location", "spremberg", new Property("startTime", 
1982, null),
+                        new Property("endTime", 2005, null)),
+                    Vp(18L, "location", "kaiserslautern", new 
Property("startTime", 2005, null),
+                        new Property("endTime", 2009, null)),
+                    Vp(19L, "location", "aachen", new Property("startTime", 
2009, null)));
+                var v10 = AddVertex(graph, 10, "software", Vp(4L, "name", 
"gremlin"));
+                var v11 = AddVertex(graph, 11, "software", Vp(5L, "name", 
"tinkergraph"));
+
+                AddEdge(graph, 13, v1, "develops", v10, new Property("since", 
2009, null));
+                AddEdge(graph, 14, v1, "develops", v11, new Property("since", 
2010, null));
+                AddEdge(graph, 15, v1, "uses", v10, new Property("skill", 4, 
null));
+                AddEdge(graph, 16, v1, "uses", v11, new Property("skill", 5, 
null));
+                AddEdge(graph, 17, v7, "develops", v10, new Property("since", 
2010, null));
+                AddEdge(graph, 18, v7, "develops", v11, new Property("since", 
2011, null));
+                AddEdge(graph, 19, v7, "uses", v10, new Property("skill", 5, 
null));
+                AddEdge(graph, 20, v7, "uses", v11, new Property("skill", 4, 
null));
+                AddEdge(graph, 21, v8, "develops", v10, new Property("since", 
2012, null));
+                AddEdge(graph, 22, v8, "uses", v10, new Property("skill", 3, 
null));
+                AddEdge(graph, 23, v8, "uses", v11, new Property("skill", 3, 
null));
+                AddEdge(graph, 24, v9, "uses", v10, new Property("skill", 5, 
null));
+                AddEdge(graph, 25, v9, "uses", v11, new Property("skill", 3, 
null));
+                AddEdge(graph, 26, v10, "traverses", v11);
+
+                return graph;
+            }
+
+            private static VertexProperty Vp(object id, string label, object? 
value, params Property[] metaProperties)
+            {
+                return new VertexProperty(id, label, value, null, 
metaProperties.Cast<dynamic>().ToArray());
+            }
+
+            private static Vertex AddVertex(Graph graph, object id, string 
label,
+                params VertexProperty[] vertexProperties)
+            {
+                var vertex = new Vertex(id, label, 
vertexProperties.Cast<dynamic>().ToArray());
+                graph.Vertices[id] = vertex;
+                return vertex;
+            }
+
+            private static Edge AddEdge(Graph graph, object id, Vertex outV, 
string label, Vertex inV,
+                params Property[] properties)
+            {
+                var edge = new Edge(id, outV, label, inV, 
properties.Cast<dynamic>().ToArray());
+                graph.Edges[id] = edge;
+                return edge;
+            }
+        }
     }
 }
diff --git 
a/gremlin-dotnet/test/Gremlin.Net.UnitTest/Structure/IO/GraphBinary4/RoundTripTests.cs
 
b/gremlin-dotnet/test/Gremlin.Net.UnitTest/Structure/IO/GraphBinary4/RoundTripTests.cs
index 8622316b49..f1e25c3507 100644
--- 
a/gremlin-dotnet/test/Gremlin.Net.UnitTest/Structure/IO/GraphBinary4/RoundTripTests.cs
+++ 
b/gremlin-dotnet/test/Gremlin.Net.UnitTest/Structure/IO/GraphBinary4/RoundTripTests.cs
@@ -22,9 +22,17 @@
 #endregion
 
 using System;
+using System.Collections.Generic;
 using System.IO;
+using System.Linq;
 using System.Threading.Tasks;
 using Gremlin.Net.Structure.IO.GraphBinary4;
+using GremlinEdge = Gremlin.Net.Structure.Edge;
+using GremlinGraph = Gremlin.Net.Structure.Graph;
+using GremlinPath = Gremlin.Net.Structure.Path;
+using GremlinProperty = Gremlin.Net.Structure.Property;
+using GremlinVertex = Gremlin.Net.Structure.Vertex;
+using GremlinVertexProperty = Gremlin.Net.Structure.VertexProperty;
 using Xunit;
 
 namespace Gremlin.Net.UnitTest.Structure.IO.GraphBinary4
@@ -159,6 +167,134 @@ namespace Gremlin.Net.UnitTest.Structure.IO.GraphBinary4
             };
         }
 
+        private static bool OrderedMapComparator(object? x, object? y)
+        {
+            if (x is not IDictionary<object, object?> expected || y is not 
IDictionary<object, object?> actual)
+                return false;
+            if (expected.Count != actual.Count)
+                return false;
+
+            return expected.Zip(actual).All(pair =>
+                Equals(pair.First.Key, pair.Second.Key) && 
Equals(pair.First.Value, pair.Second.Value));
+        }
+
+        private static bool PathComparator(object? x, object? y)
+        {
+            if (x is not GremlinPath expected || y is not GremlinPath actual)
+                return false;
+            if (!expected.Objects.SequenceEqual(actual.Objects) || 
expected.Labels.Count != actual.Labels.Count)
+                return false;
+
+            for (var ix = 0; ix < expected.Labels.Count; ix++)
+            {
+                if (!expected.Labels[ix].SetEquals(actual.Labels[ix]))
+                    return false;
+            }
+
+            return true;
+        }
+
+        private static bool VertexLabelComparator(object? x, object? y)
+        {
+            if (x is not GremlinVertex expected || y is not GremlinVertex 
actual)
+                return false;
+
+            return expected.Equals(actual) &&
+                   expected.Labels.Count == actual.Labels.Count &&
+                   expected.Labels.All(actual.Labels.Contains);
+        }
+
+        private static bool GraphComparator(object? x, object? y)
+        {
+            if (x is not GremlinGraph expected || y is not GremlinGraph actual)
+                return false;
+            if (expected.Vertices.Count != actual.Vertices.Count || 
expected.Edges.Count != actual.Edges.Count)
+                return false;
+
+            foreach (var pair in expected.Vertices)
+            {
+                if (!actual.Vertices.TryGetValue(pair.Key, out var 
actualVertex) ||
+                    !VertexComparator(pair.Value, actualVertex))
+                    return false;
+            }
+
+            foreach (var pair in expected.Edges)
+            {
+                if (!actual.Edges.TryGetValue(pair.Key, out var actualEdge) || 
!EdgeComparator(pair.Value, actualEdge))
+                    return false;
+            }
+
+            return true;
+        }
+
+        private static bool VertexComparator(GremlinVertex expected, 
GremlinVertex actual)
+        {
+            return expected.Equals(actual) &&
+                   expected.Label == actual.Label &&
+                   LabelsEqual(expected.Labels, actual.Labels) &&
+                   VertexPropertiesEqual(expected.Properties, 
actual.Properties);
+        }
+
+        private static bool EdgeComparator(GremlinEdge expected, GremlinEdge 
actual)
+        {
+            return expected.Equals(actual) &&
+                   expected.Label == actual.Label &&
+                   LabelsEqual(expected.Labels, actual.Labels) &&
+                   Equals(expected.OutV.Id, actual.OutV.Id) &&
+                   Equals(expected.InV.Id, actual.InV.Id) &&
+                   PropertiesEqual(expected.Properties, actual.Properties);
+        }
+
+        private static bool VertexPropertiesEqual(dynamic[] expected, 
dynamic[] actual)
+        {
+            if (expected.Length != actual.Length)
+                return false;
+
+            for (var ix = 0; ix < expected.Length; ix++)
+            {
+                if (expected[ix] is not GremlinVertexProperty expectedProperty 
||
+                    actual[ix] is not GremlinVertexProperty actualProperty ||
+                    !VertexPropertyComparator(expectedProperty, 
actualProperty))
+                    return false;
+            }
+
+            return true;
+        }
+
+        private static bool VertexPropertyComparator(GremlinVertexProperty 
expected, GremlinVertexProperty actual)
+        {
+            return expected.Equals(actual) &&
+                   expected.Label == actual.Label &&
+                   Equals(expected.Value, actual.Value) &&
+                   PropertiesEqual(expected.Properties, actual.Properties);
+        }
+
+        private static bool PropertiesEqual(dynamic[] expected, dynamic[] 
actual)
+        {
+            if (expected.Length != actual.Length)
+                return false;
+
+            for (var ix = 0; ix < expected.Length; ix++)
+            {
+                if (expected[ix] is not GremlinProperty expectedProperty ||
+                    actual[ix] is not GremlinProperty actualProperty ||
+                    !PropertyComparator(expectedProperty, actualProperty))
+                    return false;
+            }
+
+            return true;
+        }
+
+        private static bool PropertyComparator(GremlinProperty expected, 
GremlinProperty actual)
+        {
+            return expected.Key == actual.Key && Equals(expected.Value, 
actual.Value);
+        }
+
+        private static bool LabelsEqual(IReadOnlySet<string> expected, 
IReadOnlySet<string> actual)
+        {
+            return expected.Count == actual.Count && 
expected.All(actual.Contains);
+        }
+
         // BigInteger tests
         [Fact]
         public Task TestPosBigInteger() => Run("pos-biginteger");
@@ -166,6 +302,36 @@ namespace Gremlin.Net.UnitTest.Structure.IO.GraphBinary4
         [Fact]
         public Task TestNegBigInteger() => Run("neg-biginteger");
 
+        [Fact]
+        public Task TestZeroBigInteger() => Run("zero-biginteger");
+
+        [Fact]
+        public Task TestSignBoundaryPosBigInteger() => 
Run("sign-boundary-pos-biginteger");
+
+        [Fact]
+        public Task TestSignBoundaryNegBigInteger() => 
Run("sign-boundary-neg-biginteger");
+
+        // BigDecimal tests
+        [Fact]
+        public Task TestZeroBigDecimal() => Run("zero-bigdecimal");
+
+        [Fact]
+        public Task TestScaleZeroBigDecimal() => Run("scale-zero-bigdecimal");
+
+        [Fact]
+        public Task TestNegativeScaleBigDecimal() =>
+            RunWriteRead("negative-scale-bigdecimal"); // decimal doesn't 
preserve negative scale for byte-exact writes
+
+        [Fact]
+        public Task TestSmallDecimalBigDecimal() => 
Run("small-decimal-bigdecimal");
+
+        // Provider-defined type tests
+        [Fact]
+        public Task TestUint8PrimitivePdt() => Run("uint8-primitive-pdt");
+
+        [Fact]
+        public Task TestPointCompositePdt() => Run("point-composite-pdt");
+
         // Byte tests
         [Fact]
         public Task TestMinByte() => Run("min-byte");
@@ -235,12 +401,33 @@ namespace Gremlin.Net.UnitTest.Structure.IO.GraphBinary4
         public Task TestSingleByteChar() => Run("single-byte-char");
 
         [Fact]
-        public Task TestMultiByteChar() => Run("multi-byte-char");
+        public Task TestTwoByteChar() => Run("two-byte-char");
+
+        [Fact]
+        public Task TestThreeByteChar() => Run("three-byte-char");
 
         // Null test
         [Fact]
         public Task TestUnspecifiedNull() => Run("unspecified-null");
 
+        [Fact]
+        public Task TestNullInt() => RunRead("null-int"); // typed nulls 
deserialize to plain null
+
+        [Fact]
+        public Task TestNullLong() => RunRead("null-long"); // typed nulls 
deserialize to plain null
+
+        [Fact]
+        public Task TestNullString() => RunRead("null-string"); // typed nulls 
deserialize to plain null
+
+        [Fact]
+        public Task TestNullList() => RunRead("null-list"); // typed nulls 
deserialize to plain null
+
+        [Fact]
+        public Task TestNullMap() => RunRead("null-map"); // typed nulls 
deserialize to plain null
+
+        [Fact]
+        public Task TestNullSet() => RunRead("null-set"); // typed nulls 
deserialize to plain null
+
         // Boolean tests
         [Fact]
         public Task TestTrueBoolean() => Run("true-boolean");
@@ -255,17 +442,30 @@ namespace Gremlin.Net.UnitTest.Structure.IO.GraphBinary4
         [Fact]
         public Task TestMixedString() => Run("mixed-string");
 
+        [Fact]
+        public Task TestEmptyString() => Run("empty-string");
+
         // BulkSet tests (read only - BulkSet deserialized as List)
         [Fact]
-        public Task TestVarBulkList() => RunRead("var-bulklist");
+        public Task TestVarBulkList() => RunRead("var-bulklist"); // BulkList 
deserializes as a plain List
 
         [Fact]
-        public Task TestEmptyBulkList() => RunRead("empty-bulklist");
+        public Task TestEmptyBulkList() => RunRead("empty-bulklist"); // 
BulkList deserializes as a plain List
 
         // Duration tests
         [Fact]
         public Task TestZeroDuration() => Run("zero-duration");
 
+        [Fact]
+        public Task TestPositiveDuration() => Run("positive-duration");
+
+        [Fact]
+        public Task TestNegativeDuration() => Run("negative-duration");
+
+        [Fact]
+        public Task TestNanosDuration() =>
+            RunWriteRead("nanos-duration"); // TimeSpan truncates sub-tick 
nanoseconds for byte-exact writes
+
         // Edge tests
         [Fact]
         public Task TestTraversalEdge() => RunWriteRead("traversal-edge"); // 
properties aren't serialized
@@ -298,6 +498,10 @@ namespace Gremlin.Net.UnitTest.Structure.IO.GraphBinary4
         [Fact]
         public Task TestEmptyMap() => Run("empty-map");
 
+        [Fact]
+        public Task TestOrderedStringIntMap() =>
+            RunWriteRead("ordered-string-int-map", OrderedMapComparator); // 
writer doesn't emit the ordered-map value flag
+
         // Path tests
         [Fact]
         public Task TestTraversalPath() => RunWriteRead("traversal-path"); // 
properties written as null not empty list
@@ -305,6 +509,13 @@ namespace Gremlin.Net.UnitTest.Structure.IO.GraphBinary4
         [Fact]
         public Task TestEmptyPath() => Run("empty-path");
 
+        [Fact]
+        public Task TestPathZeroLabels() => Run("path-zero-labels", 
PathComparator);
+
+        [Fact]
+        public Task TestPathMultipleLabels() =>
+            RunWriteRead("path-multiple-labels", PathComparator); // label set 
ordering isn't guaranteed
+
         [Fact]
         public Task TestPropPath() => RunWriteRead("prop-path"); // properties 
aren't serialized
 
@@ -313,19 +524,16 @@ namespace Gremlin.Net.UnitTest.Structure.IO.GraphBinary4
         public Task TestTraversalTree() => RunWriteRead("traversal-tree"); // 
vertex properties aren't serialized
 
         [Fact]
-        public Task TestEmptyTree() => TestEmptyTreeRoundTrip();
+        public Task TestEmptyTree() => Run("empty-tree");
 
-        private async Task TestEmptyTreeRoundTrip()
-        {
-            // no .gbin resource exists for an empty tree, so exercise an 
in-memory round trip
-            var empty = 
(Gremlin.Net.Structure.Tree)Model.Entries["empty-tree"]!;
-            using var writeStream = new MemoryStream();
-            await Writer.WriteAsync(empty, writeStream);
-            using var readStream = new MemoryStream(writeStream.ToArray());
-            var roundTripped = await Reader.ReadAsync(readStream);
-            Assert.Equal(empty, roundTripped);
-            Assert.True(((Gremlin.Net.Structure.Tree)roundTripped!).IsLeaf());
-        }
+        [Fact]
+        public Task TestTreeNullKey() => Run("tree-null-key");
+
+        [Fact]
+        public Task TestTreeMixedKeyTypes() => Run("tree-mixed-key-types");
+
+        [Fact]
+        public Task TestTreeDeepNesting() => Run("tree-deep-nesting");
 
         // Property tests
         [Fact]
@@ -362,6 +570,17 @@ namespace Gremlin.Net.UnitTest.Structure.IO.GraphBinary4
         [Fact]
         public Task TestTraversalVertex() => RunWriteRead("traversal-vertex"); 
// properties aren't serialized
 
+        [Fact]
+        public Task TestTinkerGraph() => Run("tinker-graph", GraphComparator);
+
+        [Fact]
+        public Task TestMultiLabelVertex() =>
+            RunWriteRead("multi-label-vertex", VertexLabelComparator); // 
Vertex equality is id-only
+
+        [Fact]
+        public Task TestEmptyLabelVertex() =>
+            RunWriteRead("empty-label-vertex", VertexLabelComparator); // 
Vertex equality is id-only
+
         // VertexProperty tests
         [Fact]
         public Task TestTraversalVertexProperty() => 
RunWriteRead("traversal-vertexproperty"); // properties aren't serialized
@@ -379,5 +598,18 @@ namespace Gremlin.Net.UnitTest.Structure.IO.GraphBinary4
         // Direction enum test
         [Fact]
         public Task TestOutDirection() => Run("out-direction");
+
+        // Merge enum tests
+        [Fact]
+        public Task TestMergeOnCreate() => Run("merge-on-create");
+
+        [Fact]
+        public Task TestMergeOnMatch() => Run("merge-on-match");
+
+        [Fact]
+        public Task TestMergeOutV() => Run("merge-out-v");
+
+        [Fact]
+        public Task TestMergeInV() => Run("merge-in-v");
     }
 }
diff --git 
a/gremlin-js/gremlin-javascript/lib/structure/io/binary/internals/MapSerializer.js
 
b/gremlin-js/gremlin-javascript/lib/structure/io/binary/internals/MapSerializer.js
index b3db2853c9..6ca8500f28 100644
--- 
a/gremlin-js/gremlin-javascript/lib/structure/io/binary/internals/MapSerializer.js
+++ 
b/gremlin-js/gremlin-javascript/lib/structure/io/binary/internals/MapSerializer.js
@@ -111,7 +111,7 @@ export default class MapSerializer {
     if (value_flag === 0x01) {
       return null;
     }
-    if (value_flag !== 0x00) {
+    if (value_flag !== 0x00 && value_flag !== 0x02) {
       throw new Error(`MapSerializer: unexpected 
{value_flag}=0x${value_flag.toString(16)}`);
     }
     return this.deserializeValue(reader, value_flag, type_code);
diff --git a/gremlin-js/gremlin-javascript/test/unit/graphbinary/model-test.js 
b/gremlin-js/gremlin-javascript/test/unit/graphbinary/model-test.js
index bca8a47d7a..a61bfb566e 100644
--- a/gremlin-js/gremlin-javascript/test/unit/graphbinary/model-test.js
+++ b/gremlin-js/gremlin-javascript/test/unit/graphbinary/model-test.js
@@ -124,13 +124,6 @@ function runRead(name, comparator = assertEqual) {
   });
 }
 
-// unimplemented type
-function skip(name, reason) {
-  describe(name, () => {
-    it.skip(`${reason}`, () => {});
-  });
-}
-
 function assertEqual(actual, expected) {
   if (Number.isNaN(expected) && Number.isNaN(actual)) return;
   if (Object.is(expected, -0) && Object.is(actual, -0)) return;
@@ -165,6 +158,89 @@ function setComparator(actual, expected) {
   assert.deepStrictEqual([...actual].sort(), [...expected].sort());
 }
 
+function orderedMapComparator(actual, expected) {
+  assert.instanceOf(actual, Map);
+  assert.instanceOf(expected, Map);
+  assert.deepStrictEqual([...actual.entries()], [...expected.entries()]);
+}
+
+function treeComparator(actual, expected) {
+  assert.isTrue(actual.equals(expected));
+}
+
+function pathComparator(actual, expected) {
+  assert.deepStrictEqual(actual.objects, expected.objects);
+  assert.strictEqual(actual.labels.length, expected.labels.length);
+  for (let ix = 0; ix < actual.labels.length; ix++) {
+    setComparator(actual.labels[ix], expected.labels[ix]);
+  }
+}
+
+function vertexLabelComparator(actual, expected) {
+  assert.strictEqual(actual.id, expected.id);
+  setComparator(actual.labels, expected.labels);
+}
+
+function propertyComparator(actual, expected) {
+  assert.strictEqual(actual.key, expected.key);
+  assert.deepStrictEqual(actual.value, expected.value);
+}
+
+function vertexPropertyComparator(actual, expected) {
+  assert.strictEqual(actual.id, expected.id);
+  assert.strictEqual(actual.label, expected.label);
+  assert.deepStrictEqual(actual.value, expected.value);
+  assert.strictEqual(actual.properties.length, expected.properties.length);
+  for (let ix = 0; ix < actual.properties.length; ix++) {
+    propertyComparator(actual.properties[ix], expected.properties[ix]);
+  }
+}
+
+function vertexComparator(actual, expected) {
+  assert.strictEqual(actual.id, expected.id);
+  assert.strictEqual(actual.label, expected.label);
+  setComparator(actual.labels, expected.labels);
+  assert.strictEqual(actual.properties.length, expected.properties.length);
+  for (let ix = 0; ix < actual.properties.length; ix++) {
+    vertexPropertyComparator(actual.properties[ix], expected.properties[ix]);
+  }
+}
+
+function edgeComparator(actual, expected) {
+  assert.strictEqual(actual.id, expected.id);
+  assert.strictEqual(actual.label, expected.label);
+  setComparator(actual.labels, expected.labels);
+  assert.strictEqual(actual.outV.id, expected.outV.id);
+  assert.strictEqual(actual.inV.id, expected.inV.id);
+  assert.strictEqual(actual.properties.length, expected.properties.length);
+  for (let ix = 0; ix < actual.properties.length; ix++) {
+    propertyComparator(actual.properties[ix], expected.properties[ix]);
+  }
+}
+
+function graphComparator(actual, expected) {
+  assert.strictEqual(actual.vertices.size, expected.vertices.size);
+  assert.strictEqual(actual.edges.size, expected.edges.size);
+  assert.deepStrictEqual([...actual.vertices.keys()], 
[...expected.vertices.keys()]);
+  assert.deepStrictEqual([...actual.edges.keys()], [...expected.edges.keys()]);
+  for (const [id, expectedVertex] of expected.vertices) {
+    vertexComparator(actual.vertices.get(id), expectedVertex);
+  }
+  for (const [id, expectedEdge] of expected.edges) {
+    edgeComparator(actual.edges.get(id), expectedEdge);
+  }
+}
+
+function primitivePdtComparator(actual, expected) {
+  assert.strictEqual(actual.name, expected.name);
+  assert.strictEqual(actual.value, expected.value);
+}
+
+function compositePdtComparator(actual, expected) {
+  assert.strictEqual(actual.name, expected.name);
+  assert.deepStrictEqual(actual.fields, expected.fields);
+}
+
 function setCardinalityComparator(actual, expected) {
   assert.strictEqual(actual.id, expected.id);
   assert.strictEqual(actual.label, expected.label);
@@ -182,9 +258,14 @@ function invalidDateComparator(actual, expected) {
 }
 
 describe('GraphBinary v4 Model Tests', () => {
-  // run mode (31 entries)
+  // run mode
   run('pos-biginteger');
   run('neg-biginteger');
+  run('zero-biginteger');
+  run('sign-boundary-pos-biginteger');
+  run('sign-boundary-neg-biginteger');
+  run('uint8-primitive-pdt', primitivePdtComparator);
+  run('point-composite-pdt', compositePdtComparator);
   run('empty-binary');
   run('str-binary');
   run('max-double');
@@ -199,6 +280,7 @@ describe('GraphBinary v4 Model Tests', () => {
   run('false-boolean');
   run('single-byte-string');
   run('mixed-string');
+  run('empty-string');
   run('var-type-list');
   run('empty-list');
   run('no-prop-edge');
@@ -207,15 +289,26 @@ describe('GraphBinary v4 Model Tests', () => {
   run('empty-map');
   run('traversal-path');
   run('empty-path');
+  run('path-zero-labels', pathComparator);
+  run('empty-tree', treeComparator);
+  run('tree-null-key', treeComparator);
+  run('tree-mixed-key-types', treeComparator);
+  run('tree-deep-nesting', treeComparator);
   run('edge-property');
   run('null-property');
   run('empty-set');
   run('no-prop-vertex');
+  run('multi-label-vertex', vertexLabelComparator);
+  run('empty-label-vertex', vertexLabelComparator);
   run('id-t');
   run('out-direction');
+  run('merge-on-create');
+  run('merge-on-match');
+  run('merge-out-v');
+  run('merge-in-v');
   run('neg-zero-double', negZeroComparator);
 
-  // runWriteRead mode (23 entries)
+  // runWriteRead mode
   runWriteRead('min-byte');
   runWriteRead('max-byte');
   runWriteRead('max-float');
@@ -239,35 +332,29 @@ describe('GraphBinary v4 Model Tests', () => {
   runWriteRead('meta-vertexproperty');
   runWriteRead('set-cardinality-vertexproperty', setCardinalityComparator);
   runWriteRead('traversal-vertex');
+  // JS deserializes safe Long values to Number, so vertex-property ids can't 
be re-emitted byte-exactly.
+  runWriteRead('tinker-graph', graphComparator);
+  // label set ordering isn't guaranteed after a JS object round trip.
+  runWriteRead('path-multiple-labels', pathComparator);
+  // JS preserves Map entry order, but its writer doesn't emit the ordered-map 
value flag.
+  runWriteRead('ordered-string-int-map', orderedMapComparator);
+  runWriteRead('traversal-tree', treeComparator); // vertex properties aren't 
serialized
 
-  // Tree
-  runWriteRead('traversal-tree'); // vertex properties aren't serialized
-
-  // empty-tree has no .gbin resource, so exercise an in-memory round trip
-  describe('empty-tree', () => {
-    it('in-memory round trip', async () => {
-      const empty = model['empty-tree'];
-      const roundTripped = await anySerializer.deserialize(
-        StreamReader.fromBuffer(anySerializer.serialize(empty)),
-      );
-      assert.deepStrictEqual(roundTripped, empty);
-      assert.isTrue(roundTripped.isLeaf());
-    });
-  });
-
-  // runRead mode (5 entries)
+  // runRead mode
+  // JS Number can't re-emit this as a Float.
   runRead('neg-zero-float', negZeroComparator);
+  // invalid Date models can't be serialized back.
   runRead('max-offsetdatetime', invalidDateComparator);
   runRead('min-offsetdatetime', invalidDateComparator);
+  // properties aren't serialized in JS for this path fixture.
   runRead('prop-path');
+  // this fixture has complex keys that JS can't write back byte-exactly.
   runRead('var-type-map');
-
-  // skip mode (7 entries)
-  skip('single-byte-char', 'Char type (0x80) not implemented');
-  skip('multi-byte-char', 'Char type (0x80) not implemented');
-  skip('tinker-graph', 'Graph type not implemented');
-  skip('pos-bigdecimal', 'BigDecimal type not implemented');
-  skip('neg-bigdecimal', 'BigDecimal type not implemented');
-  skip('forever-duration', 'Duration type not implemented');
-  skip('zero-duration', 'Duration type not implemented');
-});
\ No newline at end of file
+  // typed nulls deserialize to plain null, so the original type can't be 
re-emitted.
+  runRead('null-int');
+  runRead('null-long');
+  runRead('null-string');
+  runRead('null-list');
+  runRead('null-map');
+  runRead('null-set');
+});
diff --git a/gremlin-js/gremlin-javascript/test/unit/graphbinary/model.js 
b/gremlin-js/gremlin-javascript/test/unit/graphbinary/model.js
index 63aa9dbfb3..c06f4c4965 100644
--- a/gremlin-js/gremlin-javascript/test/unit/graphbinary/model.js
+++ b/gremlin-js/gremlin-javascript/test/unit/graphbinary/model.js
@@ -17,15 +17,26 @@
  *  under the License.
  */
 
-import { Vertex, Edge, Property, VertexProperty, Path, Tree } from 
'../../../lib/structure/graph.js';
-import { direction, t } from '../../../lib/process/traversal.js';
+import {
+  Graph,
+  Vertex,
+  Edge,
+  Property,
+  VertexProperty,
+  Path,
+  Tree,
+  CompositePDT,
+  PrimitivePDT
+} from '../../../lib/structure/graph.js';
+import { direction, merge, t } from '../../../lib/process/traversal.js';
 
 /*
  * Unsupported types (no map entries, .gbin files exist but types not 
implemented):
- * - single-byte-char, multi-byte-char (Char 0x80 not implemented)
- * - tinker-graph (Graph type not implemented)
- * - pos-bigdecimal, neg-bigdecimal (BigDecimal type not implemented)
- * - forever-duration, zero-duration (Duration type not implemented)
+ * - single-byte-char, two-byte-char, three-byte-char, four-byte-char (Char 
0x80 not implemented)
+ * - pos-bigdecimal, neg-bigdecimal, zero-bigdecimal, scale-zero-bigdecimal,
+ *   negative-scale-bigdecimal, small-decimal-bigdecimal (BigDecimal type not 
implemented)
+ * - forever-duration, zero-duration, positive-duration, negative-duration,
+ *   nanos-duration (Duration type not implemented)
  */
 
 const model = {};
@@ -33,6 +44,13 @@ const model = {};
 // BigInteger values
 model['pos-biginteger'] = BigInt('123456789987654321123456789987654321');
 model['neg-biginteger'] = BigInt('-123456789987654321123456789987654321');
+model['zero-biginteger'] = 0n;
+model['sign-boundary-pos-biginteger'] = 128n;
+model['sign-boundary-neg-biginteger'] = -129n;
+
+// Provider-defined type values
+model['uint8-primitive-pdt'] = new PrimitivePDT('Uint8', '10');
+model['point-composite-pdt'] = new CompositePDT('Point', { x: 1, y: 2 });
 
 // Byte values (JS has no byte type, deserializes to Number)
 model['min-byte'] = -128;
@@ -64,6 +82,12 @@ model['neg-zero-float'] = -0;
 
 // Null
 model['unspecified-null'] = null;
+model['null-int'] = null;
+model['null-long'] = null;
+model['null-string'] = null;
+model['null-list'] = null;
+model['null-map'] = null;
+model['null-set'] = null;
 
 // Boolean values
 model['true-boolean'] = true;
@@ -72,6 +96,7 @@ model['false-boolean'] = false;
 // String values
 model['single-byte-string'] = 'abc';
 model['mixed-string'] = 'abc\u0391\u0392\u0393';
+model['empty-string'] = '';
 
 // List values
 model['var-bulklist'] = ['marko', 'josh', 'josh'];
@@ -111,6 +136,14 @@ model['var-type-map'] = new Map([
   ['test', 123]
 ]);
 model['empty-map'] = new Map();
+model['ordered-string-int-map'] = new Map([
+  ['delta', 4],
+  ['alpha', 1],
+  ['charlie', 3],
+  ['bravo', 2],
+  ['echo', 5],
+  ['foxtrot', 6]
+]);
 
 // Path values
 model['traversal-path'] = new Path(
@@ -118,6 +151,8 @@ model['traversal-path'] = new Path(
   [new Vertex(1, 'person'), new Vertex(10, 'software'), new Vertex(11, 
'software')]
 );
 model['empty-path'] = new Path([], []);
+model['path-zero-labels'] = new Path([new Set()], ['marko']);
+model['path-multiple-labels'] = new Path([new Set(['a', 'b'])], ['marko']);
 
 // Tree values
 // tree for g.V(10).out().tree(): v[10] -> v[11]
@@ -125,6 +160,16 @@ const traversalTree = new Tree();
 traversalTree.getOrCreateChild(new Vertex(10, 
'software')).getOrCreateChild(new Vertex(11, 'software'));
 model['traversal-tree'] = traversalTree;
 model['empty-tree'] = new Tree();
+const treeNullKey = new Tree();
+treeNullKey.getOrCreateChild(null);
+model['tree-null-key'] = treeNullKey;
+const treeMixedKeyTypes = new Tree();
+treeMixedKeyTypes.getOrCreateChild('name');
+treeMixedKeyTypes.getOrCreateChild(123);
+model['tree-mixed-key-types'] = treeMixedKeyTypes;
+const treeDeepNesting = new Tree();
+treeDeepNesting.getOrCreateChild('root').getOrCreateChild('branch').getOrCreateChild('leaf');
+model['tree-deep-nesting'] = treeDeepNesting;
 
 // Complex path with nested properties
 const propPathVertex = new Vertex(1, 'person', [
@@ -183,6 +228,10 @@ model['set-cardinality-vertexproperty'] = new 
VertexProperty(1, 'person', new Se
 // Enum values
 model['id-t'] = t.id;
 model['out-direction'] = direction.out;
+model['merge-on-create'] = merge.onCreate;
+model['merge-on-match'] = merge.onMatch;
+model['merge-out-v'] = merge.outV;
+model['merge-in-v'] = merge.inV;
 
 // Complex vertex with properties (from .gbin deserialization structure)
 const name = new VertexProperty(0, 'name', 'marko');
@@ -202,9 +251,86 @@ const santaFe = new VertexProperty(9, 'location', 'santa 
fe', [
   new Property('startTime', 2005)
 ]);
 model['traversal-vertex'] = new Vertex(1, 'person', [name, sanDiego, 
santaCruz, brussels, santaFe]);
+model['multi-label-vertex'] = new Vertex(1, ['person', 'employee']);
+model['empty-label-vertex'] = new Vertex(1, 'vertex', [], []);
 
 // DateTime values (invalid dates for overflow cases)
 model['max-offsetdatetime'] = new Date(NaN);  // Year 999999999 overflows JS 
Date
 model['min-offsetdatetime'] = new Date(NaN);  // Year -999999999 overflows JS 
Date
 
-export { model };
\ No newline at end of file
+class CrewGraphFactory {
+  static vertexProperty(id, label, value, metaProperties = []) {
+    return new VertexProperty(
+      id,
+      label,
+      value,
+      metaProperties.map(([key, metaValue]) => new Property(key, metaValue))
+    );
+  }
+
+  static addVertex(graph, id, label, propertySpecs) {
+    const vertex = new Vertex(id, label);
+    for (const spec of propertySpecs) {
+      vertex.properties.push(CrewGraphFactory.vertexProperty(spec[0], spec[1], 
spec[2], spec[3] || []));
+    }
+    graph.vertices.set(id, vertex);
+    return vertex;
+  }
+
+  static addEdge(graph, id, outV, label, inV, properties = []) {
+    const edge = new Edge(id, outV, label, inV, properties.map(([key, value]) 
=> new Property(key, value)));
+    graph.edges.set(id, edge);
+    return edge;
+  }
+
+  static create() {
+    const graph = new Graph();
+    const v1 = CrewGraphFactory.addVertex(graph, 1, 'person', [
+      [0, 'name', 'marko'],
+      [6, 'location', 'san diego', [['startTime', 1997], ['endTime', 2001]]],
+      [7, 'location', 'santa cruz', [['startTime', 2001], ['endTime', 2004]]],
+      [8, 'location', 'brussels', [['startTime', 2004], ['endTime', 2005]]],
+      [9, 'location', 'santa fe', [['startTime', 2005]]]
+    ]);
+    const v7 = CrewGraphFactory.addVertex(graph, 7, 'person', [
+      [1, 'name', 'stephen'],
+      [10, 'location', 'centreville', [['startTime', 1990], ['endTime', 
2000]]],
+      [11, 'location', 'dulles', [['startTime', 2000], ['endTime', 2006]]],
+      [12, 'location', 'purcellville', [['startTime', 2006]]]
+    ]);
+    const v8 = CrewGraphFactory.addVertex(graph, 8, 'person', [
+      [2, 'name', 'matthias'],
+      [13, 'location', 'bremen', [['startTime', 2004], ['endTime', 2007]]],
+      [14, 'location', 'baltimore', [['startTime', 2007], ['endTime', 2011]]],
+      [15, 'location', 'oakland', [['startTime', 2011], ['endTime', 2014]]],
+      [16, 'location', 'seattle', [['startTime', 2014]]]
+    ]);
+    const v9 = CrewGraphFactory.addVertex(graph, 9, 'person', [
+      [3, 'name', 'daniel'],
+      [17, 'location', 'spremberg', [['startTime', 1982], ['endTime', 2005]]],
+      [18, 'location', 'kaiserslautern', [['startTime', 2005], ['endTime', 
2009]]],
+      [19, 'location', 'aachen', [['startTime', 2009]]]
+    ]);
+    const v10 = CrewGraphFactory.addVertex(graph, 10, 'software', [[4, 'name', 
'gremlin']]);
+    const v11 = CrewGraphFactory.addVertex(graph, 11, 'software', [[5, 'name', 
'tinkergraph']]);
+    CrewGraphFactory.addEdge(graph, 13, v1, 'develops', v10, [['since', 
2009]]);
+    CrewGraphFactory.addEdge(graph, 14, v1, 'develops', v11, [['since', 
2010]]);
+    CrewGraphFactory.addEdge(graph, 15, v1, 'uses', v10, [['skill', 4]]);
+    CrewGraphFactory.addEdge(graph, 16, v1, 'uses', v11, [['skill', 5]]);
+    CrewGraphFactory.addEdge(graph, 17, v7, 'develops', v10, [['since', 
2010]]);
+    CrewGraphFactory.addEdge(graph, 18, v7, 'develops', v11, [['since', 
2011]]);
+    CrewGraphFactory.addEdge(graph, 19, v7, 'uses', v10, [['skill', 5]]);
+    CrewGraphFactory.addEdge(graph, 20, v7, 'uses', v11, [['skill', 4]]);
+    CrewGraphFactory.addEdge(graph, 21, v8, 'develops', v10, [['since', 
2012]]);
+    CrewGraphFactory.addEdge(graph, 22, v8, 'uses', v10, [['skill', 3]]);
+    CrewGraphFactory.addEdge(graph, 23, v8, 'uses', v11, [['skill', 3]]);
+    CrewGraphFactory.addEdge(graph, 24, v9, 'uses', v10, [['skill', 5]]);
+    CrewGraphFactory.addEdge(graph, 25, v9, 'uses', v11, [['skill', 3]]);
+    CrewGraphFactory.addEdge(graph, 26, v10, 'traverses', v11);
+    return graph;
+  }
+}
+
+model['tinker-graph'] = CrewGraphFactory.create();
+
+export { model };
diff --git 
a/gremlin-python/src/main/python/gremlin_python/structure/io/graphbinaryV4.py 
b/gremlin-python/src/main/python/gremlin_python/structure/io/graphbinaryV4.py
index 90556996d1..1cf5b0f856 100644
--- 
a/gremlin-python/src/main/python/gremlin_python/structure/io/graphbinaryV4.py
+++ 
b/gremlin-python/src/main/python/gremlin_python/structure/io/graphbinaryV4.py
@@ -1060,4 +1060,4 @@ class PrimitivePDTIO(_GraphBinaryTypeIO):
     def _read_primitive_pdt(cls, b, r):
         name = r.read_object(b)
         value = r.read_object(b)
-        return PrimitivePDT(name, value)
\ No newline at end of file
+        return PrimitivePDT(name, value)
diff --git a/gremlin-python/src/main/python/tests/unit/structure/io/model.py 
b/gremlin-python/src/main/python/tests/unit/structure/io/model.py
index dae5c04297..8410872d06 100644
--- a/gremlin-python/src/main/python/tests/unit/structure/io/model.py
+++ b/gremlin-python/src/main/python/tests/unit/structure/io/model.py
@@ -19,13 +19,13 @@ under the License.
 
 import datetime
 import uuid
-from gremlin_python.statics import short, long, bigint, BigDecimal, SingleByte
-from gremlin_python.structure.graph import Vertex, Edge, Property, 
VertexProperty, Path, Tree
-from gremlin_python.process.traversal import Direction, Traverser, T
+from collections import OrderedDict
+from gremlin_python.statics import short, long, bigint, BigDecimal, 
SingleByte, SingleChar
+from gremlin_python.structure.graph import Graph, Vertex, Edge, Property, 
VertexProperty, Path, Tree, CompositePDT, PrimitivePDT
+from gremlin_python.process.traversal import Direction, Merge, T
 
 """
 The following models aren't supported.
-tinker-graph         Graph type not implemented
 max-offsetdatetime   Too large for datetime
 min-offsetdatetime   Too small for datetime
 forever-duration     Too large for duration
@@ -43,8 +43,17 @@ model = {}
 
 model["pos-bigdecimal"] = BigDecimal(33, 123456789987654321123456789987654321)
 model["neg-bigdecimal"] = BigDecimal(33, -123456789987654321123456789987654321)
+model["zero-bigdecimal"] = BigDecimal(0, 0)
+model["scale-zero-bigdecimal"] = BigDecimal(0, 1234)
+model["negative-scale-bigdecimal"] = BigDecimal(-2, 1234)
+model["small-decimal-bigdecimal"] = BigDecimal(2, 1234)
 model["pos-biginteger"] = bigint(123456789987654321123456789987654321)
 model["neg-biginteger"] = bigint(-123456789987654321123456789987654321)
+model["zero-biginteger"] = bigint(0)
+model["sign-boundary-pos-biginteger"] = bigint(128)
+model["sign-boundary-neg-biginteger"] = bigint(-129)
+model["uint8-primitive-pdt"] = PrimitivePDT("Uint8", "10")
+model["point-composite-pdt"] = CompositePDT("Point", {"x": 1, "y": 2})
 model["min-byte"] = SingleByte(-128)
 model["max-byte"] = SingleByte(127)
 model["empty-binary"] = bytes("", "utf8")
@@ -58,15 +67,27 @@ model["pos-inf-double"] = float('inf')
 model["neg-inf-double"] = float('-inf')
 model["neg-zero-double"] = -0.0
 model["single-byte-char"] = 'a'
-model["multi-byte-char"] = '\u03A9'
+model["two-byte-char"] = '\u03A9'
+model["three-byte-char"] = SingleChar('\u20AC')
+model["four-byte-char"] = SingleChar('\U0001F600')
 model["unspecified-null"] = None
+model["null-int"] = None
+model["null-long"] = None
+model["null-string"] = None
+model["null-list"] = None
+model["null-map"] = None
+model["null-set"] = None
 model["true-boolean"] = True
 model["false-boolean"] = False
 model["single-byte-string"] = "abc"
 model["mixed-string"] = "abc\u0391\u0392\u0393"
+model["empty-string"] = ""
 model["var-bulklist"] = ["marko", "josh", "josh"]
 model["empty-bulklist"] = []
 model["zero-duration"] = datetime.timedelta()
+model["positive-duration"] = datetime.timedelta(seconds=123)
+model["negative-duration"] = datetime.timedelta(seconds=-123)
+model["nanos-duration"] = datetime.timedelta(seconds=123, microseconds=456.789)
 model["traversal-edge"] = Edge(
     13,
     Vertex(1, 'person'),
@@ -93,17 +114,37 @@ model["var-type-map"] = {
     None: None
 }
 model["empty-map"] = {}
+model["ordered-string-int-map"] = OrderedDict([
+    ("delta", 4),
+    ("alpha", 1),
+    ("charlie", 3),
+    ("bravo", 2),
+    ("echo", 5),
+    ("foxtrot", 6)
+])
 model["traversal-path"] = Path(
     [set(), set(), set()],
     [Vertex(1, "person"), Vertex(10, "software"), Vertex(11, "software")]
 )
 model["empty-path"] = Path([], [])
+model["path-zero-labels"] = Path([set()], ["marko"])
+model["path-multiple-labels"] = Path([{"a", "b"}], ["marko"])
 
 # tree for g.V(10).out().tree(): v[10] -> v[11]
 _traversal_tree = Tree()
 _traversal_tree.get_or_create_child(Vertex(10, 
"software")).get_or_create_child(Vertex(11, "software"))
 model["traversal-tree"] = _traversal_tree
 model["empty-tree"] = Tree()
+_tree_null_key = Tree()
+_tree_null_key.get_or_create_child(None)
+model["tree-null-key"] = _tree_null_key
+_tree_mixed_key_types = Tree()
+_tree_mixed_key_types.get_or_create_child("name")
+_tree_mixed_key_types.get_or_create_child(123)
+model["tree-mixed-key-types"] = _tree_mixed_key_types
+_tree_deep_nesting = Tree()
+_tree_deep_nesting.get_or_create_child("root").get_or_create_child("branch").get_or_create_child("leaf")
+model["tree-deep-nesting"] = _tree_deep_nesting
 
 model["prop-path"] = Path(
     [set(), set(), set()],
@@ -135,6 +176,10 @@ model["meta-vertexproperty"] = VertexProperty(1, "person", 
"stephen", None, [Pro
 model["set-cardinality-vertexproperty"] = VertexProperty(1, "person", 
{"stephen", "marko"}, None, [Property("a", "b", None)])
 model["id-t"] = T.id
 model["out-direction"] = Direction.OUT
+model["merge-on-create"] = Merge.on_create
+model["merge-on-match"] = Merge.on_match
+model["merge-out-v"] = Merge.out_v
+model["merge-in-v"] = Merge.in_v
 
 santa_fe = VertexProperty(
     9, "location", "santa fe", None,
@@ -158,4 +203,82 @@ model["traversal-vertex"] = Vertex(1, "person", [
     Property("name", name, None), Property("location", [
         san_diego, santa_cruz, brussels, santa_fe
     ], None)])
-model["vertex-traverser"] = Traverser(model["traversal-vertex"], 1)
+model["multi-label-vertex"] = Vertex(1, labels=["person", "employee"])
+model["empty-label-vertex"] = Vertex(1, labels=[])
+
+
+class _CrewGraphFactory:
+    @staticmethod
+    def vertex_property(id, label, value, vertex, meta_properties=None):
+        vp = VertexProperty(id, label, value, vertex)
+        for key, meta_value in meta_properties or []:
+            vp.properties.append(Property(key, meta_value, vp))
+        return vp
+
+    @staticmethod
+    def add_vertex(graph, id, label, property_specs):
+        vertex = Vertex(id, label)
+        for spec in property_specs:
+            vertex.properties.append(
+                _CrewGraphFactory.vertex_property(
+                    spec[0], spec[1], spec[2], vertex, spec[3] if len(spec) > 
3 else []))
+        graph.vertices[id] = vertex
+        return vertex
+
+    @staticmethod
+    def add_edge(graph, id, out_v, label, in_v, properties=None):
+        edge = Edge(id, out_v, label, in_v)
+        for key, value in properties or []:
+            edge.properties.append(Property(key, value, edge))
+        graph.edges[id] = edge
+        return edge
+
+    @staticmethod
+    def create():
+        graph = Graph()
+        v1 = _CrewGraphFactory.add_vertex(graph, 1, "person", [
+            (0, "name", "marko"),
+            (6, "location", "san diego", [("startTime", 1997), ("endTime", 
2001)]),
+            (7, "location", "santa cruz", [("startTime", 2001), ("endTime", 
2004)]),
+            (8, "location", "brussels", [("startTime", 2004), ("endTime", 
2005)]),
+            (9, "location", "santa fe", [("startTime", 2005)])
+        ])
+        v7 = _CrewGraphFactory.add_vertex(graph, 7, "person", [
+            (1, "name", "stephen"),
+            (10, "location", "centreville", [("startTime", 1990), ("endTime", 
2000)]),
+            (11, "location", "dulles", [("startTime", 2000), ("endTime", 
2006)]),
+            (12, "location", "purcellville", [("startTime", 2006)])
+        ])
+        v8 = _CrewGraphFactory.add_vertex(graph, 8, "person", [
+            (2, "name", "matthias"),
+            (13, "location", "bremen", [("startTime", 2004), ("endTime", 
2007)]),
+            (14, "location", "baltimore", [("startTime", 2007), ("endTime", 
2011)]),
+            (15, "location", "oakland", [("startTime", 2011), ("endTime", 
2014)]),
+            (16, "location", "seattle", [("startTime", 2014)])
+        ])
+        v9 = _CrewGraphFactory.add_vertex(graph, 9, "person", [
+            (3, "name", "daniel"),
+            (17, "location", "spremberg", [("startTime", 1982), ("endTime", 
2005)]),
+            (18, "location", "kaiserslautern", [("startTime", 2005), 
("endTime", 2009)]),
+            (19, "location", "aachen", [("startTime", 2009)])
+        ])
+        v10 = _CrewGraphFactory.add_vertex(graph, 10, "software", [(4, "name", 
"gremlin")])
+        v11 = _CrewGraphFactory.add_vertex(graph, 11, "software", [(5, "name", 
"tinkergraph")])
+        _CrewGraphFactory.add_edge(graph, 13, v1, "develops", v10, [("since", 
2009)])
+        _CrewGraphFactory.add_edge(graph, 14, v1, "develops", v11, [("since", 
2010)])
+        _CrewGraphFactory.add_edge(graph, 15, v1, "uses", v10, [("skill", 4)])
+        _CrewGraphFactory.add_edge(graph, 16, v1, "uses", v11, [("skill", 5)])
+        _CrewGraphFactory.add_edge(graph, 17, v7, "develops", v10, [("since", 
2010)])
+        _CrewGraphFactory.add_edge(graph, 18, v7, "develops", v11, [("since", 
2011)])
+        _CrewGraphFactory.add_edge(graph, 19, v7, "uses", v10, [("skill", 5)])
+        _CrewGraphFactory.add_edge(graph, 20, v7, "uses", v11, [("skill", 4)])
+        _CrewGraphFactory.add_edge(graph, 21, v8, "develops", v10, [("since", 
2012)])
+        _CrewGraphFactory.add_edge(graph, 22, v8, "uses", v10, [("skill", 3)])
+        _CrewGraphFactory.add_edge(graph, 23, v8, "uses", v11, [("skill", 3)])
+        _CrewGraphFactory.add_edge(graph, 24, v9, "uses", v10, [("skill", 5)])
+        _CrewGraphFactory.add_edge(graph, 25, v9, "uses", v11, [("skill", 3)])
+        _CrewGraphFactory.add_edge(graph, 26, v10, "traverses", v11)
+        return graph
+
+
+model["tinker-graph"] = _CrewGraphFactory.create()
diff --git 
a/gremlin-python/src/main/python/tests/unit/structure/io/test_graphbinaryv4model.py
 
b/gremlin-python/src/main/python/tests/unit/structure/io/test_graphbinaryv4model.py
index 4ebc739584..dc8ed2a34f 100644
--- 
a/gremlin-python/src/main/python/tests/unit/structure/io/test_graphbinaryv4model.py
+++ 
b/gremlin-python/src/main/python/tests/unit/structure/io/test_graphbinaryv4model.py
@@ -38,6 +38,37 @@ test_resource_directory = 
os.environ.get('IO_TEST_DIRECTORY', default_dir + grem
 writer = GraphBinaryWriter()
 reader = GraphBinaryReader()
 
+def decimal_cmp(x, y):
+    return x.scale == y.scale and x.unscaled_value == y.unscaled_value and 
x.value == y.value
+
+def vertex_label_cmp(x, y):
+    return x == y and x.labels == y.labels
+
+def property_cmp(x, y):
+    return x.key == y.key and x.value == y.value
+
+def vertex_property_cmp(x, y):
+    return x == y and x.label == y.label and x.value == y.value and \
+           len(x.properties) == len(y.properties) and \
+           all(property_cmp(xp, yp) for xp, yp in zip(x.properties, 
y.properties))
+
+def vertex_cmp(x, y):
+    return x == y and x.label == y.label and x.labels == y.labels and \
+           len(x.properties) == len(y.properties) and \
+           all(vertex_property_cmp(xp, yp) for xp, yp in zip(x.properties, 
y.properties))
+
+def edge_cmp(x, y):
+    return x == y and x.label == y.label and x.labels == y.labels and \
+           x.outV == y.outV and x.inV == y.inV and \
+           len(x.properties) == len(y.properties) and \
+           all(property_cmp(xp, yp) for xp, yp in zip(x.properties, 
y.properties))
+
+def graph_cmp(x, y):
+    return x.vertices.keys() == y.vertices.keys() and \
+           x.edges.keys() == y.edges.keys() and \
+           all(vertex_cmp(x.vertices[k], y.vertices[k]) for k in 
x.vertices.keys()) and \
+           all(edge_cmp(x.edges[k], y.edges[k]) for k in x.edges.keys())
+
 def get_entry(title):
     return model[title]
 
@@ -47,23 +78,29 @@ def read_file_by_name(resource_name):
         return bytearray(resource_file.read())
 
 def test_pos_bigdecimal():
-    def decimal_cmp(x, y):
-        if x.scale == y.scale and x.unscaled_value == y.unscaled_value and 
x.value == y.value:
-            return True
-        else:
-            return False
-
+    # BigDecimal writes through Python BigInteger, which may not preserve 
byte-exact sign padding.
     run_writeread("pos-bigdecimal", decimal_cmp)
 
 def test_neg_bigdecimal():
-    def decimal_cmp(x, y):
-        if x.scale == y.scale and x.unscaled_value == y.unscaled_value and 
x.value == y.value:
-            return True
-        else:
-            return False
-
+    # BigDecimal writes through Python BigInteger, which may not preserve 
byte-exact sign padding.
     run_writeread("neg-bigdecimal", decimal_cmp)
 
+def test_zero_bigdecimal():
+    # Use the same reduced mode as other Python BigDecimal model cases.
+    run_writeread("zero-bigdecimal", decimal_cmp)
+
+def test_scale_zero_bigdecimal():
+    # Use the same reduced mode as other Python BigDecimal model cases.
+    run_writeread("scale-zero-bigdecimal", decimal_cmp)
+
+def test_negative_scale_bigdecimal():
+    # Use the same reduced mode as other Python BigDecimal model cases.
+    run_writeread("negative-scale-bigdecimal", decimal_cmp)
+
+def test_small_decimal_bigdecimal():
+    # Use the same reduced mode as other Python BigDecimal model cases.
+    run_writeread("small-decimal-bigdecimal", decimal_cmp)
+
 def test_pos_biginteger():
     # gremlin-python adds an extra 0 byte to the value.
     run_writeread("pos-biginteger")
@@ -72,6 +109,25 @@ def test_neg_biginteger():
     # gremlin-python adds an extra 0 byte to the value.
     run_writeread("neg-biginteger")
 
+def test_zero_biginteger():
+    # gremlin-python may add an extra sign-padding byte to the value.
+    run_writeread("zero-biginteger")
+
+def test_sign_boundary_pos_biginteger():
+    # gremlin-python may add an extra sign-padding byte to the value.
+    run_writeread("sign-boundary-pos-biginteger")
+
+def test_sign_boundary_neg_biginteger():
+    # gremlin-python can't serialize -129 because BigIntIO computes a byte 
length
+    # that is too small for signed two's-complement encoding.
+    run_read("sign-boundary-neg-biginteger")
+
+def test_uint8_primitive_pdt():
+    run("uint8-primitive-pdt")
+
+def test_point_composite_pdt():
+    run("point-composite-pdt")
+
 def test_min_byte():
     run("min-byte")
     
@@ -116,6 +172,17 @@ def test_neg_zero_double():
 def test_zero_duration():
     run("zero-duration")
 
+def test_positive_duration():
+    run("positive-duration")
+
+def test_negative_duration():
+    # timedelta.seconds normalizes negative values, so the writer can't 
preserve the wire value.
+    run_read("negative-duration")
+
+def test_nanos_duration():
+    # Python timedelta only preserves microseconds, not arbitrary nanoseconds.
+    run_read("nanos-duration")
+
 def test_traversal_edge():
     # properties aren't serialized in gremlin-python
     run_writeread("traversal-edge")
@@ -151,6 +218,9 @@ def test_var_type_map():
 def test_empty_map():
     run("empty-map")
 
+def test_ordered_string_int_map():
+    run("ordered-string-int-map")
+
 def test_traversal_path():
     # gremlin-python serializes/deserializes "null" for props not empty list
     run_writeread("traversal-path")
@@ -158,19 +228,29 @@ def test_traversal_path():
 def test_empty_path():
     run("empty-path")
 
+def test_path_zero_labels():
+    run("path-zero-labels")
+
+def test_path_multiple_labels():
+    # label set ordering isn't guaranteed after a Python object round trip.
+    run_writeread("path-multiple-labels")
+
 def test_traversal_tree():
     # gremlin-python doesn't serialize vertex properties, so the read tree's
     # vertices have no properties; vertex equality is by id so the model 
matches.
     run_writeread("traversal-tree")
 
 def test_empty_tree():
-    # no .gbin resource exists for an empty tree, so exercise an in-memory 
round trip
-    from gremlin_python.structure.graph import Tree
-    empty = model["empty-tree"]
-    round_tripped = reader.read_object(writer.write_object(empty))
-    assert isinstance(round_tripped, Tree)
-    assert empty == round_tripped
-    assert round_tripped.is_leaf()
+    run("empty-tree")
+
+def test_tree_null_key():
+    run("tree-null-key")
+
+def test_tree_mixed_key_types():
+    run("tree-mixed-key-types")
+
+def test_tree_deep_nesting():
+    run("tree-deep-nesting")
 
 def test_prop_path():
     # gremlin-python doesn't serialize properties
@@ -226,23 +306,57 @@ def test_out_direction():
     run("out-direction")
 
 def test_var_bulklist():
+    # BulkList is deserialized as a plain list, so the writer emits a regular 
List.
     run_read("var-bulklist")
 
-def test_var_bulklist():
+def test_empty_bulklist():
+    # BulkList is deserialized as a plain list, so the writer emits a regular 
List.
     run_read("empty-bulklist")
 
 def test_single_byte_char():
     # char is serialized as string
     run_writeread("single-byte-char")
 
-def test_multi_byte_char():
+def test_two_byte_char():
     # char is serialized as string
-    run_writeread("multi-byte-char")
+    run_writeread("two-byte-char")
+
+def test_three_byte_char():
+    # char is serialized as string
+    run_writeread("three-byte-char")
+
+def test_four_byte_char():
+    # char is serialized as string
+    run_writeread("four-byte-char")
 
 def test_unspecified_null():
     # no serializer for plain null
     run_writeread("unspecified-null")
 
+def test_null_int():
+    # typed nulls deserialize to plain None, so the original type can't be 
re-emitted.
+    run_read("null-int")
+
+def test_null_long():
+    # typed nulls deserialize to plain None, so the original type can't be 
re-emitted.
+    run_read("null-long")
+
+def test_null_string():
+    # typed nulls deserialize to plain None, so the original type can't be 
re-emitted.
+    run_read("null-string")
+
+def test_null_list():
+    # typed nulls deserialize to plain None, so the original type can't be 
re-emitted.
+    run_read("null-list")
+
+def test_null_map():
+    # typed nulls deserialize to plain None, so the original type can't be 
re-emitted.
+    run_read("null-map")
+
+def test_null_set():
+    # typed nulls deserialize to plain None, so the original type can't be 
re-emitted.
+    run_read("null-set")
+
 def test_true_boolean():
     run("true-boolean")
 
@@ -255,6 +369,38 @@ def test_single_byte_string():
 def test_mixed_string():
     run("mixed-string")
 
+def test_empty_string():
+    run("empty-string")
+
+def test_traversal_vertex():
+    # properties aren't serialized in gremlin-python
+    run_writeread("traversal-vertex")
+
+def test_multi_label_vertex():
+    # Vertex equality is id-only, so compare labels explicitly.
+    run_writeread("multi-label-vertex", vertex_label_cmp)
+
+def test_empty_label_vertex():
+    # Vertex equality is id-only, so compare labels explicitly.
+    run_writeread("empty-label-vertex", vertex_label_cmp)
+
+def test_tinker_graph():
+    # The hand-built graph is structurally equivalent, but it doesn't preserve
+    # every wire-level numeric type tag needed for byte-exact writes.
+    run_writeread("tinker-graph", graph_cmp)
+
+def test_merge_on_create():
+    run("merge-on-create")
+
+def test_merge_on_match():
+    run("merge-on-match")
+
+def test_merge_out_v():
+    run("merge-out-v")
+
+def test_merge_in_v():
+    run("merge-in-v")
+
 def run(resource_name, comparator = None):
     """
     Runs the regular set of tests for the type which is
@@ -315,4 +461,4 @@ def run_writeread(resource_name, comparator = None):
         assert comparator(model, round_tripped)
     else:
         assert model == read
-        assert model == round_tripped
\ No newline at end of file
+        assert model == round_tripped
diff --git 
a/gremlin-server/src/test/java/org/apache/tinkerpop/gremlin/server/GremlinServerSerializationIntegrateTest.java
 
b/gremlin-server/src/test/java/org/apache/tinkerpop/gremlin/server/GremlinServerSerializationIntegrateTest.java
index 9e41f45d79..bbcbfd5738 100644
--- 
a/gremlin-server/src/test/java/org/apache/tinkerpop/gremlin/server/GremlinServerSerializationIntegrateTest.java
+++ 
b/gremlin-server/src/test/java/org/apache/tinkerpop/gremlin/server/GremlinServerSerializationIntegrateTest.java
@@ -338,8 +338,11 @@ public class GremlinServerSerializationIntegrateTest 
extends AbstractGremlinServ
             assertEquals("g:CompositePdt", pdtNode.get("@type").asText());
             final JsonNode value = pdtNode.get(GraphSONTokens.VALUEPROP);
             assertEquals("Point", value.get("type").asText());
-            assertEquals(1, 
value.get("fields").get("x").get(GraphSONTokens.VALUEPROP).intValue());
-            assertEquals(2, 
value.get("fields").get("y").get(GraphSONTokens.VALUEPROP).intValue());
+            final JsonNode fields = 
value.get("fields").get(GraphSONTokens.VALUEPROP);
+            assertEquals("x", fields.get(0).asText());
+            assertEquals(1, 
fields.get(1).get(GraphSONTokens.VALUEPROP).intValue());
+            assertEquals("y", fields.get(2).asText());
+            assertEquals(2, 
fields.get(3).get(GraphSONTokens.VALUEPROP).intValue());
         }
     }
 
diff --git 
a/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/structure/io/graphbinary/empty-label-vertex-v4.gbin
 
b/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/structure/io/graphbinary/empty-label-vertex-v4.gbin
new file mode 100644
index 0000000000..eae30101be
Binary files /dev/null and 
b/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/structure/io/graphbinary/empty-label-vertex-v4.gbin
 differ
diff --git 
a/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/structure/io/graphbinary/empty-string-v4.gbin
 
b/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/structure/io/graphbinary/empty-string-v4.gbin
new file mode 100644
index 0000000000..f8287f3da7
Binary files /dev/null and 
b/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/structure/io/graphbinary/empty-string-v4.gbin
 differ
diff --git 
a/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/structure/io/graphbinary/empty-tree-v4.gbin
 
b/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/structure/io/graphbinary/empty-tree-v4.gbin
new file mode 100644
index 0000000000..4be0227de7
Binary files /dev/null and 
b/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/structure/io/graphbinary/empty-tree-v4.gbin
 differ
diff --git 
a/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/structure/io/graphbinary/four-byte-char-v4.gbin
 
b/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/structure/io/graphbinary/four-byte-char-v4.gbin
new file mode 100644
index 0000000000..ed5831f36f
Binary files /dev/null and 
b/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/structure/io/graphbinary/four-byte-char-v4.gbin
 differ
diff --git 
a/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/structure/io/graphbinary/merge-in-v-v4.gbin
 
b/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/structure/io/graphbinary/merge-in-v-v4.gbin
new file mode 100644
index 0000000000..7998e4009f
Binary files /dev/null and 
b/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/structure/io/graphbinary/merge-in-v-v4.gbin
 differ
diff --git 
a/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/structure/io/graphbinary/merge-on-create-v4.gbin
 
b/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/structure/io/graphbinary/merge-on-create-v4.gbin
new file mode 100644
index 0000000000..af8f22e257
Binary files /dev/null and 
b/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/structure/io/graphbinary/merge-on-create-v4.gbin
 differ
diff --git 
a/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/structure/io/graphbinary/merge-on-match-v4.gbin
 
b/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/structure/io/graphbinary/merge-on-match-v4.gbin
new file mode 100644
index 0000000000..e31b45bfb2
Binary files /dev/null and 
b/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/structure/io/graphbinary/merge-on-match-v4.gbin
 differ
diff --git 
a/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/structure/io/graphbinary/merge-out-v-v4.gbin
 
b/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/structure/io/graphbinary/merge-out-v-v4.gbin
new file mode 100644
index 0000000000..fafab80fb0
Binary files /dev/null and 
b/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/structure/io/graphbinary/merge-out-v-v4.gbin
 differ
diff --git 
a/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/structure/io/graphbinary/multi-label-vertex-v4.gbin
 
b/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/structure/io/graphbinary/multi-label-vertex-v4.gbin
new file mode 100644
index 0000000000..f18f8fbbbd
Binary files /dev/null and 
b/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/structure/io/graphbinary/multi-label-vertex-v4.gbin
 differ
diff --git 
a/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/structure/io/graphbinary/nanos-duration-v4.gbin
 
b/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/structure/io/graphbinary/nanos-duration-v4.gbin
new file mode 100644
index 0000000000..e73df638e5
Binary files /dev/null and 
b/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/structure/io/graphbinary/nanos-duration-v4.gbin
 differ
diff --git 
a/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/structure/io/graphbinary/negative-duration-v4.gbin
 
b/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/structure/io/graphbinary/negative-duration-v4.gbin
new file mode 100644
index 0000000000..16d08d701a
Binary files /dev/null and 
b/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/structure/io/graphbinary/negative-duration-v4.gbin
 differ
diff --git 
a/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/structure/io/graphbinary/negative-scale-bigdecimal-v4.gbin
 
b/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/structure/io/graphbinary/negative-scale-bigdecimal-v4.gbin
new file mode 100644
index 0000000000..9cb1daf9e2
Binary files /dev/null and 
b/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/structure/io/graphbinary/negative-scale-bigdecimal-v4.gbin
 differ
diff --git 
a/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/structure/io/graphbinary/null-int-v4.gbin
 
b/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/structure/io/graphbinary/null-int-v4.gbin
new file mode 100644
index 0000000000..40fdece9d2
--- /dev/null
+++ 
b/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/structure/io/graphbinary/null-int-v4.gbin
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git 
a/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/structure/io/graphbinary/null-list-v4.gbin
 
b/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/structure/io/graphbinary/null-list-v4.gbin
new file mode 100644
index 0000000000..93938cf057
--- /dev/null
+++ 
b/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/structure/io/graphbinary/null-list-v4.gbin
@@ -0,0 +1 @@
+       
\ No newline at end of file
diff --git 
a/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/structure/io/graphbinary/null-long-v4.gbin
 
b/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/structure/io/graphbinary/null-long-v4.gbin
new file mode 100644
index 0000000000..03afaa5de0
--- /dev/null
+++ 
b/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/structure/io/graphbinary/null-long-v4.gbin
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git 
a/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/structure/io/graphbinary/null-map-v4.gbin
 
b/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/structure/io/graphbinary/null-map-v4.gbin
new file mode 100644
index 0000000000..bbe779818e
--- /dev/null
+++ 
b/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/structure/io/graphbinary/null-map-v4.gbin
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git 
a/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/structure/io/graphbinary/null-set-v4.gbin
 
b/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/structure/io/graphbinary/null-set-v4.gbin
new file mode 100644
index 0000000000..d67137bfd2
--- /dev/null
+++ 
b/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/structure/io/graphbinary/null-set-v4.gbin
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git 
a/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/structure/io/graphbinary/null-string-v4.gbin
 
b/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/structure/io/graphbinary/null-string-v4.gbin
new file mode 100644
index 0000000000..d8d3825962
--- /dev/null
+++ 
b/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/structure/io/graphbinary/null-string-v4.gbin
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git 
a/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/structure/io/graphbinary/ordered-string-int-map-v4.gbin
 
b/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/structure/io/graphbinary/ordered-string-int-map-v4.gbin
new file mode 100644
index 0000000000..1e8097ca81
Binary files /dev/null and 
b/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/structure/io/graphbinary/ordered-string-int-map-v4.gbin
 differ
diff --git 
a/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/structure/io/graphbinary/path-multiple-labels-v4.gbin
 
b/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/structure/io/graphbinary/path-multiple-labels-v4.gbin
new file mode 100644
index 0000000000..a01de6154a
Binary files /dev/null and 
b/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/structure/io/graphbinary/path-multiple-labels-v4.gbin
 differ
diff --git 
a/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/structure/io/graphbinary/path-zero-labels-v4.gbin
 
b/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/structure/io/graphbinary/path-zero-labels-v4.gbin
new file mode 100644
index 0000000000..a456ed2bb5
Binary files /dev/null and 
b/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/structure/io/graphbinary/path-zero-labels-v4.gbin
 differ
diff --git 
a/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/structure/io/graphbinary/point-composite-pdt-v4.gbin
 
b/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/structure/io/graphbinary/point-composite-pdt-v4.gbin
new file mode 100644
index 0000000000..3c5c3a5eb4
Binary files /dev/null and 
b/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/structure/io/graphbinary/point-composite-pdt-v4.gbin
 differ
diff --git 
a/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/structure/io/graphbinary/positive-duration-v4.gbin
 
b/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/structure/io/graphbinary/positive-duration-v4.gbin
new file mode 100644
index 0000000000..11b750d97a
Binary files /dev/null and 
b/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/structure/io/graphbinary/positive-duration-v4.gbin
 differ
diff --git 
a/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/structure/io/graphbinary/scale-zero-bigdecimal-v4.gbin
 
b/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/structure/io/graphbinary/scale-zero-bigdecimal-v4.gbin
new file mode 100644
index 0000000000..18bbdc206b
Binary files /dev/null and 
b/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/structure/io/graphbinary/scale-zero-bigdecimal-v4.gbin
 differ
diff --git 
a/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/structure/io/graphbinary/sign-boundary-neg-biginteger-v4.gbin
 
b/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/structure/io/graphbinary/sign-boundary-neg-biginteger-v4.gbin
new file mode 100644
index 0000000000..fbf5e60d4e
Binary files /dev/null and 
b/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/structure/io/graphbinary/sign-boundary-neg-biginteger-v4.gbin
 differ
diff --git 
a/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/structure/io/graphbinary/sign-boundary-pos-biginteger-v4.gbin
 
b/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/structure/io/graphbinary/sign-boundary-pos-biginteger-v4.gbin
new file mode 100644
index 0000000000..afcf9b041e
Binary files /dev/null and 
b/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/structure/io/graphbinary/sign-boundary-pos-biginteger-v4.gbin
 differ
diff --git 
a/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/structure/io/graphbinary/small-decimal-bigdecimal-v4.gbin
 
b/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/structure/io/graphbinary/small-decimal-bigdecimal-v4.gbin
new file mode 100644
index 0000000000..321d4cfc1c
Binary files /dev/null and 
b/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/structure/io/graphbinary/small-decimal-bigdecimal-v4.gbin
 differ
diff --git 
a/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/structure/io/graphbinary/three-byte-char-v4.gbin
 
b/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/structure/io/graphbinary/three-byte-char-v4.gbin
new file mode 100644
index 0000000000..e4927c407b
Binary files /dev/null and 
b/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/structure/io/graphbinary/three-byte-char-v4.gbin
 differ
diff --git 
a/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/structure/io/graphbinary/tree-deep-nesting-v4.gbin
 
b/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/structure/io/graphbinary/tree-deep-nesting-v4.gbin
new file mode 100644
index 0000000000..72b0f37865
Binary files /dev/null and 
b/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/structure/io/graphbinary/tree-deep-nesting-v4.gbin
 differ
diff --git 
a/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/structure/io/graphbinary/tree-mixed-key-types-v4.gbin
 
b/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/structure/io/graphbinary/tree-mixed-key-types-v4.gbin
new file mode 100644
index 0000000000..40f0ec93ea
Binary files /dev/null and 
b/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/structure/io/graphbinary/tree-mixed-key-types-v4.gbin
 differ
diff --git 
a/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/structure/io/graphbinary/tree-null-key-v4.gbin
 
b/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/structure/io/graphbinary/tree-null-key-v4.gbin
new file mode 100644
index 0000000000..0d7f35dfa4
Binary files /dev/null and 
b/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/structure/io/graphbinary/tree-null-key-v4.gbin
 differ
diff --git 
a/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/structure/io/graphbinary/multi-byte-char-v4.gbin
 
b/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/structure/io/graphbinary/two-byte-char-v4.gbin
similarity index 100%
rename from 
gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/structure/io/graphbinary/multi-byte-char-v4.gbin
rename to 
gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/structure/io/graphbinary/two-byte-char-v4.gbin
diff --git 
a/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/structure/io/graphbinary/uint8-primitive-pdt-v4.gbin
 
b/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/structure/io/graphbinary/uint8-primitive-pdt-v4.gbin
new file mode 100644
index 0000000000..eb81eea59c
Binary files /dev/null and 
b/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/structure/io/graphbinary/uint8-primitive-pdt-v4.gbin
 differ
diff --git 
a/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/structure/io/graphbinary/zero-bigdecimal-v4.gbin
 
b/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/structure/io/graphbinary/zero-bigdecimal-v4.gbin
new file mode 100644
index 0000000000..1e3af93198
Binary files /dev/null and 
b/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/structure/io/graphbinary/zero-bigdecimal-v4.gbin
 differ
diff --git 
a/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/structure/io/graphbinary/zero-biginteger-v4.gbin
 
b/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/structure/io/graphbinary/zero-biginteger-v4.gbin
new file mode 100644
index 0000000000..8cd75a9567
Binary files /dev/null and 
b/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/structure/io/graphbinary/zero-biginteger-v4.gbin
 differ
diff --git 
a/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/structure/io/graphson/empty-label-vertex-v4.json
 
b/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/structure/io/graphson/empty-label-vertex-v4.json
new file mode 100644
index 0000000000..0d729d3637
--- /dev/null
+++ 
b/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/structure/io/graphson/empty-label-vertex-v4.json
@@ -0,0 +1,10 @@
+{
+  "@type" : "g:Vertex",
+  "@value" : {
+    "id" : {
+      "@type" : "g:Int32",
+      "@value" : 1
+    },
+    "label" : [ ]
+  }
+}
\ No newline at end of file
diff --git 
a/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/structure/io/graphson/empty-string-v4.json
 
b/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/structure/io/graphson/empty-string-v4.json
new file mode 100644
index 0000000000..3cc762b550
--- /dev/null
+++ 
b/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/structure/io/graphson/empty-string-v4.json
@@ -0,0 +1 @@
+""
\ No newline at end of file
diff --git 
a/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/structure/io/graphson/merge-in-v-v4.json
 
b/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/structure/io/graphson/merge-in-v-v4.json
new file mode 100644
index 0000000000..3f56a2d5e1
--- /dev/null
+++ 
b/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/structure/io/graphson/merge-in-v-v4.json
@@ -0,0 +1,4 @@
+{
+  "@type" : "g:Merge",
+  "@value" : "inV"
+}
\ No newline at end of file
diff --git 
a/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/structure/io/graphson/merge-on-create-v4.json
 
b/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/structure/io/graphson/merge-on-create-v4.json
new file mode 100644
index 0000000000..b1f1094b4d
--- /dev/null
+++ 
b/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/structure/io/graphson/merge-on-create-v4.json
@@ -0,0 +1,4 @@
+{
+  "@type" : "g:Merge",
+  "@value" : "onCreate"
+}
\ No newline at end of file
diff --git 
a/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/structure/io/graphson/merge-on-match-v4.json
 
b/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/structure/io/graphson/merge-on-match-v4.json
new file mode 100644
index 0000000000..330dea4040
--- /dev/null
+++ 
b/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/structure/io/graphson/merge-on-match-v4.json
@@ -0,0 +1,4 @@
+{
+  "@type" : "g:Merge",
+  "@value" : "onMatch"
+}
\ No newline at end of file
diff --git 
a/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/structure/io/graphson/merge-out-v-v4.json
 
b/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/structure/io/graphson/merge-out-v-v4.json
new file mode 100644
index 0000000000..fa6342fd4e
--- /dev/null
+++ 
b/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/structure/io/graphson/merge-out-v-v4.json
@@ -0,0 +1,4 @@
+{
+  "@type" : "g:Merge",
+  "@value" : "outV"
+}
\ No newline at end of file
diff --git 
a/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/structure/io/graphson/multi-label-vertex-v4.json
 
b/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/structure/io/graphson/multi-label-vertex-v4.json
new file mode 100644
index 0000000000..a568881135
--- /dev/null
+++ 
b/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/structure/io/graphson/multi-label-vertex-v4.json
@@ -0,0 +1,10 @@
+{
+  "@type" : "g:Vertex",
+  "@value" : {
+    "id" : {
+      "@type" : "g:Int32",
+      "@value" : 1
+    },
+    "label" : [ "person", "employee" ]
+  }
+}
\ No newline at end of file
diff --git 
a/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/structure/io/graphson/nanos-duration-v4.json
 
b/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/structure/io/graphson/nanos-duration-v4.json
new file mode 100644
index 0000000000..9a7c0a6eac
--- /dev/null
+++ 
b/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/structure/io/graphson/nanos-duration-v4.json
@@ -0,0 +1,4 @@
+{
+  "@type" : "g:Duration",
+  "@value" : "PT2M3.000456789S"
+}
\ No newline at end of file
diff --git 
a/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/structure/io/graphson/negative-duration-v4.json
 
b/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/structure/io/graphson/negative-duration-v4.json
new file mode 100644
index 0000000000..ca738da37b
--- /dev/null
+++ 
b/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/structure/io/graphson/negative-duration-v4.json
@@ -0,0 +1,4 @@
+{
+  "@type" : "g:Duration",
+  "@value" : "PT-2M-3S"
+}
\ No newline at end of file
diff --git 
a/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/structure/io/graphson/negative-scale-bigdecimal-v4.json
 
b/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/structure/io/graphson/negative-scale-bigdecimal-v4.json
new file mode 100644
index 0000000000..e4e57921ae
--- /dev/null
+++ 
b/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/structure/io/graphson/negative-scale-bigdecimal-v4.json
@@ -0,0 +1,4 @@
+{
+  "@type" : "g:BigDecimal",
+  "@value" : 1.234E+5
+}
\ No newline at end of file
diff --git 
a/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/structure/io/graphson/ordered-string-int-map-v4.json
 
b/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/structure/io/graphson/ordered-string-int-map-v4.json
new file mode 100644
index 0000000000..fa7cc60b6a
--- /dev/null
+++ 
b/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/structure/io/graphson/ordered-string-int-map-v4.json
@@ -0,0 +1,22 @@
+{
+  "@type" : "g:Map",
+  "@value" : [ "delta", {
+    "@type" : "g:Int32",
+    "@value" : 4
+  }, "alpha", {
+    "@type" : "g:Int32",
+    "@value" : 1
+  }, "charlie", {
+    "@type" : "g:Int32",
+    "@value" : 3
+  }, "bravo", {
+    "@type" : "g:Int32",
+    "@value" : 2
+  }, "echo", {
+    "@type" : "g:Int32",
+    "@value" : 5
+  }, "foxtrot", {
+    "@type" : "g:Int32",
+    "@value" : 6
+  } ]
+}
\ No newline at end of file
diff --git 
a/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/structure/io/graphson/path-multiple-labels-v4.json
 
b/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/structure/io/graphson/path-multiple-labels-v4.json
new file mode 100644
index 0000000000..f1fc5c5f95
--- /dev/null
+++ 
b/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/structure/io/graphson/path-multiple-labels-v4.json
@@ -0,0 +1,16 @@
+{
+  "@type" : "g:Path",
+  "@value" : {
+    "labels" : {
+      "@type" : "g:List",
+      "@value" : [ {
+        "@type" : "g:Set",
+        "@value" : [ "a", "b" ]
+      } ]
+    },
+    "objects" : {
+      "@type" : "g:List",
+      "@value" : [ "marko" ]
+    }
+  }
+}
\ No newline at end of file
diff --git 
a/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/structure/io/graphson/path-zero-labels-v4.json
 
b/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/structure/io/graphson/path-zero-labels-v4.json
new file mode 100644
index 0000000000..055a9573de
--- /dev/null
+++ 
b/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/structure/io/graphson/path-zero-labels-v4.json
@@ -0,0 +1,16 @@
+{
+  "@type" : "g:Path",
+  "@value" : {
+    "labels" : {
+      "@type" : "g:List",
+      "@value" : [ {
+        "@type" : "g:Set",
+        "@value" : [ ]
+      } ]
+    },
+    "objects" : {
+      "@type" : "g:List",
+      "@value" : [ "marko" ]
+    }
+  }
+}
\ No newline at end of file
diff --git 
a/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/structure/io/graphson/point-composite-pdt-v4.json
 
b/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/structure/io/graphson/point-composite-pdt-v4.json
new file mode 100644
index 0000000000..39f2b188a8
--- /dev/null
+++ 
b/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/structure/io/graphson/point-composite-pdt-v4.json
@@ -0,0 +1,16 @@
+{
+  "@type" : "g:CompositePdt",
+  "@value" : {
+    "type" : "Point",
+    "fields" : {
+      "@type" : "g:Map",
+      "@value" : [ "x", {
+        "@type" : "g:Int32",
+        "@value" : 1
+      }, "y", {
+        "@type" : "g:Int32",
+        "@value" : 2
+      } ]
+    }
+  }
+}
diff --git 
a/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/structure/io/graphson/positive-duration-v4.json
 
b/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/structure/io/graphson/positive-duration-v4.json
new file mode 100644
index 0000000000..9d25f26d45
--- /dev/null
+++ 
b/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/structure/io/graphson/positive-duration-v4.json
@@ -0,0 +1,4 @@
+{
+  "@type" : "g:Duration",
+  "@value" : "PT2M3S"
+}
\ No newline at end of file
diff --git 
a/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/structure/io/graphson/scale-zero-bigdecimal-v4.json
 
b/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/structure/io/graphson/scale-zero-bigdecimal-v4.json
new file mode 100644
index 0000000000..248f75b08b
--- /dev/null
+++ 
b/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/structure/io/graphson/scale-zero-bigdecimal-v4.json
@@ -0,0 +1,4 @@
+{
+  "@type" : "g:BigDecimal",
+  "@value" : 1234
+}
\ No newline at end of file
diff --git 
a/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/structure/io/graphson/sign-boundary-neg-biginteger-v4.json
 
b/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/structure/io/graphson/sign-boundary-neg-biginteger-v4.json
new file mode 100644
index 0000000000..ac8603df5f
--- /dev/null
+++ 
b/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/structure/io/graphson/sign-boundary-neg-biginteger-v4.json
@@ -0,0 +1,4 @@
+{
+  "@type" : "g:BigInteger",
+  "@value" : -129
+}
\ No newline at end of file
diff --git 
a/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/structure/io/graphson/sign-boundary-pos-biginteger-v4.json
 
b/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/structure/io/graphson/sign-boundary-pos-biginteger-v4.json
new file mode 100644
index 0000000000..adb4a52396
--- /dev/null
+++ 
b/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/structure/io/graphson/sign-boundary-pos-biginteger-v4.json
@@ -0,0 +1,4 @@
+{
+  "@type" : "g:BigInteger",
+  "@value" : 128
+}
\ No newline at end of file
diff --git 
a/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/structure/io/graphson/small-decimal-bigdecimal-v4.json
 
b/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/structure/io/graphson/small-decimal-bigdecimal-v4.json
new file mode 100644
index 0000000000..ff1cd1e182
--- /dev/null
+++ 
b/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/structure/io/graphson/small-decimal-bigdecimal-v4.json
@@ -0,0 +1,4 @@
+{
+  "@type" : "g:BigDecimal",
+  "@value" : 12.34
+}
\ No newline at end of file
diff --git 
a/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/structure/io/graphson/multi-byte-char-v4.json
 
b/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/structure/io/graphson/three-byte-char-v4.json
similarity index 54%
copy from 
gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/structure/io/graphson/multi-byte-char-v4.json
copy to 
gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/structure/io/graphson/three-byte-char-v4.json
index 46acefff3e..df42204445 100644
--- 
a/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/structure/io/graphson/multi-byte-char-v4.json
+++ 
b/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/structure/io/graphson/three-byte-char-v4.json
@@ -1,4 +1,4 @@
 {
   "@type" : "g:Char",
-  "@value" : "Ω"
+  "@value" : "€"
 }
\ No newline at end of file
diff --git 
a/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/structure/io/graphson/multi-byte-char-v4.json
 
b/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/structure/io/graphson/two-byte-char-v4.json
similarity index 100%
rename from 
gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/structure/io/graphson/multi-byte-char-v4.json
rename to 
gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/structure/io/graphson/two-byte-char-v4.json
diff --git 
a/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/structure/io/graphson/uint8-primitive-pdt-v4.json
 
b/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/structure/io/graphson/uint8-primitive-pdt-v4.json
new file mode 100644
index 0000000000..ae994ba0c3
--- /dev/null
+++ 
b/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/structure/io/graphson/uint8-primitive-pdt-v4.json
@@ -0,0 +1,7 @@
+{
+  "@type" : "g:PrimitivePdt",
+  "@value" : {
+    "type" : "Uint8",
+    "value" : "10"
+  }
+}
\ No newline at end of file
diff --git 
a/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/structure/io/graphson/zero-bigdecimal-v4.json
 
b/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/structure/io/graphson/zero-bigdecimal-v4.json
new file mode 100644
index 0000000000..ef5815d110
--- /dev/null
+++ 
b/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/structure/io/graphson/zero-bigdecimal-v4.json
@@ -0,0 +1,4 @@
+{
+  "@type" : "g:BigDecimal",
+  "@value" : 0
+}
\ No newline at end of file
diff --git 
a/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/structure/io/graphson/zero-biginteger-v4.json
 
b/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/structure/io/graphson/zero-biginteger-v4.json
new file mode 100644
index 0000000000..4c719fdb15
--- /dev/null
+++ 
b/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/structure/io/graphson/zero-biginteger-v4.json
@@ -0,0 +1,4 @@
+{
+  "@type" : "g:BigInteger",
+  "@value" : 0
+}
\ No newline at end of file
diff --git 
a/gremlin-util/src/test/java/org/apache/tinkerpop/gremlin/structure/io/AbstractTypedCompatibilityTest.java
 
b/gremlin-util/src/test/java/org/apache/tinkerpop/gremlin/structure/io/AbstractTypedCompatibilityTest.java
index f38e9eb64d..a986c1b2be 100644
--- 
a/gremlin-util/src/test/java/org/apache/tinkerpop/gremlin/structure/io/AbstractTypedCompatibilityTest.java
+++ 
b/gremlin-util/src/test/java/org/apache/tinkerpop/gremlin/structure/io/AbstractTypedCompatibilityTest.java
@@ -20,6 +20,7 @@ package org.apache.tinkerpop.gremlin.structure.io;
 
 import io.netty.buffer.ByteBuf;
 import io.netty.buffer.UnpooledByteBufAllocator;
+import org.apache.tinkerpop.gremlin.process.traversal.Merge;
 import org.apache.tinkerpop.gremlin.process.traversal.Operator;
 import org.apache.tinkerpop.gremlin.process.traversal.Order;
 import org.apache.tinkerpop.gremlin.process.traversal.P;
@@ -28,7 +29,6 @@ import org.apache.tinkerpop.gremlin.process.traversal.Pop;
 import org.apache.tinkerpop.gremlin.process.traversal.SackFunctions;
 import org.apache.tinkerpop.gremlin.process.traversal.Scope;
 import org.apache.tinkerpop.gremlin.process.traversal.TextP;
-import org.apache.tinkerpop.gremlin.process.traversal.Traverser;
 import org.apache.tinkerpop.gremlin.process.traversal.step.util.BulkSet;
 import org.apache.tinkerpop.gremlin.process.traversal.step.util.Tree;
 import org.apache.tinkerpop.gremlin.process.traversal.util.Metrics;
@@ -42,6 +42,8 @@ import org.apache.tinkerpop.gremlin.structure.Vertex;
 import org.apache.tinkerpop.gremlin.structure.VertexProperty;
 import 
org.apache.tinkerpop.gremlin.structure.io.graphbinary.GraphBinaryCompatibilityTest;
 import 
org.apache.tinkerpop.gremlin.structure.io.graphson.GraphSONTypedCompatibilityTest;
+import org.apache.tinkerpop.gremlin.structure.io.pdt.CompositePDT;
+import org.apache.tinkerpop.gremlin.structure.io.pdt.PrimitivePDT;
 import org.apache.tinkerpop.gremlin.tinkergraph.structure.TinkerGraph;
 import org.apache.tinkerpop.gremlin.util.function.Lambda;
 import org.apache.tinkerpop.gremlin.util.message.RequestMessage;
@@ -73,6 +75,7 @@ import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Date;
 import java.util.HashMap;
+import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
@@ -116,6 +119,57 @@ public abstract class AbstractTypedCompatibilityTest 
extends AbstractCompatibili
         assertEquals(resource, recycled);
     }
 
+    @Test
+    public void shouldReadWriteZeroBigDecimal() throws Exception {
+        final String resourceName = "zero-bigdecimal";
+
+        final BigDecimal resource = findModelEntryObject(resourceName);
+        final BigDecimal fromStatic = read(readFromResource(resourceName), 
BigDecimal.class);
+        final BigDecimal recycled = read(write(fromStatic, BigDecimal.class, 
resourceName), BigDecimal.class);
+        assertEquals(fromStatic, recycled);
+        assertEquals(resource, fromStatic);
+        assertEquals(resource, recycled);
+    }
+
+    @Test
+    public void shouldReadWriteScaleZeroBigDecimal() throws Exception {
+        final String resourceName = "scale-zero-bigdecimal";
+
+        final BigDecimal resource = findModelEntryObject(resourceName);
+        final BigDecimal fromStatic = read(readFromResource(resourceName), 
BigDecimal.class);
+        final BigDecimal recycled = read(write(fromStatic, BigDecimal.class, 
resourceName), BigDecimal.class);
+        assertNotSame(fromStatic, recycled);
+        assertEquals(fromStatic, recycled);
+        assertEquals(resource, fromStatic);
+        assertEquals(resource, recycled);
+    }
+
+    @Test
+    public void shouldReadWriteNegativeScaleBigDecimal() throws Exception {
+        final String resourceName = "negative-scale-bigdecimal";
+
+        final BigDecimal resource = findModelEntryObject(resourceName);
+        final BigDecimal fromStatic = read(readFromResource(resourceName), 
BigDecimal.class);
+        final BigDecimal recycled = read(write(fromStatic, BigDecimal.class, 
resourceName), BigDecimal.class);
+        assertNotSame(fromStatic, recycled);
+        assertEquals(fromStatic, recycled);
+        assertEquals(resource, fromStatic);
+        assertEquals(resource, recycled);
+    }
+
+    @Test
+    public void shouldReadWriteSmallDecimalBigDecimal() throws Exception {
+        final String resourceName = "small-decimal-bigdecimal";
+
+        final BigDecimal resource = findModelEntryObject(resourceName);
+        final BigDecimal fromStatic = read(readFromResource(resourceName), 
BigDecimal.class);
+        final BigDecimal recycled = read(write(fromStatic, BigDecimal.class, 
resourceName), BigDecimal.class);
+        assertNotSame(fromStatic, recycled);
+        assertEquals(fromStatic, recycled);
+        assertEquals(resource, fromStatic);
+        assertEquals(resource, recycled);
+    }
+
     @Test
     public void shouldReadWritePositiveBigInteger() throws Exception {
         final String resourceName = "pos-biginteger";
@@ -142,6 +196,44 @@ public abstract class AbstractTypedCompatibilityTest 
extends AbstractCompatibili
         assertEquals(resource, recycled);
     }
 
+    @Test
+    public void shouldReadWriteZeroBigInteger() throws Exception {
+        final String resourceName = "zero-biginteger";
+
+        final BigInteger resource = findModelEntryObject(resourceName);
+        final BigInteger fromStatic = read(readFromResource(resourceName), 
BigInteger.class);
+        final BigInteger recycled = read(write(fromStatic, BigInteger.class, 
resourceName), BigInteger.class);
+        assertEquals(fromStatic, recycled);
+        assertEquals(resource, fromStatic);
+        assertEquals(resource, recycled);
+    }
+
+    @Test
+    public void shouldReadWriteSignBoundaryPositiveBigInteger() throws 
Exception {
+        final String resourceName = "sign-boundary-pos-biginteger";
+
+        final BigInteger resource = findModelEntryObject(resourceName);
+        final BigInteger fromStatic = read(readFromResource(resourceName), 
BigInteger.class);
+        final BigInteger recycled = read(write(fromStatic, BigInteger.class, 
resourceName), BigInteger.class);
+        assertNotSame(fromStatic, recycled);
+        assertEquals(fromStatic, recycled);
+        assertEquals(resource, fromStatic);
+        assertEquals(resource, recycled);
+    }
+
+    @Test
+    public void shouldReadWriteSignBoundaryNegativeBigInteger() throws 
Exception {
+        final String resourceName = "sign-boundary-neg-biginteger";
+
+        final BigInteger resource = findModelEntryObject(resourceName);
+        final BigInteger fromStatic = read(readFromResource(resourceName), 
BigInteger.class);
+        final BigInteger recycled = read(write(fromStatic, BigInteger.class, 
resourceName), BigInteger.class);
+        assertNotSame(fromStatic, recycled);
+        assertEquals(fromStatic, recycled);
+        assertEquals(resource, fromStatic);
+        assertEquals(resource, recycled);
+    }
+
     @Test
     public void shouldReadWriteMinByte() throws Exception {
         final String resourceName = "min-byte";
@@ -329,6 +421,45 @@ public abstract class AbstractTypedCompatibilityTest 
extends AbstractCompatibili
         assertEquals(resource, recycled);
     }
 
+    @Test
+    public void shouldReadWritePositiveDuration() throws Exception {
+        final String resourceName = "positive-duration";
+
+        final Duration resource = findModelEntryObject(resourceName);
+        final Duration fromStatic = read(readFromResource(resourceName), 
Duration.class);
+        final Duration recycled = read(write(fromStatic, Duration.class, 
resourceName), Duration.class);
+        assertNotSame(fromStatic, recycled);
+        assertEquals(fromStatic, recycled);
+        assertEquals(resource, fromStatic);
+        assertEquals(resource, recycled);
+    }
+
+    @Test
+    public void shouldReadWriteNegativeDuration() throws Exception {
+        final String resourceName = "negative-duration";
+
+        final Duration resource = findModelEntryObject(resourceName);
+        final Duration fromStatic = read(readFromResource(resourceName), 
Duration.class);
+        final Duration recycled = read(write(fromStatic, Duration.class, 
resourceName), Duration.class);
+        assertNotSame(fromStatic, recycled);
+        assertEquals(fromStatic, recycled);
+        assertEquals(resource, fromStatic);
+        assertEquals(resource, recycled);
+    }
+
+    @Test
+    public void shouldReadWriteNanosDuration() throws Exception {
+        final String resourceName = "nanos-duration";
+
+        final Duration resource = findModelEntryObject(resourceName);
+        final Duration fromStatic = read(readFromResource(resourceName), 
Duration.class);
+        final Duration recycled = read(write(fromStatic, Duration.class, 
resourceName), Duration.class);
+        assertNotSame(fromStatic, recycled);
+        assertEquals(fromStatic, recycled);
+        assertEquals(resource, fromStatic);
+        assertEquals(resource, recycled);
+    }
+
     @Test
     public void shouldReadWriteEdge() throws Exception {
         final String resourceName = "traversal-edge";
@@ -559,6 +690,21 @@ public abstract class AbstractTypedCompatibilityTest 
extends AbstractCompatibili
         assertEquals(resource, recycled);
     }
 
+    @Test
+    public void shouldReadWriteOrderedMap() throws Exception {
+        final String resourceName = "ordered-string-int-map";
+
+        final Map resource = findModelEntryObject(resourceName);
+        final Map fromStatic = read(readFromResource(resourceName), Map.class);
+        final Map recycled = read(write(fromStatic, Map.class, resourceName), 
Map.class);
+        assertThat(fromStatic instanceof LinkedHashMap, is(true));
+        assertThat(recycled instanceof LinkedHashMap, is(true));
+        assertEquals(new ArrayList<>(resource.keySet()), new 
ArrayList<>(fromStatic.keySet()));
+        assertEquals(new ArrayList<>(resource.keySet()), new 
ArrayList<>(recycled.keySet()));
+        assertEquals(resource, fromStatic);
+        assertEquals(resource, recycled);
+    }
+
     @Test
     public void shouldReadWriteMaxOffsetDateTime() throws Exception {
         final String resourceName = "max-offsetdatetime";
@@ -624,6 +770,32 @@ public abstract class AbstractTypedCompatibilityTest 
extends AbstractCompatibili
         assertEquals(resource, recycled);
     }
 
+    @Test
+    public void shouldReadWritePathWithZeroLabels() throws Exception {
+        final String resourceName = "path-zero-labels";
+
+        final Path resource = findModelEntryObject(resourceName);
+        final Path fromStatic = read(readFromResource(resourceName), 
Path.class);
+        final Path recycled = read(write(fromStatic, Path.class, 
resourceName), Path.class);
+        assertNotSame(fromStatic, recycled);
+        assertEquals(fromStatic, recycled);
+        assertEquals(resource, fromStatic);
+        assertEquals(resource, recycled);
+    }
+
+    @Test
+    public void shouldReadWritePathWithMultipleLabels() throws Exception {
+        final String resourceName = "path-multiple-labels";
+
+        final Path resource = findModelEntryObject(resourceName);
+        final Path fromStatic = read(readFromResource(resourceName), 
Path.class);
+        final Path recycled = read(write(fromStatic, Path.class, 
resourceName), Path.class);
+        assertNotSame(fromStatic, recycled);
+        assertEquals(fromStatic, recycled);
+        assertEquals(resource, fromStatic);
+        assertEquals(resource, recycled);
+    }
+
     @Test
     public void shouldReadWriteEdgeProperty() throws Exception {
         final String resourceName = "edge-property";
@@ -764,6 +936,34 @@ public abstract class AbstractTypedCompatibilityTest 
extends AbstractCompatibili
         assertVertex(resource, recycled);
     }
 
+    @Test
+    public void shouldReadWriteMultiLabelVertex() throws Exception {
+        final String resourceName = "multi-label-vertex";
+
+        final Vertex resource = findModelEntryObject(resourceName);
+        final Vertex fromStatic = read(readFromResource(resourceName), 
Vertex.class);
+        final Vertex recycled = read(write(fromStatic, Vertex.class, 
resourceName), Vertex.class);
+        assertNotSame(fromStatic, recycled);
+        assertEquals(resource.id(), fromStatic.id());
+        assertEquals(resource.id(), recycled.id());
+        assertEquals(resource.labels(), fromStatic.labels());
+        assertEquals(resource.labels(), recycled.labels());
+    }
+
+    @Test
+    public void shouldReadWriteEmptyLabelVertex() throws Exception {
+        final String resourceName = "empty-label-vertex";
+
+        final Vertex resource = findModelEntryObject(resourceName);
+        final Vertex fromStatic = read(readFromResource(resourceName), 
Vertex.class);
+        final Vertex recycled = read(write(fromStatic, Vertex.class, 
resourceName), Vertex.class);
+        assertNotSame(fromStatic, recycled);
+        assertEquals(resource.id(), fromStatic.id());
+        assertEquals(resource.id(), recycled.id());
+        assertEquals(resource.labels(), fromStatic.labels());
+        assertEquals(resource.labels(), recycled.labels());
+    }
+
     @Test
     public void shouldReadWriteVertexProperty() throws Exception {
         final String resourceName = "traversal-vertexproperty";
@@ -833,6 +1033,80 @@ public abstract class AbstractTypedCompatibilityTest 
extends AbstractCompatibili
         assertEquals(resource, recycled);
     }
 
+    @Test
+    public void shouldReadWriteMergeOnCreate() throws Exception {
+        final String resourceName = "merge-on-create";
+
+        final Merge resource = findModelEntryObject(resourceName);
+        final Merge fromStatic = read(readFromResource(resourceName), 
Merge.class);
+        final Merge recycled = read(write(fromStatic, Merge.class, 
resourceName), Merge.class);
+        assertEquals(fromStatic, recycled);
+        assertEquals(resource, fromStatic);
+        assertEquals(resource, recycled);
+    }
+
+    @Test
+    public void shouldReadWriteMergeOnMatch() throws Exception {
+        final String resourceName = "merge-on-match";
+
+        final Merge resource = findModelEntryObject(resourceName);
+        final Merge fromStatic = read(readFromResource(resourceName), 
Merge.class);
+        final Merge recycled = read(write(fromStatic, Merge.class, 
resourceName), Merge.class);
+        assertEquals(fromStatic, recycled);
+        assertEquals(resource, fromStatic);
+        assertEquals(resource, recycled);
+    }
+
+    @Test
+    public void shouldReadWriteMergeOutV() throws Exception {
+        final String resourceName = "merge-out-v";
+
+        final Merge resource = findModelEntryObject(resourceName);
+        final Merge fromStatic = read(readFromResource(resourceName), 
Merge.class);
+        final Merge recycled = read(write(fromStatic, Merge.class, 
resourceName), Merge.class);
+        assertEquals(fromStatic, recycled);
+        assertEquals(resource, fromStatic);
+        assertEquals(resource, recycled);
+    }
+
+    @Test
+    public void shouldReadWriteMergeInV() throws Exception {
+        final String resourceName = "merge-in-v";
+
+        final Merge resource = findModelEntryObject(resourceName);
+        final Merge fromStatic = read(readFromResource(resourceName), 
Merge.class);
+        final Merge recycled = read(write(fromStatic, Merge.class, 
resourceName), Merge.class);
+        assertEquals(fromStatic, recycled);
+        assertEquals(resource, fromStatic);
+        assertEquals(resource, recycled);
+    }
+
+    @Test
+    public void shouldReadWritePrimitiveProviderDefinedType() throws Exception 
{
+        final String resourceName = "uint8-primitive-pdt";
+
+        final PrimitivePDT resource = findModelEntryObject(resourceName);
+        final PrimitivePDT fromStatic = read(readFromResource(resourceName), 
PrimitivePDT.class);
+        final PrimitivePDT recycled = read(write(fromStatic, 
PrimitivePDT.class, resourceName), PrimitivePDT.class);
+        assertNotSame(fromStatic, recycled);
+        assertEquals(fromStatic, recycled);
+        assertEquals(resource, fromStatic);
+        assertEquals(resource, recycled);
+    }
+
+    @Test
+    public void shouldReadWriteCompositeProviderDefinedType() throws Exception 
{
+        final String resourceName = "point-composite-pdt";
+
+        final CompositePDT resource = findModelEntryObject(resourceName);
+        final CompositePDT fromStatic = read(readFromResource(resourceName), 
CompositePDT.class);
+        final CompositePDT recycled = read(write(fromStatic, 
CompositePDT.class, resourceName), CompositePDT.class);
+        assertNotSame(fromStatic, recycled);
+        assertEquals(fromStatic, recycled);
+        assertEquals(resource, fromStatic);
+        assertEquals(resource, recycled);
+    }
+
     @Test
     public void shouldReadWriteVarBulkList() throws Exception {
         final String resourceName = "var-bulklist";
@@ -876,8 +1150,20 @@ public abstract class AbstractTypedCompatibilityTest 
extends AbstractCompatibili
     }
 
     @Test
-    public void shouldReadWriteMultiByteChar() throws Exception {
-        final String resourceName = "multi-byte-char";
+    public void shouldReadWriteTwoByteChar() throws Exception {
+        final String resourceName = "two-byte-char";
+
+        final Character resource = findModelEntryObject(resourceName);
+        final Character fromStatic = read(readFromResource(resourceName), 
Character.class);
+        final Character recycled = read(write(fromStatic, Character.class, 
resourceName), Character.class);
+        assertEquals(fromStatic, recycled);
+        assertEquals(resource, fromStatic);
+        assertEquals(resource, recycled);
+    }
+
+    @Test
+    public void shouldReadWriteThreeByteChar() throws Exception {
+        final String resourceName = "three-byte-char";
 
         final Character resource = findModelEntryObject(resourceName);
         final Character fromStatic = read(readFromResource(resourceName), 
Character.class);
@@ -935,6 +1221,18 @@ public abstract class AbstractTypedCompatibilityTest 
extends AbstractCompatibili
         assertEquals(resource, recycled);
     }
 
+    @Test
+    public void shouldReadWriteEmptyString() throws Exception {
+        final String resourceName = "empty-string";
+
+        final String resource = findModelEntryObject(resourceName);
+        final String fromStatic = read(readFromResource(resourceName), 
String.class);
+        final String recycled = read(write(fromStatic, String.class, 
resourceName), String.class);
+        assertEquals(fromStatic, recycled);
+        assertEquals(resource, fromStatic);
+        assertEquals(resource, recycled);
+    }
+
     @Test
     public void shouldReadWriteMixedString() throws Exception {
         final String resourceName = "mixed-string";
diff --git 
a/gremlin-util/src/test/java/org/apache/tinkerpop/gremlin/structure/io/Model.java
 
b/gremlin-util/src/test/java/org/apache/tinkerpop/gremlin/structure/io/Model.java
index dcec668220..ac08ad8313 100644
--- 
a/gremlin-util/src/test/java/org/apache/tinkerpop/gremlin/structure/io/Model.java
+++ 
b/gremlin-util/src/test/java/org/apache/tinkerpop/gremlin/structure/io/Model.java
@@ -20,15 +20,22 @@ package org.apache.tinkerpop.gremlin.structure.io;
 
 import org.apache.commons.configuration2.BaseConfiguration;
 import org.apache.commons.configuration2.Configuration;
+import org.apache.tinkerpop.gremlin.process.traversal.Merge;
+import org.apache.tinkerpop.gremlin.process.traversal.Path;
 import 
org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversalSource;
 import org.apache.tinkerpop.gremlin.process.traversal.step.util.BulkSet;
 import org.apache.tinkerpop.gremlin.process.traversal.step.util.EmptyPath;
-import 
org.apache.tinkerpop.gremlin.process.traversal.traverser.util.EmptyTraverser;
+import org.apache.tinkerpop.gremlin.process.traversal.step.util.MutablePath;
+import org.apache.tinkerpop.gremlin.process.traversal.step.util.Tree;
 import org.apache.tinkerpop.gremlin.structure.Direction;
 import org.apache.tinkerpop.gremlin.structure.T;
+import org.apache.tinkerpop.gremlin.structure.Vertex;
 import org.apache.tinkerpop.gremlin.structure.VertexProperty;
+import org.apache.tinkerpop.gremlin.structure.io.pdt.CompositePDT;
+import org.apache.tinkerpop.gremlin.structure.io.pdt.PrimitivePDT;
 import org.apache.tinkerpop.gremlin.structure.util.detached.DetachedFactory;
 import org.apache.tinkerpop.gremlin.structure.util.detached.DetachedProperty;
+import org.apache.tinkerpop.gremlin.structure.util.detached.DetachedVertex;
 import 
org.apache.tinkerpop.gremlin.structure.util.detached.DetachedVertexProperty;
 import org.apache.tinkerpop.gremlin.tinkergraph.structure.TinkerFactory;
 import org.apache.tinkerpop.gremlin.tinkergraph.structure.TinkerGraph;
@@ -49,6 +56,8 @@ import java.util.Collections;
 import java.util.Date;
 import java.util.HashMap;
 import java.util.HashSet;
+import java.util.LinkedHashMap;
+import java.util.LinkedHashSet;
 import java.util.List;
 import java.util.Map;
 import java.util.Optional;
@@ -62,6 +71,9 @@ import static 
org.apache.tinkerpop.gremlin.structure.util.detached.DetachedFacto
 /**
  * Defines the supported types for IO and the versions (and configurations) to 
which they apply and are tested.
  *
+ * The following GraphBinary model resources aren't supported by the Java 
model:
+ * four-byte-char            Character cannot represent a supplementary 
Unicode code point as a single scalar value
+ *
  * @author Stephen Mallette (http://stephen.genoprime.com)
  */
 public class Model {
@@ -112,6 +124,14 @@ public class Model {
         map.put(null, null);
         addCoreEntry(map, "var-type-map", "Map is redefined so that to provide 
the ability to allow for non-String keys, which is not possible in JSON.");
         addCoreEntry(Collections.EMPTY_MAP, "empty-map");
+        final Map<Object,Object> orderedMap = new LinkedHashMap<>();
+        orderedMap.put("delta", 4);
+        orderedMap.put("alpha", 1);
+        orderedMap.put("charlie", 3);
+        orderedMap.put("bravo", 2);
+        orderedMap.put("echo", 5);
+        orderedMap.put("foxtrot", 6);
+        addCoreEntry(orderedMap, "ordered-string-int-map");
 
         addCoreEntry(new HashSet<>(Arrays.asList(2, "person", true, null)), 
"var-type-set", "Allows a JSON collection to behave as a Set.");
         addCoreEntry(Collections.EMPTY_SET, "empty-set");
@@ -121,19 +141,42 @@ public class Model {
 
         addCoreEntry(Direction.OUT, "out-direction", "");
         addCoreEntry(T.id, "id-t", "");
+        addCoreEntry(Merge.onCreate, "merge-on-create", "");
+        addCoreEntry(Merge.onMatch, "merge-on-match", "");
+        addCoreEntry(Merge.outV, "merge-out-v", "");
+        addCoreEntry(Merge.inV, "merge-in-v", "");
 
         addCoreEntry('a', "single-byte-char", "");
-        addCoreEntry('\u03A9', "multi-byte-char", "");
+        addCoreEntry('\u03A9', "two-byte-char", "");
+        addCoreEntry('\u20AC', "three-byte-char", "");
 
         addEntry("Core", () -> null, "unspecified-null", "");
+        addEntry("Typed Null", () -> null, "null-int", "");
+        addEntry("Typed Null", () -> null, "null-long", "");
+        addEntry("Typed Null", () -> null, "null-string", "");
+        addEntry("Typed Null", () -> null, "null-list", "");
+        addEntry("Typed Null", () -> null, "null-map", "");
+        addEntry("Typed Null", () -> null, "null-set", "");
 
         addCoreEntry(true, "true-boolean", "");
         addCoreEntry(false, "false-boolean", "");
 
         addCoreEntry("abc", "single-byte-string", "");
         addCoreEntry("abc\u0391\u0392\u0393", "mixed-string", "");
+        addCoreEntry("", "empty-string", "");
 
         addCoreEntry(g.V(10).out().tree().next(), "traversal-tree", "");
+        addCoreEntry(new Tree<>(), "empty-tree", "");
+        final Tree<Object> nullKeyTree = new Tree<>();
+        nullKeyTree.getOrCreateChild(null);
+        addCoreEntry(nullKeyTree, "tree-null-key", "");
+        final Tree<Object> mixedKeyTree = new Tree<>();
+        mixedKeyTree.getOrCreateChild("name");
+        mixedKeyTree.getOrCreateChild(123);
+        addCoreEntry(mixedKeyTree, "tree-mixed-key-types", "");
+        final Tree<Object> deepTree = new Tree<>();
+        
deepTree.getOrCreateChild("root").getOrCreateChild("branch").getOrCreateChild("leaf");
+        addCoreEntry(deepTree, "tree-deep-nesting", "");
 
         addGraphStructureEntry(graph.edges().next(), "traversal-edge", "");
         addGraphStructureEntry(DetachedFactory.detach(graph.edges().next(), 
false), "no-prop-edge", "");
@@ -141,6 +184,10 @@ public class Model {
         addGraphStructureEntry(detach(g.V().out().out().path().next(), false), 
"traversal-path", "");
         addGraphStructureEntry(EmptyPath.instance(), "empty-path", "");
         addGraphStructureEntry(detach(g.V().out().out().path().next(), true), 
"prop-path", "");
+        final Path zeroLabelsPath = MutablePath.make().extend("marko", 
Collections.emptySet());
+        addGraphStructureEntry(zeroLabelsPath, "path-zero-labels", "");
+        final Path multipleLabelsPath = MutablePath.make().extend("marko", new 
LinkedHashSet<>(Arrays.asList("a", "b")));
+        addGraphStructureEntry(multipleLabelsPath, "path-multiple-labels", "");
 
         addGraphStructureEntry(graph.edges().next().properties().next(), 
"edge-property", "");
         addGraphStructureEntry(new DetachedProperty<>("", null), 
"null-property", "");
@@ -149,6 +196,16 @@ public class Model {
 
         addGraphStructureEntry(graph.vertices().next(), "traversal-vertex", 
"");
         addGraphStructureEntry(DetachedFactory.detach(graph.vertices().next(), 
false), "no-prop-vertex", "");
+        final Vertex multiLabelVertex = DetachedVertex.build()
+                .setId(1)
+                .setLabels(new LinkedHashSet<>(Arrays.asList("person", 
"employee")))
+                .create();
+        addGraphStructureEntry(multiLabelVertex, "multi-label-vertex", "");
+        final Vertex emptyLabelVertex = DetachedVertex.build()
+                .setId(1)
+                .setLabels(Collections.emptySet())
+                .create();
+        addGraphStructureEntry(emptyLabelVertex, "empty-label-vertex", "");
 
         addGraphStructureEntry(graph.vertices().next().properties().next(), 
"traversal-vertexproperty", "");
         final Map<String,Object> metaProperties = new HashMap<>();
@@ -165,15 +222,24 @@ public class Model {
         addGraphProcessEntry(bulkSet, "var-bulklist", "");
         addGraphProcessEntry(new BulkSet(), "empty-bulklist", "");
 
-        
addGraphProcessEntry(g.V().hasLabel("person").asAdmin().nextTraverser(), 
"vertex-traverser", "");
-        addGraphProcessEntry(g.V().both().barrier().asAdmin().nextTraverser(), 
"bulked-traverser", "");
-        addGraphProcessEntry(EmptyTraverser.instance(), "empty-traverser", "");
-
         addExtendedEntry(new 
BigDecimal("123.456789987654321123456789987654321"), "pos-bigdecimal", "");
         addExtendedEntry(new 
BigDecimal("-123.456789987654321123456789987654321"), "neg-bigdecimal", "");
+        addExtendedEntry(BigDecimal.ZERO, "zero-bigdecimal", "");
+        addExtendedEntry(new BigDecimal(new BigInteger("1234"), 0), 
"scale-zero-bigdecimal", "");
+        addExtendedEntry(new BigDecimal(new BigInteger("1234"), -2), 
"negative-scale-bigdecimal", "");
+        addExtendedEntry(new BigDecimal("12.34"), "small-decimal-bigdecimal", 
"");
 
         addExtendedEntry(new 
BigInteger("123456789987654321123456789987654321"), "pos-biginteger", "");
         addExtendedEntry(new 
BigInteger("-123456789987654321123456789987654321"), "neg-biginteger", "");
+        addExtendedEntry(BigInteger.ZERO, "zero-biginteger", "");
+        addExtendedEntry(BigInteger.valueOf(128), 
"sign-boundary-pos-biginteger", "");
+        addExtendedEntry(BigInteger.valueOf(-129), 
"sign-boundary-neg-biginteger", "");
+
+        addExtendedEntry(new PrimitivePDT("Uint8", "10"), 
"uint8-primitive-pdt", "");
+        final Map<String,Object> pointFields = new LinkedHashMap<>();
+        pointFields.put("x", 1);
+        pointFields.put("y", 2);
+        addExtendedEntry(new CompositePDT("Point", pointFields), 
"point-composite-pdt", "");
 
         addExtendedEntry(Byte.MAX_VALUE, "max-byte", "");
         addExtendedEntry(Byte.MIN_VALUE, "min-byte", "");
@@ -183,6 +249,9 @@ public class Model {
 
         addExtendedEntry(Duration.ZERO, "zero-duration","The following example 
is a zero `Duration`");
         addExtendedEntry(ChronoUnit.FOREVER.getDuration(), 
"forever-duration","");
+        addExtendedEntry(Duration.ofSeconds(123), "positive-duration","");
+        addExtendedEntry(Duration.ofSeconds(-123), "negative-duration","");
+        addExtendedEntry(Duration.ofSeconds(123, 456789), "nanos-duration","");
 
         addExtendedEntry(OffsetDateTime.MAX, "max-offsetdatetime", "");
         addExtendedEntry(OffsetDateTime.MIN, "min-offsetdatetime", "");
diff --git 
a/gremlin-util/src/test/java/org/apache/tinkerpop/gremlin/structure/io/graphbinary/GraphBinaryCompatibilityTest.java
 
b/gremlin-util/src/test/java/org/apache/tinkerpop/gremlin/structure/io/graphbinary/GraphBinaryCompatibilityTest.java
index 092c1adf6c..7ede7cfab5 100644
--- 
a/gremlin-util/src/test/java/org/apache/tinkerpop/gremlin/structure/io/graphbinary/GraphBinaryCompatibilityTest.java
+++ 
b/gremlin-util/src/test/java/org/apache/tinkerpop/gremlin/structure/io/graphbinary/GraphBinaryCompatibilityTest.java
@@ -21,11 +21,13 @@ package 
org.apache.tinkerpop.gremlin.structure.io.graphbinary;
 import io.netty.buffer.ByteBufAllocator;
 import org.apache.commons.io.IOUtils;
 import org.apache.commons.io.output.ByteArrayOutputStream;
+import org.apache.tinkerpop.gremlin.process.traversal.step.util.Tree;
 import 
org.apache.tinkerpop.gremlin.structure.io.AbstractTypedCompatibilityTest;
 import org.apache.tinkerpop.gremlin.structure.io.Buffer;
 import org.apache.tinkerpop.gremlin.structure.io.binary.GraphBinaryReader;
 import org.apache.tinkerpop.gremlin.structure.io.binary.GraphBinaryWriter;
 import org.apache.tinkerpop.gremlin.util.ser.NettyBufferFactory;
+import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.junit.runners.Parameterized;
 
@@ -33,6 +35,12 @@ import java.io.File;
 import java.io.FileOutputStream;
 import java.io.IOException;
 import java.util.Arrays;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
 
 /**
  * @author Stephen Mallette (http://stephen.genoprime.com)
@@ -107,6 +115,102 @@ public class GraphBinaryCompatibilityTest extends 
AbstractTypedCompatibilityTest
         }
     }
 
+    @Test
+    public void shouldReadNullInt() throws Exception {
+        final String resourceName = "null-int";
+
+        final Integer fromStatic = read(readFromResource(resourceName), 
Integer.class);
+        assertNull(fromStatic);
+    }
+
+    @Test
+    public void shouldReadNullLong() throws Exception {
+        final String resourceName = "null-long";
+
+        final Long fromStatic = read(readFromResource(resourceName), 
Long.class);
+        assertNull(fromStatic);
+    }
+
+    @Test
+    public void shouldReadNullString() throws Exception {
+        final String resourceName = "null-string";
+
+        final String fromStatic = read(readFromResource(resourceName), 
String.class);
+        assertNull(fromStatic);
+    }
+
+    @Test
+    public void shouldReadNullList() throws Exception {
+        final String resourceName = "null-list";
+
+        final List fromStatic = read(readFromResource(resourceName), 
List.class);
+        assertNull(fromStatic);
+    }
+
+    @Test
+    public void shouldReadNullMap() throws Exception {
+        final String resourceName = "null-map";
+
+        final Map fromStatic = read(readFromResource(resourceName), Map.class);
+        assertNull(fromStatic);
+    }
+
+    @Test
+    public void shouldReadNullSet() throws Exception {
+        final String resourceName = "null-set";
+
+        final Set fromStatic = read(readFromResource(resourceName), Set.class);
+        assertNull(fromStatic);
+    }
+
+    @Test
+    public void shouldReadWriteEmptyTree() throws Exception {
+        final String resourceName = "empty-tree";
+
+        final Tree resource = findModelEntryObject(resourceName);
+        final Tree fromStatic = read(readFromResource(resourceName), 
Tree.class);
+        final Tree recycled = read(write(fromStatic, Tree.class, 
resourceName), Tree.class);
+        assertEquals(fromStatic, recycled);
+        assertEquals(resource, fromStatic);
+        assertEquals(resource, recycled);
+    }
+
+    @Test
+    public void shouldReadWriteTreeWithNullKey() throws Exception {
+        final String resourceName = "tree-null-key";
+
+        final Tree resource = findModelEntryObject(resourceName);
+        final Tree fromStatic = read(readFromResource(resourceName), 
Tree.class);
+        final Tree recycled = read(write(fromStatic, Tree.class, 
resourceName), Tree.class);
+        assertEquals(fromStatic, recycled);
+        assertEquals(resource, fromStatic);
+        assertEquals(resource, recycled);
+    }
+
+    @Test
+    public void shouldReadWriteTreeWithMixedKeyTypes() throws Exception {
+        final String resourceName = "tree-mixed-key-types";
+
+        final Tree resource = findModelEntryObject(resourceName);
+        final Tree fromStatic = read(readFromResource(resourceName), 
Tree.class);
+        final Tree recycled = read(write(fromStatic, Tree.class, 
resourceName), Tree.class);
+        assertEquals(fromStatic, recycled);
+        assertEquals(resource, fromStatic);
+        assertEquals(resource, recycled);
+    }
+
+    @Test
+    public void shouldReadWriteTreeWithDeepNesting() throws Exception {
+        final String resourceName = "tree-deep-nesting";
+
+        final Tree resource = findModelEntryObject(resourceName);
+        final Tree fromStatic = read(readFromResource(resourceName), 
Tree.class);
+        final Tree recycled = read(write(fromStatic, Tree.class, 
resourceName), Tree.class);
+        assertEquals(fromStatic, recycled);
+        assertEquals(resource, fromStatic);
+        assertEquals(resource, recycled);
+    }
+
 //    TODO: revisit
 //    @Override
 //    public void shouldReadWriteAuthenticationChallenge() throws Exception {


Reply via email to