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

cucumber-expressions: index 3 out of matches (IndexError) #329

Closed
nwallace opened this Issue Jan 30, 2018 · 4 comments

Comments

Projects
None yet
2 participants
@nwallace

nwallace commented Jan 30, 2018

Summary

Given a certain (slightly complex) regexp to define a step definition, cucumber fails to load and I get an unfriendly error message "index 3 out of matches (IndexError)". The step definition looks like this:

Given /^I am a person( named "(?<first_name>.+) (?<last_name>.+)")?$/ do |first_name, last_name|
  ...
end

Expected Behavior

This step definition worked fine before (known to work against v2.4.0), so it should either work correctly or give me a helpful error message to help me fix the problem.

Current Behavior

An IndexError is raised when cucumber tries to load the step definitions.

$ cucumber
index 3 out of matches (IndexError)
/home/nathan/.gem/ruby/2.5.0/gems/cucumber-expressions-5.0.13/lib/cucumber/cucumber_expressions/group_builder.rb:20:in `offset'
/home/nathan/.gem/ruby/2.5.0/gems/cucumber-expressions-5.0.13/lib/cucumber/cucumber_expressions/group_builder.rb:20:in `build'
/home/nathan/.gem/ruby/2.5.0/gems/cucumber-expressions-5.0.13/lib/cucumber/cucumber_expressions/group_builder.rb:19:in `block in build'
/home/nathan/.gem/ruby/2.5.0/gems/cucumber-expressions-5.0.13/lib/cucumber/cucumber_expressions/group_builder.rb:19:in `map'
/home/nathan/.gem/ruby/2.5.0/gems/cucumber-expressions-5.0.13/lib/cucumber/cucumber_expressions/group_builder.rb:19:in `build'
/home/nathan/.gem/ruby/2.5.0/gems/cucumber-expressions-5.0.13/lib/cucumber/cucumber_expressions/group_builder.rb:19:in `block in build'
/home/nathan/.gem/ruby/2.5.0/gems/cucumber-expressions-5.0.13/lib/cucumber/cucumber_expressions/group_builder.rb:19:in `map'
/home/nathan/.gem/ruby/2.5.0/gems/cucumber-expressions-5.0.13/lib/cucumber/cucumber_expressions/group_builder.rb:19:in `build'
/home/nathan/.gem/ruby/2.5.0/gems/cucumber-expressions-5.0.13/lib/cucumber/cucumber_expressions/tree_regexp.rb:48:in `match'
/home/nathan/.gem/ruby/2.5.0/gems/cucumber-expressions-5.0.13/lib/cucumber/cucumber_expressions/argument.rb:10:in `build'
/home/nathan/.gem/ruby/2.5.0/gems/cucumber-expressions-5.0.13/lib/cucumber/cucumber_expressions/regular_expression.rb:32:in `match'
/home/nathan/.gem/ruby/2.5.0/gems/cucumber-3.1.0/lib/cucumber/glue/step_definition.rb:101:in `arguments_from'
/home/nathan/.gem/ruby/2.5.0/gems/cucumber-3.1.0/lib/cucumber/glue/registry_and_more.rb:66:in `block in step_matches'
/home/nathan/.gem/ruby/2.5.0/gems/cucumber-3.1.0/lib/cucumber/glue/registry_and_more.rb:65:in `each'
/home/nathan/.gem/ruby/2.5.0/gems/cucumber-3.1.0/lib/cucumber/glue/registry_and_more.rb:65:in `reduce'
/home/nathan/.gem/ruby/2.5.0/gems/cucumber-3.1.0/lib/cucumber/glue/registry_and_more.rb:65:in `step_matches'
/home/nathan/.gem/ruby/2.5.0/gems/cucumber-3.1.0/lib/cucumber/step_match_search.rb:19:in `call'
/home/nathan/.gem/ruby/2.5.0/gems/cucumber-3.1.0/lib/cucumber/step_match_search.rb:19:in `call'
/home/nathan/.rubies/ruby-2.5.0/lib/ruby/2.5.0/delegate.rb:83:in `method_missing'
/home/nathan/.gem/ruby/2.5.0/gems/cucumber-3.1.0/lib/cucumber/step_match_search.rb:62:in `call'
/home/nathan/.gem/ruby/2.5.0/gems/cucumber-3.1.0/lib/cucumber/filters/activate_steps.rb:66:in `matches'
/home/nathan/.gem/ruby/2.5.0/gems/cucumber-3.1.0/lib/cucumber/filters/activate_steps.rb:47:in `result'
/home/nathan/.gem/ruby/2.5.0/gems/cucumber-3.1.0/lib/cucumber/filters/activate_steps.rb:37:in `find_match'
/home/nathan/.gem/ruby/2.5.0/gems/cucumber-3.1.0/lib/cucumber/filters/activate_steps.rb:33:in `attempt_to_activate'
/home/nathan/.gem/ruby/2.5.0/gems/cucumber-3.1.0/lib/cucumber/filters/activate_steps.rb:29:in `map'
/home/nathan/.gem/ruby/2.5.0/gems/cucumber-3.1.0/lib/cucumber/filters/activate_steps.rb:29:in `new_test_steps'
/home/nathan/.gem/ruby/2.5.0/gems/cucumber-3.1.0/lib/cucumber/filters/activate_steps.rb:23:in `test_case'
/home/nathan/.gem/ruby/2.5.0/gems/cucumber-3.1.0/lib/cucumber/filters/activate_steps.rb:12:in `test_case'
/home/nathan/.gem/ruby/2.5.0/gems/cucumber-core-3.1.0/lib/cucumber/core/test/case.rb:25:in `describe_to'
/home/nathan/.gem/ruby/2.5.0/gems/cucumber-core-3.1.0/lib/cucumber/core/test/filters/locations_filter.rb:18:in `block in done'
/home/nathan/.gem/ruby/2.5.0/gems/cucumber-core-3.1.0/lib/cucumber/core/test/filters/locations_filter.rb:17:in `each'
/home/nathan/.gem/ruby/2.5.0/gems/cucumber-core-3.1.0/lib/cucumber/core/test/filters/locations_filter.rb:17:in `done'
/home/nathan/.gem/ruby/2.5.0/gems/cucumber-core-3.1.0/lib/cucumber/core/filter.rb:62:in `done'
/home/nathan/.gem/ruby/2.5.0/gems/cucumber-core-3.1.0/lib/cucumber/core/test/filters/tag_filter.rb:18:in `done'
/home/nathan/.gem/ruby/2.5.0/gems/cucumber-core-3.1.0/lib/cucumber/core/compiler.rb:24:in `done'
/home/nathan/.gem/ruby/2.5.0/gems/cucumber-core-3.1.0/lib/cucumber/core/gherkin/parser.rb:37:in `done'
/home/nathan/.gem/ruby/2.5.0/gems/cucumber-core-3.1.0/lib/cucumber/core.rb:32:in `parse'
/home/nathan/.gem/ruby/2.5.0/gems/cucumber-core-3.1.0/lib/cucumber/core.rb:21:in `compile'
/home/nathan/.gem/ruby/2.5.0/gems/cucumber-3.1.0/lib/cucumber/runtime.rb:74:in `run!'
/home/nathan/.gem/ruby/2.5.0/gems/cucumber-3.1.0/lib/cucumber/cli/main.rb:33:in `execute!'
/home/nathan/.gem/ruby/2.5.0/gems/cucumber-3.1.0/bin/cucumber:9:in `<top (required)>'
/home/nathan/.gem/ruby/2.5.0/bin/cucumber:23:in `load'
/home/nathan/.gem/ruby/2.5.0/bin/cucumber:23:in `<top (required)>'
/home/nathan/.gem/ruby/2.5.0/gems/bundler-1.16.1/lib/bundler/cli/exec.rb:75:in `load'
/home/nathan/.gem/ruby/2.5.0/gems/bundler-1.16.1/lib/bundler/cli/exec.rb:75:in `kernel_load'
/home/nathan/.gem/ruby/2.5.0/gems/bundler-1.16.1/lib/bundler/cli/exec.rb:28:in `run'
/home/nathan/.gem/ruby/2.5.0/gems/bundler-1.16.1/lib/bundler/cli.rb:424:in `exec'
/home/nathan/.gem/ruby/2.5.0/gems/bundler-1.16.1/lib/bundler/vendor/thor/lib/thor/command.rb:27:in `run'
/home/nathan/.gem/ruby/2.5.0/gems/bundler-1.16.1/lib/bundler/vendor/thor/lib/thor/invocation.rb:126:in `invoke_command'
/home/nathan/.gem/ruby/2.5.0/gems/bundler-1.16.1/lib/bundler/vendor/thor/lib/thor.rb:387:in `dispatch'
/home/nathan/.gem/ruby/2.5.0/gems/bundler-1.16.1/lib/bundler/cli.rb:27:in `dispatch'
/home/nathan/.gem/ruby/2.5.0/gems/bundler-1.16.1/lib/bundler/vendor/thor/lib/thor/base.rb:466:in `start'
/home/nathan/.gem/ruby/2.5.0/gems/bundler-1.16.1/lib/bundler/cli.rb:18:in `start'
/home/nathan/.gem/ruby/2.5.0/gems/bundler-1.16.1/exe/bundle:30:in `block in <top (required)>'
/home/nathan/.gem/ruby/2.5.0/gems/bundler-1.16.1/lib/bundler/friendly_errors.rb:122:in `with_friendly_errors'
/home/nathan/.gem/ruby/2.5.0/gems/bundler-1.16.1/exe/bundle:22:in `<top (required)>'
/home/nathan/.gem/ruby/2.5.0/bin/bundle:23:in `load'
/home/nathan/.gem/ruby/2.5.0/bin/bundle:23:in `<main>'

