Skip to content
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

DONT MERGE YET - REVIEW COMMENTS PLEASE - Test renovation - in preparation for fixing the Hook handler bug and adding other features. #659

Open
wants to merge 17 commits into
base: main
Choose a base branch
from

Conversation

Johnlon
Copy link
Member

@Johnlon Johnlon commented Oct 30, 2024

🤔 What's changed?

suite_context_test.go is now testing solely using the Public of godog api.
To highlight it's new lease of life it is renamed to functional_tests.go

Previously this suite wasn't really testing godog at all, because it was testing a fake godog-like mock up.
Consequently a bunch of the tests weren't actually doing anything useful.
The most useful tests therefore were the fmt_output_tests which DID go via the real product.

Much of the code changes are a consequence of fixing these issues.

  • Fixed broken tests
  • Removed duplicates tests
  • Removed bogus / useless tests
  • Relocated tests
  • Added new tests

The public api has an additiive change TestSuite.RunWithResults() that returns the exit code plus a reference to the storage.
This addiitonal public api is really useful for various reasons (eg postprocessing) but importantly its a hook needed to do some of the tests without totally hackery.

Formatter.Close()

This is conceivably a breaking if anyone has written their own formatter.

I added Close() to the Formatter api so that the framework will reliably close any files opened inside the formatters (eg cuke).

This gap was the cause of a testing problem that became apparent once using the proper cuke public api because the formatter output files were not getting flushed because there were never getting closed during the tests. The close is gives predictable (and correct) behaviour.
This problem would only afflict godog where it's used in the "Library" mode because users of the CLI would have has the files closed as the CLI exited.

⚡️ What's your motivation?

Product is a bit too hard to work with and I want to add some features/fixes.
This is only a partial renovation.

🏷️ What kind of change is this?

  • 🏦 Refactoring/debt/DX (improvement to code design, tooling, etc. without changing behaviour)
  • 🐛 Bug fix (non-breaking change which fixes a defect)
  • ⚡ New feature (non-breaking change which adds new behaviour)

♻️ Anything particular you want feedback on?

📋 Checklist:

  • I agree to respect and uphold the Cucumber Community Code of Conduct
  • I've changed the behaviour of the code
    • I have added/updated tests to cover my changes.
  • My change requires a change to the documentation.
    • I have updated the documentation accordingly.
  • Users should know about my change
    • [] I have added an entry to the "Unreleased" section of the CHANGELOG, linking to this pull request.

This text was originally generated from a template, then edited by hand. You can modify the template here.

…"features" and it's massive set of steps into run_test

- removed some duplicate tests from example_subtests_test.go
Previously the suite_context_tests weren't actually testing godog at all but an exploded version of what the internal code might have looked like at some point in the past.
Removed some junk test cases around events.feature that weren't actually testing godog but some irrelevant stuff in the exploded code.
…s on it or do whatever the end user wants

make honnef scan whole tree
Copy link

Go API Changes

# github.com/cucumber/godog
## incompatible changes
NewBaseFmt: changed from func(string, io.Writer) *github.com/cucumber/godog/internal/formatters.Base to func(string, io.WriteCloser) *github.com/cucumber/godog/internal/formatters.Base
NewCukeFmt: changed from func(string, io.Writer) *github.com/cucumber/godog/internal/formatters.Cuke to func(string, io.WriteCloser) *github.com/cucumber/godog/internal/formatters.Cuke
NewEventsFmt: changed from func(string, io.Writer) *github.com/cucumber/godog/internal/formatters.Events to func(string, io.WriteCloser) *github.com/cucumber/godog/internal/formatters.Events
NewJUnitFmt: changed from func(string, io.Writer) *github.com/cucumber/godog/internal/formatters.JUnit to func(string, io.WriteCloser) *github.com/cucumber/godog/internal/formatters.JUnit
NewPrettyFmt: changed from func(string, io.Writer) *github.com/cucumber/godog/internal/formatters.Pretty to func(string, io.WriteCloser) *github.com/cucumber/godog/internal/formatters.Pretty
NewProgressFmt: changed from func(string, io.Writer) *github.com/cucumber/godog/internal/formatters.Progress to func(string, io.WriteCloser) *github.com/cucumber/godog/internal/formatters.Progress
## compatible changes
Attach: added
Attachment: added
Attachments: added
ErrAmbiguous: added
ExitFailure: added
ExitOptionError: added
ExitSuccess: added
NopCloser: added
RunResult: added
StepAmbiguous: added
TestSuite.RunWithResult: added

# github.com/cucumber/godog/colors
## incompatible changes
Colored: changed from func(io.Writer) io.Writer to func(io.WriteCloser) io.WriteCloser
Uncolored: changed from func(io.Writer) io.Writer to func(io.WriteCloser) io.WriteCloser

# github.com/cucumber/godog/formatters
## incompatible changes
Formatter.Ambiguous: added
Formatter.Close: added
FormatterFunc: changed from func(string, io.Writer) Formatter to func(string, io.WriteCloser) Formatter

# summary
Inferred base version: v0.14.1
Suggested version: v0.15.0

