diff --git a/index.go b/index.go index 25c554c..6d3463d 100644 --- a/index.go +++ b/index.go @@ -32,10 +32,11 @@ func (dirent *DirEnt) fill(pathSegs []string, hunk Hunk) { next.fill(pathSegs[1:], hunk) } else { l := len(dirent.ChildrenList) - dirent.ChildrenList = append(dirent.ChildrenList, DirEnt{ + child := DirEnt{ Name: pathSegs[0], - }) - dirent.Children[pathSegs[0]] = &dirent.ChildrenList[l] + } + dirent.ChildrenList = append(dirent.ChildrenList, &child) + dirent.Children[pathSegs[0]] = &child dirent.ChildrenList[l].fill(pathSegs[1:], hunk) } } diff --git a/index_test.go b/index_test.go index a4c9c19..6c4fef8 100644 --- a/index_test.go +++ b/index_test.go @@ -1,8 +1,12 @@ -package testmark +package testmark_test import ( + "fmt" "path/filepath" + "strings" "testing" + + "github.com/warpfork/go-testmark" ) func TestIndexingDirs(t *testing.T) { @@ -10,13 +14,17 @@ func TestIndexingDirs(t *testing.T) { if err != nil { panic(err) } - doc, err := ReadFile(filepath.Join(testdata, "exampleWithDirs.md")) + doc, err := testmark.ReadFile(filepath.Join(testdata, "exampleWithDirs.md")) + if err != nil { + panic(err) + } doc.BuildDirIndex() + if len(doc.DirEnt.ChildrenList) != 2 { - t.Errorf("root dirent list should be length 2") + t.Errorf("root dirent list should be length 2 but was %d", len(doc.DirEnt.ChildrenList)) } if len(doc.DirEnt.Children) != 2 { - t.Errorf("root dirent map should be length 2") + t.Errorf("root dirent map should be length 2 but was %d", len(doc.DirEnt.Children)) } if doc.DirEnt.ChildrenList[0].Name != "one" { t.Errorf("first child of root dirent should've been 'one'") @@ -37,3 +45,91 @@ func TestIndexingDirs(t *testing.T) { t.Errorf("hunk 'really/deep/dirs/wow' looked up through dir maps should have the right content") } } + +func TestIndexingTree(t *testing.T) { + testdata, err := filepath.Abs("testdata") + if err != nil { + panic(err) + } + doc, err := testmark.ReadFile(filepath.Join(testdata, "exampleWithDirs.md")) + if err != nil { + panic(err) + } + if len(doc.DataHunks) != len(doc.HunksByName) { + t.Errorf("doc hunk list has different length than hunks-by-name: %d != %d", + len(doc.DataHunks), len(doc.HunksByName)) + } + doc.BuildDirIndex() + + for _, _hunk := range doc.DataHunks { + hunk := _hunk + t.Run("index:"+hunk.Name, func(t *testing.T) { + assertHunkReachable(t, doc, hunk) + }) + } + assertChildren(t, doc.DirEnt) +} + +func assertHunkReachable(t *testing.T, doc *testmark.Document, hunk testmark.DocHunk) { + splits := strings.Split(hunk.Name, "/") + dir := doc.DirEnt + for _, split := range splits { + if len(dir.Children) != len(dir.ChildrenList) { + t.Errorf("expected dir to have equal number of children in both data structures") + } + child, ok := dir.Children[split] + if !ok { + t.Errorf("expected dir %q to have child named %q", dir.Name, split) + } + dir = child + } + assert(t, hunk.Hunk, fmt.Sprintf("%v", *dir.Hunk)) +} + +func assertChildren(t *testing.T, dir *testmark.DirEnt) { + foundChildren := make(map[string]struct{}) + for _, _child := range dir.ChildrenList { + child := _child + t.Run(child.Name, func(t *testing.T) { + _, exists := foundChildren[child.Name] + if exists { + t.Errorf("dir %q has duplicate child: %q", dir.Name, child.Name) + } + foundChildren[child.Name] = struct{}{} + + mapChild, exists := dir.Children[child.Name] + if !exists { + t.Errorf("dir %q missing child: %q", dir.Name, child.Name) + } + if child != mapChild { + t.Errorf("child %q should have equivalent pointers: %p %p", child.Name, child, mapChild) + } + + assertChildren(t, child) + }) + } + // If the lengths are equal then the map and list should contain entries with the same names. + // We don't know if the dir entries are _actually_ equivalent but the test recurses above so it should be fine. + if len(dir.Children) != len(dir.ChildrenList) { + t.Errorf( + "expected dir to have equal number of children in both data structures"+ + "\n\t%s:\n\tlist: %v\n\tkeys: %v", + dir.Name, names(dir.ChildrenList), keys(dir.Children)) + } +} + +func keys(dirs map[string]*testmark.DirEnt) []string { + names := make([]string, 0, len(dirs)) + for k := range dirs { + names = append(names, k) + } + return names +} + +func names(dirs []*testmark.DirEnt) []string { + names := make([]string, 0, len(dirs)) + for _, d := range dirs { + names = append(names, d.Name) + } + return names +} diff --git a/read_test.go b/read_test.go index 4ce03c8..41d1c12 100644 --- a/read_test.go +++ b/read_test.go @@ -18,7 +18,9 @@ func TestRead(t *testing.T) { if err != nil { t.Fatal(err) } - + if len(doc.DataHunks) != len(doc.HunksByName) { + t.Errorf("document hunk list has different length than hunks-by-name: %d != %d", len(doc.DataHunks), len(doc.HunksByName)) + } readFixturesExample(t, doc) } diff --git a/testexec/testexec.go b/testexec/testexec.go index 1981444..8006d83 100644 --- a/testexec/testexec.go +++ b/testexec/testexec.go @@ -139,22 +139,22 @@ func (tcfg *Tester) init() { // Regen mode will only update hunks that already exist; it won't add them. // As an edge case, note that if that an exitcode hunk is absent, but a nonzero exitcode is encountered, // the test will still be failed, even though in patch regen mode most assertions are usually skipped. -func (tcfg Tester) TestSequence(t *testing.T, data testmark.DirEnt) { +func (tcfg Tester) TestSequence(t *testing.T, data *testmark.DirEnt) { t.Helper() tcfg.test(t, data, true, false, "") } -func (tcfg Tester) TestScript(t *testing.T, data testmark.DirEnt) { +func (tcfg Tester) TestScript(t *testing.T, data *testmark.DirEnt) { t.Helper() tcfg.test(t, data, false, true, "") } -func (tcfg Tester) Test(t *testing.T, data testmark.DirEnt) { +func (tcfg Tester) Test(t *testing.T, data *testmark.DirEnt) { t.Helper() tcfg.test(t, data, true, true, "") } -func (tcfg Tester) test(t *testing.T, data testmark.DirEnt, allowExec, allowScript bool, parentTmpdir string) { +func (tcfg Tester) test(t *testing.T, data *testmark.DirEnt, allowExec, allowScript bool, parentTmpdir string) { t.Helper() tcfg.init() diff --git a/testmark.go b/testmark.go index 7b87315..4113279 100644 --- a/testmark.go +++ b/testmark.go @@ -72,5 +72,5 @@ type DirEnt struct { // Children, recursively. Children map[string]*DirEnt - ChildrenList []DirEnt + ChildrenList []*DirEnt } diff --git a/testutil_test.go b/testutil_test.go index cea3512..8eddc7a 100644 --- a/testutil_test.go +++ b/testutil_test.go @@ -22,6 +22,7 @@ import ( // anything else relies on "%v". // It'll emit both the expected and actual values as strings if there's a mismatch. func assert(t *testing.T, actual interface{}, expect string) { + t.Helper() var actualStr string if s, ok := actual.(string); ok { actualStr = s