Skip to content

Diffing empty DataTables crashes with a nil error #1096

Closed
@botandrose

Description

@botandrose

Current Behavior

Opinions seem to vary on exactly how to construct an empty (0x0) DataTable, but in either case:

2.2.6 :001 > require 'cucumber'
2.2.6 :002 > include Cucumber::MultilineArgument
2.2.6 :003 > DataTable.from([[]]).diff! [[]]
NoMethodError: undefined method `length' for nil:NilClass
	from /home/micah/.rvm/gems/ruby-2.2.6@chop/gems/cucumber-2.4.0/lib/cucumber/multiline_argument/data_table.rb:357:in `diff!'
2.2.6 :004 > DataTable.from([]).diff! []
NoMethodError: undefined method `length' for nil:NilClass
	from /home/micah/.rvm/gems/ruby-2.2.6@chop/gems/cucumber-2.4.0/lib/cucumber/multiline_argument/data_table.rb:355:in `diff!'

This ends up boiling down to C::MA::DT#pad_and_match calling Array#transpose on the internal 2d array, and both [[]].transpose and [].transpose both return [], which causes the DataTable implementation to crash with a nil error, because its expecting a 2d array, but it only has a 1d array to work with.

Expected Behavior

One might argue that the bug is truly in Ruby's Array#transpose function (confirmed same behavior in ruby-head, btw), but regardless, I think we can agree that #diff! shouldn't nil crash in this circumstance. I would like to see at least one of those two invocations succeed, and possibly both.

Context & Motivation

The motivation behind this request is that I am heavily using DataTables in my cucumber scenarios, and have even written a library named chop that makes DataTables much more powerful when used in conjunction with ActiveRecord and Capybara. The specific use-case is as follows:

I have a method that creates a data structure based off of the current state of the Capybara page, lets say its a table of users, and I want to use the #diff! method to assert that it matches an expected state, which could include being completely empty.

Then "I should see the following users:" |table|
  table.diff! users_table
end

Then "I should see no users"
  empty_table.diff! users_table
end

def users_table
  all("table.users tr").map do |row|
    row.all("td").map do |cell|
      cell.text
    end
  end
end

def empty_table
  Cucumber::MultilineArgument::DataTable.from([[]]) # or [], perhaps
end

This may seem a bit awkward, like why not just do expect(users_table).to eq([]), but this is a gross simplification of the actual system. Suffice it to say that using C::MA::DT#diff! in both circumstances is greatly preferable. One could look at the implementation and usage of Chop::Diff for a better understanding of the actual system, if desired.

Next Steps

Would a PR addressing this issue be considered for merging? If so, I am happy and eager to do the work.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions