package cnm import ( "bytes" "strings" "testing" ) var ( sec11 = &SectionBlock{ContentBlock{ name: "section", args: []string{"foo/bar"}, children: []Block{ &TextBlock{ Format: "pre", Contents: TextPreContents{"qwe"}, }, }, }} sec1 = &SectionBlock{ContentBlock{ name: "section", args: []string{"bar"}, children: []Block{ sec11, &TextBlock{ Format: "pre", Contents: TextPreContents{"..."}, }, }, }} sec31 = &SectionBlock{ContentBlock{ name: "section", args: []string{"baz"}, children: []Block{ &TextBlock{ Format: "pre", Contents: TextPreContents{"zxc"}, }, }, }} sec32 = &SectionBlock{ContentBlock{ name: "section", children: []Block{ &TextBlock{ Format: "pre", Contents: TextPreContents{"zxc"}, }, }, }} sec332 = &SectionBlock{ContentBlock{ name: "section", args: []string{"baz"}, children: []Block{ &TextBlock{ Format: "pre", Contents: TextPreContents{"xyz"}, }, }, }} sec33 = &SectionBlock{ContentBlock{ name: "section", args: []string{"quux"}, children: []Block{ &TextBlock{ Format: "pre", Contents: TextPreContents{"123"}, }, sec332, }, }} sec4 = &SectionBlock{ContentBlock{ name: "section", args: []string{"foo/bar"}, children: []Block{ &TextBlock{ Format: "pre", Contents: TextPreContents{"abc"}, }, }, }} doc1 = &Document{ Title: "test title", Content: &ContentBlock{ name: "content", children: []Block{ sec1, &TextBlock{ Format: "pre", Contents: TextPreContents{"asd"}, }, &ListBlock{ContentBlock{ name: "list", children: []Block{ sec31, sec32, sec33, &TextBlock{ Format: "pre", Contents: TextPreContents{"rty"}, }, }, }}, sec4, }, }, } doc1Shallow = &Document{ Title: doc1.Title, Content: &ContentBlock{ name: "content", children: []Block{ &SectionBlock{ContentBlock{ name: "section", args: sec1.Args(), }}, doc1.Content.Children()[1], &ListBlock{ContentBlock{ name: "list", children: []Block{ &SectionBlock{ContentBlock{ name: "section", args: sec31.Args(), }}, sec32, &SectionBlock{ContentBlock{ name: "section", args: sec33.Args(), }}, doc1.Content.Children()[2].(ContainerBlock).Children()[3], }, }}, &SectionBlock{ContentBlock{ name: "section", args: sec4.Args(), }}, }, }, } sec1Shallow = &Document{Content: &ContentBlock{ name: "content", children: []Block{ &SectionBlock{ContentBlock{ name: sec1.Name(), args: sec1.Args(), children: []Block{ &SectionBlock{ContentBlock{ name: "section", args: sec11.Args(), }}, sec1.Children()[1], }, }}, }, }} ) var selectTests = []struct { sel string doc *Document res *Document }{ {"", doc1, doc1}, {"/", doc1, &Document{Content: doc1.Content}}, {"$", doc1, &Document{Content: doc1.Content}}, {"#", doc1, &Document{Content: doc1.Content}}, {"!", doc1, doc1Shallow}, {"!/", doc1, &Document{Content: doc1Shallow.Content}}, {"!$", doc1, &Document{Content: doc1Shallow.Content}}, {"!#", doc1, &Document{Content: doc1Shallow.Content}}, {"/bar", doc1, &Document{Content: &ContentBlock{ name: "content", children: []Block{ sec1, }, }}}, {"!/bar", doc1, sec1Shallow}, {"#bar", doc1, &Document{Content: &ContentBlock{ name: "content", children: []Block{sec1}, }}}, {"!#bar", doc1, sec1Shallow}, {"$1", doc1, &Document{Content: &ContentBlock{ name: "content", children: []Block{sec1}, }}}, {"!$1", doc1, sec1Shallow}, } func TestSelect(t *testing.T) { for _, v := range selectTests { t.Run(strings.Replace(v.sel, "/", "_", -1), func(t *testing.T) { res, err := v.doc.Select(v.sel) if err != nil { printTestDoc(t, v.doc) t.Fatalf("Select(%q) on %#v:\nerror: %v", v.sel, v.doc, err) } if !documentEqual(t, res, v.res) { t.Log("=============== doc") printTestDoc(t, v.doc) t.Log("=============== expect") printTestDoc(t, v.res) t.Log("=============== got") printTestDoc(t, res) t.Fatalf("Select(%q) on %#v:\nexpected: %#v\n got: %#v", v.sel, v.doc, v.res, res) } }) } } var selectBlockTests = []struct { sel string doc *Document res Block ind []int }{ {"", doc1, doc1, []int{}}, {"/", doc1, doc1.Content, []int{}}, {"/qwe", doc1, nil, nil}, {"/bar/qwe", doc1, nil, nil}, {"/bar/", doc1, nil, nil}, {"//", doc1, nil, nil}, {"/./", doc1, nil, nil}, {"/../", doc1, nil, nil}, {"/.", doc1, nil, nil}, {"/..", doc1, nil, nil}, {"/foo%2fbar", doc1, sec4, []int{3}}, {"/bar/foo%2fbar", doc1, sec11, []int{0, 0}}, {"/baz", doc1, sec31, []int{2, 0}}, {"/quux", doc1, sec33, []int{2, 2}}, {"/quux/baz", doc1, sec332, []int{2, 2, 1}}, {"/bar/baz", doc1, nil, nil}, {"$", doc1, doc1.Content, []int{}}, {"$0", doc1, nil, nil}, {"$1", doc1, sec1, []int{0}}, {"$1.1", doc1, sec11, []int{0, 0}}, {"$2", doc1, sec31, []int{2, 0}}, {"$3", doc1, sec33, []int{2, 2}}, {"$4", doc1, sec4, []int{3}}, {"$5", doc1, nil, nil}, {"$2.1", doc1, nil, nil}, {"$3.1", doc1, sec332, []int{2, 2, 1}}, {"$3.0", doc1, nil, nil}, {"$3.2", doc1, nil, nil}, {"#", doc1, doc1.Content, []int{}}, {"#qwe", doc1, nil, nil}, {"#bar", doc1, sec1, []int{0}}, {"#foo%2fbar", doc1, sec11, []int{0, 0}}, {"#baz", doc1, sec31, []int{2, 0}}, {"#quux", doc1, sec33, []int{2, 2}}, } func TestSelectBlock(t *testing.T) { for _, v := range selectBlockTests { t.Run(strings.Replace(v.sel, "/", "_", -1), func(t *testing.T) { ind, res, err := v.doc.SelectBlock(v.sel) if err != nil { printTestDoc(t, v.doc) t.Fatalf("SelectBlock(%q) on %#v:\nerror: %v", v.sel, v.doc, err) } if !indicesEqual(t, ind, v.ind) { printTestDoc(t, v.doc) t.Fatalf("SelectBlock(%q) on %#v:\nexpected indices %#v, got %#v", v.sel, v.doc, v.ind, ind) } if !blockEqual(t, res, v.res) { printTestDoc(t, v.doc) t.Fatalf("SelectBlock(%q) on %#v:\nexpected: %#v\n got: %#v", v.sel, v.doc, v.res, res) } if doc, ok := res.(*Document); ok { res = doc.Content } b := v.doc.SelectIndex(ind...) if !blockEqual(t, b, res) { printTestDoc(t, v.doc) t.Fatalf("SelectIndex(%#v) (for %q):\nexpected: %#v\n got: %#v", ind, v.sel, res, b) } }) } } func indicesEqual(t *testing.T, a, b []int) bool { if a == nil && b == nil { return true } if a == nil || b == nil { t.Log("indice slice nilness differs") return false } if len(a) != len(b) { t.Log("indice slice lengths differ") return false } for i := range a { if a[i] != b[i] { t.Log("indice slice element differs") return false } } return true } func printTestDoc(t *testing.T, doc *Document) { if doc == nil { t.Log(doc) return } var buf bytes.Buffer doc.Write(&buf) t.Log("\n" + buf.String()) }