Skip to content
This repository has been archived by the owner on Sep 9, 2020. It is now read-only.

Table based tests for importers #897

Merged
merged 12 commits into from
Aug 9, 2017
Merged

Table based tests for importers #897

merged 12 commits into from
Aug 9, 2017

Conversation

sebdah
Copy link
Contributor

@sebdah sebdah commented Jul 25, 2017

What does this do / why do we need it?

The current tests for the importers (like godep or glide) have lots of similarities. Using table based tests would reduce code and make it easier to add more test cases.

What should your reviewer look out for in this PR?

I'm adding gps.ProjectRootPtr() as well as gps.RevisionPtr() functions. They are helpers to make it easy to create pointers to ProjectRoot / Revision easily. Since they are only used for tests at the moment, it may be better that I just create them in my _test.go file for now. Suggestions welcome.

Do you need help or clarification on anything?

I'm not entirely satisfied with the amount of lines that ended up in the for loop for the test cases. Ideally I'd use something like testify/assert to reduce the line count, increase readability and make test failure output more descriptive. But I'm not sure that importing dependencies into the project is desirable. Please advice.

To do

  • Implement table based tests for godep
  • Implement table based tests for glide
  • Possibly move helpers to their own file. Currently intPtr() lives in godep_importer_test.go, which is not desirable

Which issue(s) does this PR fix?

fixes #824

@sebdah sebdah force-pushed the issue/824 branch 3 times, most recently from 419855f to 8f12cdb Compare July 26, 2017 01:02
@sebdah sebdah changed the title [WIP] Table based tests for importers Table based tests for importers Jul 26, 2017
Copy link
Member

@sdboyer sdboyer left a comment

Choose a reason for hiding this comment

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

