This is an automated email from the ASF dual-hosted git repository. xiazcy pushed a commit to branch 3.8-dev in repository https://gitbox.apache.org/repos/asf/tinkerpop.git
The following commit(s) were added to refs/heads/3.8-dev by this push: new e2cc4ea409 Switched `gremlin-net` byte serializers to signed bytes (`sbyte`) to be consistent with IO doc (#3206) e2cc4ea409 is described below commit e2cc4ea409412c677229454f68277188d46370f3 Author: Yang Xia <55853655+xia...@users.noreply.github.com> AuthorDate: Mon Sep 22 16:10:38 2025 -0700 Switched `gremlin-net` byte serializers to signed bytes (`sbyte`) to be consistent with IO doc (#3206) --- CHANGELOG.asciidoc | 1 + docs/src/upgrade/release-3.8.x.asciidoc | 9 +++ .../translator/DotNetTranslateVisitor.java | 4 +- .../language/translator/GremlinTranslatorTest.java | 6 +- .../Structure/IO/GraphBinary/StreamExtensions.cs | 23 ++++++++ .../IO/GraphBinary/TypeSerializerRegistry.cs | 2 +- .../IO/GraphBinary/Types/SingleTypeSerializer.cs | 8 +-- .../Structure/IO/GraphSON/GraphSONReader.cs | 2 +- .../Structure/IO/GraphSON/GraphSONWriter.cs | 2 +- .../{ByteConverter.cs => SByteConverter.cs} | 6 +- .../Gherkin/CommonSteps.cs | 2 +- .../Gremlin.Net.IntegrationTest/Gherkin/Gremlin.cs | 38 ++++++------- .../Structure/IO/GraphBinary/GraphBinaryTests.cs | 65 +++++++++++++++++++++- .../Structure/IO/GraphSON/GraphSONReaderTests.cs | 14 +++++ .../Structure/IO/GraphSON/GraphSONWriterTests.cs | 29 +++++++++- 15 files changed, 174 insertions(+), 37 deletions(-) diff --git a/CHANGELOG.asciidoc b/CHANGELOG.asciidoc index 26bf36c219..83aa2cc808 100644 --- a/CHANGELOG.asciidoc +++ b/CHANGELOG.asciidoc @@ -102,6 +102,7 @@ This release also includes changes from <<release-3-7-XXX, 3.7.XXX>>. * Introduced `GValue` to represent parameterized values in a `GraphTraversal` * Added optional traversal cache to `GremlinLangScriptEngine` * Introduced step interfaces for all parameterizable steps +* Switched `gremlin-net` byte serializers to signed bytes (`sbyte`) to be consistent with IO doc * Removed auto-unfold of singleton collections from `range()`, `limit()`, and `tail()` local scope steps to improve consistency of output. * Renamed `MergeElementStep` to `MergeElementStep` as it is a base class to `mergeV()` and `mergeE()`. * Renamed `MergeStep` of `merge()` to `MergeElementStep` for consistency. diff --git a/docs/src/upgrade/release-3.8.x.asciidoc b/docs/src/upgrade/release-3.8.x.asciidoc index 297742f13a..8578783ab4 100644 --- a/docs/src/upgrade/release-3.8.x.asciidoc +++ b/docs/src/upgrade/release-3.8.x.asciidoc @@ -277,6 +277,15 @@ See: link:https://issues.apache.org/jira/browse/TINKERPOP-3083[TINKERPOP-3083] Starting from this version, `gremlin-javascript` will deserialize `Set` data into a ECMAScript 2015 Set. Previously, these were deserialized into arrays. +==== .NET Byte Serialization Change + +The Gremlin .NET serializers has been updated to correctly handle byte values as signed integers to align with the IO +specification, whereas previously it incorrectly serialized and deserialized bytes as unsigned values. + +This is a breaking change for .NET applications that rely on byte values. Existing applications using byte values should consider switching to `sbyte` for signed byte operations or `short` for a wider range of values to maintain compatibility. + +See: link:https://issues.apache.org/jira/browse/TINKERPOP-3161[TINKERPOP-3161] + ==== Removal of P.getOriginalValue() `P.getOriginalValue()` has been removed as it was not offering much value and was often confused with `P.getValue()`. diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/language/translator/DotNetTranslateVisitor.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/language/translator/DotNetTranslateVisitor.java index 04660efa18..638fb450e4 100644 --- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/language/translator/DotNetTranslateVisitor.java +++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/language/translator/DotNetTranslateVisitor.java @@ -93,8 +93,8 @@ public class DotNetTranslateVisitor extends AbstractTranslateVisitor { final char lastCharacter = integerLiteral.charAt(lastCharIndex); switch (lastCharacter) { case 'b': - // parse B/b as byte - sb.append("(byte) "); + // parse B/b as sbyte + sb.append("(sbyte) "); sb.append(integerLiteral, 0, lastCharIndex); break; case 's': diff --git a/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/language/translator/GremlinTranslatorTest.java b/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/language/translator/GremlinTranslatorTest.java index d04b4d10fb..1b557be5d6 100644 --- a/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/language/translator/GremlinTranslatorTest.java +++ b/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/language/translator/GremlinTranslatorTest.java @@ -353,7 +353,7 @@ public class GremlinTranslatorTest { {"g.with('x', 1b)", null, "g.with(string0, byte0)", - "g.With(\"x\", (byte) 1)", + "g.With(\"x\", (sbyte) 1)", "g.With(\"x\", int8(1))", "g.with('x', new Byte(1))", "g.with(\"x\", new Byte(1))", @@ -362,7 +362,7 @@ public class GremlinTranslatorTest { {"g.with('x', 1B)", "g.with('x', 1b)", "g.with(string0, byte0)", - "g.With(\"x\", (byte) 1)", + "g.With(\"x\", (sbyte) 1)", "g.With(\"x\", int8(1))", "g.with('x', new Byte(1))", "g.with(\"x\", new Byte(1))", @@ -371,7 +371,7 @@ public class GremlinTranslatorTest { {"g.with('x', -1b)", null, "g.with(string0, byte0)", - "g.With(\"x\", (byte) -1)", + "g.With(\"x\", (sbyte) -1)", "g.With(\"x\", int8(-1))", "g.with('x', new Byte(-1))", "g.with(\"x\", new Byte(-1))", diff --git a/gremlin-dotnet/src/Gremlin.Net/Structure/IO/GraphBinary/StreamExtensions.cs b/gremlin-dotnet/src/Gremlin.Net/Structure/IO/GraphBinary/StreamExtensions.cs index 155a5758ed..6a81b1c2f7 100644 --- a/gremlin-dotnet/src/Gremlin.Net/Structure/IO/GraphBinary/StreamExtensions.cs +++ b/gremlin-dotnet/src/Gremlin.Net/Structure/IO/GraphBinary/StreamExtensions.cs @@ -59,6 +59,29 @@ namespace Gremlin.Net.Structure.IO.GraphBinary return readBuffer[0]; } + /// <summary> + /// Asynchronously writes a <see cref="sbyte"/> to a <see cref="Stream"/>. + /// </summary> + /// <param name="stream">The <see cref="Stream"/> to write the <see cref="sbyte"/> to.</param> + /// <param name="value">The <see cref="sbyte"/> to write.</param> + /// <param name="cancellationToken">The token to cancel the operation. The default value is None.</param> + public static async Task WriteSByteAsync(this Stream stream, sbyte value, + CancellationToken cancellationToken = default) + { + await stream.WriteByteAsync((byte)value, cancellationToken).ConfigureAwait(false); + } + + /// <summary> + /// Asynchronously reads a <see cref="sbyte"/> from a <see cref="Stream"/>. + /// </summary> + /// <param name="stream">The <see cref="Stream"/> to read from.</param> + /// <param name="cancellationToken">The token to cancel the operation. The default value is None.</param> + /// <returns>The read <see cref="sbyte"/>.</returns> + public static async Task<sbyte> ReadSByteAsync(this Stream stream, CancellationToken cancellationToken = default) + { + return (sbyte)await stream.ReadByteAsync(cancellationToken).ConfigureAwait(false); + } + /// <summary> /// Asynchronously writes an <see cref="int"/> to a <see cref="Stream"/>. /// </summary> diff --git a/gremlin-dotnet/src/Gremlin.Net/Structure/IO/GraphBinary/TypeSerializerRegistry.cs b/gremlin-dotnet/src/Gremlin.Net/Structure/IO/GraphBinary/TypeSerializerRegistry.cs index 74e00001e0..5c0894af9b 100644 --- a/gremlin-dotnet/src/Gremlin.Net/Structure/IO/GraphBinary/TypeSerializerRegistry.cs +++ b/gremlin-dotnet/src/Gremlin.Net/Structure/IO/GraphBinary/TypeSerializerRegistry.cs @@ -76,7 +76,7 @@ namespace Gremlin.Net.Structure.IO.GraphBinary {typeof(Traverser), new TraverserSerializer()}, {typeof(decimal), new BigDecimalSerializer()}, {typeof(BigInteger), new BigIntegerSerializer()}, - {typeof(byte), SingleTypeSerializers.ByteSerializer}, + {typeof(sbyte), SingleTypeSerializers.ByteSerializer}, {typeof(byte[]), new ByteBufferSerializer()}, {typeof(short), SingleTypeSerializers.ShortSerializer}, {typeof(bool), SingleTypeSerializers.BooleanSerializer}, diff --git a/gremlin-dotnet/src/Gremlin.Net/Structure/IO/GraphBinary/Types/SingleTypeSerializer.cs b/gremlin-dotnet/src/Gremlin.Net/Structure/IO/GraphBinary/Types/SingleTypeSerializer.cs index 04830cffba..537f15eac9 100644 --- a/gremlin-dotnet/src/Gremlin.Net/Structure/IO/GraphBinary/Types/SingleTypeSerializer.cs +++ b/gremlin-dotnet/src/Gremlin.Net/Structure/IO/GraphBinary/Types/SingleTypeSerializer.cs @@ -81,11 +81,11 @@ namespace Gremlin.Net.Structure.IO.GraphBinary.Types (stream, cancellationToken) => stream.ReadBoolAsync(cancellationToken)); /// <summary> - /// A serializer for <see cref="byte"/> values. + /// A serializer for <see cref="sbyte"/> values. /// </summary> - public static readonly SingleTypeSerializer<byte> ByteSerializer = new SingleTypeSerializer<byte>(DataType.Byte, - (value, stream, cancellationToken) => stream.WriteByteAsync(value, cancellationToken), - (stream, cancellationToken) => stream.ReadByteAsync(cancellationToken)); + public static readonly SingleTypeSerializer<sbyte> ByteSerializer = new SingleTypeSerializer<sbyte>(DataType.Byte, + (value, stream, cancellationToken) => stream.WriteSByteAsync(value, cancellationToken), + (stream, cancellationToken) => stream.ReadSByteAsync(cancellationToken)); } /// <summary> diff --git a/gremlin-dotnet/src/Gremlin.Net/Structure/IO/GraphSON/GraphSONReader.cs b/gremlin-dotnet/src/Gremlin.Net/Structure/IO/GraphSON/GraphSONReader.cs index 52e26992a6..b9c7039493 100644 --- a/gremlin-dotnet/src/Gremlin.Net/Structure/IO/GraphSON/GraphSONReader.cs +++ b/gremlin-dotnet/src/Gremlin.Net/Structure/IO/GraphSON/GraphSONReader.cs @@ -63,7 +63,7 @@ namespace Gremlin.Net.Structure.IO.GraphSON { "gx:BigDecimal", new DecimalConverter() }, { "gx:Duration", new DurationDeserializer() }, { "gx:BigInteger", new BigIntegerDeserializer() }, - { "gx:Byte", new ByteConverter() }, + { "gx:Byte", new SByteConverter() }, { "gx:ByteBuffer", new ByteBufferDeserializer() }, { "gx:Char", new CharConverter() }, { "gx:Int16", new Int16Converter() } diff --git a/gremlin-dotnet/src/Gremlin.Net/Structure/IO/GraphSON/GraphSONWriter.cs b/gremlin-dotnet/src/Gremlin.Net/Structure/IO/GraphSON/GraphSONWriter.cs index b883582321..4424505261 100644 --- a/gremlin-dotnet/src/Gremlin.Net/Structure/IO/GraphSON/GraphSONWriter.cs +++ b/gremlin-dotnet/src/Gremlin.Net/Structure/IO/GraphSON/GraphSONWriter.cs @@ -73,7 +73,7 @@ namespace Gremlin.Net.Structure.IO.GraphSON { typeof(decimal), new DecimalConverter() }, { typeof(TimeSpan), new DurationSerializer() }, { typeof(BigInteger), new BigIntegerSerializer() }, - { typeof(byte), new ByteConverter() }, + { typeof(sbyte), new SByteConverter() }, { typeof(byte[]), new ByteBufferSerializer() }, { typeof(char), new CharConverter() }, { typeof(short), new Int16Converter() } diff --git a/gremlin-dotnet/src/Gremlin.Net/Structure/IO/GraphSON/ByteConverter.cs b/gremlin-dotnet/src/Gremlin.Net/Structure/IO/GraphSON/SByteConverter.cs similarity index 92% rename from gremlin-dotnet/src/Gremlin.Net/Structure/IO/GraphSON/ByteConverter.cs rename to gremlin-dotnet/src/Gremlin.Net/Structure/IO/GraphSON/SByteConverter.cs index c9fc8a5abe..755405205d 100644 --- a/gremlin-dotnet/src/Gremlin.Net/Structure/IO/GraphSON/ByteConverter.cs +++ b/gremlin-dotnet/src/Gremlin.Net/Structure/IO/GraphSON/SByteConverter.cs @@ -24,10 +24,10 @@ using System.Text.Json; namespace Gremlin.Net.Structure.IO.GraphSON { - internal class ByteConverter : NumberConverter<byte> + internal class SByteConverter : NumberConverter<sbyte> { protected override string GraphSONTypeName => "Byte"; protected override string Prefix => "gx"; - protected override dynamic FromJsonElement(JsonElement graphson) => graphson.GetByte(); + protected override dynamic FromJsonElement(JsonElement graphson) => graphson.GetSByte(); } -} \ No newline at end of file +} diff --git a/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Gherkin/CommonSteps.cs b/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Gherkin/CommonSteps.cs index 2ffe19a577..29f40ed955 100644 --- a/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Gherkin/CommonSteps.cs +++ b/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Gherkin/CommonSteps.cs @@ -87,7 +87,7 @@ namespace Gremlin.Net.IntegrationTest.Gherkin private static readonly IDictionary<char, Func<string, object>> NumericParsers = new Dictionary<char, Func<string, object>> { - { 'b', s => Convert.ToByte(s) }, + { 'b', s => Convert.ToSByte(s) }, { 's', s => Convert.ToInt16(s) }, { 'i', s => Convert.ToInt32(s) }, { 'l', s => Convert.ToInt64(s) }, diff --git a/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Gherkin/Gremlin.cs b/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Gherkin/Gremlin.cs index f9787aec36..bb686d83bd 100644 --- a/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Gherkin/Gremlin.cs +++ b/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Gherkin/Gremlin.cs @@ -726,7 +726,7 @@ namespace Gremlin.Net.IntegrationTest.Gherkin {"g_injectX1_2X_asDate", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.Inject<object>(new List<object> { 1, 2 }).AsDate()}}, {"g_injectXnullX_asDate", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.Inject<object>(null).AsDate()}}, {"g_injectXinvalidstrX_asDate", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.Inject<object>("This String is not an ISO 8601 Date").AsDate()}}, - {"g_injectX5bX_asNumber", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.Inject<object>((byte) 5).AsNumber()}}, + {"g_injectX5bX_asNumber", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.Inject<object>((sbyte) 5).AsNumber()}}, {"g_injectX5sX_asNumber", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.Inject<object>((short) 5).AsNumber()}}, {"g_injectX5iX_asNumber", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.Inject<object>(5).AsNumber()}}, {"g_injectX5lX_asNumber", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.Inject<object>(5l).AsNumber()}}, @@ -1447,7 +1447,7 @@ namespace Gremlin.Net.IntegrationTest.Gherkin {"g_V_hasLabelXsoftwareX_valueXnameX_substringX1_neg1X", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.V().HasLabel("software").Values<object>("name").Substring(1, -1)}}, {"g_V_hasLabelXsoftwareX_valueXnameX_substringXneg4_2X", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.V().HasLabel("software").Values<object>("name").Substring(-4, 2)}}, {"g_V_hasLabelXsoftwareX_valueXnameX_substringXneg3_neg1X", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.V().HasLabel("software").Values<object>("name").Substring(-3, -1)}}, - {"g_V_injectX127b_1bX_sumXX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.Inject<object>((byte) 127, (byte) 1).Sum<object>()}}, + {"g_V_injectX127b_1bX_sumXX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.Inject<object>((sbyte) 127, (sbyte) 1).Sum<object>()}}, {"g_V_injectX_128b__1bX_sumXX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.Inject<object>((short) -128, (short) -1).Sum<object>()}}, {"g_V_injectX32767s_1sX_sumXX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.Inject<object>((short) 32767, (short) 1).Sum<object>()}}, {"g_V_injectX_32768s__1sX_sumXX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.Inject<object>((short) -32768, (short) -1).Sum<object>()}}, @@ -1471,14 +1471,14 @@ namespace Gremlin.Net.IntegrationTest.Gherkin {"g_VX1X_valuesXageX_sumXlocalX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.V(p["vid1"]).Values<object>("age").Sum<object>(Scope.Local)}}, {"g_V_localXunionXvaluesXageX_outE_valuesXweightXX_foldX_sumXlocalX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.V().Local<object>(__.Union<object>(__.Values<object>("age"), __.OutE().Values<object>("weight")).Fold()).Sum<object>(Scope.Local)}}, {"g_V_age_injectX1000nX_sum", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.V().Values<object>("age").Inject(BigInteger.Parse("1000")).Sum<object>()}}, - {"g_injectX1b_2b_3bX_sum", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.Inject<object>((byte) 1, (byte) 2, (byte) 3).Sum<object>()}}, - {"g_injectX1b_2b_3sX_sum", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.Inject<object>((byte) 1, (byte) 2, (short) 3).Sum<object>()}}, - {"g_injectX1b_26b_3iX_sum", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.Inject<object>((byte) 1, (byte) 2, 3).Sum<object>()}}, + {"g_injectX1b_2b_3bX_sum", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.Inject<object>((sbyte) 1, (sbyte) 2, (sbyte) 3).Sum<object>()}}, + {"g_injectX1b_2b_3sX_sum", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.Inject<object>((sbyte) 1, (sbyte) 2, (short) 3).Sum<object>()}}, + {"g_injectX1b_26b_3iX_sum", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.Inject<object>((sbyte) 1, (sbyte) 2, 3).Sum<object>()}}, {"g_injectX1f_26f_3fX_sum", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.Inject<object>(1f, 2f, 3f).Sum<object>()}}, {"g_V_age_injectX1000nX_fold_sumXlocalX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.V().Values<object>("age").Inject(BigInteger.Parse("1000")).Fold().Sum<object>(Scope.Local)}}, - {"g_injectX1b_2b_3bX_fold_sumXlocalX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.Inject<object>((byte) 1, (byte) 2, (byte) 3).Fold().Sum<object>(Scope.Local)}}, - {"g_injectX1b_2b_3sX_fold_sumXlocalX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.Inject<object>((byte) 1, (byte) 2, (short) 3).Fold().Sum<object>(Scope.Local)}}, - {"g_injectX1b_26b_3iX_fold_sumXlocalX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.Inject<object>((byte) 1, (byte) 2, 3).Fold().Sum<object>(Scope.Local)}}, + {"g_injectX1b_2b_3bX_fold_sumXlocalX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.Inject<object>((sbyte) 1, (sbyte) 2, (sbyte) 3).Fold().Sum<object>(Scope.Local)}}, + {"g_injectX1b_2b_3sX_fold_sumXlocalX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.Inject<object>((sbyte) 1, (sbyte) 2, (short) 3).Fold().Sum<object>(Scope.Local)}}, + {"g_injectX1b_26b_3iX_fold_sumXlocalX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.Inject<object>((sbyte) 1, (sbyte) 2, 3).Fold().Sum<object>(Scope.Local)}}, {"g_injectX1f_26f_3fX_fold_sumXlocalX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.Inject<object>(1f, 2f, 3f).Fold().Sum<object>(Scope.Local)}}, {"g_injectXfeature_test_nullX_toLower", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.Inject<object>("FEATURE", "tESt", null).ToLower()}}, {"g_injectXfeature_test_nullX_toLowerXlocalX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.Inject<object>(new List<object> { "FEATURE", "tESt", null }).ToLower<object>(Scope.Local)}}, @@ -1635,14 +1635,14 @@ namespace Gremlin.Net.IntegrationTest.Gherkin {"InjectXInfX_ltXNegInfX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.Inject<object>(Double.PositiveInfinity).Is(P.Lt(Double.NegativeInfinity))}}, {"InjectXNegInfX_ltXInfX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.Inject<object>(Double.NegativeInfinity).Is(P.Lt(Double.PositiveInfinity))}}, {"InjectXNegInfX_gtXInfX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.Inject<object>(Double.NegativeInfinity).Is(P.Gt(Double.PositiveInfinity))}}, - {"Primitives_Number_eqXbyteX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.Inject<object>(new List<object> { (byte) 1, (short) 1, 1, 1l, 1f, 1d, 1000, (decimal) 1, BigInteger.Parse("1") }).Unfold<object>().Where(__.Is(p["xx1"]))}}, - {"Primitives_Number_eqXshortX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.Inject<object>(new List<object> { (byte) 1, (short) 1, 1, 1l, 1f, 1d, 1000, (decimal) 1, BigInteger.Parse("1") }).Unfold<object>().Where(__.Is(p["xx1"]))}}, - {"Primitives_Number_eqXintX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.Inject<object>(new List<object> { (byte) 1, (short) 1, 1, 1l, 1f, 1d, 1000, (decimal) 1, BigInteger.Parse("1") }).Unfold<object>().Where(__.Is(p["xx1"]))}}, - {"Primitives_Number_eqXlongX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.Inject<object>(new List<object> { (byte) 1, (short) 1, 1, 1l, 1f, 1d, 1000, (decimal) 1, BigInteger.Parse("1") }).Unfold<object>().Where(__.Is(p["xx1"]))}}, - {"Primitives_Number_eqXbigintX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.Inject<object>(new List<object> { (byte) 1, (short) 1, 1, 1l, 1f, 1d, 1000, (decimal) 1, BigInteger.Parse("1") }).Unfold<object>().Where(__.Is(p["xx1"]))}}, - {"Primitives_Number_eqXfloatX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.Inject<object>(new List<object> { (byte) 1, (short) 1, 1, 1l, 1f, 1d, 1000, (decimal) 1, BigInteger.Parse("1") }).Unfold<object>().Where(__.Is(p["xx1"]))}}, - {"Primitives_Number_eqXdoubleX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.Inject<object>(new List<object> { (byte) 1, (short) 1, 1, 1l, 1f, 1d, 1000, (decimal) 1, BigInteger.Parse("1") }).Unfold<object>().Where(__.Is(p["xx1"]))}}, - {"Primitives_Number_eqXbigdecimalX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.Inject<object>(new List<object> { (byte) 1, (short) 1, 1, 1l, 1f, 1d, 1000, (decimal) 1, BigInteger.Parse("1") }).Unfold<object>().Where(__.Is(p["xx1"]))}}, + {"Primitives_Number_eqXbyteX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.Inject<object>(new List<object> { (sbyte) 1, (short) 1, 1, 1l, 1f, 1d, 1000, (decimal) 1, BigInteger.Parse("1") }).Unfold<object>().Where(__.Is(p["xx1"]))}}, + {"Primitives_Number_eqXshortX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.Inject<object>(new List<object> { (sbyte) 1, (short) 1, 1, 1l, 1f, 1d, 1000, (decimal) 1, BigInteger.Parse("1") }).Unfold<object>().Where(__.Is(p["xx1"]))}}, + {"Primitives_Number_eqXintX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.Inject<object>(new List<object> { (sbyte) 1, (short) 1, 1, 1l, 1f, 1d, 1000, (decimal) 1, BigInteger.Parse("1") }).Unfold<object>().Where(__.Is(p["xx1"]))}}, + {"Primitives_Number_eqXlongX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.Inject<object>(new List<object> { (sbyte) 1, (short) 1, 1, 1l, 1f, 1d, 1000, (decimal) 1, BigInteger.Parse("1") }).Unfold<object>().Where(__.Is(p["xx1"]))}}, + {"Primitives_Number_eqXbigintX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.Inject<object>(new List<object> { (sbyte) 1, (short) 1, 1, 1l, 1f, 1d, 1000, (decimal) 1, BigInteger.Parse("1") }).Unfold<object>().Where(__.Is(p["xx1"]))}}, + {"Primitives_Number_eqXfloatX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.Inject<object>(new List<object> { (sbyte) 1, (short) 1, 1, 1l, 1f, 1d, 1000, (decimal) 1, BigInteger.Parse("1") }).Unfold<object>().Where(__.Is(p["xx1"]))}}, + {"Primitives_Number_eqXdoubleX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.Inject<object>(new List<object> { (sbyte) 1, (short) 1, 1, 1l, 1f, 1d, 1000, (decimal) 1, BigInteger.Parse("1") }).Unfold<object>().Where(__.Is(p["xx1"]))}}, + {"Primitives_Number_eqXbigdecimalX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.Inject<object>(new List<object> { (sbyte) 1, (short) 1, 1, 1l, 1f, 1d, 1000, (decimal) 1, BigInteger.Parse("1") }).Unfold<object>().Where(__.Is(p["xx1"]))}}, {"g_V_values_order", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.V().Values<object>().Order()}}, {"g_V_properties_order", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.V().Properties<object>().Order()}}, {"g_V_properties_order_id", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.V().Properties<object>().Order().Id()}}, @@ -1771,7 +1771,7 @@ namespace Gremlin.Net.IntegrationTest.Gherkin {"g_io_read_withXreader_graphsonX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.Io<object>("data/tinkerpop-modern.json").With(IO.Reader, IO.GraphSON).Read(), (g,p) =>g.V(), (g,p) =>g.E()}}, {"g_io_readXgraphmlX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.Io<object>("data/tinkerpop-modern.xml").Read(), (g,p) =>g.V(), (g,p) =>g.E()}}, {"g_io_read_withXreader_graphmlX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.Io<object>("data/tinkerpop-modern.xml").With(IO.Reader, IO.GraphML).Read(), (g,p) =>g.V(), (g,p) =>g.E()}}, - {"g_withSackX127bX_injectX1bX_sackXsumX_sack", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.WithSack((byte) 127).Inject<object>((byte) 1).Sack(Operator.Sum).Sack<object>()}}, + {"g_withSackX127bX_injectX1bX_sackXsumX_sack", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.WithSack((sbyte) 127).Inject<object>((sbyte) 1).Sack(Operator.Sum).Sack<object>()}}, {"g_withSackX32767sX_injectX1sX_sackXsumX_sack", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.WithSack((short) 32767).Inject<object>((short) 1).Sack(Operator.Sum).Sack<object>()}}, {"g_withSackX2147483647iX_injectX1iX_sackXsumX_sack", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.WithSack(2147483647).Inject<object>(1).Sack(Operator.Sum).Sack<object>()}}, {"g_withSackX1_7976931348623157E_308dX_injectX1_7976931348623157E_308dX_sackXsumX_sack", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.WithSack(1.7976931348623157e+308d).Inject<object>(1.7976931348623157e+308d).Sack(Operator.Sum).Sack<object>()}}, @@ -1779,11 +1779,11 @@ namespace Gremlin.Net.IntegrationTest.Gherkin {"g_withSackX_32768sX_injectX1sX_sackXminusX_sack", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.WithSack((short) -32768).Inject<object>((short) 1).Sack(Operator.Minus).Sack<object>()}}, {"g_withSackX_2147483648iX_injectX1iX_sackXminusX_sack", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.WithSack(-2147483648).Inject<object>(1).Sack(Operator.Minus).Sack<object>()}}, {"g_withSackX_1_7976931348623157E_308dX_injectX1_7976931348623157E_308dX_sackXminusX_sack", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.WithSack(-1.7976931348623157e+308d).Inject<object>(1.7976931348623157e+308d).Sack(Operator.Minus).Sack<object>()}}, - {"g_withSackX127bX_injectX2bX_sackXmultX_sack", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.WithSack((byte) 127).Inject<object>((byte) 2).Sack(Operator.Mult).Sack<object>()}}, + {"g_withSackX127bX_injectX2bX_sackXmultX_sack", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.WithSack((sbyte) 127).Inject<object>((sbyte) 2).Sack(Operator.Mult).Sack<object>()}}, {"g_withSackX32767sX_injectX2sX_sackXmultX_sack", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.WithSack((short) 32767).Inject<object>((short) 2).Sack(Operator.Mult).Sack<object>()}}, {"g_withSackX2147483647iX_injectX2iX_sackXmultX_sack", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.WithSack(2147483647).Inject<object>(2).Sack(Operator.Mult).Sack<object>()}}, {"g_withSackX1_7976931348623157E_308dX_injectX2dX_sackXmultX_sack", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.WithSack(1.7976931348623157e+308d).Inject<object>(2d).Sack(Operator.Mult).Sack<object>()}}, - {"g_withSackX127bX_injectX0_5fX_sackXdivX_sack", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.WithSack((byte) 127).Inject<object>(0.5f).Sack(Operator.Div).Sack<object>()}}, + {"g_withSackX127bX_injectX0_5fX_sackXdivX_sack", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.WithSack((sbyte) 127).Inject<object>(0.5f).Sack(Operator.Div).Sack<object>()}}, {"g_withSackX32767sX_injectX0_5fX_sackXdivX_sack", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.WithSack((short) 32767).Inject<object>(0.5f).Sack(Operator.Div).Sack<object>()}}, {"g_withSackX2147483647iX_injectX0_5fX_sackXdivX_sack", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.WithSack(2147483647).Inject<object>(0.5f).Sack(Operator.Div).Sack<object>()}}, {"g_withSackX1_7976931348623157E_308dX_injectX0_5dX_sackXdivX_sack", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.WithSack(1.7976931348623157e+308d).Inject<object>(0.5d).Sack(Operator.Div).Sack<object>()}}, diff --git a/gremlin-dotnet/test/Gremlin.Net.UnitTest/Structure/IO/GraphBinary/GraphBinaryTests.cs b/gremlin-dotnet/test/Gremlin.Net.UnitTest/Structure/IO/GraphBinary/GraphBinaryTests.cs index 1f172f6acc..df8e189cdf 100644 --- a/gremlin-dotnet/test/Gremlin.Net.UnitTest/Structure/IO/GraphBinary/GraphBinaryTests.cs +++ b/gremlin-dotnet/test/Gremlin.Net.UnitTest/Structure/IO/GraphBinary/GraphBinaryTests.cs @@ -708,7 +708,10 @@ namespace Gremlin.Net.UnitTest.Structure.IO.GraphBinary [Theory] [InlineData(1)] [InlineData(123)] - public async Task TestByte(byte expected) + [InlineData(-1)] + [InlineData(-128)] + [InlineData(127)] + public async Task TestSByte(sbyte expected) { var writer = CreateGraphBinaryWriter(); var reader = CreateGraphBinaryReader(); @@ -721,6 +724,66 @@ namespace Gremlin.Net.UnitTest.Structure.IO.GraphBinary Assert.Equal(expected, actual); } + [Theory] + [InlineData((sbyte)0, new byte[] { 0x00 })] + [InlineData((sbyte)1, new byte[] { 0x01 })] + [InlineData((sbyte)127, new byte[] { 0x7F })] + [InlineData((sbyte)-1, new byte[] { 0xFF })] + [InlineData((sbyte)-128, new byte[] { 0x80 })] + public async Task TestSByteSerializationSpec(sbyte value, byte[] expected) + { + var writer = new GraphBinaryWriter(); + var serializationStream = new MemoryStream(); + + await writer.WriteNonNullableValueAsync(value, serializationStream); + + var serBytes = serializationStream.ToArray(); + Assert.Equal(expected, serBytes); + } + + [Theory] + [InlineData((sbyte)0)] + [InlineData((sbyte)1)] + [InlineData((sbyte)127)] + [InlineData((sbyte)-1)] + [InlineData((sbyte)-128)] + [InlineData((sbyte)42)] + [InlineData((sbyte)-42)] + public async Task TestSByteRoundTrip(sbyte expected) + { + var writer = new GraphBinaryWriter(); + var reader = new GraphBinaryReader(); + var serializationStream = new MemoryStream(); + + await writer.WriteAsync(expected, serializationStream); + serializationStream.Position = 0; + var actual = await reader.ReadAsync(serializationStream); + + Assert.Equal(expected, actual); + Assert.IsType<sbyte>(actual); + } + + [Fact] + public async Task TestSByteMinMaxValues() + { + var writer = new GraphBinaryWriter(); + var reader = new GraphBinaryReader(); + + // Test minimum value + var minStream = new MemoryStream(); + await writer.WriteAsync(sbyte.MinValue, minStream); + minStream.Position = 0; + var actualMin = await reader.ReadAsync(minStream); + Assert.Equal(sbyte.MinValue, actualMin); + + // Test maximum value + var maxStream = new MemoryStream(); + await writer.WriteAsync(sbyte.MaxValue, maxStream); + maxStream.Position = 0; + var actualMax = await reader.ReadAsync(maxStream); + Assert.Equal(sbyte.MaxValue, actualMax); + } + [Fact] public async Task TestByteBuffer() { diff --git a/gremlin-dotnet/test/Gremlin.Net.UnitTest/Structure/IO/GraphSON/GraphSONReaderTests.cs b/gremlin-dotnet/test/Gremlin.Net.UnitTest/Structure/IO/GraphSON/GraphSONReaderTests.cs index e5c1d8a88a..05a3ee1585 100644 --- a/gremlin-dotnet/test/Gremlin.Net.UnitTest/Structure/IO/GraphSON/GraphSONReaderTests.cs +++ b/gremlin-dotnet/test/Gremlin.Net.UnitTest/Structure/IO/GraphSON/GraphSONReaderTests.cs @@ -262,6 +262,20 @@ namespace Gremlin.Net.UnitTest.Structure.IO.GraphSON var deserializedValue = reader.ToObject(jsonElement); Assert.Equal(1, deserializedValue); + Assert.IsType<sbyte>(deserializedValue); + } + + [Theory, MemberData(nameof(Versions))] + public void ShouldDeserializeByteNegative(int version) + { + const string serializedValue = "{\"@type\":\"gx:Byte\",\"@value\":-42}"; + var reader = CreateStandardGraphSONReader(version); + + var jsonElement = JsonSerializer.Deserialize<JsonElement>(serializedValue); + var deserializedValue = reader.ToObject(jsonElement); + + Assert.Equal(-42, deserializedValue); + Assert.IsType<sbyte>(deserializedValue); } [Theory, MemberData(nameof(Versions))] diff --git a/gremlin-dotnet/test/Gremlin.Net.UnitTest/Structure/IO/GraphSON/GraphSONWriterTests.cs b/gremlin-dotnet/test/Gremlin.Net.UnitTest/Structure/IO/GraphSON/GraphSONWriterTests.cs index 34cdd8e534..a73c8d1693 100644 --- a/gremlin-dotnet/test/Gremlin.Net.UnitTest/Structure/IO/GraphSON/GraphSONWriterTests.cs +++ b/gremlin-dotnet/test/Gremlin.Net.UnitTest/Structure/IO/GraphSON/GraphSONWriterTests.cs @@ -24,6 +24,7 @@ using System; using System.Collections.Generic; using System.Numerics; +using System.Text.Json; using Gremlin.Net.Process.Traversal; using Gremlin.Net.Process.Traversal.Strategy.Decoration; using Gremlin.Net.Process.Traversal.Strategy.Optimization; @@ -582,10 +583,36 @@ namespace Gremlin.Net.UnitTest.Structure.IO.GraphSON { var writer = CreateGraphSONWriter(version); - var graphSon = writer.WriteObject((byte)1); + var graphSon = writer.WriteObject((sbyte)1); Assert.Equal("{\"@type\":\"gx:Byte\",\"@value\":1}", graphSon); } + + [Theory, MemberData(nameof(Versions))] + public void ShouldSerializeByteNegativeValue(int version) + { + var writer = CreateGraphSONWriter(version); + + var graphSon = writer.WriteObject((sbyte)-42); + + Assert.Equal("{\"@type\":\"gx:Byte\",\"@value\":-42}", graphSon); + } + + [Theory, MemberData(nameof(Versions))] + public void TestSByteGraphSONFormat(int version) + { + var writer = CreateGraphSONWriter(version); + var value = (sbyte)42; + + var serialized = writer.WriteObject(value); + var jsonDoc = JsonDocument.Parse(serialized); + + Assert.True(jsonDoc.RootElement.TryGetProperty("@type", out var typeProperty)); + Assert.Equal("gx:Byte", typeProperty.GetString()); + + Assert.True(jsonDoc.RootElement.TryGetProperty("@value", out var valueProperty)); + Assert.Equal(42, valueProperty.GetSByte()); + } [Theory, MemberData(nameof(Versions))] public void ShouldSerializeByteBuffer(int version)