diff --git a/CHANGELOG.md b/CHANGELOG.md index 95dec064..1098b23d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,8 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/) and this project adheres to [Semantic Versioning](http://semver.org/). ## [Unreleased] +### Added +- Add `stackTrace` prop to `Exception` message ([#182](https://github.com/cucumber/messages/pull/182)) ## [23.0.0] - 2023-11-01 ### Added diff --git a/cpp/include/cucumber/messages/exception.hpp b/cpp/include/cucumber/messages/exception.hpp index c231ca4e..be55c71e 100644 --- a/cpp/include/cucumber/messages/exception.hpp +++ b/cpp/include/cucumber/messages/exception.hpp @@ -22,6 +22,7 @@ struct exception { std::string type; std::optional message; + std::optional stack_trace; std::string to_string() const; diff --git a/cpp/src/lib/cucumber-messages/cucumber/messages/exception.cpp b/cpp/src/lib/cucumber-messages/cucumber/messages/exception.cpp index c7a6a29c..e6ea57a7 100644 --- a/cpp/src/lib/cucumber-messages/cucumber/messages/exception.cpp +++ b/cpp/src/lib/cucumber-messages/cucumber/messages/exception.cpp @@ -12,6 +12,7 @@ exception::to_string() const cucumber::messages::to_string(oss, "type=", type); cucumber::messages::to_string(oss, ", message=", message); + cucumber::messages::to_string(oss, ", stack_trace=", stack_trace); return oss.str(); } @@ -21,6 +22,7 @@ exception::to_json(json& j) const { cucumber::messages::to_json(j, camelize("type"), type); cucumber::messages::to_json(j, camelize("message"), message); + cucumber::messages::to_json(j, camelize("stack_trace"), stack_trace); } std::string diff --git a/go/messages.go b/go/messages.go index 1f8c603f..e7dc6169 100644 --- a/go/messages.go +++ b/go/messages.go @@ -37,8 +37,9 @@ type Envelope struct { } type Exception struct { - Type string `json:"type"` - Message string `json:"message,omitempty"` + Type string `json:"type"` + Message string `json:"message,omitempty"` + StackTrace string `json:"stackTrace,omitempty"` } type GherkinDocument struct { diff --git a/java/src/generated/java/io/cucumber/messages/types/Exception.java b/java/src/generated/java/io/cucumber/messages/types/Exception.java index d29a505a..f8396472 100644 --- a/java/src/generated/java/io/cucumber/messages/types/Exception.java +++ b/java/src/generated/java/io/cucumber/messages/types/Exception.java @@ -18,13 +18,16 @@ public final class Exception { private final String type; private final String message; + private final String stackTrace; public Exception( String type, - String message + String message, + String stackTrace ) { this.type = requireNonNull(type, "Exception.type cannot be null"); this.message = message; + this.stackTrace = stackTrace; } /** @@ -41,6 +44,13 @@ public Optional getMessage() { return Optional.ofNullable(message); } + /** + * The stringified stack trace of the exception that caused this result + */ + public Optional getStackTrace() { + return Optional.ofNullable(stackTrace); + } + @Override public boolean equals(Object o) { if (this == o) return true; @@ -48,14 +58,16 @@ public boolean equals(Object o) { Exception that = (Exception) o; return type.equals(that.type) && - Objects.equals(message, that.message); + Objects.equals(message, that.message) && + Objects.equals(stackTrace, that.stackTrace); } @Override public int hashCode() { return Objects.hash( type, - message + message, + stackTrace ); } @@ -64,6 +76,7 @@ public String toString() { return "Exception{" + "type=" + type + ", message=" + message + + ", stackTrace=" + stackTrace + '}'; } } diff --git a/java/src/main/java/io/cucumber/messages/Convertor.java b/java/src/main/java/io/cucumber/messages/Convertor.java index 021e8fb8..a1677138 100644 --- a/java/src/main/java/io/cucumber/messages/Convertor.java +++ b/java/src/main/java/io/cucumber/messages/Convertor.java @@ -11,7 +11,7 @@ private Convertor(){ } public static Exception toMessage(Throwable t) { - return new Exception(t.getClass().getName(), t.getMessage()); + return new Exception(t.getClass().getName(), t.getMessage(), null); } public static Timestamp toMessage(java.time.Instant instant) { diff --git a/javascript/src/messages.ts b/javascript/src/messages.ts index 24f6122e..ceec6923 100644 --- a/javascript/src/messages.ts +++ b/javascript/src/messages.ts @@ -87,6 +87,8 @@ export class Exception { type: string = '' message?: string + + stackTrace?: string } export class GherkinDocument { diff --git a/jsonschema/Exception.json b/jsonschema/Exception.json index a8187b58..b7d9d986 100644 --- a/jsonschema/Exception.json +++ b/jsonschema/Exception.json @@ -14,6 +14,10 @@ "message": { "type": "string", "description": "The message of exception that caused this result. E.g. expected: \"a\" but was: \"b\"" + }, + "stackTrace": { + "type": "string", + "description": "The stringified stack trace of the exception that caused this result" } }, "type": "object" diff --git a/messages.md b/messages.md index 685233d5..8183c829 100644 --- a/messages.md +++ b/messages.md @@ -51,6 +51,7 @@ will only have one of its fields set, which indicates the payload of the message | ----- | ---- | ----------- | ----------- | | `type` | string | yes | | | `message` | string | no | | +| `stackTrace` | string | no | | ## GherkinDocument diff --git a/perl/lib/Cucumber/Messages.pm b/perl/lib/Cucumber/Messages.pm index 342db43e..5e5d51e8 100644 --- a/perl/lib/Cucumber/Messages.pm +++ b/perl/lib/Cucumber/Messages.pm @@ -546,6 +546,7 @@ use Scalar::Util qw( blessed ); my %types = ( type => 'string', message => 'string', + stack_trace => 'string', ); # This is a work-around for the fact that Moo doesn't have introspection @@ -580,6 +581,17 @@ has message => ); +=head4 stack_trace + +The stringified stack trace of the exception that caused this result + +=cut + +has stack_trace => + (is => 'ro', + ); + + } package Cucumber::Messages::GherkinDocument { diff --git a/php/src-generated/Exception.php b/php/src-generated/Exception.php index dca44cac..ad21e051 100644 --- a/php/src-generated/Exception.php +++ b/php/src-generated/Exception.php @@ -35,6 +35,11 @@ public function __construct( * The message of exception that caused this result. E.g. expected: "a" but was: "b" */ public readonly ?string $message = null, + + /** + * The stringified stack trace of the exception that caused this result + */ + public readonly ?string $stackTrace = null, ) { } @@ -47,10 +52,12 @@ public static function fromArray(array $arr): self { self::ensureType($arr); self::ensureMessage($arr); + self::ensureStackTrace($arr); return new self( (string) $arr['type'], isset($arr['message']) ? (string) $arr['message'] : null, + isset($arr['stackTrace']) ? (string) $arr['stackTrace'] : null, ); } @@ -76,4 +83,14 @@ private static function ensureMessage(array $arr): void throw new SchemaViolationException('Property \'message\' was array'); } } + + /** + * @psalm-assert array{stackTrace?: string|int|bool} $arr + */ + private static function ensureStackTrace(array $arr): void + { + if (array_key_exists('stackTrace', $arr) && is_array($arr['stackTrace'])) { + throw new SchemaViolationException('Property \'stackTrace\' was array'); + } + } } diff --git a/ruby/lib/cucumber/messages.deserializers.rb b/ruby/lib/cucumber/messages.deserializers.rb index 3eacad5e..a5b26a65 100644 --- a/ruby/lib/cucumber/messages.deserializers.rb +++ b/ruby/lib/cucumber/messages.deserializers.rb @@ -104,6 +104,7 @@ def self.from_h(hash) self.new( type: hash[:type], message: hash[:message], + stack_trace: hash[:stackTrace], ) end end diff --git a/ruby/lib/cucumber/messages.dtos.rb b/ruby/lib/cucumber/messages.dtos.rb index 55196e8c..5a70d4d6 100644 --- a/ruby/lib/cucumber/messages.dtos.rb +++ b/ruby/lib/cucumber/messages.dtos.rb @@ -229,12 +229,19 @@ class Exception < ::Cucumber::Messages::Message ## attr_reader :message + ## + # The stringified stack trace of the exception that caused this result + ## + attr_reader :stack_trace + def initialize( type: '', - message: nil + message: nil, + stack_trace: nil ) @type = type @message = message + @stack_trace = stack_trace end end