Correct directory diffing test and algorithm

Two steps:

1. TestDiffDirectories did not check if the expected only return value
was correct; the intention was there to do so (judging by the
comment "change in order"), but my eye for detail failed me.

2. Reversing the directory comparison in the test revealed bugs in the
comparison code -- in general, it should skip any directory that is
not a directory in the comparator.

To make this easier, the code now keeps track of the expected files it
saw. That means the check for whether an actual file has an expected
counterpart only has two cases, yes or no (rather that trying to
account for whether it's a directory and so on). If a directory was
skipped while scanning the expected files, it won't be in the actual
files anyway.

Signed-off-by: Michael Bridgen <michael@weave.works>
This commit is contained in:
Michael Bridgen 2021-10-18 14:35:45 +01:00 committed by Sunny
parent ffbc825dcd
commit 5affa3a34b
No known key found for this signature in database
GPG Key ID: 9F3D25DDFF7FA3CF
2 changed files with 25 additions and 20 deletions

View File

@ -82,16 +82,21 @@ func (d dirfile) FailedExpectation(g *WithT) {
// `foo.yaml~`). It panics if it encounters any error apart from a // `foo.yaml~`). It panics if it encounters any error apart from a
// file not found. // file not found.
func DiffDirectories(actual, expected string) (actualonly []string, expectedonly []string, different []Diff) { func DiffDirectories(actual, expected string) (actualonly []string, expectedonly []string, different []Diff) {
seen := make(map[string]struct{})
filepath.Walk(expected, func(expectedPath string, expectedInfo os.FileInfo, err error) error { filepath.Walk(expected, func(expectedPath string, expectedInfo os.FileInfo, err error) error {
if err != nil { if err != nil {
panic(err) panic(err)
} }
relPath := expectedPath[len(expected):]
seen[relPath] = struct{}{}
// ignore emacs backups // ignore emacs backups
if strings.HasSuffix(expectedPath, "~") { if strings.HasSuffix(expectedPath, "~") {
return nil return nil
} }
relPath := expectedPath[len(expected):]
actualPath := filepath.Join(actual, relPath)
// ignore dotfiles // ignore dotfiles
if strings.HasPrefix(filepath.Base(expectedPath), ".") { if strings.HasPrefix(filepath.Base(expectedPath), ".") {
if expectedInfo.IsDir() { if expectedInfo.IsDir() {
@ -100,24 +105,30 @@ func DiffDirectories(actual, expected string) (actualonly []string, expectedonly
return nil return nil
} }
actualPath := filepath.Join(actual, relPath)
actualInfo, err := os.Stat(actualPath) actualInfo, err := os.Stat(actualPath)
switch { switch {
case err == nil: case err == nil:
break break
case os.IsNotExist(err): case os.IsNotExist(err):
expectedonly = append(expectedonly, relPath) expectedonly = append(expectedonly, relPath)
if expectedInfo.IsDir() {
return filepath.SkipDir
}
return nil return nil
default: default:
panic(err) panic(err)
} }
// file exists in both places // file exists in both places
switch { switch {
case actualInfo.IsDir() && expectedInfo.IsDir(): case actualInfo.IsDir() && expectedInfo.IsDir():
return nil // i.e., keep recursing return nil // i.e., keep recursing
case actualInfo.IsDir() || expectedInfo.IsDir(): case actualInfo.IsDir() || expectedInfo.IsDir():
different = append(different, dirfile{path: relPath, abspath: actualPath, expectedRegularFile: actualInfo.IsDir()}) different = append(different, dirfile{path: relPath, abspath: actualPath, expectedRegularFile: actualInfo.IsDir()})
if expectedInfo.IsDir() {
return filepath.SkipDir
}
return nil return nil
} }
@ -151,18 +162,12 @@ func DiffDirectories(actual, expected string) (actualonly []string, expectedonly
if actualInfo.IsDir() && strings.HasPrefix(filepath.Base(actualPath), ".") { if actualInfo.IsDir() && strings.HasPrefix(filepath.Base(actualPath), ".") {
return filepath.SkipDir return filepath.SkipDir
} }
// since I've already compared any file that exists in
// expected or both, I'm only concerned with files that appear if _, ok := seen[relPath]; !ok {
// in actual but not in expected.
expectedPath := filepath.Join(expected, relPath)
_, err = os.Stat(expectedPath)
switch {
case err == nil:
break
case os.IsNotExist(err):
actualonly = append(actualonly, relPath) actualonly = append(actualonly, relPath)
default: if actualInfo.IsDir() {
panic(err) return filepath.SkipDir
}
} }
return nil return nil
}) })

View File

@ -51,13 +51,13 @@ func TestExpectMatchingDirectories(t *testing.T) {
func TestDiffDirectories(t *testing.T) { func TestDiffDirectories(t *testing.T) {
g := NewWithT(t) g := NewWithT(t)
// Finds files in expected from a/ but not in actual b/.
aonly, _, _ := DiffDirectories("testdata/diff/a", "testdata/diff/b")
g.Expect(aonly).To(Equal([]string{"/only", "/only/here.yaml", "/onlyhere.yaml"}))
// Finds files in actual a/ that weren't expected from b/. // Finds files in actual a/ that weren't expected from b/.
bonly, _, _ := DiffDirectories("testdata/diff/a", "testdata/diff/b") // change in order actualonly, _, _ := DiffDirectories("testdata/diff/a", "testdata/diff/b")
g.Expect(bonly).To(Equal([]string{"/only", "/only/here.yaml", "/onlyhere.yaml"})) g.Expect(actualonly).To(Equal([]string{"/only", "/onlyhere.yaml"}))
// Finds files in expected from a/ but not in actual b/.
_, expectedonly, _ := DiffDirectories("testdata/diff/b", "testdata/diff/a") // NB change in order
g.Expect(expectedonly).To(Equal([]string{"/only", "/onlyhere.yaml"}))
// Finds files that are different in a and b. // Finds files that are different in a and b.
_, _, diffs := DiffDirectories("testdata/diff/a", "testdata/diff/b") _, _, diffs := DiffDirectories("testdata/diff/a", "testdata/diff/b")