diff --git a/dotnet/Cucumber.Messages/CucumberMessagEnumConverter.cs b/dotnet/Cucumber.Messages.Specs/CucumberMessagEnumConverter.cs similarity index 94% rename from dotnet/Cucumber.Messages/CucumberMessagEnumConverter.cs rename to dotnet/Cucumber.Messages.Specs/CucumberMessagEnumConverter.cs index 9da68504..e46309b2 100644 --- a/dotnet/Cucumber.Messages/CucumberMessagEnumConverter.cs +++ b/dotnet/Cucumber.Messages.Specs/CucumberMessagEnumConverter.cs @@ -5,12 +5,12 @@ using System.Text.Json; using System.Text.Json.Serialization; -namespace Cucumber.Messages +namespace Cucumber.Messages.Specs { internal class CucumberMessageEnumConverter : JsonConverter where T : struct, Enum { - private readonly Dictionary _enumToString = new Dictionary(); - private readonly Dictionary _stringToEnum = new Dictionary(); + private readonly Dictionary _enumToString = new(); + private readonly Dictionary _stringToEnum = new(); protected internal CucumberMessageEnumConverter() { diff --git a/dotnet/Cucumber.Messages.Specs/MessageToNdjsonWriterSUT.cs b/dotnet/Cucumber.Messages.Specs/MessageToNdjsonWriterSUT.cs new file mode 100644 index 00000000..9cf447ad --- /dev/null +++ b/dotnet/Cucumber.Messages.Specs/MessageToNdjsonWriterSUT.cs @@ -0,0 +1,17 @@ +using Io.Cucumber.Messages.Types; +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Cucumber.Messages.Specs +{ + internal class MessageToNdjsonWriterSUT : MessageToNdjsonWriter + { + public MessageToNdjsonWriterSUT(Stream stream) : base(stream, (StreamWriter streamWriter, Envelope envelope) => streamWriter.Write(NdjsonSerializer.Serialize(envelope))) + { + } + } +} diff --git a/dotnet/Cucumber.Messages.Specs/NdJsonMessageReaderSUT.cs b/dotnet/Cucumber.Messages.Specs/NdJsonMessageReaderSUT.cs new file mode 100644 index 00000000..95727bdc --- /dev/null +++ b/dotnet/Cucumber.Messages.Specs/NdJsonMessageReaderSUT.cs @@ -0,0 +1,16 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Cucumber.Messages.Specs +{ + internal class NdjsonMessageReaderSUT : NdjsonMessageReader + { + public NdjsonMessageReaderSUT(Stream inputStream) : base(inputStream, (string line) => NdjsonSerializer.Deserialize(line)) + { + } + } +} diff --git a/dotnet/Cucumber.Messages/NdjsonSerializer.cs b/dotnet/Cucumber.Messages.Specs/NdjsonSerializer.cs similarity index 79% rename from dotnet/Cucumber.Messages/NdjsonSerializer.cs rename to dotnet/Cucumber.Messages.Specs/NdjsonSerializer.cs index 26656a22..53fd357a 100644 --- a/dotnet/Cucumber.Messages/NdjsonSerializer.cs +++ b/dotnet/Cucumber.Messages.Specs/NdjsonSerializer.cs @@ -2,11 +2,16 @@ using System; using System.Text.Json; -namespace Cucumber.Messages +namespace Cucumber.Messages.Specs { + /// + /// When using System.Text.Json to serialize a Cucumber Message Envelope, the following serialization options are used. + /// Consumers of Cucumber.Messages should use these options, or their serialization library's equivalent options. + /// These options should work with System.Text.Json v6 or above. + /// public class NdjsonSerializer { - private static Lazy _jsonOptions = new Lazy(() => + private static readonly Lazy _jsonOptions = new(() => { var options = new JsonSerializerOptions(); options.PropertyNamingPolicy = JsonNamingPolicy.CamelCase; diff --git a/dotnet/Cucumber.Messages.Specs/NdjsonStreamSerializationTests.cs b/dotnet/Cucumber.Messages.Specs/NdjsonStreamSerializationTests.cs index 4b9c882a..cb51b3ed 100644 --- a/dotnet/Cucumber.Messages.Specs/NdjsonStreamSerializationTests.cs +++ b/dotnet/Cucumber.Messages.Specs/NdjsonStreamSerializationTests.cs @@ -18,7 +18,7 @@ public class NdjsonStreamSerializationTests public void WritesSourceEnvelope() { MemoryStream memoryStream = new MemoryStream(); - var writer = new MessageToNdjsonWriter(memoryStream); + var writer = new MessageToNdjsonWriterSUT(memoryStream); writer.Write(Envelope.Create(new Source("hello.feature", "Feature: Hello", SourceMediaType.TEXT_X_CUCUMBER_GHERKIN_PLAIN))); var json = Encoding.UTF8.GetString(memoryStream.ToArray()); @@ -29,7 +29,7 @@ public void WritesSourceEnvelope() public void DoesNotSerializeNullFields() { MemoryStream memoryStream = new MemoryStream(); - var writer = new MessageToNdjsonWriter(memoryStream); + var writer = new MessageToNdjsonWriterSUT(memoryStream); writer.Write(new Envelope(null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null)); var json = Encoding.UTF8.GetString(memoryStream.ToArray()); @@ -40,7 +40,7 @@ public void DoesNotSerializeNullFields() public void IgnoresEmptyLines() { MemoryStream memoryStream = new MemoryStream(Encoding.UTF8.GetBytes("{}\n{}\n\n{}\n")); - var enumerator = new NdjsonMessageReader(memoryStream).GetEnumerator(); + var enumerator = new NdjsonMessageReaderSUT(memoryStream).GetEnumerator(); var expectedEnvelope = new Envelope(null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null); for (int i = 0; i < 3; i++) @@ -56,7 +56,7 @@ public void IgnoresEmptyLines() public void Handles_Enums() { MemoryStream memoryStream = new MemoryStream(Encoding.UTF8.GetBytes("{\"attachment\":{\"contentEncoding\":\"BASE64\", \"body\":\"the-body\", \"mediaType\":\"text/plain\"}}\n")); - var enumerator = new NdjsonMessageReader(memoryStream).GetEnumerator(); + var enumerator = new NdjsonMessageReaderSUT(memoryStream).GetEnumerator(); Assert.True(enumerator.MoveNext()); Envelope envelope = enumerator.Current; @@ -70,7 +70,7 @@ public void Handles_Enums() public void Handles_Single_Argument_Constructor() { MemoryStream memoryStream = new MemoryStream(Encoding.UTF8.GetBytes("{\"testRunStarted\": {\"timestamp\":{\"nanos\":0,\"seconds\":0}}}\n")); - var enumerator = new NdjsonMessageReader(memoryStream).GetEnumerator(); + var enumerator = new NdjsonMessageReaderSUT(memoryStream).GetEnumerator(); Assert.True(enumerator.MoveNext()); Envelope envelope = enumerator.Current; Envelope expected = Envelope.Create(new TestRunStarted(new Timestamp(0, 0))); @@ -83,7 +83,7 @@ public void Handles_Single_Argument_Constructor() public void Includes_Offending_Line_In_Error_Message() { MemoryStream memoryStream = new MemoryStream(Encoding.UTF8.GetBytes("BLA BLA")); - var enumerator = new NdjsonMessageReader(memoryStream).GetEnumerator(); + var enumerator = new NdjsonMessageReaderSUT(memoryStream).GetEnumerator(); var exception = Assert.Throws( () => enumerator.MoveNext()); Assert.Equal("Could not parse JSON: BLA BLA", exception.Message); } diff --git a/dotnet/Cucumber.Messages/Converters.cs b/dotnet/Cucumber.Messages/Converters.cs index f3eb7407..195e8265 100644 --- a/dotnet/Cucumber.Messages/Converters.cs +++ b/dotnet/Cucumber.Messages/Converters.cs @@ -8,8 +8,8 @@ namespace Cucumber.Messages { public class Converters { - private static DateTime EpochStart = new DateTime(1970, 1, 1, 0, 0, 0, 0, System.DateTimeKind.Utc); - private static long NanoSecondsPerTick = 100L; + private static readonly DateTime EpochStart = new(1970, 1, 1, 0, 0, 0, 0, System.DateTimeKind.Utc); + private static readonly long NanoSecondsPerTick = 100L; public static Timestamp ToTimestamp(DateTime dateTime) { diff --git a/dotnet/Cucumber.Messages/Cucumber.Messages.csproj b/dotnet/Cucumber.Messages/Cucumber.Messages.csproj index c9157435..ee13258a 100644 --- a/dotnet/Cucumber.Messages/Cucumber.Messages.csproj +++ b/dotnet/Cucumber.Messages/Cucumber.Messages.csproj @@ -39,8 +39,4 @@ true - - - - diff --git a/dotnet/Cucumber.Messages/MessageToNdjsonWriter.cs b/dotnet/Cucumber.Messages/MessageToNdjsonWriter.cs index 19b3aa46..3d055703 100644 --- a/dotnet/Cucumber.Messages/MessageToNdjsonWriter.cs +++ b/dotnet/Cucumber.Messages/MessageToNdjsonWriter.cs @@ -6,14 +6,16 @@ namespace Cucumber.Messages { + /// + /// The NdjsonMessageWriter class provides a stream based interface for writing Cucumber messages in ndjson format + /// + /// The actual serialization is delegated to a Action<StreamWriter, Envelope>. This is done to avoid tying this library to any specific JSON library or library version. + /// public class MessageToNdjsonWriter : IDisposable { - private StreamWriter _streamWriter; - private Action _serializer; + private readonly StreamWriter _streamWriter; + private readonly Action _serializer; - public MessageToNdjsonWriter(Stream stream) : this(stream, (StreamWriter streamWriter, Envelope envelope) => streamWriter.Write(NdjsonSerializer.Serialize(envelope))) - { - } public MessageToNdjsonWriter(Stream stream, Action serializer) { if (stream == null) diff --git a/dotnet/Cucumber.Messages/NdjsonMessageReader.cs b/dotnet/Cucumber.Messages/NdjsonMessageReader.cs index e21aabad..73f52154 100644 --- a/dotnet/Cucumber.Messages/NdjsonMessageReader.cs +++ b/dotnet/Cucumber.Messages/NdjsonMessageReader.cs @@ -7,12 +7,16 @@ namespace Cucumber.Messages { + /// + /// The NdjsonMessageReader class provides a stream based interface for reading Cucumber messages in ndjson format and produces Cucumber messages (Envelope) + /// + /// The actual deserialization is delegated to a Func<string, Envelope>. This is done to avoid tying this library to any specific JSON library or library version. + /// public class NdjsonMessageReader : IDisposable, System.Collections.Generic.IEnumerable { - private StreamReader _inputStreamReader; - private Func _deserializer; + private readonly StreamReader _inputStreamReader; + private readonly Func _deserializer; - public NdjsonMessageReader(Stream inputStream) : this( inputStream, (string line) => NdjsonSerializer.Deserialize(line)) { } public NdjsonMessageReader(Stream inputStream, Func deserializer) { if (inputStream == null) @@ -41,7 +45,7 @@ public System.Collections.Generic.IEnumerator GetEnumerator() { envelope = _deserializer(line); } - catch (System.Text.Json.JsonException e) + catch (System.Exception e) { throw new InvalidOperationException($"Could not parse JSON: {line}", e); }