Skip to content

Conversation

botandrose
Copy link
Contributor

@botandrose botandrose commented Jan 18, 2023

Description

Teach Cucumber::Core::Test::Case to match on parent locations. This will allow it to match on lines like Feature:, Background:, and Rule:. This particular strategy was identified as potentially reasonable while pairing with @mattwynne.

This is the second half of a two-repo PR (including cucumber/gherkin#89) that ultimately resolves the remaining bits of cucumber/cucumber-ruby#1469

Type of change

  • Bug fix (non-breaking change which fixes an issue)

Checklist:

  • Tests have been added for any changes to behaviour of the code
  • New and existing tests are passing locally and on CI
  • bundle exec rubocop reports no offenses
  • RDoc comments have been updated
  • CHANGELOG.md has been updated

If this looks favorable, and cucumber/gherkin#89 gets merged, I'll update the PR to remove the Gemfile diff pointing at that branch, and resolve the remaining checklist items above.

@botandrose botandrose force-pushed the match_parent_locations branch from e4e5b7a to 39b7fa2 Compare January 18, 2023 20:11
@luke-hill
Copy link
Contributor

Hi @botandrose

As/when I get around to releasing a v12 version of this gem (Nothing huge changing don't worry). This PR will need a rebase and slight fix / re-gen of the rubocop TODO file.

I'll then plan during the v12 phase to start clicking off some of the offenses (Which number 2k+)

@botandrose
Copy link
Contributor Author

Hi @luke-hill!

Sounds good... This PR Is waiting on cucumber/gherkin#89 to be merged and released anyways.

To make sure I understand, you're expecting to release v12.0 before merging this, and then merge it in during the v12.x cycle?

@luke-hill
Copy link
Contributor

@botandrose Ideally yes. But maybe there will be a v13 before then. this gem has been long abandoned so there's a lot of refactoring to do here.

@botandrose botandrose force-pushed the match_parent_locations branch from 0b2c471 to 4774386 Compare September 17, 2023 15:18
@botandrose botandrose force-pushed the match_parent_locations branch from 4774386 to c411d6e Compare September 17, 2023 15:23
@botandrose
Copy link
Contributor Author

@luke-hill Okay, I've rebased this on current main, resolved the merge conflicts, and the bumped the gherkin dependency, rather than my git branch in the Gemfile. Everything is green, and I think its ready for merge! What do you think?

@luke-hill
Copy link
Contributor

So I'm not holding you up, if I don't get any traction on moving the cck forward I will start reviewing this in a week or so.

As a heads up, the next core version will be v13 so this will be in it as well. The next version of cucumber (v9.0.3), won't include this. We'll need to give it some time for the other refactors to be used first.

Copy link
Contributor

@luke-hill luke-hill left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Partial review done to give you something to look at / respond to. Some items here may be opening up pandoras box so feel free to let me know if they are.

@@ -68,6 +71,10 @@ def source_lines_for_pickle(pickle)
pickle.ast_node_ids.map { |id| source_line(id) }
end

def source_lines_for_pickle_parents(pickle)
gherkin_query.scenario_parent_locations(pickle.ast_node_ids[0]).map(&:line)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just briefly analysing this, is there a reason we're using index 0 here. I've not dived into the code enough. If you can paste something in here to explain this it'll speed it up for me.

Copy link
Contributor Author

@botandrose botandrose Sep 30, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@luke-hill Great question, took me a minute to figure out why. So the pickle variable represents a scenario, and usually a scenario only has one corresponding line in the document: the line that starts with Scenario:, so we would expect the accessor to actually be #ast_node_id. However, there are circumstances in which a Scenario can have multiple lines, for example:

Scenario Outline: eating
  Given there are <start> cucumbers
  When I eat <eat> cucumbers
  Then I should have <left> cucumbers

  Examples:
    | start | eat | left |
    |    12 |   5 |    7 |
    |    20 |   5 |   15 |

In this case, the scenario has two lines, the line beginning with Scenario Outline: and then also the line containing the values, e.g. | 12 | 5 | 7 |. Therefore, the pickle has to have an Array accessor of #ast_node_ids.

However, in our use-case, finding the parent lines of a given scenario, we don't care which scenario line is used, because they both have the same parents, and there's always at least one, so we can just grab the first one with [0].

That's a lot of explanation! We should probably put this in the code somewhere? Maybe extract a well-named variable?

      def source_lines_for_pickle_parents(pickle)
        first_scenario_line_ast_node_id = pickle.ast_node_ids.first # Scenario Outlines have multiple lines per scenario, but they both have the same parents
        gherkin_query.scenario_parent_locations(first_scenario_line_ast_node_id).map(&:line)
      end

WDYT?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@luke-hill Taking another look at this, if we change #matching_locations to a Set, rather than an Array, we could just pickle.ast_node_ids.flat_map(&:line) and call it a day. Set semantics would get rid of the duplicates. Which, now that I think about it, honestly wouldn't really be an issue, if we just left it as an array. Who cares if there's duplicate lines? The array just needs to match the given line number or not.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Given this is an ancient piece of buggy functionality, I'm happy to go with whatever is easier.

But I'm trying to nuke huge 100's / 1000's of rubocop offenses. So if this is something you think would be better placed dealt with easily now and then refactored later I'm happy if you think we'll get to it later.

Copy link
Contributor

@luke-hill luke-hill left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Last review before I dash for a few days onto other stuff. Hope some of this stuff makes sense.

@@ -38,8 +38,11 @@ def create_test_case(pickle)
uri = pickle.uri
test_steps = pickle.steps.map { |step| create_test_step(step, uri) }
lines = source_lines_for_pickle(pickle).sort.reverse
location = Test::Location.new(uri, lines)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If these are becoming procedural lets try isolate a few into something a bit more private. I'm only briefly here for a few days though so I might be wrong

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@luke-hill I think you're right. I just pushed another commit moving more of these details into private methods. If we move to a kwargs constructor for Test::Case and Test::Step we can reduce this even further by omitting the variable assignment, which is mostly for constructor arg readability.

expect(receiver.test_case_locations).to eq test_cases.map(&:location)
end

it 'matches the background line to all scenarios' do
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

matches the background line (of the feature), to all scenarios

it 'matches the location on a background step to all scenarios' do
location = Test::Location.new(file, 3)
filter = described_class.new([location])
compile([doc], receiver, [filter])
expect(receiver.test_case_locations).to eq test_cases.map(&:location)
end

it 'matches the rule with background line to all of its scenarios' do
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

matches the rule line (containing a background), to all of the rule scenarios

]
end

it 'matches the location on a rule background step to all of the rules scenarios' do
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

not sure what we mean by location? Do we just mean any GWT boomf?

Copy link
Contributor Author

@botandrose botandrose Oct 6, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks like we're using "line" and "location" interchangeably. It's a line from a human perspective, but since this is testing the Test::LocationsFilter class in isolation, the parameter is a Test::Location, and the return is a Test::Location, I guess we should normalize this to "location." I'll make a pass and try to bring consistency to all the spec names, including your feedback above.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@luke-hill Okay, pushed another commit tightening up the spec nomenclature.

Copy link
Contributor

@luke-hill luke-hill left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Final tweaks, only to testing code. The regular code is all good now.


Rule: A rule without a background
Scenario: with a rule and no background
Given rule a
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can we rename this portion of the test just for documentation purposes, to say something like Given a rule without a background

Given background

Scenario: with a rule and background
Given rule a
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could we rename these two steps to be Given a rule with a background

location = Test::Location.new(file, 29)
filter = described_class.new([location])
compile [doc], receiver, [filter]
expect(receiver.test_case_locations).to eq [
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm slowly updating the styles of eq to use brackets (We cannot use the rubocop linting yet owing to version).

i.e. .to eq(value)

@botandrose
Copy link
Contributor Author

@luke-hill Okay, two new commits pushed.

test_case_named('one').location,
test_case_named('two').location
])
test_case_named('one').location,
Copy link
Contributor

