Skip to content

cucumber-messages: Use pure-Ruby protobuf implementation (experimental) #813

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 12 commits into from
Jan 8, 2020
Merged
4 changes: 2 additions & 2 deletions .circleci/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ executors:
docker:
# Remember to update to the latest image tag.
# Run `source scripts/functions.sh && docker_image Dockerfile` to print it.
- image: cucumber/cucumber-build:9ce5f6a542de12ceecdd84caaad47365
- image: cucumber/cucumber-build:7e07f786ac72e3597a8e5bbe435b4c90
working_directory: ~/cucumber
# Go
docker-circleci-golang:
Expand Down Expand Up @@ -397,7 +397,7 @@ jobs:
- persist_to_workspace:
root: ~/cucumber
paths:
- cucumber-messages/ruby/lib/cucumber/messages_pb.rb
- cucumber-messages/ruby

gherkin-ruby-23:
executor: docker-circleci-ruby-23
Expand Down
4 changes: 4 additions & 0 deletions .templates/ruby/Rakefile
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,10 @@ Bundler::GemHelper.install_tasks

$:.unshift File.expand_path("../lib", __FILE__)

Dir['./rake/*.rb'].each do |f|
require f
end

require "rspec/core/rake_task"
RSpec::Core::RakeTask.new(:spec) do |t|
t.ruby_opts = %w[-r./spec/coverage -w]
Expand Down
1 change: 1 addition & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ RUN apk add --no-cache \
py2-pip \
rsync \
ruby \
ruby-bigdecimal \
ruby-dev \
sed \
su-exec \
Expand Down
4 changes: 4 additions & 0 deletions c21e/ruby/Rakefile
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,10 @@ Bundler::GemHelper.install_tasks

$:.unshift File.expand_path("../lib", __FILE__)

Dir['./rake/*.rb'].each do |f|
require f
end

require "rspec/core/rake_task"
RSpec::Core::RakeTask.new(:spec) do |t|
t.ruby_opts = %w[-r./spec/coverage -w]
Expand Down
4 changes: 4 additions & 0 deletions cucumber-demo-formatter/ruby/Rakefile
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,10 @@ Bundler::GemHelper.install_tasks

$:.unshift File.expand_path("../lib", __FILE__)

Dir['./rake/*.rb'].each do |f|
require f
end

require "rspec/core/rake_task"
RSpec::Core::RakeTask.new(:spec) do |t|
t.ruby_opts = %w[-r./spec/coverage -w]
Expand Down
4 changes: 2 additions & 2 deletions cucumber-demo-formatter/ruby/bin/cucumber-demo-formatter
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ require 'cucumber_demo_formatter'
ARGV << '-h' if ARGV.empty?

option_parser = OptionParser.new do |opts|
opts.on '-f', '--format=ndjson|protobuf', 'Output format'
opts.on '-f', '--format=ndjson|protobuf', 'Input format'

opts.on_tail("-h", "--help", "Show this message") do
puts opts
Expand All @@ -29,4 +29,4 @@ else
raise "Unsupported format: '#{format}'"
end

formatter.process_messages(message_enumerator, STDOUT)
formatter.process_messages(message_enumerator, STDOUT)
21 changes: 12 additions & 9 deletions cucumber-demo-formatter/ruby/lib/cucumber_demo_formatter.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,21 +3,24 @@
class CucumberDemoFormatter
def process_messages(message_enumerator, output)
emoji = {
UNKNOWN: '👽',
PASSED: '😃',
SKIPPED: '🥶',
PENDING: '⏰',
UNDEFINED: '🤷',
AMBIGUOUS: '🦄',
FAILED: '💣',
::Cucumber::Messages::TestResult::Status::UNKNOWN => '👽',
::Cucumber::Messages::TestResult::Status::PASSED => '😃',
::Cucumber::Messages::TestResult::Status::SKIPPED => '🥶',
::Cucumber::Messages::TestResult::Status::PENDING => '⏰',
::Cucumber::Messages::TestResult::Status::UNDEFINED => '🤷',
::Cucumber::Messages::TestResult::Status::AMBIGUOUS => '🦄',
::Cucumber::Messages::TestResult::Status::FAILED => '💣',
}
message_enumerator.each do |message|
if message.test_step_finished
output.write(emoji[message.test_step_finished.test_result.status])
status = message.test_step_finished.test_result.status
em = emoji[status]
raise "No emoji found for status #{status}" if em.nil?
output.write(em)
end
if message.test_run_finished
output.write("\n")
end
end
end
end
end
4 changes: 4 additions & 0 deletions cucumber-expressions/ruby/Rakefile
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,10 @@ Bundler::GemHelper.install_tasks

