Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/)
and this project adheres to [Semantic Versioning](http://semver.org/).

## [Unreleased]
- [Dotnet] Fixed code generation for types that accept List<T> as parameters. Constructors were not properly handling null input. [clrudolphi]
Copy link
Contributor

@mpkorstanje mpkorstanje Sep 12, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Spotted this after merging. Changes should be listed as one of Added, Changed, ect. I'll fix it on main.


### Changed
- [Go] Switch to Google's UUID module ([#251](https://github.com/cucumber/messages/pull/251)
Expand Down
38 changes: 21 additions & 17 deletions codegen/templates/dotnet.dotnet.erb
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<%- @schemas.each do |key, schema| -%>
<%= class_name(key) -%>.cs
<%= class_name(key) %>.cs
using System;
using System.Collections.Generic;

Expand Down Expand Up @@ -32,21 +32,21 @@ public sealed class <%= class_name(key) %>
*/
<%- end -%>
<%- if isValueType && !required -%>
public Nullable<<%= type_for(class_name(key), property_name, property) -%>> <%= capitalize(property_name) %> { get; private set; }
public Nullable<<%= type_for(class_name(key), property_name, property) %>> <%= capitalize(property_name) %> { get; private set; }
<%- else -%>
public <%= type_for(class_name(key), property_name, property) -%> <%= capitalize(property_name) %> { get; private set; }
public <%= type_for(class_name(key), property_name, property) %> <%= capitalize(property_name) %> { get; private set; }
<%- end -%>
<%- end -%>

<%- if (schema['required'] || []).empty? -%>
<%- schema['properties'].each do |property_name, property| -%>

public static <%= class_name(key) %> Create(<%= type_for(class_name(key), property_name, property) -%> <%= property_name %>)
public static <%= class_name(key) %> Create(<%= type_for(class_name(key), property_name, property) %> <%= property_name %>)
{
return new <%= class_name(key) %>(
<%- schema['properties'].each.with_index(1) do |(property_name_2, _property_2), index| -%>
<%- if property_name_2 == property_name -%>
Require<<%= type_for(class_name(key), property_name, property) -%>>(<%= property_name %>, "<%= capitalize(property_name) %>", "<%= class_name(key) %>.<%= capitalize(property_name) %> cannot be null")<%= index < schema['properties'].length ? ',' : '' %>
Require<<%= type_for(class_name(key), property_name, property) %>>(<%= property_name %>, "<%= capitalize(property_name) %>", "<%= class_name(key) %>.<%= capitalize(property_name) %> cannot be null")<%= index < schema['properties'].length ? ',' : '' %>
<%- else -%>
null<%= index < schema['properties'].length ? ',' : '' %>
<%- end -%>
Expand All @@ -62,21 +62,25 @@ public sealed class <%= class_name(key) %>
required = (schema['required'] || []).index(property_name)
-%>
<%- if isValueType && !required -%>
Nullable<<%= type_for(class_name(key), property_name, property) -%>> <%= property_name %><%= index < schema['properties'].length ? ',' : ''%>
Nullable<<%= type_for(class_name(key), property_name, property) %>> <%= property_name %><%= index < schema['properties'].length ? ',' : ''%>
<%- else -%>
<%= type_for(class_name(key), property_name, property) -%> <%= property_name %><%= index < schema['properties'].length ? ',' : ''%>
<%= type_for(class_name(key), property_name, property) %> <%= property_name %><%= index < schema['properties'].length ? ',' : ''%>
<%- end -%>
<%- end -%>
)
{
<%- schema['properties'].each do |property_name, property|
required = (schema['required'] || []).index(property_name)
-%>
<% if required -%>
RequireNonNull<<%= type_for(class_name(key), property_name, property) -%>>(<%= property_name %>, "<%= capitalize(property_name) %>", "<%= class_name(key) %>.<%= capitalize(property_name) %> cannot be null");
<%- if required -%>
RequireNonNull<<%= type_for(class_name(key), property_name, property) %>>(<%= property_name %>, "<%= capitalize(property_name) %>", "<%= class_name(key) %>.<%= capitalize(property_name) %> cannot be null");
<%- end -%>
<%- if property['items'] -%>
this.<%= capitalize(property_name) %> = new <%= type_for(class_name(key), property_name, property) -%>(<%= property_name %>);
<%- if required -%>
this.<%= capitalize(property_name) %> = new <%= type_for(class_name(key), property_name, property) %>(<%= property_name %>);
<%- else -%>
this.<%= capitalize(property_name) %> = <%= property_name %> == null ? null : new <%= type_for(class_name(key), property_name, property) %>(<%= property_name %>);
<%- end -%>
<%- else -%>
this.<%= capitalize(property_name) %> = <%= property_name %>;
<%- end -%>
Expand All @@ -90,9 +94,9 @@ public sealed class <%= class_name(key) %>
<%= class_name(key) %> that = (<%= class_name(key) %>) o;
return <%- schema['properties'].each.with_index(1) do |(property_name, _property), index| %>
<%- if (schema['required'] || []).index(property_name) -%>
<%= capitalize(property_name) -%>.Equals(that.<%= capitalize(property_name) -%>)<%= index < schema['properties'].length ? ' && ' : ';' -%>
<%= capitalize(property_name) %>.Equals(that.<%= capitalize(property_name) %>)<%= index < schema['properties'].length ? ' && ' : ';' -%>
<%- else -%>
Object.Equals(<%= capitalize(property_name) -%>, that.<%= capitalize(property_name) -%>)<%= index < schema['properties'].length ? ' && ' : ';' -%>
Object.Equals(<%= capitalize(property_name) %>, that.<%= capitalize(property_name) %>)<%= index < schema['properties'].length ? ' && ' : ';' -%>
<%- end -%>
<% end %>
}
Expand All @@ -106,15 +110,15 @@ public sealed class <%= class_name(key) %>
isEnum = property['enum']
-%>
<%- if isEnum -%>
hash = hash * 31 + <%= capitalize(property_name) -%>.GetHashCode();
hash = hash * 31 + <%= capitalize(property_name) %>.GetHashCode();
<%- elsif isValueType && !required -%>
if (<%= capitalize(property_name) -%>.HasValue)
hash = hash * 31 + <%= capitalize(property_name) -%>.Value.GetHashCode();
hash = hash * 31 + <%= capitalize(property_name) %>.Value.GetHashCode();
<%- elsif isValueType -%>
hash = hash * 31 + <%= capitalize(property_name) -%>.GetHashCode();
hash = hash * 31 + <%= capitalize(property_name) %>.GetHashCode();
<%- else -%>
if (<%= capitalize(property_name) -%> != null)
hash = hash * 31 + <%= capitalize(property_name) -%>.GetHashCode();
hash = hash * 31 + <%= capitalize(property_name) %>.GetHashCode();
<%- end -%>
<%- end -%>
return hash;
Expand All @@ -128,7 +132,7 @@ public sealed class <%= class_name(key) %>
isValueType = type_for(class_name(key), property_name, property) == 'long' || type_for(class_name(key), property_name, property) == 'bool'
-%>
<%- if isValueType && !required -%>
(<%= capitalize(property_name) -%>.HasValue ? "<%= index.zero? ? '' : ', '%><%= property_name %>=" + <%= capitalize(property_name) %>.Value : "") +
(<%= capitalize(property_name) %>.HasValue ? "<%= index.zero? ? '' : ', '%><%= property_name %>=" + <%= capitalize(property_name) %>.Value : "") +
<%- else -%>
"<%= index.zero? ? '' : ', '%><%= property_name %>=" + <%= capitalize(property_name) %> +
<%- end -%>
Expand Down
9 changes: 7 additions & 2 deletions codegen/templates/dotnet.enum.dotnet.erb
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,12 @@ using System.Reflection;

namespace Io.Cucumber.Messages.Types;

// Generated code
// ------------------------------------------------------------------------------
// This code was generated based on the Cucumber JSON schema
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
// ------------------------------------------------------------------------------

public enum <%= enum[:name] %> {
<% enum[:values].each.with_index(1) do |value, index| -%>

Expand All @@ -15,7 +20,7 @@ public enum <%= enum[:name] %> {
<% end -%>
}

public static class <%= enum[:name] -%>Extensions
public static class <%= enum[:name] %>Extensions
{
public static string Value(this <%= enum[:name] %> v) {
var attribute = v.GetType().GetField(v.ToString()).GetCustomAttribute<DescriptionAttribute>();
Expand Down
Binary file not shown.
33 changes: 33 additions & 0 deletions dotnet/Cucumber.Messages.Specs/BasicMessageSerializationTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
using System;
using System.ComponentModel;
using Cucumber.Messages;
using System.Linq;

namespace Cucumber.Messages.Specs
{
Expand All @@ -32,5 +33,37 @@ public void SerializesAnEnvelopeToNDJSONCorrectly()
Assert.Equal(expectedStepDefinition, reconstructedStepDefinition);
}

[Fact]
public void ProperlyDeserializesCollectionProperties() {

// The following is from the CCK hooks sample
var json = @"{""id"":""33"",""pickleId"":""20"",""testSteps"":[{""hookId"":""0"",""id"":""29""},{""hookId"":""1"",""id"":""30""},{""id"":""31"",""pickleStepId"":""19"",""stepDefinitionIds"":[""2""],""stepMatchArgumentsLists"":[{""stepMatchArguments"":[]}]},{""hookId"":""4"",""id"":""32""}]}";

// This test will pass if the deserializer does not throw an exception
var testCase = NdjsonSerializer.Deserialize<TestCase>(json);
Assert.Equal(4, testCase.TestSteps.Count);

var json2 = @"{""id"":""43"",""pickleId"":""24"",""testSteps"":[{""hookId"":""0"",""id"":""39""},{""hookId"":""1"",""id"":""40""},{""id"":""41"",""pickleStepId"":""23"",""stepDefinitionIds"":[],""stepMatchArgumentsLists"":[]},{""hookId"":""4"",""id"":""42""}]}";

// This test will pass if the deserializer does not throw an exception
var testCase2 = NdjsonSerializer.Deserialize<TestCase>(json2);
Assert.Equal(4, testCase2.TestSteps.Count);

var envText = @"{""testCase"":{""id"":""33"",""pickleId"":""20"",""testSteps"":[{""hookId"":""0"",""id"":""29""},{""hookId"":""1"",""id"":""30""},{""id"":""31"",""pickleStepId"":""19"",""stepDefinitionIds"":[""2""],""stepMatchArgumentsLists"":[{""stepMatchArguments"":[]}]},{""hookId"":""4"",""id"":""32""}]}}
{""testCase"":{""id"":""38"",""pickleId"":""22"",""testSteps"":[{""hookId"":""0"",""id"":""34""},{""hookId"":""1"",""id"":""35""},{""id"":""36"",""pickleStepId"":""21"",""stepDefinitionIds"":[""3""],""stepMatchArgumentsLists"":[{""stepMatchArguments"":[]}]},{""hookId"":""4"",""id"":""37""}]}}
{""testCase"":{""id"":""43"",""pickleId"":""24"",""testSteps"":[{""hookId"":""0"",""id"":""39""},{""hookId"":""1"",""id"":""40""},{""id"":""41"",""pickleStepId"":""23"",""stepDefinitionIds"":[],""stepMatchArgumentsLists"":[]},{""hookId"":""4"",""id"":""42""}]}}
{""testCase"":{""id"":""49"",""pickleId"":""26"",""testSteps"":[{""hookId"":""0"",""id"":""44""},{""hookId"":""1"",""id"":""45""},{""id"":""46"",""pickleStepId"":""25"",""stepDefinitionIds"":[""2""],""stepMatchArgumentsLists"":[{""stepMatchArguments"":[]}]},{""hookId"":""5"",""id"":""47""},{""hookId"":""4"",""id"":""48""}]}}
{""testCase"":{""id"":""55"",""pickleId"":""28"",""testSteps"":[{""hookId"":""0"",""id"":""50""},{""hookId"":""1"",""id"":""51""},{""id"":""52"",""pickleStepId"":""27"",""stepDefinitionIds"":[""2""],""stepMatchArgumentsLists"":[{""stepMatchArguments"":[]}]},{""hookId"":""6"",""id"":""53""},{""hookId"":""4"",""id"":""54""}]}}";
var lines = envText.Replace("\r\n", "\n").Split("\n").ToList();

List<Envelope> envelopes = new List<Envelope>();

foreach (var line in lines)
{
var envelope = NdjsonSerializer.Deserialize<Envelope>(line);
envelopes.Add(envelope);
}
Assert.Equal(5, envelopes.Count);
}
}
}
16 changes: 8 additions & 8 deletions dotnet/Cucumber.Messages/generated/Attachment.cs
Original file line number Diff line number Diff line change
Expand Up @@ -86,17 +86,17 @@ public Attachment(
string url
)
{
RequireNonNull<string>(body, "Body", "Attachment.Body cannot be null");
RequireNonNull<string>(body, "Body", "Attachment.Body cannot be null");
this.Body = body;
RequireNonNull<AttachmentContentEncoding>(contentEncoding, "ContentEncoding", "Attachment.ContentEncoding cannot be null");
RequireNonNull<AttachmentContentEncoding>(contentEncoding, "ContentEncoding", "Attachment.ContentEncoding cannot be null");
this.ContentEncoding = contentEncoding;
this.FileName = fileName;
RequireNonNull<string>(mediaType, "MediaType", "Attachment.MediaType cannot be null");
this.FileName = fileName;
RequireNonNull<string>(mediaType, "MediaType", "Attachment.MediaType cannot be null");
this.MediaType = mediaType;
this.Source = source;
this.TestCaseStartedId = testCaseStartedId;
this.TestStepId = testStepId;
this.Url = url;
this.Source = source;
this.TestCaseStartedId = testCaseStartedId;
this.TestStepId = testStepId;
this.Url = url;
}

public override bool Equals(Object o)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,12 @@

namespace Io.Cucumber.Messages.Types;

// Generated code
// ------------------------------------------------------------------------------
// This code was generated based on the Cucumber JSON schema
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
// ------------------------------------------------------------------------------

public enum AttachmentContentEncoding {

[Description("IDENTITY")]
Expand Down
14 changes: 7 additions & 7 deletions dotnet/Cucumber.Messages/generated/Background.cs
Original file line number Diff line number Diff line change
Expand Up @@ -36,17 +36,17 @@ public Background(
string id
)
{
RequireNonNull<Location>(location, "Location", "Background.Location cannot be null");
RequireNonNull<Location>(location, "Location", "Background.Location cannot be null");
this.Location = location;
RequireNonNull<string>(keyword, "Keyword", "Background.Keyword cannot be null");
RequireNonNull<string>(keyword, "Keyword", "Background.Keyword cannot be null");
this.Keyword = keyword;
RequireNonNull<string>(name, "Name", "Background.Name cannot be null");
RequireNonNull<string>(name, "Name", "Background.Name cannot be null");
this.Name = name;
RequireNonNull<string>(description, "Description", "Background.Description cannot be null");
RequireNonNull<string>(description, "Description", "Background.Description cannot be null");
this.Description = description;
RequireNonNull<List<Step>>(steps, "Steps", "Background.Steps cannot be null");
this.Steps = new List<Step>(steps);
RequireNonNull<string>(id, "Id", "Background.Id cannot be null");
RequireNonNull<List<Step>>(steps, "Steps", "Background.Steps cannot be null");
this.Steps = new List<Step>(steps);
RequireNonNull<string>(id, "Id", "Background.Id cannot be null");
this.Id = id;
}

Expand Down
8 changes: 4 additions & 4 deletions dotnet/Cucumber.Messages/generated/Ci.cs
Original file line number Diff line number Diff line change
Expand Up @@ -40,11 +40,11 @@ public Ci(
Git git
)
{
RequireNonNull<string>(name, "Name", "Ci.Name cannot be null");
RequireNonNull<string>(name, "Name", "Ci.Name cannot be null");
this.Name = name;
this.Url = url;
this.BuildNumber = buildNumber;
this.Git = git;
this.Url = url;
this.BuildNumber = buildNumber;
this.Git = git;
}

public override bool Equals(Object o)
Expand Down
4 changes: 2 additions & 2 deletions dotnet/Cucumber.Messages/generated/Comment.cs
Original file line number Diff line number Diff line change
Expand Up @@ -33,9 +33,9 @@ public Comment(
string text
)
{
RequireNonNull<Location>(location, "Location", "Comment.Location cannot be null");
RequireNonNull<Location>(location, "Location", "Comment.Location cannot be null");
this.Location = location;
RequireNonNull<string>(text, "Text", "Comment.Text cannot be null");
RequireNonNull<string>(text, "Text", "Comment.Text cannot be null");
this.Text = text;
}

Expand Down
6 changes: 3 additions & 3 deletions dotnet/Cucumber.Messages/generated/DataTable.cs
Original file line number Diff line number Diff line change
Expand Up @@ -25,10 +25,10 @@ public DataTable(
List<TableRow> rows
)
{
RequireNonNull<Location>(location, "Location", "DataTable.Location cannot be null");
RequireNonNull<Location>(location, "Location", "DataTable.Location cannot be null");
this.Location = location;
RequireNonNull<List<TableRow>>(rows, "Rows", "DataTable.Rows cannot be null");
this.Rows = new List<TableRow>(rows);
RequireNonNull<List<TableRow>>(rows, "Rows", "DataTable.Rows cannot be null");
this.Rows = new List<TableRow>(rows);
}

public override bool Equals(Object o)
Expand Down
8 changes: 4 additions & 4 deletions dotnet/Cucumber.Messages/generated/DocString.cs
Original file line number Diff line number Diff line change
Expand Up @@ -29,12 +29,12 @@ public DocString(
string delimiter
)
{
RequireNonNull<Location>(location, "Location", "DocString.Location cannot be null");
RequireNonNull<Location>(location, "Location", "DocString.Location cannot be null");
this.Location = location;
this.MediaType = mediaType;
RequireNonNull<string>(content, "Content", "DocString.Content cannot be null");
this.MediaType = mediaType;
RequireNonNull<string>(content, "Content", "DocString.Content cannot be null");
this.Content = content;
RequireNonNull<string>(delimiter, "Delimiter", "DocString.Delimiter cannot be null");
RequireNonNull<string>(delimiter, "Delimiter", "DocString.Delimiter cannot be null");
this.Delimiter = delimiter;
}

Expand Down
4 changes: 2 additions & 2 deletions dotnet/Cucumber.Messages/generated/Duration.cs
Original file line number Diff line number Diff line change
Expand Up @@ -34,9 +34,9 @@ public Duration(
long nanos
)
{
RequireNonNull<long>(seconds, "Seconds", "Duration.Seconds cannot be null");
RequireNonNull<long>(seconds, "Seconds", "Duration.Seconds cannot be null");
this.Seconds = seconds;
RequireNonNull<long>(nanos, "Nanos", "Duration.Nanos cannot be null");
RequireNonNull<long>(nanos, "Nanos", "Duration.Nanos cannot be null");
this.Nanos = nanos;
}

Expand Down
34 changes: 17 additions & 17 deletions dotnet/Cucumber.Messages/generated/Envelope.cs
Original file line number Diff line number Diff line change
Expand Up @@ -453,23 +453,23 @@ public Envelope(
UndefinedParameterType undefinedParameterType
)
{
this.Attachment = attachment;
this.GherkinDocument = gherkinDocument;
this.Hook = hook;
this.Meta = meta;
this.ParameterType = parameterType;
this.ParseError = parseError;
this.Pickle = pickle;
this.Source = source;
this.StepDefinition = stepDefinition;
this.TestCase = testCase;
this.TestCaseFinished = testCaseFinished;
this.TestCaseStarted = testCaseStarted;
this.TestRunFinished = testRunFinished;
this.TestRunStarted = testRunStarted;
this.TestStepFinished = testStepFinished;
this.TestStepStarted = testStepStarted;
this.UndefinedParameterType = undefinedParameterType;
this.Attachment = attachment;
this.GherkinDocument = gherkinDocument;
this.Hook = hook;
this.Meta = meta;
this.ParameterType = parameterType;
this.ParseError = parseError;
this.Pickle = pickle;
this.Source = source;
this.StepDefinition = stepDefinition;
this.TestCase = testCase;
this.TestCaseFinished = testCaseFinished;
this.TestCaseStarted = testCaseStarted;
this.TestRunFinished = testRunFinished;
this.TestRunStarted = testRunStarted;
this.TestStepFinished = testStepFinished;
this.TestStepStarted = testStepStarted;
this.UndefinedParameterType = undefinedParameterType;
}

public override bool Equals(Object o)
Expand Down
Loading