@Johnlon Johnlon changed the title DONT MERGE YET - Test renovation - in preparation for fixing the Hook handler bug and adding other features. DONT MERGE YET - REVIEW COMMENTS PLEASE - Test renovation - in preparation for fixing the Hook handler bug and adding other features. Oct 30, 2024
@Johnlon Johnlon requested a review from vearutop October 31, 2024 01:55
/*
This file contains some functional tests for the godog library.
This is the glue code that can be run against features/*feature
and those feature files are written to use godog features to test godod.

Choose a reason for hiding this comment

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

godod (sp)

The general pattern is the feature files have top level or "outer" steps with
that carry mini-features in docstrings and also expected outputs
also in docstrings.
The glue code for the "inner" features is separated below into "inner" and "outer" steps.

Choose a reason for hiding this comment

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

Huh??

@@ -19,3 +20,100 @@ func S(n int) string {
var TimeNowFunc = func() time.Time {
return time.Now()
}

Choose a reason for hiding this comment

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

mega-nit:

  • Consider creating a new file for your new formatter utilities - e.g., formatters.go
  • Adding a new set of tools - especially if they focus on a single, new concern - into something as abstract a utils.go is a pet peeve of mine.

Copy link
Member Author

Choose a reason for hiding this comment

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

will look

return multiFmt.FormatterFunc(suiteName, output), nil
}

func configureMultiFormatter(opt Options, output io.WriteCloser) (multiFmt ifmt.MultiFormatter, err error) {

Choose a reason for hiding this comment

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

nit: Consider dropping use of named return values (cost/benefit)?

Copy link
Member Author

Choose a reason for hiding this comment

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

agree

Copy link

@noodnik2 noodnik2 left a comment

Choose a reason for hiding this comment

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

First, thanks for the effort to counter entropy - always a good & worthy battle if the heart is in the right place, and I "feel" it is here from what I've seen. 😊

I've sprinkled in a few "first impression" (mostly nit type) comments without having really grokked the changes, which were just too many to take in at once.

Consider breaking this up into (maybe prioritized) sub-concerns? I'd be more likely to be interested & able to give you better feedback & generate more excitement for pulling downstream. 🤓

Comment on lines +37 to +48
// TestRunStarted triggers TestRunStarted for all added formatters.
func (r repeater) Close() error {
var err error
for _, f := range r {
e := f.Close()
if e != nil {
err = e
}
}
return err
}

Copy link

Choose a reason for hiding this comment

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

You forgot to update the comment. Also do we need to add test for this method?

Copy link
Member Author

Choose a reason for hiding this comment

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

Will see what the existing test strat is for this and look to improve.

@Johnlon
Copy link
Member Author

Johnlon commented Oct 31, 2024

First, thanks for the effort to counter entropy - always a good & worthy battle if the heart is in the right place, and I "feel" it is here from what I've seen. 😊

I've sprinkled in a few "first impression" (mostly nit type) comments without having really grokked the changes, which were just too many to take in at once.

Consider breaking this up into (maybe prioritized) sub-concerns? I'd be more likely to be interested & able to give you better feedback & generate more excitement for pulling downstream. 🤓

I think this PR is already the atom of improvement.
I'd prefer to get it merged and then add the fixes for the bugs that motivated this

Name string
TestSuiteInitializer func(*TestSuiteContext)
ScenarioInitializer func(*ScenarioContext)
Options *Options // TODO mutable value - is this necessary?
Copy link

@nhv96 nhv96 Oct 31, 2024

Choose a reason for hiding this comment

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

With your refactoring and move the function body to one upper level, I can only think the reason for us to keep using pointer here is to avoid incompatible with older version 🤔
Looks like they wanted to separate the concerns, test suite initialization vs runner logic to run that suite, so changes to the options configuration within the function body of runner should not reflected to the test suite.

Copy link
Member Author

Choose a reason for hiding this comment

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

yes I suspect you are right - how tolerant should we be of breaking changes.
I had undone some work to avoid breaks in this pr.

Copy link

Choose a reason for hiding this comment

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

I'd say we can tolerant to the point that user's expectations and use cases of godog library still remain the same. Except for the hook ordering bug we're going to fix, I really don't see we're going to "break" anything else.

Comment on lines +29 to 32
func NewBase(suite string, out io.WriteCloser) *Base {
return &Base{
suiteName: suite,
indent: 2,
Copy link

Choose a reason for hiding this comment

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

Regarding this approach, do you think it will force the users (who use custom fmt) to working too much on their end to adapt to our changes? I think having a Close() method is a great enhancement, but I'm considering the case that someone is having problem with hook ordering bug, and expecting the new release will fix it, but then in order to use it they need to update their custom code first...

Should we use another way such as saying "we're going to fix this, and also we're migrating the Formatter API to a new version, you don't need to update anything yet and still have new features to use"? I have the following code that seems to work on my local testing

Suggested change
func NewBase(suite string, out io.WriteCloser) *Base {
return &Base{
suiteName: suite,
indent: 2,
type baseAdapter struct {
out io.Writer
}
func (f baseAdapter) Write(p []byte) (n int, err error) {
return f.out.Write(p)
}
func (f baseAdapter) Close() error {
return nil
}
// NewBase creates a new base formatter.
func NewBase(suite string, out io.Writer) *Base {
var wc io.WriteCloser
w, ok := out.(io.WriteCloser)
if ok {
wc = w
} else {
wc = baseAdapter{out: out}
}
return &Base{
suiteName: suite,
indent: 2,
out: wc,
Lock: new(sync.Mutex),
}
}
// Base is a base formatter.
type Base struct {
suiteName string
out io.WriteCloser
indent int
Storage *storage.Storage
Lock *sync.Mutex
}

This way we can still migrate to use io.WriteCloser in our internal logic, but user don't need to know or don't need to update anything right away.
Ps: sorry my english is not that good so my reasoning is kinda long.

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.

3 participants