@luke-hill luke-hill Oct 27, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You should be able to have these 1 indented. I think the issue is the [] chars tripping it up.

Copy link
Contributor

@luke-hill luke-hill Oct 27, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should work

expect(receiver.test_case_locations).to eq(
  [test_case_named('one').location, test_case_named('two').location]
)

Or multiline

expect(receiver.test_case_locations).to eq(
  [
    test_case_named('one').location,
    test_case_named('two').location
  ]
)

Copy link
Contributor Author

@botandrose botandrose Oct 27, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Honestly, I think that's worse, because its less clear that there are two elements in the array, and we lose symmetry with the multiline input arrays. For example:

      it 'sorts by the given locations' do
        locations = [
          Test::Location.new('features/test.feature', 6),
          Test::Location.new('features/test.feature', 3)
        ]
        ...
        expect(receiver.test_case_locations).to eq([
          Test::Location.new('features/test.feature', 3),
          Test::Location.new('features/test.feature', 6)
        ])
      end

I gotta say, I'm not super sold on Rubocop's utility, overall. I recognize the value of having an automated single style enforcer in an open source project with many different contributors, but here we are, having a style discussion anyways, despite Rubocop being here, trying to figure out how to game around it.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here's a concrete suggestion, rather than just me complaining :)

How about we disable (or make an exception for) the rule that prompted the excessive indentation in the first place?