$:.unshift File.expand_path("../lib", __FILE__)

Dir['./rake/*.rb'].each do |f|
require f
end

require "rspec/core/rake_task"
RSpec::Core::RakeTask.new(:spec) do |t|
t.ruby_opts = %w[-r./spec/coverage -w]
Expand Down
4 changes: 4 additions & 0 deletions cucumber-json-formatter/ruby/Rakefile
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,10 @@ Bundler::GemHelper.install_tasks

$:.unshift File.expand_path("../lib", __FILE__)

Dir['./rake/*.rb'].each do |f|
require f
end

require "rspec/core/rake_task"
RSpec::Core::RakeTask.new(:spec) do |t|
t.ruby_opts = %w[-r./spec/coverage -w]
Expand Down
5 changes: 2 additions & 3 deletions cucumber-messages/dotnet/messages.proto
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
syntax = "proto3";
package io.cucumber.messages;
option ruby_package = "Cucumber::Messages";
option go_package = "messages";

// When removing a field, replace it with reserved, rather than deleting the line.
Expand Down Expand Up @@ -426,13 +425,13 @@ message TestCase {
string pickle_step_id = 2;
// Pointer to all the matching `StepDefinition`s (if derived from a PickleStep)
repeated string step_definition_ids = 3;
// A list of list of StepMatchArgument (if derived from a `StepDefinition`).
// A list of list of StepMatchArgument (if derived from a `StepDefinition`).
// Each element represents a matching step definition. A size of 0 means `UNDEFINED`,
// and a size of 2+ means `AMBIGUOUS`
repeated StepMatchArgumentsList step_match_arguments_lists = 4;
// Pointer to the `Hook` (if derived from a Hook)
string hook_id = 5;

message StepMatchArgumentsList {
repeated StepMatchArgument step_match_arguments = 1;
}
Expand Down
5 changes: 2 additions & 3 deletions cucumber-messages/go/messages.proto
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
syntax = "proto3";
package io.cucumber.messages;
option ruby_package = "Cucumber::Messages";
option go_package = "messages";

// When removing a field, replace it with reserved, rather than deleting the line.
Expand Down Expand Up @@ -426,13 +425,13 @@ message TestCase {
string pickle_step_id = 2;
// Pointer to all the matching `StepDefinition`s (if derived from a PickleStep)
repeated string step_definition_ids = 3;
// A list of list of StepMatchArgument (if derived from a `StepDefinition`).
// A list of list of StepMatchArgument (if derived from a `StepDefinition`).
// Each element represents a matching step definition. A size of 0 means `UNDEFINED`,
// and a size of 2+ means `AMBIGUOUS`
repeated StepMatchArgumentsList step_match_arguments_lists = 4;
// Pointer to the `Hook` (if derived from a Hook)
string hook_id = 5;

message StepMatchArgumentsList {
repeated StepMatchArgument step_match_arguments = 1;
}
Expand Down
5 changes: 2 additions & 3 deletions cucumber-messages/java/messages.proto
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
syntax = "proto3";
package io.cucumber.messages;
option ruby_package = "Cucumber::Messages";
option go_package = "messages";

// When removing a field, replace it with reserved, rather than deleting the line.
Expand Down Expand Up @@ -426,13 +425,13 @@ message TestCase {
string pickle_step_id = 2;
// Pointer to all the matching `StepDefinition`s (if derived from a PickleStep)
repeated string step_definition_ids = 3;
// A list of list of StepMatchArgument (if derived from a `StepDefinition`).
// A list of list of StepMatchArgument (if derived from a `StepDefinition`).
// Each element represents a matching step definition. A size of 0 means `UNDEFINED`,
// and a size of 2+ means `AMBIGUOUS`
repeated StepMatchArgumentsList step_match_arguments_lists = 4;
// Pointer to the `Hook` (if derived from a Hook)
string hook_id = 5;

message StepMatchArgumentsList {
repeated StepMatchArgument step_match_arguments = 1;
}
Expand Down
5 changes: 2 additions & 3 deletions cucumber-messages/javascript/messages.proto
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
syntax = "proto3";
package io.cucumber.messages;
option ruby_package = "Cucumber::Messages";
option go_package = "messages";

// When removing a field, replace it with reserved, rather than deleting the line.
Expand Down Expand Up @@ -426,13 +425,13 @@ message TestCase {
string pickle_step_id = 2;
// Pointer to all the matching `StepDefinition`s (if derived from a PickleStep)
repeated string step_definition_ids = 3;
// A list of list of StepMatchArgument (if derived from a `StepDefinition`).
// A list of list of StepMatchArgument (if derived from a `StepDefinition`).
// Each element represents a matching step definition. A size of 0 means `UNDEFINED`,
// and a size of 2+ means `AMBIGUOUS`
repeated StepMatchArgumentsList step_match_arguments_lists = 4;
// Pointer to the `Hook` (if derived from a Hook)
string hook_id = 5;

message StepMatchArgumentsList {
repeated StepMatchArgument step_match_arguments = 1;
}
Expand Down
5 changes: 2 additions & 3 deletions cucumber-messages/messages.proto
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
syntax = "proto3";
package io.cucumber.messages;
option ruby_package = "Cucumber::Messages";
option go_package = "messages";

// When removing a field, replace it with reserved, rather than deleting the line.
Expand Down Expand Up @@ -426,13 +425,13 @@ message TestCase {
string pickle_step_id = 2;
// Pointer to all the matching `StepDefinition`s (if derived from a PickleStep)
repeated string step_definition_ids = 3;
// A list of list of StepMatchArgument (if derived from a `StepDefinition`).
// A list of list of StepMatchArgument (if derived from a `StepDefinition`).
// Each element represents a matching step definition. A size of 0 means `UNDEFINED`,
// and a size of 2+ means `AMBIGUOUS`
repeated StepMatchArgumentsList step_match_arguments_lists = 4;
// Pointer to the `Hook` (if derived from a Hook)
string hook_id = 5;

message StepMatchArgumentsList {
repeated StepMatchArgument step_match_arguments = 1;
}
Expand Down
3 changes: 0 additions & 3 deletions cucumber-messages/ruby/Gemfile
Original file line number Diff line number Diff line change
@@ -1,7 +1,4 @@
# frozen_string_literal: true
source 'https://rubygems.org'

# Use an older protobuf on JRuby
gem 'google-protobuf', '~> 3.2.0.2' if RUBY_PLATFORM == 'java'

gemspec
11 changes: 7 additions & 4 deletions cucumber-messages/ruby/Makefile
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
include default.mk

.deps: lib/cucumber/messages_pb.rb
.deps: lib/cucumber/messages.pb.rb

lib/cucumber/messages_pb.rb: messages.proto
protoc -I. --ruby_out lib/cucumber $<
lib/cucumber/messages.pb.rb: messages.proto
mv $< $<.bak
cat $<.bak | sed "s/package io.cucumber.messages/package cucumber.messages/" > $<
bundle exec rake protobuf:compile[.,.,lib/cucumber]
mv $<.bak $<

clean:
rm -f lib/cucumber/messages_pb.rb
rm -f lib/cucumber/messages.pb.rb
4 changes: 4 additions & 0 deletions cucumber-messages/ruby/Rakefile
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,10 @@ Bundler::GemHelper.install_tasks

$:.unshift File.expand_path("../lib", __FILE__)

Dir['./rake/*.rb'].each do |f|
require f
end

require "rspec/core/rake_task"
RSpec::Core::RakeTask.new(:spec) do |t|
t.ruby_opts = %w[-r./spec/coverage -w]
Expand Down
6 changes: 4 additions & 2 deletions cucumber-messages/ruby/cucumber-messages.gemspec
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,11 @@ Gem::Specification.new do |s|
'source_code_uri' => 'https://github.com/cucumber/cucumber/blob/master/cucumber-messages/ruby',
}

# Users of JRuby should use google-protobuf 3.2.0.2 (later versions don't work)
s.add_dependency('google-protobuf', ['>= 3.2', '<= 3.8'])
# TODO: Switch back to 'protobuf' when this PR is merged and released:
# https://github.com/ruby-protobuf/protobuf/pull/411
s.add_dependency 'protobuf-cucumber', '~> 3.10', '>= 3.10.4'
s.add_dependency 'json', '~> 2.3', '>= 2.3.0'

s.add_development_dependency 'rake', '~> 13.0', '>= 13.0.1'
s.add_development_dependency 'rspec', '~> 3.9', '>= 3.9.0'

Expand Down
2 changes: 1 addition & 1 deletion cucumber-messages/ruby/lib/cucumber/.gitignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
# protoc-generated file
messages_pb.rb
messages.pb.rb
6 changes: 1 addition & 5 deletions cucumber-messages/ruby/lib/cucumber/messages.rb
Original file line number Diff line number Diff line change
@@ -1,8 +1,4 @@
module Cucumber
module Messages
end
end
require 'cucumber/messages_pb'
require 'cucumber/messages.pb'
require 'cucumber/messages/binary_to_message_enumerator'
require 'cucumber/messages/ndjson_to_message_enumerator'
require 'cucumber/messages/protobuf_delimited'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@ class NdjsonToMessageEnumerator < Enumerator
def initialize(io)
super() do |yielder|
io.each_line do |json|
yielder.yield(Cucumber::Messages::Envelope.decode_json(json))
m = Cucumber::Messages::Envelope.from_json(json)
yielder.yield(m)
end
end
end
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@ module Cucumber
module Messages
module WriteNdjson
def write_ndjson_to(io)
json = self.class.encode_json(self)
# https://github.com/ruby-protobuf/protobuf/pull/410
json = self.to_json(lower_camel_case: true)
ob = JSON.parse(json)
remove_empties(ob)
io.puts(JSON.generate(ob))
Expand All @@ -13,7 +14,7 @@ def write_ndjson_to(io)
def remove_empties(ob)
if Hash === ob
ob.each do |key, value|
if value == []
if value == [] || value == '' || value == 0
ob.delete(key)
else
remove_empties(value)
Expand All @@ -27,4 +28,4 @@ def remove_empties(ob)
end
end
end
end
end
5 changes: 2 additions & 3 deletions cucumber-messages/ruby/messages.proto
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
syntax = "proto3";
package io.cucumber.messages;
option ruby_package = "Cucumber::Messages";
option go_package = "messages";

// When removing a field, replace it with reserved, rather than deleting the line.
Expand Down Expand Up @@ -426,13 +425,13 @@ message TestCase {
string pickle_step_id = 2;
// Pointer to all the matching `StepDefinition`s (if derived from a PickleStep)
repeated string step_definition_ids = 3;
// A list of list of StepMatchArgument (if derived from a `StepDefinition`).
// A list of list of StepMatchArgument (if derived from a `StepDefinition`).
// Each element represents a matching step definition. A size of 0 means `UNDEFINED`,
// and a size of 2+ means `AMBIGUOUS`
repeated StepMatchArgumentsList step_match_arguments_lists = 4;
// Pointer to the `Hook` (if derived from a Hook)
string hook_id = 5;

message StepMatchArgumentsList {
repeated StepMatchArgument step_match_arguments = 1;
}
Expand Down
1 change: 1 addition & 0 deletions cucumber-messages/ruby/rake/protobuf.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
load 'protobuf/tasks/compile.rake'
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,24 @@ module Cucumber
module Messages
describe Messages do

it "json-roundtrips messages with bytes fields" do
a1 = Attachment.new(binary: [1,2,3,4].pack('C*'))
expect(a1.binary.length).to eq(4)
a2 = Attachment.new(JSON.parse(a1.to_json))
expect(a2).to(eq(a1))
end

it "omits empty string fields in output" do
io = StringIO.new
message = Envelope.new(source: Source.new(data: ''))
message.write_ndjson_to(io)

io.rewind
json = io.read

expect(json).to eq("{\"source\":{}}\n")
end

it "can be serialised over an ndjson stream" do
outgoing_messages = [
Envelope.new(source: Source.new(data: 'Feature: Hello')),
Expand Down
Loading