Steps to Reproduce (for bugs)

Minimal demo of behavior here: https://github.com/nwallace/cucumber-index-error-bug
Clone the repo, install ruby 2.5.0 (if necessary), bundle install, cucumber

Your Environment

  • Version used: 3.1.0
  • Operating System and version: Arch Linux
@nwallace

This comment has been minimized.

nwallace commented Jan 30, 2018

It appears to be caused by the named captures inside the optional group. Without the names, I get a nice error message:

Given /^I am a person( named "(.+) (.+)")?$/ do |first_name, last_name|
  ...
end
$ cucumber
Feature: Demonstrate IndexError bug

  Scenario: It cannot handle this Regexp       # features/index_error.feature:3
    Given I am a person named "Charlotte Rose" # features/step_definitions/error_steps.rb:1
      Single transformer unexpectedly matched 2 values - "Charlotte" and "Rose" (Cucumber::CucumberExpressions::CucumberExpressionError)
      features/index_error.feature:4:in `Given I am a person named "Charlotte Rose"'
    Then my first name is "Charlotte"          # features/step_definitions/error_steps.rb:6
    And my last name is "Rose"                 # features/step_definitions/error_steps.rb:10

Failing Scenarios:
cucumber features/index_error.feature:3 # Scenario: It cannot handle this Regexp
@aslakhellesoy

This comment has been minimized.

Contributor

aslakhellesoy commented Jan 30, 2018

Hi @nwallace adding support for named capture groups should be easy to do, I'll handle that. When that's fixed, you'll get the same error message as when you use (.*).

Out of curiosity, why did you wrap thefirst_name and last_name capture groups in a surrounding one?

@nwallace

This comment has been minimized.

nwallace commented Jan 30, 2018

Sometimes I want to specify the person's name and sometimes I don't care what their name is. I was surprised when it worked the way I wanted back on the old version, but it's understandable that that would be hard to parse by cucumber. It's easy enough to just have two separate step definitions 👍

@aslakhellesoy

This comment has been minimized.

Contributor

aslakhellesoy commented Jan 31, 2018

I've tracked this down to what seems to be a bug in Ruby's RegExp. It turns out a RegExp with a mix of unnamed and named capture groups only exposes access to the named capture groups, and not the unnamed ones.

m = /(a)(?<x>b)/.match('ab')
m.length # => 2
m[0] # => 'ab'
m[1] # => 'b'
m[:x] # => 'b'

There is no way to access the capture group containing the a.

Because of this bug I think our best option is to simply disallow named capture groups in Cucumber.

WDYT?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment