diff --git a/.circleci/config.yml b/.circleci/config.yml index 1cedf42a1f..3b6c3ca47c 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -812,6 +812,17 @@ jobs: cd gherkin/perl make + messages-perl: + executor: docker-cucumber-build + steps: + - attach_workspace: + at: '~/cucumber' + - run: + name: messages/perl + command: | + cd messages/perl + make + ### Python tag-expressions-python: @@ -1133,6 +1144,10 @@ workflows: requires: - prepare-parallel + - messages-perl: + requires: + - prepare-parallel + ## Python - tag-expressions-python: diff --git a/messages/CHANGELOG.md b/messages/CHANGELOG.md index 72b4038e19..47517717d4 100644 --- a/messages/CHANGELOG.md +++ b/messages/CHANGELOG.md @@ -9,6 +9,9 @@ and this project adheres to [Semantic Versioning](http://semver.org/). ### Added +* Initial Perl implementation + ([#1670](https://github.com/cucumber/common/pull/1670) [ehuelsmann]) + ### Changed ### Deprecated @@ -620,6 +623,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/). [brasmusson]: https://github.com/brasmusson [charlierudolph]: https://github.com/charlierudolph [david1995]: https://github.com/david1995 +[ehuelsmann]: https://github.com/ehuelsmann [luke-hill]: https://github.com/luke-hill [mpkorstanje]: https://github.com/mpkorstanje [mvz]: https://github.com/mvz diff --git a/messages/Makefile b/messages/Makefile index 7f2c068b0e..cad0fee501 100644 --- a/messages/Makefile +++ b/messages/Makefile @@ -1,3 +1,3 @@ -LANGUAGES ?= jsonschema javascript go java ruby python +LANGUAGES ?= jsonschema javascript go java ruby python perl -include default.mk \ No newline at end of file +include default.mk diff --git a/messages/jsonschema/scripts/codegen.rb b/messages/jsonschema/scripts/codegen.rb index 5f6b9167d9..fbe548cb14 100644 --- a/messages/jsonschema/scripts/codegen.rb +++ b/messages/jsonschema/scripts/codegen.rb @@ -144,6 +144,59 @@ def array_type_for(type_name) end end +class Perl < Codegen + def initialize(paths, template_file_name: 'perl.pm.erb') + template = File.read("#{TEMPLATES_DIRECTORY}/#{template_file_name}") + enum_template = File.read("#{TEMPLATES_DIRECTORY}/perl.enum.pm.erb") + + language_type_by_schema_type = { + 'integer' => 'number', + 'string' => 'string', + 'boolean' => 'boolean', + } + + super(paths, template, enum_template, language_type_by_schema_type) + end + + def array_type_for(type_name) + "[]#{type_name}" + end + + def default_value(parent_type_name, property_name, property) + if property['type'] == 'string' + if property['enum'] + "#{property_name.upcase}_#{enum_constant(property['enum'][0])}" + else + "''" + end + elsif property['type'] == 'boolean' + "''" # an empty string renders evaluates to false + elsif property['$ref'] + type = type_for(parent_type_name, nil, property) + "#{type}->new()" + else + super(parent_type_name, property_name, property) + end + end + + def property_type_from_ref(ref) + "Cucumber::Messages::#{class_name(ref)}" + end + + def property_type_from_enum(enum) + '' + end + + def format_description(raw_description) + return '' if raw_description.nil? + + raw_description + .split("\n") + .map { |description_line| "#{description_line}" } + .join("\n") + end +end + class Ruby < Codegen def initialize(paths, template_file_name: 'ruby.rb.erb') template = File.read("#{TEMPLATES_DIRECTORY}/#{template_file_name}") diff --git a/messages/jsonschema/scripts/templates/perl.enum.pm.erb b/messages/jsonschema/scripts/templates/perl.enum.pm.erb new file mode 100644 index 0000000000..e69de29bb2 diff --git a/messages/jsonschema/scripts/templates/perl.pm.erb b/messages/jsonschema/scripts/templates/perl.pm.erb new file mode 100644 index 0000000000..dfab124c9d --- /dev/null +++ b/messages/jsonschema/scripts/templates/perl.pm.erb @@ -0,0 +1,146 @@ +package Cucumber::Messages; + +# DO NOT CHANGE THIS FILE!! + +# The code was auto-generated by this script: +# https://github.com/cucumber/common/blob/main/messages/jsonschema/scripts/codegen.rb + +=head1 NAME + +Cucumber::Messages - Library of classes to encapsulate Cucumber messages + +=head1 SYNOPSIS + + use Cucumber::Messages; + + my $loc = Cucumber::Messages::Location->new( + line => 12, column => 26 + ); + my $loc_json = $loc->to_json; + + my $envelope = Cucumber::Messages::Envelope->from_json($serialized_envelope); + +=head1 DESCRIPTION + +L +define the central protocol in the Cucumber ecosystem by which the various +components communicate. Messages are serialized to NDJSON. + +This library provides both serialization/deserialization to/from NDJSON as +well as the in-memory representation of the messages for Perl applications. + +Each serialized message should be wrapped in a C +and can thereby be deserialized by calling the C class message +with the serialized representation as its argument, like shown in the SYNOPSIS. + +=cut + +use strict; +use warnings; + +use Cucumber::Messages::Message; + +=head1 MESSAGE CLASSES + +=cut + +<%- @schemas.sort.each do |key, schema| %> + +package Cucumber::Messages::<%= class_name(key) %> { + +=head2 Cucumber::Messages::<%= class_name(key) %> + +=head3 DESCRIPTION + +Represents the <%= class_name(key) %> message in Cucumber's +L. + +<%= format_description(schema['description']) %> + +=head3 ATTRIBUTES + +=cut + +use Moo; +extends 'Cucumber::Messages::Message'; + +use Scalar::Util qw( blessed ); + +my %types = ( +<%- schema['properties'].each do |property_name, property| -%> + <%= underscore(property_name) %> => '<%= type_for(class_name(key), property_name, property) %>', +<%- end -%> +); + +# This is a work-around for the fact that Moo doesn't have introspection +# and Perl doesn't have boolean values... +sub _types { + return \%types; +} + + +<%- schema['properties'].each do |property_name, property| -%> + +=head4 <%= underscore(property_name) %> + +<%- if property['description'] -%> +<%= format_description(property['description']) %> +<%- end -%> + +<% if property['enum'] -%> + +Available constants for valid values of this field: + +=over + +<%- property['enum'].to_a.each_with_index do |value, index| -%> +=item * <%= property_name.upcase %>_<%= enum_constant(value) %> + +<%- end -%> +=back + +<% end -%> +=cut + +<% if property['enum'] -%> + +use constant +<%- property['enum'].to_a.each_with_index do |value, index| -%> + <%= property_name.upcase %>_<%= enum_constant(value) %> => '<%= value %>', +<%- end -%> + ; + +<% end -%> +has <%= underscore(property_name) %> => + (is => 'ro', + <%- if (schema['required'] || []).index(property_name) -%> + required => 1, + <%- if default_value(class_name(key), property_name, property) -%> + default => sub { <%= default_value(class_name(key), property_name, property) %> }, + <%- end -%> + <%- end -%> + ); + +<%- end %> +}<%- end -%> + + +1; + +__END__ + +=head1 LICENSE + +Please see the included LICENSE for the canonical version. In summary: + +The MIT License (MIT) + + Copyright (c) 2021 Erik Huelsmann + Copyright (c) 2021 Cucumber Ltd + +This work is loosely derived from prior work of the same library for Ruby, +called C. + +=cut + + diff --git a/messages/perl/.gitignore b/messages/perl/.gitignore new file mode 100644 index 0000000000..02034d48e4 --- /dev/null +++ b/messages/perl/.gitignore @@ -0,0 +1,3 @@ +perl5/ +.* +*~ diff --git a/messages/perl/.rsync b/messages/perl/.rsync new file mode 100644 index 0000000000..9e595acace --- /dev/null +++ b/messages/perl/.rsync @@ -0,0 +1 @@ +../../.templates/perl/ . diff --git a/messages/perl/CHANGELOG.md b/messages/perl/CHANGELOG.md new file mode 100644 index 0000000000..47517717d4 --- /dev/null +++ b/messages/perl/CHANGELOG.md @@ -0,0 +1,631 @@ +# CHANGE LOG +All notable changes to this project will be documented in this file. + +The format is based on [Keep a Changelog](http://keepachangelog.com/) +and this project adheres to [Semantic Versioning](http://semver.org/). + +---- +## [Unreleased] + +### Added + +* Initial Perl implementation + ([#1670](https://github.com/cucumber/common/pull/1670) [ehuelsmann]) + +### Changed + +### Deprecated + +### Removed + +### Fixed + +## [17.0.1] - 2021-07-19 + +### Fixed + +- [Ruby] `seconds_to_duration` convert nanos to an integer to match the json schema + ([#1655](https://github.com/cucumber/common/pull/1655)) + +## [17.0.0] - 2021-07-08 + +### Added + +* Added `Ci#buildNumber` field + ([#1632](https://github.com/cucumber/common/pull/1632) + [#1606](https://github.com/cucumber/common/issues/1606) + [aslakhellesoy]) +* [Ruby] Generate DTOs from the json schemas to avoid usage of plain ruby hashes + ([#1574](https://github.com/cucumber/common/issues/1574) + [#1605](https://github.com/cucumber/common/pull/1605)) +* [Java] Added various overloads to `JSON` to work with streams efficiently + +### Changed + +* **BREAKING** Move `willBeRetried` field from `TestStepResult` to `TestCaseFinished` + ([#902](https://github.com/cucumber/common/issues/902) [#1631](https://github.com/cucumber/common/pull/1631)) +* [Go] Move module paths to point to monorepo + ([#1550](https://github.com/cucumber/common/issues/1550)) +* [Java] Removed implicit utility class constructors and made classes final + +## [16.0.1] - 2021-05-24 + +### Fixed + +* [JavaScript] `addDurations` works with legacy messages that represent `seconds` as a `string`. + +## [16.0.0] - 2021-05-15 + +### Added + +* [Ruby] The generated code is added to Git. + ([#1461](https://github.com/cucumber/cucumber/pull/1461) + [aslakhellesoy]) +* [JavaScript] Added `getWorstTestStepResult` function (moved from `@cucumber/query`) + +### Changed + +* [Go, Java, JavaScript, Ruby] The library no longer depends on protocol buffers. The message classes are generated from [JSON Schemas](https://github.com/cucumber/common/tree/messages/v16.0.0/messages). + ([#1414](https://github.com/cucumber/cucumber/pull/1414) + [aslakhellesoy]) + * Empty `string` properties are set to `""` rather than being omitted. + * Empty `Array` properties are set to `[]` rather than being omitted. + * The `seconds` property on `Timestamp` and `Diration` is now a `number` rather than a `string`. +* [Go, Java, JavaScript, Ruby] Packages and structs have changed: + * [JavaScript]: Import with `import * as messages from '@cucumber/messages'` + * [Ruby] Messages are now plain ruby hashes with camelCase symbol keys instead of objects with snake_case properties. + +## [15.0.0] - 2021-03-23 + +### Added + +* [All] Added tags to Rule node + ([#1356](https://github.com/cucumber/cucumber/pull/1356) [sebrose], [gasparnagy], [brasmusson], [WannesFransen1994]) + +### Removed + +* [JavaScript] moved code that depends on Node.js stream APIs to `@cucumber/message-streams` + +### Fixed + +* [Elixir] Make sure messages.proto is updated with ../messages.proto +* [Elixir] Generated new message modules based on the new messages.proto file + +## [14.1.2] - 2021-03-23 + +### Fixed + +* [JavaScript] revert breaking changes in 14.1.1 + ([#1437](https://github.com/cucumber/cucumber/issues/1437)) + +## [14.1.1] - 2021-03-22 + +### Fixed + +* The 14.1.0 release failed half way through due to build script problems. + +## [14.1.0] - 2021-03-22 + +### Added + +* [All] Added tags to Rule node + ([#1356](https://github.com/cucumber/cucumber/pull/1356) [sebrose], [gasparnagy], [brasmusson], [WannesFransen1994]) + +### Fixed + +* [Elixir] Make sure messages.proto is updated with ../messages.proto +* [Elixir] Generated new message modules based on the new messages.proto file + +## [14.0.1] - 2021-02-08 + +### Fixed + +* [JavaScript] Remove `Long` from function signatures in `TimeConversion` + +## [14.0.0] - 2021-02-05 + +### Changed + +* [JavaScript] The stream classes have to be imported separately: + `import { MessageToBinaryStream, MessageToNdjsonStream, BinaryToMessageStream, NdjsonToMessageStream} from '@cucumber/messages/dist/src/stream'` + ([#1331](https://github.com/cucumber/cucumber/issues/1331)) +* In JavaScript, Int64 fields are now typed as `number`, rather than `number|Long` + +### Fixed + +* [JavaScript] removed circular dependencies. + ([#1292](https://github.com/cucumber/cucumber/pull/1292) + [davidjgoss] + [aslakhellesoy]) + +## [13.2.1] - 2020-11-19 + +### Fixed + +* In case a line cannot be parsed as JSON, throw an error + with the offending line in the exception message. + +## [13.2.0] - 2020-11-12 + +### Added + +* Elixir implementation + ([#1175](https://github.com/cucumber/cucumber/pull/1175) + [WannesFransen1994]) + +## [13.1.0] - 2020-09-14 + +### Added + +* Add `Attachment#url`. This makes it possible to "detach" attachments so the main stream + gets smaller and the attachments can be processed/downloaded separately with more ease. + ([#1183](https://github.com/cucumber/cucumber/pull/1183) + [aslakhellesoy]) + +### Fixed + +* Ignore empty lines rather than throwing an error. + +## [13.0.1] - 2020-08-07 + +### Fixed + +* Release again since the 13.0.0 release didn't fully sync to subrepos + +## [13.0.0] - 2020-08-07 + +### Changed + +* Move `JavaMethod` and `JavaStackTraceElement` to be children of `SourceReference` + +## [12.4.0] - 2020-07-31 + +### Added + +* Suggested file name to the Attachment messages ([#1128](https://github.com/cucumber/cucumber/pull/1128)) +* Added ProtocolVersion to access messages version reliably ([#1127](https://github.com/cucumber/cucumber/pull/1127) [mpkorstanje]) + +## [12.3.2] - 2020-07-29 + +### Fixed + +* Release process for 12.3.1 failed + +## [12.3.1] - 2020-07-29 + +### Fixed + +* Release process for 12.3.0 failed + +## [12.3.0] - 2020-07-29 + +### Added + +* Add `JavaMethod` and `JavaStackTraceElement` as `SourceReference` ([#1120](https://github.com/cucumber/cucumber/pull/1120)) + +### Fixed + +* [Ruby] Fix computing of Timestamp (see [cucumber-ruby#1438](https://github.com/cucumber/cucumber-ruby/issues/1438)) + +## [12.2.0] - 2020-06-26 + +### Added + +* Added field `ci` to `Meta` message. + +## [12.1.1] - 2020-04-21 + +### Fixed + +* [Ruby] Add `VERSION` file to gem, so `Messages::Envelope::VERSION` works + +## [12.1.0] - 2020-04-21 + +### Added + +* Expose the library version through the API + * Java: `io.cucumber.messages.Messages.Envelope.class.getPackage().getImplementationVersion()` + * Ruby: `Messages::Envelope::VERSION` + * JavaScript: `import { version } from '@cucumber/messages'` +* Add `Meta` message + ([#976](https://github.com/cucumber/cucumber/pull/976) + [aslakhellesoy]) +* [Java] Upgrade to `cucumber-parent:2.1.0` (needed to expose library version) + ([#976](https://github.com/cucumber/cucumber/pull/976) + [aslakhellesoy]) + +## [12.0.0] - 2020-04-14 + +### Added + +* Add `id` field to `Background`, `Rule` and `Examples`. Needed for search engine indexing. + +### Changed + +* Some messages have been nested under other messages (removal of other messages) + made it possible to scope them more locally + +### Removed + +* Several messages that weren't used have been removed + ([#962](https://github.com/cucumber/cucumber/pull/962) + [#951](https://github.com/cucumber/cucumber/issues/951) + [aslakhellesoy]) + +### Fixed + +* Ignore unknown fields when parsing JSON. This is to ensure forward and backward + compatibility when using JSON encoding (NDJSON). + ([#959](https://github.com/cucumber/cucumber/pull/959) + [#957](https://github.com/cucumber/cucumber/pull/957) + [aslakhellesoy] + [mpkorstanje]) + +## [11.1.1] - 2020-03-30 + +### Fixed + +* The [#932](https://github.com/cucumber/cucumber/pull/932) pull request wasn't actually + included in 11.1.0 + +## [11.1.0] - 2020-03-30 + +### Added + +* [JavaScript] Add `#addDurations` function to `TimeConversion` + ([#932](https://github.com/cucumber/cucumber/pull/932) + [charlierudolph]) + +## [11.0.1] - 2020-03-30 + +### Fixed + +* Release process + +## [11.0.0] - 2020-03-30 + +### Changed + +* `Attachment#text` and `Attachment#binary` have been replaced with `Attachment#body`, + and `Attachment#content_encoding` has been added. + ([#947](https://github.com/cucumber/cucumber/pull/947) + [aslakhellesoy]) + +## [10.0.3] - 2020-03-05 + +### Fixed + +* 10.0.2 release process failed. + +## [10.0.2] - 2020-03-02 + +### Fixed + +* [JavaScript] make stream readable/writable object modes explicit. Better buffer handling. +* [Ruby] Don't depend on the `json` gem, since it only installs on Windows with + a C compiler toolchain. The `json` gem is part of the Ruby stdlib. +* [Go] Increase max size of a JSON message to 10Mb + ([#901](https://github.com/cucumber/cucumber/issues/901) + [#903](https://github.com/cucumber/cucumber/pull/903) + [aslakhellesoy]) + +## [10.0.1] - 2020-02-13 + +### Fixed + +* The 10.0.0 release failed. + +## [10.0.0] - 2020-02-13 + +### Added + +* Add `UndefinedParameterType` + ([#890](https://github.com/cucumber/cucumber/pull/890) + [aslakhellesoy]) +* Add `TestCaseFinished#message` + ([#890](https://github.com/cucumber/cucumber/pull/890) + [aslakhellesoy]) + +### Changed + +* Rename `TestResult` to `TestStepResult` + ([#890](https://github.com/cucumber/cucumber/pull/890) + [aslakhellesoy]) + +### Fixed + +* Ruby: Fixed rounding errors in `TimeConversion` + +## [9.0.3] - 2020-01-10 + +### Fixed + +* Ruby: Fix inclusion of generated lib/messages.pb.rb + +## [9.0.2] - 2020-01-10 + +* Ruby: Fix error in gemspec + +## [9.0.1] - 2020-01-09 + +### Fixed + +* Fixed release scripts for Go and JavaScript + +## [9.0.0] - 2020-01-09 + +### Added + +* Added `ParameterType` as an envelope message + +### Changed + +* Renamed module name from `cucumber-messages` to `messsages` + ([#850](https://github.com/cucumber/cucumber/pull/850) + [aslakhellesoy]) +* Use pure Ruby protobuf in order to avoid problems with Ruby 2.7.0 and JRuby. + ([#813](https://github.com/cucumber/cucumber/pull/813) + [#843](https://github.com/cucumber/cucumber/issues/843) + [mvz] + [aslakhellesoy]) +* A `TestCase` now has a list of `StepMatchArgumentsList` rather than a list of `StepMatchArgument`. + This allows a `TestCase` to hold matches from more than one `StepDefinition`, which is possible + in the case of ambiguous matches. +* Renamed `content_type` fields to `media_type` +* Removed the `Media` message +* Attachments can have one of `string text` and `bytes binary` as the `body` + +### Removed + +* Removed `TestCase#test_result`. Aggregate results will be computed by `cucumber-query` instead + +## [8.0.0] - 2019-12-10 + +### Added + +* Added `testStepId` and `testCaseStartedId` to `Attachment` + ([#814](https://github.com/cucumber/cucumber/pull/814) + [charlierudolph]) +* Added new classes for working with NDJSON streams +* Added new `TimeConversion` utilities for converting between protobuf `Timestamp/Duration` and the + platform's representation of timestamps (clock time) and duration (monotonic time) + +### Changed + +* Renamed some of the classes for dealing with streams + +## [7.0.0] - 2019-11-14 + +### Added + +* Support retry + ([#722](https://github.com/cucumber/cucumber/pull/722) + [charlierudolph]) + +### Changed + +* Added more reference ids and removed deprecated/reserved fields + ([#790](https://github.com/cucumber/cucumber/pull/790) + [#794](https://github.com/cucumber/cucumber/pull/794) + [vincent-psarga] + [aslakhellesoy]) + +## [6.0.2] - 2019-10-16 + +* [Java] Fix shading of `com.google.protobuf.util` + +## [6.0.1] - 2019-10-03 + +### Fixed + +* [Go] Release 6.0.0 is unusable. + +## [6.0.0] - 2019-10-02 + +### Added + +* New `TestStepMatched` message +* `Duration` message to express duration in a seconds + nano format +* field `duration` in `TestResult` message + +### Changed + +* Renamed `PatternMatch` to `StepMatchArgument` +* Renamed `CommandRunTestStep#patternMatches` to `CommandRunTestStep#stepMatchArguments` +* Replace Google's Timestamp by our own message + +### Removed + +* `durationNanoSeconds` field in `TestResult` message + +## [5.0.1] - 2019-08-23 + +### Fixed + +* [Go] Fix module version (5.0.0 left it at v4) + +## [5.0.0] - 2019-08-23 + +### Changed + +* The ordinal numbers of the `Status` enum have changed, to easier compute an + aggregated status. + +### Fixed + +* [JavaScript] Don't swallow exceptions happening in `ProtobufMessageStream` + +## [4.0.0] - 2019-08-14 + +* [Go] Append v4 to go module name to comply with the go.mod spec + +## [3.0.5] - 2019-08-14 + +### Fixed + +* [Go] Tag the cucumber/messages-go subrepo (again) + +## [3.0.4] - 2019-08-14 + +### Fixed + +* [Go] Tag the cucumber/messages-go subrepo (again) + +## [3.0.3] - 2019-08-14 + +### Fixed + +* [Go] Tag the cucumber/messages-go subrepo + +## [3.0.2] - 2019-08-01 + +### Added + +* Add Timestamp property and comments to TestRunFinished message + ([#665](https://github.com/cucumber/cucumber/pull/665) + [SabotageAndi] + +## [3.0.1] - 2019-07-15 + +### Added + +* [.NET] Publish NuGet package + ([#635](https://github.com/cucumber/cucumber/pull/635) + [SabotageAndi] + [aslakhellesoy] + [vincent-psarga]) + +## [3.0.0] - 2019-06-05 + +### Added +* Added `TestRunStarted#timestamp` field + ([#615](https://github.com/cucumber/cucumber/pull/615) + [david1995]) +* Added `TestCaseStarted#platform` field + ([#626](https://github.com/cucumber/cucumber/pull/626) + [#616](https://github.com/cucumber/cucumber/pull/616) + [david1995]) + +### Changed +* Use an enum for the `encoding` field. +* Misc changes for `cucumber-engine` + ([#549](https://github.com/cucumber/cucumber/pull/549)) + +### Deprecated + +### Removed + +### Fixed + +## [2.1.2] - 2019-03-29 + +### Fixed + +* Fix Project struggling to build across JRuby and Ruby 2.6 + ([#578](https://github.com/cucumber/cucumber/pull/578) + [luke-hill]) + +## [2.1.1] - 2018-11-02 + +### Fixed + +* Release process improvements + +## [2.1.0] - 2018-11-01 + +### Added + +* Add testResult to the TestCaseFinished message + ([#488](https://github.com/cucumber/cucumber/pull/488) + [brasmusson]) + +## [2.0.0] - 2018-10-14 + +### Added + +* Several messages to support [cucumber-engine]() + ([#502](https://github.com/cucumber/cucumber/pull/502) + [charlierudolph]) + +## [1.1.2] - 2018-10-01 + +### Added + +* Added `TestHookStarted` and `TestHookFinished` to distinguish between messages about Gherkin steps and hooks + ([aslakhellesoy]) + +## [1.0.0] - 2018-09-15 + +### Added + +* Protobuf messages for Go, Java, JavaScript, TypeScript and Ruby + + +[Unreleased]: https://github.com/cucumber/cucumber/compare/messages/v17.0.1...main +[17.0.1]: https://github.com/cucumber/cucumber/compare/messages/v17.0.0...messages/v17.0.1 +[17.0.0]: https://github.com/cucumber/cucumber/compare/messages/v16.0.1...messages/v17.0.0 +[16.0.1]: https://github.com/cucumber/cucumber/compare/messages/v16.0.0...messages/v16.0.1 +[16.0.0]: https://github.com/cucumber/cucumber/compare/messages/v15.0.0...messages/v16.0.0 +[15.0.0]: https://github.com/cucumber/cucumber/compare/messages/v14.1.2...messages/v15.0.0 +[14.1.2]: https://github.com/cucumber/cucumber/compare/messages/v14.0.1...messages/v14.1.2 +[14.1.1]: https://github.com/cucumber/cucumber/compare/messages/v14.1.0...messages/v14.1.1 +[14.1.0]: https://github.com/cucumber/cucumber/compare/messages/v14.0.1...messages/v14.1.0 +[14.0.1]: https://github.com/cucumber/cucumber/compare/messages/v14.0.0...messages/v14.0.1 +[14.0.0]: https://github.com/cucumber/cucumber/compare/messages/v13.2.1...messages/v14.0.0 +[13.2.1]: https://github.com/cucumber/cucumber/compare/messages/v13.2.0...messages/v13.2.1 +[13.2.0]: https://github.com/cucumber/cucumber/compare/messages/v13.1.0...messages/v13.2.0 +[13.1.0]: https://github.com/cucumber/cucumber/compare/messages/v13.0.1...messages/v13.1.0 +[13.0.1]: https://github.com/cucumber/cucumber/compare/messages/v13.0.0...messages/v13.0.1 +[13.0.0]: https://github.com/cucumber/cucumber/compare/messages/v12.4.0...messages/v13.0.0 +[12.4.0]: https://github.com/cucumber/cucumber/compare/messages/v12.3.2...messages/v12.4.0 +[12.3.2]: https://github.com/cucumber/cucumber/compare/messages/v12.3.1...messages/v12.3.2 +[12.3.1]: https://github.com/cucumber/cucumber/compare/messages/v12.3.0...messages/v12.3.1 +[12.3.0]: https://github.com/cucumber/cucumber/compare/messages/v12.2.0...messages/v12.3.0 +[12.2.0]: https://github.com/cucumber/cucumber/compare/messages/v12.1.1...messages/v12.2.0 +[12.1.1]: https://github.com/cucumber/cucumber/compare/messages/v12.1.0...messages/v12.1.1 +[12.1.0]: https://github.com/cucumber/cucumber/compare/messages/v12.0.0...messages/v12.1.0 +[12.0.0]: https://github.com/cucumber/cucumber/compare/messages/v11.1.1...messages/v12.0.0 +[11.1.1]: https://github.com/cucumber/cucumber/compare/messages/v11.1.0...messages/v11.1.1 +[11.1.0]: https://github.com/cucumber/cucumber/compare/messages/v11.0.1...messages/v11.1.0 +[11.0.1]: https://github.com/cucumber/cucumber/compare/messages/v11.0.0...messages/v11.0.1 +[11.0.0]: https://github.com/cucumber/cucumber/compare/messages/v10.0.3...messages/v11.0.0 +[10.0.3]: https://github.com/cucumber/cucumber/compare/messages/v10.0.2...messages/v10.0.3 +[10.0.2]: https://github.com/cucumber/cucumber/compare/messages/v10.0.1...messages/v10.0.2 +[10.0.1]: https://github.com/cucumber/cucumber/compare/messages/v10.0.0...messages/v10.0.1 +[10.0.0]: https://github.com/cucumber/cucumber/compare/messages/v9.0.3...messages/v10.0.0 +[9.0.3]: https://github.com/cucumber/cucumber/compare/messages/v9.0.2...messages/v9.0.3 +[9.0.2]: https://github.com/cucumber/cucumber/compare/messages/v9.0.1...messages/v9.0.2 +[9.0.1]: https://github.com/cucumber/cucumber/compare/messages/v9.0.0...messages/v9.0.1 +[9.0.0]: https://github.com/cucumber/cucumber/compare/cucumber-messages/v8.0.0...messages/v9.0.0 +[8.0.0]: https://github.com/cucumber/cucumber/compare/cucumber-messages/v7.0.0...cucumber-messages/v8.0.0 +[7.0.0]: https://github.com/cucumber/cucumber/compare/cucumber-messages/v6.0.2...cucumber-messages/v7.0.0 +[6.0.2]: https://github.com/cucumber/cucumber/compare/cucumber-messages/v6.0.1...cucumber-messages/v6.0.2 +[6.0.1]: https://github.com/cucumber/cucumber/compare/cucumber-messages/v6.0.0...cucumber-messages/v6.0.1 +[6.0.0]: https://github.com/cucumber/cucumber/compare/cucumber-messages/v5.0.1...cucumber-messages/v6.0.0 +[5.0.1]: https://github.com/cucumber/cucumber/compare/cucumber-messages/v5.0.0...cucumber-messages/v5.0.1 +[5.0.0]: https://github.com/cucumber/cucumber/compare/cucumber-messages/v4.0.0...cucumber-messages/v5.0.0 +[4.0.0]: https://github.com/cucumber/cucumber/compare/cucumber-messages/v3.0.5...cucumber-messages/v4.0.0 +[3.0.5]: https://github.com/cucumber/cucumber/compare/cucumber-messages/v3.0.4...cucumber-messages/v3.0.5 +[3.0.4]: https://github.com/cucumber/cucumber/compare/cucumber-messages/v3.0.3...cucumber-messages/v3.0.4 +[3.0.3]: https://github.com/cucumber/cucumber/compare/cucumber-messages/v3.0.2...cucumber-messages/v3.0.3 +[3.0.2]: https://github.com/cucumber/cucumber/compare/cucumber-messages/v3.0.1...cucumber-messages/v3.0.2 +[3.0.1]: https://github.com/cucumber/cucumber/compare/cucumber-messages/v3.0.0...cucumber-messages/v3.0.1 +[3.0.0]: https://github.com/cucumber/cucumber/compare/cucumber-messages/v2.1.2...cucumber-messages/v3.0.0 +[2.1.2]: https://github.com/cucumber/cucumber/compare/cucumber-messages/v2.1.1...cucumber-messages/v2.1.2 +[2.1.1]: https://github.com/cucumber/cucumber/compare/cucumber-messages/v2.1.0...cucumber-messages/v2.1.1 +[2.1.0]: https://github.com/cucumber/cucumber/compare/cucumber-messages/v2.0.0...cucumber-messages/v2.1.0 +[2.0.0]: https://github.com/cucumber/cucumber/compare/cucumber-messages/v1.1.2...cucumber-messages/v2.0.0 +[1.1.2]: https://github.com/cucumber/cucumber/compare/cucumber-messages-v1.0.0...cucumber-messages/v1.1.2 +[1.0.0]: https://github.com/cucumber/cucumber/releases/tag/cucumber-messages-v1.0.0 + + +[aslakhellesoy]: https://github.com/aslakhellesoy +[brasmusson]: https://github.com/brasmusson +[charlierudolph]: https://github.com/charlierudolph +[david1995]: https://github.com/david1995 +[ehuelsmann]: https://github.com/ehuelsmann +[luke-hill]: https://github.com/luke-hill +[mpkorstanje]: https://github.com/mpkorstanje +[mvz]: https://github.com/mvz +[SabotageAndi]: https://github.com/SabotageAndi +[vincent-psarga]: https://github.com/vincent-psarga diff --git a/messages/perl/LICENSE b/messages/perl/LICENSE new file mode 100644 index 0000000000..725ba9f4ac --- /dev/null +++ b/messages/perl/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) Cucumber Ltd + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/messages/perl/Makefile b/messages/perl/Makefile new file mode 100644 index 0000000000..9a48c3fab0 --- /dev/null +++ b/messages/perl/Makefile @@ -0,0 +1,14 @@ +include default.mk + +JSONSCHEMAS = $(shell find ../jsonschema -name "*.json") + +test: lib/Cucumber/Messages.pm .cpanfile_dependencies + PERL5LIB=./perl5/lib/perl5 AUTHOR_TESTS=1 prove -l +.PHONY: test + +lib/Cucumber/Messages.pm: $(JSONSCHEMAS) ../jsonschema/scripts/codegen.rb ../jsonschema/scripts/templates/perl.pm.erb ../jsonschema/scripts/templates/perl.enum.pm.erb + ruby ../jsonschema/scripts/codegen.rb Perl ../jsonschema > $@ + +clean: + rm -f lib/Cucumber/Messages.pm .cpanfile_dependencies .built + diff --git a/messages/perl/README.md b/messages/perl/README.md new file mode 100644 index 0000000000..70cbf7cc78 --- /dev/null +++ b/messages/perl/README.md @@ -0,0 +1,2 @@ +# Cucumber Messages for Perl (JSON schema) + diff --git a/messages/perl/VERSION b/messages/perl/VERSION new file mode 100644 index 0000000000..aac58983e6 --- /dev/null +++ b/messages/perl/VERSION @@ -0,0 +1 @@ +17.0.0 diff --git a/messages/perl/cpanfile b/messages/perl/cpanfile new file mode 100644 index 0000000000..4c599c1b73 --- /dev/null +++ b/messages/perl/cpanfile @@ -0,0 +1,18 @@ + +requires "perl", "5.14.0"; + +requires "JSON::MaybeXS"; +requires "Moo"; + + +on 'test' => sub { + requires "File::Find::Rule"; + requires "Test2::Tools::ClassicCompare"; + requires "Test2::V0"; + +}; + + +on 'develop' => sub { + # There are no specific development dependencies +} \ No newline at end of file diff --git a/messages/perl/default.mk b/messages/perl/default.mk new file mode 100644 index 0000000000..ff01f453e1 --- /dev/null +++ b/messages/perl/default.mk @@ -0,0 +1,61 @@ +# Please update /.templates/perl/default.mk and sync: +# +# source scripts/functions.sh && rsync_files +# +SHELL := /usr/bin/env bash +ALPINE := $(shell which apk 2> /dev/null) + +### COMMON stuff for all platforms + +BERP_VERSION = 1.3.0 +BERP_GRAMMAR = gherkin.berp + +define berp-generate-parser = +-! dotnet tool list --tool-path /usr/bin | grep "berp\s*$(BERP_VERSION)" && dotnet tool update Berp --version $(BERP_VERSION) --tool-path /usr/bin +berp -g $(BERP_GRAMMAR) -t $< -o $@ --noBOM +endef + + +### Common targets for all functionalities implemented on Perl + +default: test +.PHONY: default + +CHANGELOG.md: ../CHANGELOG.md + cp ../CHANGELOG.md CHANGELOG.md + +distribution: predistribution + PERL5LIB=$$PWD/perl5/lib/perl5 PATH=$$PATH:$$PWD/perl5/bin dzil test --release + PERL5LIB=$$PWD/perl5/lib/perl5 PATH=$$PATH:$$PWD/perl5/bin dzil build +.PHONY: distribution + +publish: predistribution + PERL5LIB=$$PWD/perl5/lib/perl5 PATH=$$PATH:$$PWD/perl5/bin dzil release +.PHONY: publish + +update-version: +ifdef NEW_VERSION + echo $(NEW_VERSION) > VERSION +else + @echo -e "\033[0;31mNEW_VERSION is not defined. Can't update version :-(\033[0m" + exit 1 +endif +.PHONY: update-version + +.cpanfile_dependencies: cpanfile + cpanm --notest --local-lib ./perl5 --installdeps . + touch $@ + +predistribution: test CHANGELOG.md +# --notest to keep the number of dependencies low: it doesn't install the +# testing dependencies of the dependencies. + cpanm --notest --local-lib ./perl5 --installdeps --with-develop . + cpanm --notest --local-lib ./perl5 'Dist::Zilla' + PERL5LIB=./perl5/lib/perl5 PATH=$$PATH:./perl5/bin dzil authordeps --missing | cpanm --notest --local-lib ./perl5 + PERL5LIB=./perl5/lib/perl5 PATH=$$PATH:./perl5/bin dzil clean + @(git status --porcelain 2>/dev/null | grep "^??" | perl -ne\ + 'die "The `release` target includes all files in the working directory. Please remove [$$_], or add it to .gitignore if it should be included\n" if s!.+ perl/(.+?)\n!$$1!') +.PHONY: predistribution + +pre-release: update-version +.PHONY: pre-release diff --git a/messages/perl/dist.ini b/messages/perl/dist.ini new file mode 100644 index 0000000000..afb822bb40 --- /dev/null +++ b/messages/perl/dist.ini @@ -0,0 +1,36 @@ +; The name of the 'dist' (the base name of the release tarball) +name = Cucumber-Messages +; A short description of the content of the dist +abstract = A library for (de)serializing Cucumber protocol messages +; The main module presents the primary page shown on MetaCPAN.org for the dist +main_module = lib/Cucumber/Messages.pm +; A list of authors, one author per 'author=' row +author = Erik Huelsmann +author = Cucumber Ltd +license = MIT +is_trial = 0 +copyright_holder = Erik Huelsmann, Cucumber Ltd + +[MetaResources] +bugtracker.web = https://github.com/cucumber/common/issues +repository.url = https://github.com/cucumber/common.git +repository.web = https://github.com/cucumber/common/messages/perl +repository.type = git + +[@Filter] +-bundle=@Basic +-remove=Readme +-remove=ConfirmRelease +-remove=License +-remove=GatherDir + +[MetaJSON] +[MetaProvides::Package] +[PkgVersion] +[Prereqs::FromCPANfile] +[Git::GatherDir] +exclude_filename=VERSION +exclude_filename=default.mk + +[Hook::VersionProvider] +. = my $v = `cat ./VERSION`; chomp( $v ); $v; diff --git a/messages/perl/lib/Cucumber/Messages.pm b/messages/perl/lib/Cucumber/Messages.pm new file mode 100644 index 0000000000..a906aa2160 --- /dev/null +++ b/messages/perl/lib/Cucumber/Messages.pm @@ -0,0 +1,4580 @@ +package Cucumber::Messages; + +# DO NOT CHANGE THIS FILE!! + +# The code was auto-generated by this script: +# https://github.com/cucumber/common/blob/main/messages/jsonschema/scripts/codegen.rb + +=head1 NAME + +Cucumber::Messages - Library of classes to encapsulate Cucumber messages + +=head1 SYNOPSIS + + use Cucumber::Messages; + + my $loc = Cucumber::Messages::Location->new( + line => 12, column => 26 + ); + my $loc_json = $loc->to_json; + + my $envelope = Cucumber::Messages::Envelope->from_json($serialized_envelope); + +=head1 DESCRIPTION + +L +define the central protocol in the Cucumber ecosystem by which the various +components communicate. Messages are serialized to NDJSON. + +This library provides both serialization/deserialization to/from NDJSON as +well as the in-memory representation of the messages for Perl applications. + +Each serialized message should be wrapped in a C +and can thereby be deserialized by calling the C class message +with the serialized representation as its argument, like shown in the SYNOPSIS. + +=cut + +use strict; +use warnings; + +use Cucumber::Messages::Message; + +=head1 MESSAGE CLASSES + +=cut + + + +package Cucumber::Messages::Attachment { + +=head2 Cucumber::Messages::Attachment + +=head3 DESCRIPTION + +Represents the Attachment message in Cucumber's +L. + +//// Attachments (parse errors, execution errors, screenshots, links...) + +* + An attachment represents any kind of data associated with a line in a + [Source](#io.cucumber.messages.Source) file. It can be used for: + + * Syntax errors during parse time + * Screenshots captured and attached during execution + * Logs captured and attached during execution + + It is not to be used for runtime errors raised/thrown during execution. This + is captured in `TestResult`. + +=head3 ATTRIBUTES + +=cut + +use Moo; +extends 'Cucumber::Messages::Message'; + +use Scalar::Util qw( blessed ); + +my %types = ( + body => 'string', + content_encoding => '', + file_name => 'string', + media_type => 'string', + source => 'Cucumber::Messages::Source', + test_case_started_id => 'string', + test_step_id => 'string', + url => 'string', +); + +# This is a work-around for the fact that Moo doesn't have introspection +# and Perl doesn't have boolean values... +sub _types { + return \%types; +} + + + +=head4 body + +* + The body of the attachment. If `contentEncoding` is `IDENTITY`, the attachment + is simply the string. If it's `BASE64`, the string should be Base64 decoded to + obtain the attachment. + +=cut + +has body => + (is => 'ro', + required => 1, + default => sub { '' }, + ); + + +=head4 content_encoding + +* + Whether to interpret `body` "as-is" (IDENTITY) or if it needs to be Base64-decoded (BASE64). + + Content encoding is *not* determined by the media type, but rather by the type + of the object being attached: + + - string => IDENTITY + - byte array => BASE64 + - stream => BASE64 + + +Available constants for valid values of this field: + +=over + +=item * CONTENTENCODING_IDENTITY + +=item * CONTENTENCODING_BASE64 + +=back + +=cut + + +use constant + CONTENTENCODING_IDENTITY => 'IDENTITY', + CONTENTENCODING_BASE64 => 'BASE64', + ; + +has content_encoding => + (is => 'ro', + required => 1, + default => sub { CONTENTENCODING_IDENTITY }, + ); + + +=head4 file_name + +* + Suggested file name of the attachment. (Provided by the user as an argument to `attach`) + +=cut + +has file_name => + (is => 'ro', + ); + + +=head4 media_type + +* + The media type of the data. This can be any valid + [IANA Media Type](https://www.iana.org/assignments/media-types/media-types.xhtml) + as well as Cucumber-specific media types such as `text/x.cucumber.gherkin+plain` + and `text/x.cucumber.stacktrace+plain` + +=cut + +has media_type => + (is => 'ro', + required => 1, + default => sub { '' }, + ); + + +=head4 source + + +=cut + +has source => + (is => 'ro', + ); + + +=head4 test_case_started_id + + +=cut + +has test_case_started_id => + (is => 'ro', + ); + + +=head4 test_step_id + + +=cut + +has test_step_id => + (is => 'ro', + ); + + +=head4 url + +* + A URL where the attachment can be retrieved. This field should not be set by Cucumber. + It should be set by a program that reads a message stream and does the following for + each Attachment message: + + - Writes the body (after base64 decoding if necessary) to a new file. + - Sets `body` and `contentEncoding` to `null` + - Writes out the new attachment message + + This will result in a smaller message stream, which can improve performance and + reduce bandwidth of message consumers. It also makes it easier to process and download attachments + separately from reports. + +=cut + +has url => + (is => 'ro', + ); + + +} + +package Cucumber::Messages::Duration { + +=head2 Cucumber::Messages::Duration + +=head3 DESCRIPTION + +Represents the Duration message in Cucumber's +L. + +The structure is pretty close of the Timestamp one. For clarity, a second type + of message is used. + +=head3 ATTRIBUTES + +=cut + +use Moo; +extends 'Cucumber::Messages::Message'; + +use Scalar::Util qw( blessed ); + +my %types = ( + seconds => 'number', + nanos => 'number', +); + +# This is a work-around for the fact that Moo doesn't have introspection +# and Perl doesn't have boolean values... +sub _types { + return \%types; +} + + + +=head4 seconds + + +=cut + +has seconds => + (is => 'ro', + required => 1, + default => sub { 0 }, + ); + + +=head4 nanos + +Non-negative fractions of a second at nanosecond resolution. Negative + second values with fractions must still have non-negative nanos values + that count forward in time. Must be from 0 to 999,999,999 + inclusive. + +=cut + +has nanos => + (is => 'ro', + required => 1, + default => sub { 0 }, + ); + + +} + +package Cucumber::Messages::Envelope { + +=head2 Cucumber::Messages::Envelope + +=head3 DESCRIPTION + +Represents the Envelope message in Cucumber's +L. + +When removing a field, replace it with reserved, rather than deleting the line. + When adding a field, add it to the end and increment the number by one. + See https://developers.google.com/protocol-buffers/docs/proto#updating for details + +* + All the messages that are passed between different components/processes are Envelope + messages. + +=head3 ATTRIBUTES + +=cut + +use Moo; +extends 'Cucumber::Messages::Message'; + +use Scalar::Util qw( blessed ); + +my %types = ( + attachment => 'Cucumber::Messages::Attachment', + gherkin_document => 'Cucumber::Messages::GherkinDocument', + hook => 'Cucumber::Messages::Hook', + meta => 'Cucumber::Messages::Meta', + parameter_type => 'Cucumber::Messages::ParameterType', + parse_error => 'Cucumber::Messages::ParseError', + pickle => 'Cucumber::Messages::Pickle', + source => 'Cucumber::Messages::Source', + step_definition => 'Cucumber::Messages::StepDefinition', + test_case => 'Cucumber::Messages::TestCase', + test_case_finished => 'Cucumber::Messages::TestCaseFinished', + test_case_started => 'Cucumber::Messages::TestCaseStarted', + test_run_finished => 'Cucumber::Messages::TestRunFinished', + test_run_started => 'Cucumber::Messages::TestRunStarted', + test_step_finished => 'Cucumber::Messages::TestStepFinished', + test_step_started => 'Cucumber::Messages::TestStepStarted', + undefined_parameter_type => 'Cucumber::Messages::UndefinedParameterType', +); + +# This is a work-around for the fact that Moo doesn't have introspection +# and Perl doesn't have boolean values... +sub _types { + return \%types; +} + + + +=head4 attachment + + +=cut + +has attachment => + (is => 'ro', + ); + + +=head4 gherkin_document + + +=cut + +has gherkin_document => + (is => 'ro', + ); + + +=head4 hook + + +=cut + +has hook => + (is => 'ro', + ); + + +=head4 meta + + +=cut + +has meta => + (is => 'ro', + ); + + +=head4 parameter_type + + +=cut + +has parameter_type => + (is => 'ro', + ); + + +=head4 parse_error + + +=cut + +has parse_error => + (is => 'ro', + ); + + +=head4 pickle + + +=cut + +has pickle => + (is => 'ro', + ); + + +=head4 source + + +=cut + +has source => + (is => 'ro', + ); + + +=head4 step_definition + + +=cut + +has step_definition => + (is => 'ro', + ); + + +=head4 test_case + + +=cut + +has test_case => + (is => 'ro', + ); + + +=head4 test_case_finished + + +=cut + +has test_case_finished => + (is => 'ro', + ); + + +=head4 test_case_started + + +=cut + +has test_case_started => + (is => 'ro', + ); + + +=head4 test_run_finished + + +=cut + +has test_run_finished => + (is => 'ro', + ); + + +=head4 test_run_started + + +=cut + +has test_run_started => + (is => 'ro', + ); + + +=head4 test_step_finished + + +=cut + +has test_step_finished => + (is => 'ro', + ); + + +=head4 test_step_started + + +=cut + +has test_step_started => + (is => 'ro', + ); + + +=head4 undefined_parameter_type + + +=cut + +has undefined_parameter_type => + (is => 'ro', + ); + + +} + +package Cucumber::Messages::GherkinDocument { + +=head2 Cucumber::Messages::GherkinDocument + +=head3 DESCRIPTION + +Represents the GherkinDocument message in Cucumber's +L. + +* + The [AST](https://en.wikipedia.org/wiki/Abstract_syntax_tree) of a Gherkin document. + Cucumber implementations should *not* depend on `GherkinDocument` or any of its + children for execution - use [Pickle](#io.cucumber.messages.Pickle) instead. + + The only consumers of `GherkinDocument` should only be formatters that produce + "rich" output, resembling the original Gherkin document. + +=head3 ATTRIBUTES + +=cut + +use Moo; +extends 'Cucumber::Messages::Message'; + +use Scalar::Util qw( blessed ); + +my %types = ( + uri => 'string', + feature => 'Cucumber::Messages::Feature', + comments => '[]Cucumber::Messages::Comment', +); + +# This is a work-around for the fact that Moo doesn't have introspection +# and Perl doesn't have boolean values... +sub _types { + return \%types; +} + + + +=head4 uri + +* + The [URI](https://en.wikipedia.org/wiki/Uniform_Resource_Identifier) + of the source, typically a file path relative to the root directory + +=cut + +has uri => + (is => 'ro', + ); + + +=head4 feature + + +=cut + +has feature => + (is => 'ro', + ); + + +=head4 comments + +All the comments in the Gherkin document + +=cut + +has comments => + (is => 'ro', + required => 1, + default => sub { [] }, + ); + + +} + +package Cucumber::Messages::Background { + +=head2 Cucumber::Messages::Background + +=head3 DESCRIPTION + +Represents the Background message in Cucumber's +L. + + + +=head3 ATTRIBUTES + +=cut + +use Moo; +extends 'Cucumber::Messages::Message'; + +use Scalar::Util qw( blessed ); + +my %types = ( + location => 'Cucumber::Messages::Location', + keyword => 'string', + name => 'string', + description => 'string', + steps => '[]Cucumber::Messages::Step', + id => 'string', +); + +# This is a work-around for the fact that Moo doesn't have introspection +# and Perl doesn't have boolean values... +sub _types { + return \%types; +} + + + +=head4 location + +The location of the `Background` keyword + +=cut + +has location => + (is => 'ro', + required => 1, + default => sub { Cucumber::Messages::Location->new() }, + ); + + +=head4 keyword + + +=cut + +has keyword => + (is => 'ro', + required => 1, + default => sub { '' }, + ); + + +=head4 name + + +=cut + +has name => + (is => 'ro', + required => 1, + default => sub { '' }, + ); + + +=head4 description + + +=cut + +has description => + (is => 'ro', + required => 1, + default => sub { '' }, + ); + + +=head4 steps + + +=cut + +has steps => + (is => 'ro', + required => 1, + default => sub { [] }, + ); + + +=head4 id + + +=cut + +has id => + (is => 'ro', + required => 1, + default => sub { '' }, + ); + + +} + +package Cucumber::Messages::Comment { + +=head2 Cucumber::Messages::Comment + +=head3 DESCRIPTION + +Represents the Comment message in Cucumber's +L. + +* + A comment in a Gherkin document + +=head3 ATTRIBUTES + +=cut + +use Moo; +extends 'Cucumber::Messages::Message'; + +use Scalar::Util qw( blessed ); + +my %types = ( + location => 'Cucumber::Messages::Location', + text => 'string', +); + +# This is a work-around for the fact that Moo doesn't have introspection +# and Perl doesn't have boolean values... +sub _types { + return \%types; +} + + + +=head4 location + +The location of the comment + +=cut + +has location => + (is => 'ro', + required => 1, + default => sub { Cucumber::Messages::Location->new() }, + ); + + +=head4 text + +The text of the comment + +=cut + +has text => + (is => 'ro', + required => 1, + default => sub { '' }, + ); + + +} + +package Cucumber::Messages::DataTable { + +=head2 Cucumber::Messages::DataTable + +=head3 DESCRIPTION + +Represents the DataTable message in Cucumber's +L. + + + +=head3 ATTRIBUTES + +=cut + +use Moo; +extends 'Cucumber::Messages::Message'; + +use Scalar::Util qw( blessed ); + +my %types = ( + location => 'Cucumber::Messages::Location', + rows => '[]Cucumber::Messages::TableRow', +); + +# This is a work-around for the fact that Moo doesn't have introspection +# and Perl doesn't have boolean values... +sub _types { + return \%types; +} + + + +=head4 location + + +=cut + +has location => + (is => 'ro', + required => 1, + default => sub { Cucumber::Messages::Location->new() }, + ); + + +=head4 rows + + +=cut + +has rows => + (is => 'ro', + required => 1, + default => sub { [] }, + ); + + +} + +package Cucumber::Messages::DocString { + +=head2 Cucumber::Messages::DocString + +=head3 DESCRIPTION + +Represents the DocString message in Cucumber's +L. + + + +=head3 ATTRIBUTES + +=cut + +use Moo; +extends 'Cucumber::Messages::Message'; + +use Scalar::Util qw( blessed ); + +my %types = ( + location => 'Cucumber::Messages::Location', + media_type => 'string', + content => 'string', + delimiter => 'string', +); + +# This is a work-around for the fact that Moo doesn't have introspection +# and Perl doesn't have boolean values... +sub _types { + return \%types; +} + + + +=head4 location + + +=cut + +has location => + (is => 'ro', + required => 1, + default => sub { Cucumber::Messages::Location->new() }, + ); + + +=head4 media_type + + +=cut + +has media_type => + (is => 'ro', + ); + + +=head4 content + + +=cut + +has content => + (is => 'ro', + required => 1, + default => sub { '' }, + ); + + +=head4 delimiter + + +=cut + +has delimiter => + (is => 'ro', + required => 1, + default => sub { '' }, + ); + + +} + +package Cucumber::Messages::Examples { + +=head2 Cucumber::Messages::Examples + +=head3 DESCRIPTION + +Represents the Examples message in Cucumber's +L. + + + +=head3 ATTRIBUTES + +=cut + +use Moo; +extends 'Cucumber::Messages::Message'; + +use Scalar::Util qw( blessed ); + +my %types = ( + location => 'Cucumber::Messages::Location', + tags => '[]Cucumber::Messages::Tag', + keyword => 'string', + name => 'string', + description => 'string', + table_header => 'Cucumber::Messages::TableRow', + table_body => '[]Cucumber::Messages::TableRow', + id => 'string', +); + +# This is a work-around for the fact that Moo doesn't have introspection +# and Perl doesn't have boolean values... +sub _types { + return \%types; +} + + + +=head4 location + +The location of the `Examples` keyword + +=cut + +has location => + (is => 'ro', + required => 1, + default => sub { Cucumber::Messages::Location->new() }, + ); + + +=head4 tags + + +=cut + +has tags => + (is => 'ro', + required => 1, + default => sub { [] }, + ); + + +=head4 keyword + + +=cut + +has keyword => + (is => 'ro', + required => 1, + default => sub { '' }, + ); + + +=head4 name + + +=cut + +has name => + (is => 'ro', + required => 1, + default => sub { '' }, + ); + + +=head4 description + + +=cut + +has description => + (is => 'ro', + required => 1, + default => sub { '' }, + ); + + +=head4 table_header + + +=cut + +has table_header => + (is => 'ro', + ); + + +=head4 table_body + + +=cut + +has table_body => + (is => 'ro', + required => 1, + default => sub { [] }, + ); + + +=head4 id + + +=cut + +has id => + (is => 'ro', + required => 1, + default => sub { '' }, + ); + + +} + +package Cucumber::Messages::Feature { + +=head2 Cucumber::Messages::Feature + +=head3 DESCRIPTION + +Represents the Feature message in Cucumber's +L. + + + +=head3 ATTRIBUTES + +=cut + +use Moo; +extends 'Cucumber::Messages::Message'; + +use Scalar::Util qw( blessed ); + +my %types = ( + location => 'Cucumber::Messages::Location', + tags => '[]Cucumber::Messages::Tag', + language => 'string', + keyword => 'string', + name => 'string', + description => 'string', + children => '[]Cucumber::Messages::FeatureChild', +); + +# This is a work-around for the fact that Moo doesn't have introspection +# and Perl doesn't have boolean values... +sub _types { + return \%types; +} + + + +=head4 location + +The location of the `Feature` keyword + +=cut + +has location => + (is => 'ro', + required => 1, + default => sub { Cucumber::Messages::Location->new() }, + ); + + +=head4 tags + +All the tags placed above the `Feature` keyword + +=cut + +has tags => + (is => 'ro', + required => 1, + default => sub { [] }, + ); + + +=head4 language + +The [ISO 639-1](https://en.wikipedia.org/wiki/ISO_639-1) language code of the Gherkin document + +=cut + +has language => + (is => 'ro', + required => 1, + default => sub { '' }, + ); + + +=head4 keyword + +The text of the `Feature` keyword (in the language specified by `language`) + +=cut + +has keyword => + (is => 'ro', + required => 1, + default => sub { '' }, + ); + + +=head4 name + +The name of the feature (the text following the `keyword`) + +=cut + +has name => + (is => 'ro', + required => 1, + default => sub { '' }, + ); + + +=head4 description + +The line(s) underneath the line with the `keyword` that are used as description + +=cut + +has description => + (is => 'ro', + required => 1, + default => sub { '' }, + ); + + +=head4 children + +Zero or more children + +=cut + +has children => + (is => 'ro', + required => 1, + default => sub { [] }, + ); + + +} + +package Cucumber::Messages::FeatureChild { + +=head2 Cucumber::Messages::FeatureChild + +=head3 DESCRIPTION + +Represents the FeatureChild message in Cucumber's +L. + +* + A child node of a `Feature` node + +=head3 ATTRIBUTES + +=cut + +use Moo; +extends 'Cucumber::Messages::Message'; + +use Scalar::Util qw( blessed ); + +my %types = ( + rule => 'Cucumber::Messages::Rule', + background => 'Cucumber::Messages::Background', + scenario => 'Cucumber::Messages::Scenario', +); + +# This is a work-around for the fact that Moo doesn't have introspection +# and Perl doesn't have boolean values... +sub _types { + return \%types; +} + + + +=head4 rule + + +=cut + +has rule => + (is => 'ro', + ); + + +=head4 background + + +=cut + +has background => + (is => 'ro', + ); + + +=head4 scenario + + +=cut + +has scenario => + (is => 'ro', + ); + + +} + +package Cucumber::Messages::Rule { + +=head2 Cucumber::Messages::Rule + +=head3 DESCRIPTION + +Represents the Rule message in Cucumber's +L. + + + +=head3 ATTRIBUTES + +=cut + +use Moo; +extends 'Cucumber::Messages::Message'; + +use Scalar::Util qw( blessed ); + +my %types = ( + location => 'Cucumber::Messages::Location', + tags => '[]Cucumber::Messages::Tag', + keyword => 'string', + name => 'string', + description => 'string', + children => '[]Cucumber::Messages::RuleChild', + id => 'string', +); + +# This is a work-around for the fact that Moo doesn't have introspection +# and Perl doesn't have boolean values... +sub _types { + return \%types; +} + + + +=head4 location + +The location of the `Rule` keyword + +=cut + +has location => + (is => 'ro', + required => 1, + default => sub { Cucumber::Messages::Location->new() }, + ); + + +=head4 tags + +All the tags placed above the `Rule` keyword + +=cut + +has tags => + (is => 'ro', + required => 1, + default => sub { [] }, + ); + + +=head4 keyword + + +=cut + +has keyword => + (is => 'ro', + required => 1, + default => sub { '' }, + ); + + +=head4 name + + +=cut + +has name => + (is => 'ro', + required => 1, + default => sub { '' }, + ); + + +=head4 description + + +=cut + +has description => + (is => 'ro', + required => 1, + default => sub { '' }, + ); + + +=head4 children + + +=cut + +has children => + (is => 'ro', + required => 1, + default => sub { [] }, + ); + + +=head4 id + + +=cut + +has id => + (is => 'ro', + required => 1, + default => sub { '' }, + ); + + +} + +package Cucumber::Messages::RuleChild { + +=head2 Cucumber::Messages::RuleChild + +=head3 DESCRIPTION + +Represents the RuleChild message in Cucumber's +L. + +* + A child node of a `Rule` node + +=head3 ATTRIBUTES + +=cut + +use Moo; +extends 'Cucumber::Messages::Message'; + +use Scalar::Util qw( blessed ); + +my %types = ( + background => 'Cucumber::Messages::Background', + scenario => 'Cucumber::Messages::Scenario', +); + +# This is a work-around for the fact that Moo doesn't have introspection +# and Perl doesn't have boolean values... +sub _types { + return \%types; +} + + + +=head4 background + + +=cut + +has background => + (is => 'ro', + ); + + +=head4 scenario + + +=cut + +has scenario => + (is => 'ro', + ); + + +} + +package Cucumber::Messages::Scenario { + +=head2 Cucumber::Messages::Scenario + +=head3 DESCRIPTION + +Represents the Scenario message in Cucumber's +L. + + + +=head3 ATTRIBUTES + +=cut + +use Moo; +extends 'Cucumber::Messages::Message'; + +use Scalar::Util qw( blessed ); + +my %types = ( + location => 'Cucumber::Messages::Location', + tags => '[]Cucumber::Messages::Tag', + keyword => 'string', + name => 'string', + description => 'string', + steps => '[]Cucumber::Messages::Step', + examples => '[]Cucumber::Messages::Examples', + id => 'string', +); + +# This is a work-around for the fact that Moo doesn't have introspection +# and Perl doesn't have boolean values... +sub _types { + return \%types; +} + + + +=head4 location + +The location of the `Scenario` keyword + +=cut + +has location => + (is => 'ro', + required => 1, + default => sub { Cucumber::Messages::Location->new() }, + ); + + +=head4 tags + + +=cut + +has tags => + (is => 'ro', + required => 1, + default => sub { [] }, + ); + + +=head4 keyword + + +=cut + +has keyword => + (is => 'ro', + required => 1, + default => sub { '' }, + ); + + +=head4 name + + +=cut + +has name => + (is => 'ro', + required => 1, + default => sub { '' }, + ); + + +=head4 description + + +=cut + +has description => + (is => 'ro', + required => 1, + default => sub { '' }, + ); + + +=head4 steps + + +=cut + +has steps => + (is => 'ro', + required => 1, + default => sub { [] }, + ); + + +=head4 examples + + +=cut + +has examples => + (is => 'ro', + required => 1, + default => sub { [] }, + ); + + +=head4 id + + +=cut + +has id => + (is => 'ro', + required => 1, + default => sub { '' }, + ); + + +} + +package Cucumber::Messages::Step { + +=head2 Cucumber::Messages::Step + +=head3 DESCRIPTION + +Represents the Step message in Cucumber's +L. + +A step + +=head3 ATTRIBUTES + +=cut + +use Moo; +extends 'Cucumber::Messages::Message'; + +use Scalar::Util qw( blessed ); + +my %types = ( + location => 'Cucumber::Messages::Location', + keyword => 'string', + text => 'string', + doc_string => 'Cucumber::Messages::DocString', + data_table => 'Cucumber::Messages::DataTable', + id => 'string', +); + +# This is a work-around for the fact that Moo doesn't have introspection +# and Perl doesn't have boolean values... +sub _types { + return \%types; +} + + + +=head4 location + +The location of the steps' `keyword` + +=cut + +has location => + (is => 'ro', + required => 1, + default => sub { Cucumber::Messages::Location->new() }, + ); + + +=head4 keyword + + +=cut + +has keyword => + (is => 'ro', + required => 1, + default => sub { '' }, + ); + + +=head4 text + + +=cut + +has text => + (is => 'ro', + required => 1, + default => sub { '' }, + ); + + +=head4 doc_string + + +=cut + +has doc_string => + (is => 'ro', + ); + + +=head4 data_table + + +=cut + +has data_table => + (is => 'ro', + ); + + +=head4 id + +Unique ID to be able to reference the Step from PickleStep + +=cut + +has id => + (is => 'ro', + required => 1, + default => sub { '' }, + ); + + +} + +package Cucumber::Messages::TableCell { + +=head2 Cucumber::Messages::TableCell + +=head3 DESCRIPTION + +Represents the TableCell message in Cucumber's +L. + +A cell in a `TableRow` + +=head3 ATTRIBUTES + +=cut + +use Moo; +extends 'Cucumber::Messages::Message'; + +use Scalar::Util qw( blessed ); + +my %types = ( + location => 'Cucumber::Messages::Location', + value => 'string', +); + +# This is a work-around for the fact that Moo doesn't have introspection +# and Perl doesn't have boolean values... +sub _types { + return \%types; +} + + + +=head4 location + +The location of the cell + +=cut + +has location => + (is => 'ro', + required => 1, + default => sub { Cucumber::Messages::Location->new() }, + ); + + +=head4 value + +The value of the cell + +=cut + +has value => + (is => 'ro', + required => 1, + default => sub { '' }, + ); + + +} + +package Cucumber::Messages::TableRow { + +=head2 Cucumber::Messages::TableRow + +=head3 DESCRIPTION + +Represents the TableRow message in Cucumber's +L. + +A row in a table + +=head3 ATTRIBUTES + +=cut + +use Moo; +extends 'Cucumber::Messages::Message'; + +use Scalar::Util qw( blessed ); + +my %types = ( + location => 'Cucumber::Messages::Location', + cells => '[]Cucumber::Messages::TableCell', + id => 'string', +); + +# This is a work-around for the fact that Moo doesn't have introspection +# and Perl doesn't have boolean values... +sub _types { + return \%types; +} + + + +=head4 location + +The location of the first cell in the row + +=cut + +has location => + (is => 'ro', + required => 1, + default => sub { Cucumber::Messages::Location->new() }, + ); + + +=head4 cells + +Cells in the row + +=cut + +has cells => + (is => 'ro', + required => 1, + default => sub { [] }, + ); + + +=head4 id + + +=cut + +has id => + (is => 'ro', + required => 1, + default => sub { '' }, + ); + + +} + +package Cucumber::Messages::Tag { + +=head2 Cucumber::Messages::Tag + +=head3 DESCRIPTION + +Represents the Tag message in Cucumber's +L. + +* + A tag + +=head3 ATTRIBUTES + +=cut + +use Moo; +extends 'Cucumber::Messages::Message'; + +use Scalar::Util qw( blessed ); + +my %types = ( + location => 'Cucumber::Messages::Location', + name => 'string', + id => 'string', +); + +# This is a work-around for the fact that Moo doesn't have introspection +# and Perl doesn't have boolean values... +sub _types { + return \%types; +} + + + +=head4 location + +Location of the tag + +=cut + +has location => + (is => 'ro', + required => 1, + default => sub { Cucumber::Messages::Location->new() }, + ); + + +=head4 name + +The name of the tag (including the leading `@`) + +=cut + +has name => + (is => 'ro', + required => 1, + default => sub { '' }, + ); + + +=head4 id + +Unique ID to be able to reference the Tag from PickleTag + +=cut + +has id => + (is => 'ro', + required => 1, + default => sub { '' }, + ); + + +} + +package Cucumber::Messages::Hook { + +=head2 Cucumber::Messages::Hook + +=head3 DESCRIPTION + +Represents the Hook message in Cucumber's +L. + + + +=head3 ATTRIBUTES + +=cut + +use Moo; +extends 'Cucumber::Messages::Message'; + +use Scalar::Util qw( blessed ); + +my %types = ( + id => 'string', + source_reference => 'Cucumber::Messages::SourceReference', + tag_expression => 'string', +); + +# This is a work-around for the fact that Moo doesn't have introspection +# and Perl doesn't have boolean values... +sub _types { + return \%types; +} + + + +=head4 id + + +=cut + +has id => + (is => 'ro', + required => 1, + default => sub { '' }, + ); + + +=head4 source_reference + + +=cut + +has source_reference => + (is => 'ro', + required => 1, + default => sub { Cucumber::Messages::SourceReference->new() }, + ); + + +=head4 tag_expression + + +=cut + +has tag_expression => + (is => 'ro', + ); + + +} + +package Cucumber::Messages::Location { + +=head2 Cucumber::Messages::Location + +=head3 DESCRIPTION + +Represents the Location message in Cucumber's +L. + +* + Points to a line and a column in a text file + +=head3 ATTRIBUTES + +=cut + +use Moo; +extends 'Cucumber::Messages::Message'; + +use Scalar::Util qw( blessed ); + +my %types = ( + line => 'number', + column => 'number', +); + +# This is a work-around for the fact that Moo doesn't have introspection +# and Perl doesn't have boolean values... +sub _types { + return \%types; +} + + + +=head4 line + + +=cut + +has line => + (is => 'ro', + required => 1, + default => sub { 0 }, + ); + + +=head4 column + + +=cut + +has column => + (is => 'ro', + ); + + +} + +package Cucumber::Messages::Meta { + +=head2 Cucumber::Messages::Meta + +=head3 DESCRIPTION + +Represents the Meta message in Cucumber's +L. + +* + This message contains meta information about the environment. Consumers can use + this for various purposes. + +=head3 ATTRIBUTES + +=cut + +use Moo; +extends 'Cucumber::Messages::Message'; + +use Scalar::Util qw( blessed ); + +my %types = ( + protocol_version => 'string', + implementation => 'Cucumber::Messages::Product', + runtime => 'Cucumber::Messages::Product', + os => 'Cucumber::Messages::Product', + cpu => 'Cucumber::Messages::Product', + ci => 'Cucumber::Messages::Ci', +); + +# This is a work-around for the fact that Moo doesn't have introspection +# and Perl doesn't have boolean values... +sub _types { + return \%types; +} + + + +=head4 protocol_version + +* + The [SEMVER](https://semver.org/) version number of the protocol + +=cut + +has protocol_version => + (is => 'ro', + required => 1, + default => sub { '' }, + ); + + +=head4 implementation + +SpecFlow, Cucumber-JVM, Cucumber.js, Cucumber-Ruby, Behat etc. + +=cut + +has implementation => + (is => 'ro', + required => 1, + default => sub { Cucumber::Messages::Product->new() }, + ); + + +=head4 runtime + +Java, Ruby, Node.js etc + +=cut + +has runtime => + (is => 'ro', + required => 1, + default => sub { Cucumber::Messages::Product->new() }, + ); + + +=head4 os + +Windows, Linux, MacOS etc + +=cut + +has os => + (is => 'ro', + required => 1, + default => sub { Cucumber::Messages::Product->new() }, + ); + + +=head4 cpu + +386, arm, amd64 etc + +=cut + +has cpu => + (is => 'ro', + required => 1, + default => sub { Cucumber::Messages::Product->new() }, + ); + + +=head4 ci + + +=cut + +has ci => + (is => 'ro', + ); + + +} + +package Cucumber::Messages::Ci { + +=head2 Cucumber::Messages::Ci + +=head3 DESCRIPTION + +Represents the Ci message in Cucumber's +L. + +CI environment + +=head3 ATTRIBUTES + +=cut + +use Moo; +extends 'Cucumber::Messages::Message'; + +use Scalar::Util qw( blessed ); + +my %types = ( + name => 'string', + url => 'string', + build_number => 'string', + git => 'Cucumber::Messages::Git', +); + +# This is a work-around for the fact that Moo doesn't have introspection +# and Perl doesn't have boolean values... +sub _types { + return \%types; +} + + + +=head4 name + +Name of the CI product, e.g. "Jenkins", "CircleCI" etc. + +=cut + +has name => + (is => 'ro', + required => 1, + default => sub { '' }, + ); + + +=head4 url + +Link to the build + +=cut + +has url => + (is => 'ro', + ); + + +=head4 build_number + +The build number. Some CI servers use non-numeric build numbers, which is why this is a string + +=cut + +has build_number => + (is => 'ro', + ); + + +=head4 git + + +=cut + +has git => + (is => 'ro', + ); + + +} + +package Cucumber::Messages::Git { + +=head2 Cucumber::Messages::Git + +=head3 DESCRIPTION + +Represents the Git message in Cucumber's +L. + +Information about Git, provided by the Build/CI server as environment + variables. + +=head3 ATTRIBUTES + +=cut + +use Moo; +extends 'Cucumber::Messages::Message'; + +use Scalar::Util qw( blessed ); + +my %types = ( + remote => 'string', + revision => 'string', + branch => 'string', + tag => 'string', +); + +# This is a work-around for the fact that Moo doesn't have introspection +# and Perl doesn't have boolean values... +sub _types { + return \%types; +} + + + +=head4 remote + + +=cut + +has remote => + (is => 'ro', + required => 1, + default => sub { '' }, + ); + + +=head4 revision + + +=cut + +has revision => + (is => 'ro', + required => 1, + default => sub { '' }, + ); + + +=head4 branch + + +=cut + +has branch => + (is => 'ro', + ); + + +=head4 tag + + +=cut + +has tag => + (is => 'ro', + ); + + +} + +package Cucumber::Messages::Product { + +=head2 Cucumber::Messages::Product + +=head3 DESCRIPTION + +Represents the Product message in Cucumber's +L. + +Used to describe various properties of Meta + +=head3 ATTRIBUTES + +=cut + +use Moo; +extends 'Cucumber::Messages::Message'; + +use Scalar::Util qw( blessed ); + +my %types = ( + name => 'string', + version => 'string', +); + +# This is a work-around for the fact that Moo doesn't have introspection +# and Perl doesn't have boolean values... +sub _types { + return \%types; +} + + + +=head4 name + +The product name + +=cut + +has name => + (is => 'ro', + required => 1, + default => sub { '' }, + ); + + +=head4 version + +The product version + +=cut + +has version => + (is => 'ro', + ); + + +} + +package Cucumber::Messages::ParameterType { + +=head2 Cucumber::Messages::ParameterType + +=head3 DESCRIPTION + +Represents the ParameterType message in Cucumber's +L. + + + +=head3 ATTRIBUTES + +=cut + +use Moo; +extends 'Cucumber::Messages::Message'; + +use Scalar::Util qw( blessed ); + +my %types = ( + name => 'string', + regular_expressions => '[]string', + prefer_for_regular_expression_match => 'boolean', + use_for_snippets => 'boolean', + id => 'string', +); + +# This is a work-around for the fact that Moo doesn't have introspection +# and Perl doesn't have boolean values... +sub _types { + return \%types; +} + + + +=head4 name + +The name is unique, so we don't need an id. + +=cut + +has name => + (is => 'ro', + required => 1, + default => sub { '' }, + ); + + +=head4 regular_expressions + + +=cut + +has regular_expressions => + (is => 'ro', + required => 1, + default => sub { [] }, + ); + + +=head4 prefer_for_regular_expression_match + + +=cut + +has prefer_for_regular_expression_match => + (is => 'ro', + required => 1, + default => sub { '' }, + ); + + +=head4 use_for_snippets + + +=cut + +has use_for_snippets => + (is => 'ro', + required => 1, + default => sub { '' }, + ); + + +=head4 id + + +=cut + +has id => + (is => 'ro', + required => 1, + default => sub { '' }, + ); + + +} + +package Cucumber::Messages::ParseError { + +=head2 Cucumber::Messages::ParseError + +=head3 DESCRIPTION + +Represents the ParseError message in Cucumber's +L. + + + +=head3 ATTRIBUTES + +=cut + +use Moo; +extends 'Cucumber::Messages::Message'; + +use Scalar::Util qw( blessed ); + +my %types = ( + source => 'Cucumber::Messages::SourceReference', + message => 'string', +); + +# This is a work-around for the fact that Moo doesn't have introspection +# and Perl doesn't have boolean values... +sub _types { + return \%types; +} + + + +=head4 source + + +=cut + +has source => + (is => 'ro', + required => 1, + default => sub { Cucumber::Messages::SourceReference->new() }, + ); + + +=head4 message + + +=cut + +has message => + (is => 'ro', + required => 1, + default => sub { '' }, + ); + + +} + +package Cucumber::Messages::Pickle { + +=head2 Cucumber::Messages::Pickle + +=head3 DESCRIPTION + +Represents the Pickle message in Cucumber's +L. + +//// Pickles + +* + A `Pickle` represents a template for a `TestCase`. It is typically derived + from another format, such as [GherkinDocument](#io.cucumber.messages.GherkinDocument). + In the future a `Pickle` may be derived from other formats such as Markdown or + Excel files. + + By making `Pickle` the main data structure Cucumber uses for execution, the + implementation of Cucumber itself becomes simpler, as it doesn't have to deal + with the complex structure of a [GherkinDocument](#io.cucumber.messages.GherkinDocument). + + Each `PickleStep` of a `Pickle` is matched with a `StepDefinition` to create a `TestCase` + +=head3 ATTRIBUTES + +=cut + +use Moo; +extends 'Cucumber::Messages::Message'; + +use Scalar::Util qw( blessed ); + +my %types = ( + id => 'string', + uri => 'string', + name => 'string', + language => 'string', + steps => '[]Cucumber::Messages::PickleStep', + tags => '[]Cucumber::Messages::PickleTag', + ast_node_ids => '[]string', +); + +# This is a work-around for the fact that Moo doesn't have introspection +# and Perl doesn't have boolean values... +sub _types { + return \%types; +} + + + +=head4 id + +* + A unique id for the pickle. This is a [SHA1](https://en.wikipedia.org/wiki/SHA-1) hash + from the source data and the `locations` of the pickle. + This ID will change if source the file is modified. + +=cut + +has id => + (is => 'ro', + required => 1, + default => sub { '' }, + ); + + +=head4 uri + +The uri of the source file + +=cut + +has uri => + (is => 'ro', + required => 1, + default => sub { '' }, + ); + + +=head4 name + +The name of the pickle + +=cut + +has name => + (is => 'ro', + required => 1, + default => sub { '' }, + ); + + +=head4 language + +The language of the pickle + +=cut + +has language => + (is => 'ro', + required => 1, + default => sub { '' }, + ); + + +=head4 steps + +One or more steps + +=cut + +has steps => + (is => 'ro', + required => 1, + default => sub { [] }, + ); + + +=head4 tags + +* + One or more tags. If this pickle is constructed from a Gherkin document, + It includes inherited tags from the `Feature` as well. + +=cut + +has tags => + (is => 'ro', + required => 1, + default => sub { [] }, + ); + + +=head4 ast_node_ids + +* + Points to the AST node locations of the pickle. The last one represents the unique + id of the pickle. A pickle constructed from `Examples` will have the first + id originating from the `Scenario` AST node, and the second from the `TableRow` AST node. + +=cut + +has ast_node_ids => + (is => 'ro', + required => 1, + default => sub { [] }, + ); + + +} + +package Cucumber::Messages::PickleDocString { + +=head2 Cucumber::Messages::PickleDocString + +=head3 DESCRIPTION + +Represents the PickleDocString message in Cucumber's +L. + + + +=head3 ATTRIBUTES + +=cut + +use Moo; +extends 'Cucumber::Messages::Message'; + +use Scalar::Util qw( blessed ); + +my %types = ( + media_type => 'string', + content => 'string', +); + +# This is a work-around for the fact that Moo doesn't have introspection +# and Perl doesn't have boolean values... +sub _types { + return \%types; +} + + + +=head4 media_type + + +=cut + +has media_type => + (is => 'ro', + ); + + +=head4 content + + +=cut + +has content => + (is => 'ro', + required => 1, + default => sub { '' }, + ); + + +} + +package Cucumber::Messages::PickleStep { + +=head2 Cucumber::Messages::PickleStep + +=head3 DESCRIPTION + +Represents the PickleStep message in Cucumber's +L. + +* + An executable step + +=head3 ATTRIBUTES + +=cut + +use Moo; +extends 'Cucumber::Messages::Message'; + +use Scalar::Util qw( blessed ); + +my %types = ( + argument => 'Cucumber::Messages::PickleStepArgument', + ast_node_ids => '[]string', + id => 'string', + text => 'string', +); + +# This is a work-around for the fact that Moo doesn't have introspection +# and Perl doesn't have boolean values... +sub _types { + return \%types; +} + + + +=head4 argument + + +=cut + +has argument => + (is => 'ro', + ); + + +=head4 ast_node_ids + +References the IDs of the source of the step. For Gherkin, this can be + the ID of a Step, and possibly also the ID of a TableRow + +=cut + +has ast_node_ids => + (is => 'ro', + required => 1, + default => sub { [] }, + ); + + +=head4 id + +A unique ID for the PickleStep + +=cut + +has id => + (is => 'ro', + required => 1, + default => sub { '' }, + ); + + +=head4 text + + +=cut + +has text => + (is => 'ro', + required => 1, + default => sub { '' }, + ); + + +} + +package Cucumber::Messages::PickleStepArgument { + +=head2 Cucumber::Messages::PickleStepArgument + +=head3 DESCRIPTION + +Represents the PickleStepArgument message in Cucumber's +L. + +An optional argument + +=head3 ATTRIBUTES + +=cut + +use Moo; +extends 'Cucumber::Messages::Message'; + +use Scalar::Util qw( blessed ); + +my %types = ( + doc_string => 'Cucumber::Messages::PickleDocString', + data_table => 'Cucumber::Messages::PickleTable', +); + +# This is a work-around for the fact that Moo doesn't have introspection +# and Perl doesn't have boolean values... +sub _types { + return \%types; +} + + + +=head4 doc_string + + +=cut + +has doc_string => + (is => 'ro', + ); + + +=head4 data_table + + +=cut + +has data_table => + (is => 'ro', + ); + + +} + +package Cucumber::Messages::PickleTable { + +=head2 Cucumber::Messages::PickleTable + +=head3 DESCRIPTION + +Represents the PickleTable message in Cucumber's +L. + + + +=head3 ATTRIBUTES + +=cut + +use Moo; +extends 'Cucumber::Messages::Message'; + +use Scalar::Util qw( blessed ); + +my %types = ( + rows => '[]Cucumber::Messages::PickleTableRow', +); + +# This is a work-around for the fact that Moo doesn't have introspection +# and Perl doesn't have boolean values... +sub _types { + return \%types; +} + + + +=head4 rows + + +=cut + +has rows => + (is => 'ro', + required => 1, + default => sub { [] }, + ); + + +} + +package Cucumber::Messages::PickleTableCell { + +=head2 Cucumber::Messages::PickleTableCell + +=head3 DESCRIPTION + +Represents the PickleTableCell message in Cucumber's +L. + + + +=head3 ATTRIBUTES + +=cut + +use Moo; +extends 'Cucumber::Messages::Message'; + +use Scalar::Util qw( blessed ); + +my %types = ( + value => 'string', +); + +# This is a work-around for the fact that Moo doesn't have introspection +# and Perl doesn't have boolean values... +sub _types { + return \%types; +} + + + +=head4 value + + +=cut + +has value => + (is => 'ro', + required => 1, + default => sub { '' }, + ); + + +} + +package Cucumber::Messages::PickleTableRow { + +=head2 Cucumber::Messages::PickleTableRow + +=head3 DESCRIPTION + +Represents the PickleTableRow message in Cucumber's +L. + + + +=head3 ATTRIBUTES + +=cut + +use Moo; +extends 'Cucumber::Messages::Message'; + +use Scalar::Util qw( blessed ); + +my %types = ( + cells => '[]Cucumber::Messages::PickleTableCell', +); + +# This is a work-around for the fact that Moo doesn't have introspection +# and Perl doesn't have boolean values... +sub _types { + return \%types; +} + + + +=head4 cells + + +=cut + +has cells => + (is => 'ro', + required => 1, + default => sub { [] }, + ); + + +} + +package Cucumber::Messages::PickleTag { + +=head2 Cucumber::Messages::PickleTag + +=head3 DESCRIPTION + +Represents the PickleTag message in Cucumber's +L. + +* + A tag + +=head3 ATTRIBUTES + +=cut + +use Moo; +extends 'Cucumber::Messages::Message'; + +use Scalar::Util qw( blessed ); + +my %types = ( + name => 'string', + ast_node_id => 'string', +); + +# This is a work-around for the fact that Moo doesn't have introspection +# and Perl doesn't have boolean values... +sub _types { + return \%types; +} + + + +=head4 name + + +=cut + +has name => + (is => 'ro', + required => 1, + default => sub { '' }, + ); + + +=head4 ast_node_id + +Points to the AST node this was created from + +=cut + +has ast_node_id => + (is => 'ro', + required => 1, + default => sub { '' }, + ); + + +} + +package Cucumber::Messages::Source { + +=head2 Cucumber::Messages::Source + +=head3 DESCRIPTION + +Represents the Source message in Cucumber's +L. + +//// Source + +* + A source file, typically a Gherkin document or Java/Ruby/JavaScript source code + +=head3 ATTRIBUTES + +=cut + +use Moo; +extends 'Cucumber::Messages::Message'; + +use Scalar::Util qw( blessed ); + +my %types = ( + uri => 'string', + data => 'string', + media_type => '', +); + +# This is a work-around for the fact that Moo doesn't have introspection +# and Perl doesn't have boolean values... +sub _types { + return \%types; +} + + + +=head4 uri + +* + The [URI](https://en.wikipedia.org/wiki/Uniform_Resource_Identifier) + of the source, typically a file path relative to the root directory + +=cut + +has uri => + (is => 'ro', + required => 1, + default => sub { '' }, + ); + + +=head4 data + +The contents of the file + +=cut + +has data => + (is => 'ro', + required => 1, + default => sub { '' }, + ); + + +=head4 media_type + +The media type of the file. Can be used to specify custom types, such as + text/x.cucumber.gherkin+plain + + +Available constants for valid values of this field: + +=over + +=item * MEDIATYPE_TEXT_X_CUCUMBER_GHERKIN_PLAIN + +=item * MEDIATYPE_TEXT_X_CUCUMBER_GHERKIN_MARKDOWN + +=back + +=cut + + +use constant + MEDIATYPE_TEXT_X_CUCUMBER_GHERKIN_PLAIN => 'text/x.cucumber.gherkin+plain', + MEDIATYPE_TEXT_X_CUCUMBER_GHERKIN_MARKDOWN => 'text/x.cucumber.gherkin+markdown', + ; + +has media_type => + (is => 'ro', + required => 1, + default => sub { MEDIATYPE_TEXT_X_CUCUMBER_GHERKIN_PLAIN }, + ); + + +} + +package Cucumber::Messages::SourceReference { + +=head2 Cucumber::Messages::SourceReference + +=head3 DESCRIPTION + +Represents the SourceReference message in Cucumber's +L. + +* + Points to a [Source](#io.cucumber.messages.Source) identified by `uri` and a + [Location](#io.cucumber.messages.Location) within that file. + +=head3 ATTRIBUTES + +=cut + +use Moo; +extends 'Cucumber::Messages::Message'; + +use Scalar::Util qw( blessed ); + +my %types = ( + uri => 'string', + java_method => 'Cucumber::Messages::JavaMethod', + java_stack_trace_element => 'Cucumber::Messages::JavaStackTraceElement', + location => 'Cucumber::Messages::Location', +); + +# This is a work-around for the fact that Moo doesn't have introspection +# and Perl doesn't have boolean values... +sub _types { + return \%types; +} + + + +=head4 uri + + +=cut + +has uri => + (is => 'ro', + ); + + +=head4 java_method + + +=cut + +has java_method => + (is => 'ro', + ); + + +=head4 java_stack_trace_element + + +=cut + +has java_stack_trace_element => + (is => 'ro', + ); + + +=head4 location + + +=cut + +has location => + (is => 'ro', + ); + + +} + +package Cucumber::Messages::JavaMethod { + +=head2 Cucumber::Messages::JavaMethod + +=head3 DESCRIPTION + +Represents the JavaMethod message in Cucumber's +L. + + + +=head3 ATTRIBUTES + +=cut + +use Moo; +extends 'Cucumber::Messages::Message'; + +use Scalar::Util qw( blessed ); + +my %types = ( + class_name => 'string', + method_name => 'string', + method_parameter_types => '[]string', +); + +# This is a work-around for the fact that Moo doesn't have introspection +# and Perl doesn't have boolean values... +sub _types { + return \%types; +} + + + +=head4 class_name + + +=cut + +has class_name => + (is => 'ro', + required => 1, + default => sub { '' }, + ); + + +=head4 method_name + + +=cut + +has method_name => + (is => 'ro', + required => 1, + default => sub { '' }, + ); + + +=head4 method_parameter_types + + +=cut + +has method_parameter_types => + (is => 'ro', + required => 1, + default => sub { [] }, + ); + + +} + +package Cucumber::Messages::JavaStackTraceElement { + +=head2 Cucumber::Messages::JavaStackTraceElement + +=head3 DESCRIPTION + +Represents the JavaStackTraceElement message in Cucumber's +L. + + + +=head3 ATTRIBUTES + +=cut + +use Moo; +extends 'Cucumber::Messages::Message'; + +use Scalar::Util qw( blessed ); + +my %types = ( + class_name => 'string', + file_name => 'string', + method_name => 'string', +); + +# This is a work-around for the fact that Moo doesn't have introspection +# and Perl doesn't have boolean values... +sub _types { + return \%types; +} + + + +=head4 class_name + + +=cut + +has class_name => + (is => 'ro', + required => 1, + default => sub { '' }, + ); + + +=head4 file_name + + +=cut + +has file_name => + (is => 'ro', + required => 1, + default => sub { '' }, + ); + + +=head4 method_name + + +=cut + +has method_name => + (is => 'ro', + required => 1, + default => sub { '' }, + ); + + +} + +package Cucumber::Messages::StepDefinition { + +=head2 Cucumber::Messages::StepDefinition + +=head3 DESCRIPTION + +Represents the StepDefinition message in Cucumber's +L. + + + +=head3 ATTRIBUTES + +=cut + +use Moo; +extends 'Cucumber::Messages::Message'; + +use Scalar::Util qw( blessed ); + +my %types = ( + id => 'string', + pattern => 'Cucumber::Messages::StepDefinitionPattern', + source_reference => 'Cucumber::Messages::SourceReference', +); + +# This is a work-around for the fact that Moo doesn't have introspection +# and Perl doesn't have boolean values... +sub _types { + return \%types; +} + + + +=head4 id + + +=cut + +has id => + (is => 'ro', + required => 1, + default => sub { '' }, + ); + + +=head4 pattern + + +=cut + +has pattern => + (is => 'ro', + required => 1, + default => sub { Cucumber::Messages::StepDefinitionPattern->new() }, + ); + + +=head4 source_reference + + +=cut + +has source_reference => + (is => 'ro', + required => 1, + default => sub { Cucumber::Messages::SourceReference->new() }, + ); + + +} + +package Cucumber::Messages::StepDefinitionPattern { + +=head2 Cucumber::Messages::StepDefinitionPattern + +=head3 DESCRIPTION + +Represents the StepDefinitionPattern message in Cucumber's +L. + + + +=head3 ATTRIBUTES + +=cut + +use Moo; +extends 'Cucumber::Messages::Message'; + +use Scalar::Util qw( blessed ); + +my %types = ( + source => 'string', + type => '', +); + +# This is a work-around for the fact that Moo doesn't have introspection +# and Perl doesn't have boolean values... +sub _types { + return \%types; +} + + + +=head4 source + + +=cut + +has source => + (is => 'ro', + required => 1, + default => sub { '' }, + ); + + +=head4 type + + + +Available constants for valid values of this field: + +=over + +=item * TYPE_CUCUMBER_EXPRESSION + +=item * TYPE_REGULAR_EXPRESSION + +=back + +=cut + + +use constant + TYPE_CUCUMBER_EXPRESSION => 'CUCUMBER_EXPRESSION', + TYPE_REGULAR_EXPRESSION => 'REGULAR_EXPRESSION', + ; + +has type => + (is => 'ro', + required => 1, + default => sub { TYPE_CUCUMBER_EXPRESSION }, + ); + + +} + +package Cucumber::Messages::TestCase { + +=head2 Cucumber::Messages::TestCase + +=head3 DESCRIPTION + +Represents the TestCase message in Cucumber's +L. + +//// TestCases + +* + A `TestCase` contains a sequence of `TestStep`s. + +=head3 ATTRIBUTES + +=cut + +use Moo; +extends 'Cucumber::Messages::Message'; + +use Scalar::Util qw( blessed ); + +my %types = ( + id => 'string', + pickle_id => 'string', + test_steps => '[]Cucumber::Messages::TestStep', +); + +# This is a work-around for the fact that Moo doesn't have introspection +# and Perl doesn't have boolean values... +sub _types { + return \%types; +} + + + +=head4 id + + +=cut + +has id => + (is => 'ro', + required => 1, + default => sub { '' }, + ); + + +=head4 pickle_id + +The ID of the `Pickle` this `TestCase` is derived from. + +=cut + +has pickle_id => + (is => 'ro', + required => 1, + default => sub { '' }, + ); + + +=head4 test_steps + + +=cut + +has test_steps => + (is => 'ro', + required => 1, + default => sub { [] }, + ); + + +} + +package Cucumber::Messages::Group { + +=head2 Cucumber::Messages::Group + +=head3 DESCRIPTION + +Represents the Group message in Cucumber's +L. + + + +=head3 ATTRIBUTES + +=cut + +use Moo; +extends 'Cucumber::Messages::Message'; + +use Scalar::Util qw( blessed ); + +my %types = ( + children => '[]Cucumber::Messages::Group', + start => 'number', + value => 'string', +); + +# This is a work-around for the fact that Moo doesn't have introspection +# and Perl doesn't have boolean values... +sub _types { + return \%types; +} + + + +=head4 children + + +=cut + +has children => + (is => 'ro', + required => 1, + default => sub { [] }, + ); + + +=head4 start + + +=cut + +has start => + (is => 'ro', + ); + + +=head4 value + + +=cut + +has value => + (is => 'ro', + ); + + +} + +package Cucumber::Messages::StepMatchArgument { + +=head2 Cucumber::Messages::StepMatchArgument + +=head3 DESCRIPTION + +Represents the StepMatchArgument message in Cucumber's +L. + +* + Represents a single argument extracted from a step match and passed to a step definition. + This is used for the following purposes: + - Construct an argument to pass to a step definition (possibly through a parameter type transform) + - Highlight the matched parameter in rich formatters such as the HTML formatter + + This message closely matches the `Argument` class in the `cucumber-expressions` library. + +=head3 ATTRIBUTES + +=cut + +use Moo; +extends 'Cucumber::Messages::Message'; + +use Scalar::Util qw( blessed ); + +my %types = ( + group => 'Cucumber::Messages::Group', + parameter_type_name => 'string', +); + +# This is a work-around for the fact that Moo doesn't have introspection +# and Perl doesn't have boolean values... +sub _types { + return \%types; +} + + + +=head4 group + +* + Represents the outermost capture group of an argument. This message closely matches the + `Group` class in the `cucumber-expressions` library. + +=cut + +has group => + (is => 'ro', + required => 1, + default => sub { Cucumber::Messages::Group->new() }, + ); + + +=head4 parameter_type_name + + +=cut + +has parameter_type_name => + (is => 'ro', + ); + + +} + +package Cucumber::Messages::StepMatchArgumentsList { + +=head2 Cucumber::Messages::StepMatchArgumentsList + +=head3 DESCRIPTION + +Represents the StepMatchArgumentsList message in Cucumber's +L. + + + +=head3 ATTRIBUTES + +=cut + +use Moo; +extends 'Cucumber::Messages::Message'; + +use Scalar::Util qw( blessed ); + +my %types = ( + step_match_arguments => '[]Cucumber::Messages::StepMatchArgument', +); + +# This is a work-around for the fact that Moo doesn't have introspection +# and Perl doesn't have boolean values... +sub _types { + return \%types; +} + + + +=head4 step_match_arguments + + +=cut + +has step_match_arguments => + (is => 'ro', + required => 1, + default => sub { [] }, + ); + + +} + +package Cucumber::Messages::TestStep { + +=head2 Cucumber::Messages::TestStep + +=head3 DESCRIPTION + +Represents the TestStep message in Cucumber's +L. + +* + A `TestStep` is derived from either a `PickleStep` + combined with a `StepDefinition`, or from a `Hook`. + +=head3 ATTRIBUTES + +=cut + +use Moo; +extends 'Cucumber::Messages::Message'; + +use Scalar::Util qw( blessed ); + +my %types = ( + hook_id => 'string', + id => 'string', + pickle_step_id => 'string', + step_definition_ids => '[]string', + step_match_arguments_lists => '[]Cucumber::Messages::StepMatchArgumentsList', +); + +# This is a work-around for the fact that Moo doesn't have introspection +# and Perl doesn't have boolean values... +sub _types { + return \%types; +} + + + +=head4 hook_id + +Pointer to the `Hook` (if derived from a Hook) + +=cut + +has hook_id => + (is => 'ro', + ); + + +=head4 id + + +=cut + +has id => + (is => 'ro', + required => 1, + default => sub { '' }, + ); + + +=head4 pickle_step_id + +Pointer to the `PickleStep` (if derived from a `PickleStep`) + +=cut + +has pickle_step_id => + (is => 'ro', + ); + + +=head4 step_definition_ids + +Pointer to all the matching `StepDefinition`s (if derived from a `PickleStep`) + +=cut + +has step_definition_ids => + (is => 'ro', + ); + + +=head4 step_match_arguments_lists + +A list of list of StepMatchArgument (if derived from a `PickleStep`). + Each element represents a matching step definition. A size of 0 means `UNDEFINED`, + and a size of 2+ means `AMBIGUOUS` + +=cut + +has step_match_arguments_lists => + (is => 'ro', + ); + + +} + +package Cucumber::Messages::TestCaseFinished { + +=head2 Cucumber::Messages::TestCaseFinished + +=head3 DESCRIPTION + +Represents the TestCaseFinished message in Cucumber's +L. + + + +=head3 ATTRIBUTES + +=cut + +use Moo; +extends 'Cucumber::Messages::Message'; + +use Scalar::Util qw( blessed ); + +my %types = ( + test_case_started_id => 'string', + timestamp => 'Cucumber::Messages::Timestamp', + will_be_retried => 'boolean', +); + +# This is a work-around for the fact that Moo doesn't have introspection +# and Perl doesn't have boolean values... +sub _types { + return \%types; +} + + + +=head4 test_case_started_id + + +=cut + +has test_case_started_id => + (is => 'ro', + required => 1, + default => sub { '' }, + ); + + +=head4 timestamp + + +=cut + +has timestamp => + (is => 'ro', + required => 1, + default => sub { Cucumber::Messages::Timestamp->new() }, + ); + + +=head4 will_be_retried + + +=cut + +has will_be_retried => + (is => 'ro', + required => 1, + default => sub { '' }, + ); + + +} + +package Cucumber::Messages::TestCaseStarted { + +=head2 Cucumber::Messages::TestCaseStarted + +=head3 DESCRIPTION + +Represents the TestCaseStarted message in Cucumber's +L. + + + +=head3 ATTRIBUTES + +=cut + +use Moo; +extends 'Cucumber::Messages::Message'; + +use Scalar::Util qw( blessed ); + +my %types = ( + attempt => 'number', + id => 'string', + test_case_id => 'string', + timestamp => 'Cucumber::Messages::Timestamp', +); + +# This is a work-around for the fact that Moo doesn't have introspection +# and Perl doesn't have boolean values... +sub _types { + return \%types; +} + + + +=head4 attempt + +* + The first attempt should have value 0, and for each retry the value + should increase by 1. + +=cut + +has attempt => + (is => 'ro', + required => 1, + default => sub { 0 }, + ); + + +=head4 id + +* + Because a `TestCase` can be run multiple times (in case of a retry), + we use this field to group messages relating to the same attempt. + +=cut + +has id => + (is => 'ro', + required => 1, + default => sub { '' }, + ); + + +=head4 test_case_id + + +=cut + +has test_case_id => + (is => 'ro', + required => 1, + default => sub { '' }, + ); + + +=head4 timestamp + + +=cut + +has timestamp => + (is => 'ro', + required => 1, + default => sub { Cucumber::Messages::Timestamp->new() }, + ); + + +} + +package Cucumber::Messages::TestRunFinished { + +=head2 Cucumber::Messages::TestRunFinished + +=head3 DESCRIPTION + +Represents the TestRunFinished message in Cucumber's +L. + + + +=head3 ATTRIBUTES + +=cut + +use Moo; +extends 'Cucumber::Messages::Message'; + +use Scalar::Util qw( blessed ); + +my %types = ( + message => 'string', + success => 'boolean', + timestamp => 'Cucumber::Messages::Timestamp', +); + +# This is a work-around for the fact that Moo doesn't have introspection +# and Perl doesn't have boolean values... +sub _types { + return \%types; +} + + + +=head4 message + +Error message. Can be a stack trace from a failed `BeforeAll` or `AfterAll`. + If there are undefined parameter types, the message is simply + "The following parameter type(s() are not defined: xxx, yyy". + The independent `UndefinedParameterType` messages can be used to generate + snippets for those parameter types. + +=cut + +has message => + (is => 'ro', + ); + + +=head4 success + +success = StrictModeEnabled ? (failed_count == 0 && ambiguous_count == 0 && undefined_count == 0 && pending_count == 0) : (failed_count == 0 && ambiguous_count == 0) + +=cut + +has success => + (is => 'ro', + required => 1, + default => sub { '' }, + ); + + +=head4 timestamp + +Timestamp when the TestRun is finished + +=cut + +has timestamp => + (is => 'ro', + required => 1, + default => sub { Cucumber::Messages::Timestamp->new() }, + ); + + +} + +package Cucumber::Messages::TestRunStarted { + +=head2 Cucumber::Messages::TestRunStarted + +=head3 DESCRIPTION + +Represents the TestRunStarted message in Cucumber's +L. + + + +=head3 ATTRIBUTES + +=cut + +use Moo; +extends 'Cucumber::Messages::Message'; + +use Scalar::Util qw( blessed ); + +my %types = ( + timestamp => 'Cucumber::Messages::Timestamp', +); + +# This is a work-around for the fact that Moo doesn't have introspection +# and Perl doesn't have boolean values... +sub _types { + return \%types; +} + + + +=head4 timestamp + + +=cut + +has timestamp => + (is => 'ro', + required => 1, + default => sub { Cucumber::Messages::Timestamp->new() }, + ); + + +} + +package Cucumber::Messages::TestStepFinished { + +=head2 Cucumber::Messages::TestStepFinished + +=head3 DESCRIPTION + +Represents the TestStepFinished message in Cucumber's +L. + + + +=head3 ATTRIBUTES + +=cut + +use Moo; +extends 'Cucumber::Messages::Message'; + +use Scalar::Util qw( blessed ); + +my %types = ( + test_case_started_id => 'string', + test_step_id => 'string', + test_step_result => 'Cucumber::Messages::TestStepResult', + timestamp => 'Cucumber::Messages::Timestamp', +); + +# This is a work-around for the fact that Moo doesn't have introspection +# and Perl doesn't have boolean values... +sub _types { + return \%types; +} + + + +=head4 test_case_started_id + + +=cut + +has test_case_started_id => + (is => 'ro', + required => 1, + default => sub { '' }, + ); + + +=head4 test_step_id + + +=cut + +has test_step_id => + (is => 'ro', + required => 1, + default => sub { '' }, + ); + + +=head4 test_step_result + + +=cut + +has test_step_result => + (is => 'ro', + required => 1, + default => sub { Cucumber::Messages::TestStepResult->new() }, + ); + + +=head4 timestamp + + +=cut + +has timestamp => + (is => 'ro', + required => 1, + default => sub { Cucumber::Messages::Timestamp->new() }, + ); + + +} + +package Cucumber::Messages::TestStepResult { + +=head2 Cucumber::Messages::TestStepResult + +=head3 DESCRIPTION + +Represents the TestStepResult message in Cucumber's +L. + + + +=head3 ATTRIBUTES + +=cut + +use Moo; +extends 'Cucumber::Messages::Message'; + +use Scalar::Util qw( blessed ); + +my %types = ( + duration => 'Cucumber::Messages::Duration', + message => 'string', + status => '', +); + +# This is a work-around for the fact that Moo doesn't have introspection +# and Perl doesn't have boolean values... +sub _types { + return \%types; +} + + + +=head4 duration + + +=cut + +has duration => + (is => 'ro', + required => 1, + default => sub { Cucumber::Messages::Duration->new() }, + ); + + +=head4 message + + +=cut + +has message => + (is => 'ro', + ); + + +=head4 status + + + +Available constants for valid values of this field: + +=over + +=item * STATUS_UNKNOWN + +=item * STATUS_PASSED + +=item * STATUS_SKIPPED + +=item * STATUS_PENDING + +=item * STATUS_UNDEFINED + +=item * STATUS_AMBIGUOUS + +=item * STATUS_FAILED + +=back + +=cut + + +use constant + STATUS_UNKNOWN => 'UNKNOWN', + STATUS_PASSED => 'PASSED', + STATUS_SKIPPED => 'SKIPPED', + STATUS_PENDING => 'PENDING', + STATUS_UNDEFINED => 'UNDEFINED', + STATUS_AMBIGUOUS => 'AMBIGUOUS', + STATUS_FAILED => 'FAILED', + ; + +has status => + (is => 'ro', + required => 1, + default => sub { STATUS_UNKNOWN }, + ); + + +} + +package Cucumber::Messages::TestStepStarted { + +=head2 Cucumber::Messages::TestStepStarted + +=head3 DESCRIPTION + +Represents the TestStepStarted message in Cucumber's +L. + + + +=head3 ATTRIBUTES + +=cut + +use Moo; +extends 'Cucumber::Messages::Message'; + +use Scalar::Util qw( blessed ); + +my %types = ( + test_case_started_id => 'string', + test_step_id => 'string', + timestamp => 'Cucumber::Messages::Timestamp', +); + +# This is a work-around for the fact that Moo doesn't have introspection +# and Perl doesn't have boolean values... +sub _types { + return \%types; +} + + + +=head4 test_case_started_id + + +=cut + +has test_case_started_id => + (is => 'ro', + required => 1, + default => sub { '' }, + ); + + +=head4 test_step_id + + +=cut + +has test_step_id => + (is => 'ro', + required => 1, + default => sub { '' }, + ); + + +=head4 timestamp + + +=cut + +has timestamp => + (is => 'ro', + required => 1, + default => sub { Cucumber::Messages::Timestamp->new() }, + ); + + +} + +package Cucumber::Messages::Timestamp { + +=head2 Cucumber::Messages::Timestamp + +=head3 DESCRIPTION + +Represents the Timestamp message in Cucumber's +L. + + + +=head3 ATTRIBUTES + +=cut + +use Moo; +extends 'Cucumber::Messages::Message'; + +use Scalar::Util qw( blessed ); + +my %types = ( + seconds => 'number', + nanos => 'number', +); + +# This is a work-around for the fact that Moo doesn't have introspection +# and Perl doesn't have boolean values... +sub _types { + return \%types; +} + + + +=head4 seconds + +Represents seconds of UTC time since Unix epoch + 1970-01-01T00:00:00Z. Must be from 0001-01-01T00:00:00Z to + 9999-12-31T23:59:59Z inclusive. + +=cut + +has seconds => + (is => 'ro', + required => 1, + default => sub { 0 }, + ); + + +=head4 nanos + +Non-negative fractions of a second at nanosecond resolution. Negative + second values with fractions must still have non-negative nanos values + that count forward in time. Must be from 0 to 999,999,999 + inclusive. + +=cut + +has nanos => + (is => 'ro', + required => 1, + default => sub { 0 }, + ); + + +} + +package Cucumber::Messages::UndefinedParameterType { + +=head2 Cucumber::Messages::UndefinedParameterType + +=head3 DESCRIPTION + +Represents the UndefinedParameterType message in Cucumber's +L. + + + +=head3 ATTRIBUTES + +=cut + +use Moo; +extends 'Cucumber::Messages::Message'; + +use Scalar::Util qw( blessed ); + +my %types = ( + expression => 'string', + name => 'string', +); + +# This is a work-around for the fact that Moo doesn't have introspection +# and Perl doesn't have boolean values... +sub _types { + return \%types; +} + + + +=head4 expression + + +=cut + +has expression => + (is => 'ro', + required => 1, + default => sub { '' }, + ); + + +=head4 name + + +=cut + +has name => + (is => 'ro', + required => 1, + default => sub { '' }, + ); + + +} + +1; + +__END__ + +=head1 LICENSE + +Please see the included LICENSE.txt for the canonical version. In summary: + +The MIT License (MIT) + +Copyright (c) 2021 Erik Huelsmann + + +This work is loosely derived from prior work of the same library for Ruby, +called C. + +=cut + + diff --git a/messages/perl/lib/Cucumber/Messages/Message.pm b/messages/perl/lib/Cucumber/Messages/Message.pm new file mode 100644 index 0000000000..2f26f5df3c --- /dev/null +++ b/messages/perl/lib/Cucumber/Messages/Message.pm @@ -0,0 +1,179 @@ + +package Cucumber::Messages::Message; + +=head1 NAME + +Cucumber::Messages::Message - Base class for cucumber messages + +=head1 SYNOPSIS + + + # Create a new message class: + use Moo; + extends 'Cucumber::Messages::Message'; + + has 'prop1'; + has 'prop2'; + + +=head1 DESCRIPTION + + + +=cut + +use Carp qw(croak); +use JSON::MaybeXS; + +use Scalar::Util qw( blessed ); + +use Moo; +# 'use Moo' implies 'use strict; use warnings;' + +# The message classes have been inspired by the Ruby implementation, which +# existed before this Perl implementation was created. + +# Perl has multiple object systems: Moo, Moose, Mouse, ... +# Moo is Perl's minimalistic object system: geared towards speed, not aimed +# at completeness. Moose (not used in this code) is the one aiming at +# completeness. Moose has type checking of attributes, Moo lacks that. +# In that respect, Ruby and Perl are very much alike. Looking at the Ruby +# code (which doesn't have type checking), I decided not to go for +# type-checking. Given the expected short-livedness of the objects +# and as a way to reduce the dependency tree for the Cucumber::Messages +# library, I decided to go for Moo instead of Moose. + + +my $json = JSON::MaybeXS->new( + utf8 => 0, pretty => 0, indent => 0 + ); + +sub _camelize { + my ($self, $str) = @_; + + # "abc_def_ghi -> abcDefGhi + return ($str =~ s/(?:_(\w))/uc($1)/egr); +} + +sub _snaked { + my ($self, $str) = @_; + + # abcDefGhi -> abc_def_ghi + return ($str =~ s/([A-Z])/lc("_$1")/egr); +} + +sub _to_hash { + my ($value, %args) = @_; + + if (my $ref = ref $value) { + if ($ref eq 'ARRAY') { + $args{type} //= ''; + my $type = $args{type} =~ s/^\[\]//r; + return [ map { _to_hash( $_, %args, type => $type ) } @$value ]; + } + + croak 'Cucumber::Messages::Message expected in message serialization; found: ' . $ref + unless blessed( $value ) and $value->isa( 'Cucumber::Messages::Message' ); + + my $types = $value->_types; + return { + map { + __PACKAGE__->_camelize($_) + => _to_hash( $value->{$_}, %args, type => $types->{$_} ) + } keys %$value + }; + } + else { + if (not $args{type} or $args{type} ne 'boolean') { + return $value; + } + else { + return $value ? JSON::MaybeXS->true : JSON::MaybeXS->false; + } + } +} + + +sub _from_hash { + my ($value, %args) = @_; + + if (my $ref = ref $value) { + return $value ? 1 : '' + if $json->is_bool( $value ); + + if ($ref eq 'ARRAY') { + $args{type} //= ''; + my $type = $args{type} =~ s/^\[\]//r; + return [ map { _from_hash( $_, %args, type => $type ) } @$value ]; + } + use Data::Dumper; + croak 'No type supplied to deserialize hash' + unless $args{type}; + + my $types = $args{type}->_types; + return $args{type}->new( + map { + my $propname = __PACKAGE__->_snaked( $_ ); + $propname + => _from_hash( $value->{$_}, %args, + type => $types->{$propname} ) + } keys %$value + ); + } + else { + return $value; + } +} + +=head1 METHODS + +=head2 $self->to_json + +Instance method. + +Returns the data encapsulated by C<$self> as a serialized byte string +represented as a single NDJSON line. Note that line-terminating newline +character (C<\n>) is not included in the return value. + +=cut + +sub to_json { + my ($self, %args) = @_; + + return $json->encode( _to_hash( $self, %args ) ); +} + +=head2 $class->from_json( $str ) + +Returns an instance of class C<$class> which encapsulates the data +from the bytestring C<$str>, assuming that it is a single valid NDJSON +line. + +=cut + +sub from_json { + my ($class, $msgstr ) = @_; + + my $args = $json->decode( $msgstr ); + my $rv = _from_hash( $args, type => $class ); + $rv; +} + +1; + +__END__ + +=head1 LICENSE + +Please see the included LICENSE for the canonical version. In summary: + +The MIT License (MIT) + + Copyright (c) 2021 Erik Huelsmann + Copyright (c) 2021 Cucumber Ltd + +This work is loosely derived from prior work of the same library for Ruby, +called C. + +=cut + diff --git a/messages/perl/t/01-load.t b/messages/perl/t/01-load.t new file mode 100644 index 0000000000..05ea887153 --- /dev/null +++ b/messages/perl/t/01-load.t @@ -0,0 +1,10 @@ +#!perl + +use Test2::V0; + +use Cucumber::Messages::Message; +use Cucumber::Messages; + +pass 'Both libraries loaded fine'; + +done_testing; diff --git a/messages/perl/t/02-instantiation.t b/messages/perl/t/02-instantiation.t new file mode 100644 index 0000000000..8667176569 --- /dev/null +++ b/messages/perl/t/02-instantiation.t @@ -0,0 +1,21 @@ +#!perl + +use Test2::V0; +no strict 'refs'; + +use Cucumber::Messages; + +# Go over all subpackages of the Cucumber::Messages package; +# each subpackage should also be a Moo class, which we can instantiate. +# Verify that each class can be instantiated without supplying arguments +# (all required parameters have a default value; this tests the execution +# of the subs that provide the default values) +for my $pkg ( grep { m/::$/ } keys %{ "Cucumber::Messages::" }) { + my $class = ($pkg =~ s/::$//r); + + ok lives { "Cucumber::Messages::$class"->new() } + or diag $@; +} + + +done_testing; diff --git a/messages/perl/t/03-deserialize.t b/messages/perl/t/03-deserialize.t new file mode 100644 index 0000000000..4e3d1ffb39 --- /dev/null +++ b/messages/perl/t/03-deserialize.t @@ -0,0 +1,61 @@ +#!perl + +use Test2::V0; +use Test2::Tools::ClassicCompare qw( is_deeply ); +use File::Find::Rule; +use JSON::MaybeXS; + +use Cucumber::Messages; + + +skip_all "AUTHOR_TESTS=1 environment variable not set" + unless $ENV{AUTHOR_TESTS}; + +my $json = JSON::MaybeXS->new( + utf8 => 0, pretty => 0, indent => 0 + ); + + + +my @files = + File::Find::Rule->file()->name( '*.ndjson' ) + ->in( '../../compatibility-kit/javascript/features/' ); + + + + +for my $file (@files) { + open my $fh, '<:encoding(UTF-8)', $file + or die "Failed to open $file: $!"; + + while (my $content = <$fh>) { + subtest "(de)serialization of $file", sub { + serialize( $content ); + }; + } + close $fh + or warn "Failed to close $file: $!"; +} + +done_testing; + +use Data::Dumper; +sub serialize { + my $msgtxt = shift; + my $env = Cucumber::Messages::Envelope->from_json( $msgtxt ); + + ok( $env->isa('Cucumber::Messages::Message'), + 'Message correctly deserialized' ); + + my $serial = $env->to_json; + my $parsed_msg = $json->decode( $msgtxt ); + my $parsed_serial = $json->decode( $serial ); + + is_deeply( $parsed_msg, $parsed_serial, + 'Round-tripping messages results in the same JSON structure' ) + or do { + diag Dumper($env); + diag $serial; + diag $msgtxt; + }; +}