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

Correctly parse nested go tests #16817

Merged

Conversation

smarterclayton
Copy link
Contributor

Use a stateful parser approach to process each section of the test
output independently. Correctly handles nested tests and nested output
and puts any stdout/err into the SystemOut field of the generated test
result.

Note: this breaks --suite=nested, but was not able to find any users.

@liggitt @stevekuznetsov tests are updated but want to soak this a bit

@openshift-merge-robot openshift-merge-robot added the approved Indicates a PR has been approved by an approver from all required OWNERS files. label Oct 12, 2017
@openshift-ci-robot openshift-ci-robot added the size/XXL Denotes a PR that changes 1000+ lines, ignoring generated files. label Oct 12, 2017
@liggitt liggitt assigned stevekuznetsov and unassigned liggitt and adelton Oct 12, 2017
@smarterclayton smarterclayton force-pushed the parse_test_data branch 5 times, most recently from bcb4e0e to af44e17 Compare October 13, 2017 16:39
@smarterclayton
Copy link
Contributor Author

/test integration
/test unit

@smarterclayton
Copy link
Contributor Author

Where's a flake when you need one.

@smarterclayton
Copy link
Contributor Author

/test integration
/test unit

@smarterclayton
Copy link
Contributor Author

/test all

@smarterclayton
Copy link
Contributor Author

smarterclayton commented Oct 14, 2017 via email

1 similar comment
@smarterclayton
Copy link
Contributor Author

/test integration

@smarterclayton
Copy link
Contributor Author

Ready for review. Biggest change is that integration runner now only returns the last 20k of any successful job. I also ended up doing a bit of a hack to split the output since there's no perfect way to nest the results for consumption by the calling go test.

@smarterclayton
Copy link
Contributor Author

/test integration

Copy link
Contributor

@enj enj left a comment

Choose a reason for hiding this comment

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

I have no idea what most of this does 😄

}
return err
}

if testing.Verbose() {
t.Log(string(out))
if len(out) < 20000 {
Copy link
Contributor

Choose a reason for hiding this comment

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

I thought you wanted the last 20k lines - this seems to ignore anything longer than 20k.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I can change but last is only slightly better

Copy link
Contributor

@stevekuznetsov stevekuznetsov left a comment

Choose a reason for hiding this comment

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

Can we please factor this to be a net-new parser that doesn't support nested output?

}
return nil
}

func transformOutput(out string) string {
Copy link
Contributor

Choose a reason for hiding this comment

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

Is there a test for this? It's a little dense without test or explanatory comment

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Renamed and documented.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

It's tested in 17.txt

// The first submatch of this regex is the result of the test (PASS, FAIL, or SKIP)
// The second submatch of this regex is the name of the test
// The third submatch of this regex is the time taken in seconds for the test to finish
var testResultPattern = regexp.MustCompile(`^(\s*)--- (PASS|FAIL|SKIP):\s+([^\s]+)\s+\((\d+\.\d+)(s| seconds)\)$`)
Copy link
Contributor

Choose a reason for hiding this comment

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

Comment isn't valid anymore, since the first sub-match is the leading whitespace?

testStartPattern *regexp.Regexp
testResultPattern *regexp.Regexp
}
var testOutputPattern = regexp.MustCompile(`^(\s*)(.*)$`)

// MarksBeginning determines if the line marks the beginning of a test case
Copy link
Contributor

Choose a reason for hiding this comment

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

Godoc drift?

