Test and benchmark the Single[Matcher] funcs

This commit is contained in:
Martin Angers
2021-06-13 15:02:43 -04:00
parent 0126a1fd88
commit ee8a7e0a3a
3 changed files with 70 additions and 6 deletions

View File

@@ -2,6 +2,8 @@ package goquery
import (
"testing"
"github.com/andybalholm/cascadia"
)
func BenchmarkFind(b *testing.B) {
@@ -800,3 +802,21 @@ func BenchmarkClosestNodes(b *testing.B) {
b.Fatalf("want 2, got %d", n)
}
}
func BenchmarkSingleMatcher(b *testing.B) {
doc := Doc()
multi := cascadia.MustCompile(`div`)
single := SingleMatcher(multi)
b.ResetTimer()
b.Run("multi", func(b *testing.B) {
for i := 0; i < b.N; i++ {
_ = doc.FindMatcher(multi)
}
})
b.Run("single", func(b *testing.B) {
for i := 0; i < b.N; i++ {
_ = doc.FindMatcher(single)
}
})
}

18
type.go
View File

@@ -124,9 +124,17 @@ type Matcher interface {
// Single compiles a selector string to a Matcher that stops after the first
// match is found.
//
// By default, Selection.Find and other functions that accept a selector string
// will use all matches corresponding to that selector. By using the Matcher
// returned by Single, at most the first match will be used.
// By default, Selection.Find and other functions that accept a selector
// string to find nodes will use all matches corresponding to that selector.
// By using the Matcher returned by Single, at most the first match will be
// used.
//
// Note that the single-selection property of the Matcher only applies for
// methods where the Matcher is used to select nodes, not to filter or check
// if a node matches the Matcher - in those cases, the behaviour of the
// Matcher is unchanged (e.g. FilterMatcher(Single("div")) will still result
// in a Selection with multiple "div"s if there were many "div"s in the
// Selection to begin with).
func Single(selector string) Matcher {
return singleMatcher{compileMatcher(selector)}
}
@@ -134,9 +142,7 @@ func Single(selector string) Matcher {
// SingleMatcher returns a Matcher matches the same nodes as m, but that stops
// after the first match is found.
//
// By default, Selection.FindMatcher and other functions that accept a Matcher
// will use all corresponding matches. By using the Matcher returned by
// SingleMatcher, at most the first match will be used.
// See the documentation of function Single for more details.
func SingleMatcher(m Matcher) Matcher {
if _, ok := m.(singleMatcher); ok {
// m is already a singleMatcher

View File

@@ -7,6 +7,7 @@ import (
"strings"
"testing"
"github.com/andybalholm/cascadia"
"golang.org/x/net/html"
)
@@ -208,3 +209,40 @@ func TestIssue103(t *testing.T) {
}
t.Log(text)
}
func TestSingle(t *testing.T) {
data := `
<html>
<body>
<div class="b">1</div>
<div class="a">2</div>
<div class="a">3</div>
<p class="b">4</p>
</body>
</html>
`
doc, err := NewDocumentFromReader(strings.NewReader(data))
if err != nil {
t.Fatal(err)
}
text := doc.FindMatcher(Single("div")).Text()
if text != "1" {
t.Fatalf("want %q, got %q", "1", text)
}
// Here, the Single has no effect as the selector is used to filter
// from the existing selection, not to find nodes in the document.
divs := doc.Find("div")
text = divs.FilterMatcher(Single(".a")).Text()
if text != "23" {
t.Fatalf("want %q, got %q", "23", text)
}
classA := cascadia.MustCompile(".a")
classB := cascadia.MustCompile(".b")
text = doc.FindMatcher(classB).AddMatcher(SingleMatcher(classA)).Text()
if text != "142" {
t.Fatalf("want %q, got %q", "142", text)
}
}