i've not reviewed in detail, but this seems fundamentally 💯 sane. however, we definitely want to use subtests for this! if you've not used them before, just grep for t.Run( in the project - we use them all over the place, plenty of examples to go from.

defer sm.Release()

for name, testCase := range testCases {
t.Logf("Running test case: %s", name)
Copy link
Member

Choose a reason for hiding this comment

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

Ideal situation for using subtests, here 😄

@sebdah
Copy link
Contributor Author

sebdah commented Jul 26, 2017 via email

@sebdah
Copy link
Contributor Author

sebdah commented Jul 27, 2017

I've make the changes this morning. But wondering if something is up with CI for the golang/dep builds. The failures seems unrelated to my fix.

Copy link
Member

@sdboyer sdboyer left a comment

Choose a reason for hiding this comment

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

gettin pretty close, just a few issues.

projectRoot gps.ProjectRoot
sourceRepo string
notExpectedProjectRoot *gps.ProjectRoot
expectedConstraint string
Copy link
Member

Choose a reason for hiding this comment

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

nit: in Go-land, i've learned we apparently prefer "want" to "expect".

expectConvertErr bool
matchPairedVersion bool
projectRoot gps.ProjectRoot
notExpectedProjectRoot *gps.ProjectRoot
Copy link
Member

Choose a reason for hiding this comment

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

why does this need to be a pointer?

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'm having it as a pointer to represent that it's not set. So that we can distinguish between:

  • A project root "github.com/a/b/c"
  • An empty project root ""
  • A test case that does not have a notExpectedProjectRoot test case

In other words: If it's nil it won't be part of the test case, while making it possible to have any other value when we use it for testing.

Copy link
Member

Choose a reason for hiding this comment

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

hmm. the basic argument makes sense, though it still seems like a bit of a code smell. i can't come up with a specific objection, though, so i'm OK to move ahead with it.

@@ -40,6 +40,11 @@ import (
// the type system when a path-ish string must have particular semantics.
type ProjectRoot string

// ProjectRootPtr takes a ProjectRoot value and returns a pointer.
func ProjectRootPtr(p ProjectRoot) *ProjectRoot {
Copy link
Member

Choose a reason for hiding this comment

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

if we must have this (and i'm not clear on why we do), can it at least not be exported?

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 think we should remove this and make it an unexported part of the tests. Since it's not used outside the tests I think this is not providing value to any part of the implementation.

I'll move it to the test files.

@@ -198,6 +198,11 @@ func (r Revision) identical(c Constraint) bool {
return r == r2
}

// RevisionPtr takes a revision value and return it's pointer.
func RevisionPtr(r Revision) *Revision {
Copy link
Member

Choose a reason for hiding this comment

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

same question as with ProjectRootPtr()

@sebdah
Copy link
Contributor Author

sebdah commented Jul 29, 2017

Thanks, I have updated the tests according to the review feedback!

@@ -53,7 +236,7 @@ func TestGodepConfig_Import(t *testing.T) {
t.Fatal("Expected the lock to be generated")
}

goldenFile := "godep/expected_import_output.txt"
goldenFile := "godep/want_import_output.txt"
got := verboseOutput.String()
Copy link
Collaborator

Choose a reason for hiding this comment

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

Filename change 😬 seems to be causing the test to fail. No such file exists. I think it's fine to keep it expected_import_output.txt.

@@ -395,3 +338,13 @@ func equalImports(a, b []godepPackage) bool {

return true
}

// projectRootPtr takes a ProjectRoot value and returns a pointer.
func projectRootPtr(p gps.ProjectRoot) *gps.ProjectRoot {
Copy link
Collaborator

@carolynvs carolynvs Jul 31, 2017

Choose a reason for hiding this comment

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

Here's how you can deal with these string types (gps.Revision and gps.ProjectRoot) without introducing these helper functions:

  1. Change the want/got in the testcase matrix from pointers to the struct type, e.g. *gps.Revision to gps.Revision.
  2. Update the assignments to not use projectRootPtr/revisionPtr.
  3. Update the asserts to compare the want testcase value against the empty string, instead of nil, e.g.
    if testCase.notWantedProjectRoot != "" {
    

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 do that, if you'd like me to. The reason I have it this way is to be able to represent the difference between e.g. an empty notWantedProjectRoot ("") and a test case without a project root (nil). Otherwise we could not test for the case where notWantedProjectRoot is empty.

Let me know which approach you'd like and I'll adjust accordingly.

Copy link
Member

@sdboyer sdboyer Aug 7, 2017

Choose a reason for hiding this comment

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

i'm on the fence - ordinarily, i'd say that we've tried to architect around situations where the zero could have multiple interpretations. but the importers are tricky, because we're converting other systems' semantics into our own, so it's harder to maintain invariants like that. at the same time, it is just a single, confined test pattern, so i don't know that it really matters too much.

either way, it's your call, @carolynvs 😄

Copy link
Collaborator

Choose a reason for hiding this comment

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

I've laid out in the comments an alternative way to simplify the test cases and remove these helper functions. Let me know if they make sense.

I don't mean to hate on them, but if we can reorganize the tests to not need them, that would make me happy. 😀

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Alright! I'll adjust the tests according to your comments @carolynvs. Will to that within a week, I'm currently on vacation. Thanks for the feedback!

Copy link
Collaborator

@carolynvs carolynvs left a comment

Choose a reason for hiding this comment

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

Sorry this took me so long to look at!

p.Ident().ProjectRoot)
}

if p.Ident().Source != testCase.sourceRepo {
Copy link
Collaborator

Choose a reason for hiding this comment

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

nit: Can we rename this to wantSourceRepo? Just to be consistent with the other fields in the test case.

},
},
projectRoot: gps.ProjectRoot("github.com/sdboyer/deptest"),
notWantedProjectRoot: projectRootPtr(gps.ProjectRoot("github.com/sdboyer/deptest/foo")),
Copy link
Collaborator

Choose a reason for hiding this comment

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

Between the check for an Constraint entry for projectRoot and the check on wantLockCount, I would be quite happy to remove notWantedProjectRoot entirely. It will effectively test what the importer cared about: consolidating multiple subpackages into a single entry for the root package.

Then we can remove projectRootPtr as well. Sound good?

t.Fatalf("Expected locked version to be '%s', got %s", testCase.wantVersion, ver)
}

if testCase.wantRevision != nil {
Copy link
Collaborator

Choose a reason for hiding this comment

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

Since we only check wantRevision when matchPairedVersion is set, how about simplifying it to a Revision struct, not pointer. Then revisionPtr can be removed as well.

@@ -395,3 +338,13 @@ func equalImports(a, b []godepPackage) bool {

return true
}

// projectRootPtr takes a ProjectRoot value and returns a pointer.
func projectRootPtr(p gps.ProjectRoot) *gps.ProjectRoot {
Copy link
Collaborator

Choose a reason for hiding this comment

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

I've laid out in the comments an alternative way to simplify the test cases and remove these helper functions. Let me know if they make sense.

I don't mean to hate on them, but if we can reorganize the tests to not need them, that would make me happy. 😀

Copy link
Collaborator

@carolynvs carolynvs left a comment

Choose a reason for hiding this comment

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

Wahoo! Thank you for applying some cleansing fire to our tests. 🔥

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

Successfully merging this pull request may close these issues.

Add table tests for importers
5 participants