return matches[3] + "s", true
func ExtractOutput(line string) (string, int, bool) {
if matches := testOutputPattern.FindStringSubmatch(line); len(matches) > 1 {
return matches[2], len(matches[1]) / 4, true
Copy link
Contributor

Choose a reason for hiding this comment

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

Not clear what the int returned here is ... or why we divide it by four

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Will clarify but it's go indentation

func (p *testDataParser) ExtractResult(line string) (api.TestResult, bool) {
if matches := p.testResultPattern.FindStringSubmatch(line); len(matches) > 1 && len(matches[1]) > 0 {
switch matches[1] {
func ExtractResult(line string) (r api.TestResult, name string, depth int, duration string, ok bool) {
Copy link
Contributor

Choose a reason for hiding this comment

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

An internal type to bind this data together may be less unwieldy

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Not worth it

tests[name] = test
}
orderedTests = append(orderedTests, name)
currentTestName = []string{name}
Copy link
Contributor

Choose a reason for hiding this comment

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

Why is this a slice?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Stack

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Renamed

test.SkipMessage = &api.SkipMessage{}
}
if err := test.SetDuration(duration); err != nil {
return nil, fmt.Errorf("unexpected duration on line %d: %s", count, duration)
Copy link
Contributor

Choose a reason for hiding this comment

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

isnt this a parser failure, not unexpected duration?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Not sure what you mean - the duration is wrog

Copy link
Contributor

Choose a reason for hiding this comment

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

It's not unexpected that we found the duration, it just doesn't parse

name := currentTestName[len(currentTestName)-1]
test := tests[name]
switch {
case test.FailureOutput != nil, test.SkipMessage != nil:
Copy link
Contributor

Choose a reason for hiding this comment

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

if

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Switch is idiomatic for more than one condition

Copy link
Contributor

Choose a reason for hiding this comment

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

There's only one?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

There are two conditions

if err := test.SetDuration(duration); err != nil {
return nil, fmt.Errorf("unexpected duration on line %d: %s", count, duration)
}
switch {
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 hide this behind a stack library so it's easier to read?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I don't think it will help.

Copy link
Contributor

Choose a reason for hiding this comment

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

I mean, it took me a million and a half years to figure it out ...

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I renamed it.

@@ -6,6 +6,8 @@ import (
"reflect"
"testing"

"k8s.io/apimachinery/pkg/util/diff"
Copy link
Contributor

Choose a reason for hiding this comment

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

Please let's not do this

Copy link
Contributor Author

Choose a reason for hiding this comment

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

diff.ObjectReflectDiff is the most useful piece of code I have ever written.

Use a stateful parser approach to process each section of the test
output independently. Correctly handles nested tests and nested output
and puts any stdout/err into the SystemOut field of the generated test
result.

Note: this breaks --suite=nested, but was not able to find any users.
Allows junitreport to parse output correctly for nested integration
tests.
@smarterclayton
Copy link
Contributor Author

Updated

@smarterclayton
Copy link
Contributor Author

/retest

Flakes, unit verified that test output is correct.

@smarterclayton
Copy link
Contributor Author

/retest

1 similar comment
@smarterclayton
Copy link
Contributor Author

/retest

@smarterclayton smarterclayton added kind/bug Categorizes issue or PR as related to a bug. lgtm Indicates that a PR is ready to be merged. labels Oct 23, 2017
@stevekuznetsov
Copy link
Contributor

/lgtm

@openshift-merge-robot
Copy link
Contributor

[APPROVALNOTIFIER] This PR is APPROVED

This pull-request has been approved by: smarterclayton, stevekuznetsov

The full list of commands accepted by this bot can be found here.

Needs approval from an approver in each of these OWNERS Files:

You can indicate your approval by writing /approve in a comment
You can cancel your approval by writing /approve cancel in a comment

@openshift-merge-robot
Copy link
Contributor

Automatic merge from submit-queue (batch tested with PRs 16068, 16817).

@openshift-merge-robot openshift-merge-robot merged commit 7884876 into openshift:master Oct 23, 2017
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
approved Indicates a PR has been approved by an approver from all required OWNERS files. kind/bug Categorizes issue or PR as related to a bug. lgtm Indicates that a PR is ready to be merged. size/XXL Denotes a PR that changes 1000+ lines, ignoring generated files.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

7 participants