@luke-hill luke-hill merged commit fd431fc into cucumber:main Nov 10, 2023
@aslakhellesoy
Copy link
Contributor

Hi @botandrose,

Thanks for your making your first contribution to Cucumber, and welcome to the Cucumber committers team! You can now push directly to this repo and all other repos under the cucumber organization! 🍾

In return for this generous offer we hope you will:

  • ✅ Continue to use branches and pull requests. When someone on the core team approves a pull request (yours or someone else's), you're welcome to merge it yourself.
  • 💚 Commit to setting a good example by following and upholding our code of conduct in your interactions with other collaborators and users.
  • 💬 Join the community Slack channel to meet the rest of the team and make yourself at home.
  • ℹ️ Don't feel obliged to help, just do what you can if you have the time and the energy.
  • 🙋 Ask if you need anything. We're looking for feedback about how to make the project more welcoming, so please tell us!

On behalf of the Cucumber core team,
Aslak Hellesøy
Creator of Cucumber

@mattwynne
Copy link
Member

@botandrose I just want to re-iterate what the Aslak-bot said there... you've done some amazing work on these PRs, and we'd love to have you stick around and keep helping out with this code. If you want to chat any more about that, you can book me at https://calendly.com/mattwynne or find me in our community Slack.

@botandrose botandrose deleted the match_parent_locations branch November 18, 2023 08:36
@botandrose
Copy link
Contributor Author

Hey Matt, thank you very much! That's such a nice message to receive. I definitely enjoyed pairing with you on this a year ago or so, so thanks for that, as well!

Cucumber is one of the mainstays of my dev toolbox, and I'm happy to keep investing back into it, and I'll certainty continue to do so when I run into issues myself.

I was toying with the idea of expanding my involvement with Cucumber, and making a habit of spending a few hours every weekend contributing beyond addressing my own issues. But I have to admit that a few factors have got me feeling a bit discouraged.

  1. Negative response to my last refactoring PR.
  2. Length of time from submitting this PR to it finally getting merged.
  3. Length of code review process.

I don't want to imply that there has been anything but superb conduct from anyone! I'm in no way feeling that anything was unjust or incorrect or bad or wrong. But I'm simply more motivated to work on projects where I can ship more code more quickly.

@luke-hill
Copy link
Contributor

luke-hill commented Nov 20, 2023

FYI Length of time was due to the previous full-time maintainer Aurelien no longer working full-time for Smartbear (I think previously he will have tracked most things, whereas I now do this ad-hoc using Excel). Things have changed a lot in the last few months. I'm now back here actively, so if you're wanting to contribute more I'm able to help/support reasonably promptly.

@mattwynne
Copy link
Member

I appreciate the feedback @botandrose, and yes I enjoyed pairing with you too.

As Luke says we've been though a bit of a tumultuous time recently, and although that situation is hopefully drawing to a close, it has been quite disruptive and distracting for us. Sorry if you didn't get the attention you needed.

One thing I would say, in reference to being able to ship stuff, is that we encourage everyone with commit rights to the main branches to follow the Ship / Show / Ask protocol. In other words, while code reviews can be a great way to get buy-in or feedback on bigger changes you're not sure about, you're absolutely encouraged to just commit directly to main or merge your own PRs as long as you feel the changes are generally moving us forwards.

@botandrose
Copy link
Contributor Author

Hey Luke, Matt, thanks for the responses! Really encouraging to hear.

I read your blog post with some sadness, Matt, when you first posted it. So I figured those events were a big factor. And I can definitely understand how it was disruptive to managing OSS development here, to say the least. I am also pleasantly surprised to see you back in here again!

Also very good to understand the Ship / Show / Ask protocol that is followed here! Consider my concerns to be completely addressed. I'll see if I can't pitch in some time every weekend or so.

I'm looking forward to more collaboration together with both of you, here!

@luke-hill
Copy link
Contributor

If you're interested in finding out a bit more (Either async or sync), we have a weekly document that updates what we're delivering and we aim to try get together to chat for 30-45mins each week

If you find us on slack we can put you in touch with our weekly zoom/g-doc that contains more info.

Again no pressure if you don't want to / aren't able to, just figured I'd mention what's there.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants