Compare commits

..
57 Commits
Author SHA1 Message Date
Martin AngersandGitHub 09540e5659 Merge pull request #234 from PuerkitoBio/wip-slice-end
Add ToEnd constant to use as special end index in Slice, closes #232.
2018-01-28 14:56:50 -05:00
Martin Angers 46f28d87ad document addition of ToEnd 2018-01-28 14:43:53 -05:00
Martin Angers 520f19d599 add ToEnd marker to Slice until the end of the Selection 2018-01-28 14:41:06 -05:00
Martin AngersandGitHub 2324bda66f Merge pull request #231 from Strum355/patch-1
Removed unnecessary conditional check
2018-01-18 09:26:56 -05:00
Noah Santschi-CooneyandGitHub 3855699cc0 Removed unnecessary if
IsMatcher returns false if len(s.Nodes) !> 0, so Is was double checking if len(s.Nodes) > 0
2018-01-17 13:49:09 +00:00
Martin Angers a41cf096e6 update changelog 2018-01-11 08:54:59 -05:00
Martin Angers 7d9aa5c1e6 misc/git: remove excessively verbose lint tools 2018-01-11 08:49:46 -05:00
Martin Angers c56812c109 fix godoc comment to comply with golint 2018-01-11 08:48:49 -05:00
Martin AngersandGitHub cab5e2c9d6 Merge pull request #229 from davidjwilkins/feature-226
Fixes #226 - Added the AddBack, AddBackFiltered, and AddBackMatcher methods.
2018-01-11 08:45:45 -05:00
David Wilkins 802bece6d0 Move deprecated notice to end of comment 2018-01-10 21:36:54 -08:00
David Wilkins 499bd47957 Fixes #226 - Added the AddBack, AddBackFiltered, and AddBackMatcher methods. Changed AndSelf to simply be an alias for AddBack, and added a deprecation notice to AddBack (as with https://api.jquery.com/andself/) 2018-01-09 20:34:18 -08:00
Martin AngersandGitHub bc4e06eb07 Merge pull request #221 from y-yagi/patch-1
Add go1.9 to travis
2017-12-06 07:16:06 -05:00
Yuji YaginumaandGitHub d8ab9abbf4 Add go1.9 to travis 2017-12-06 18:46:13 +09:00
Martin AngersandGitHub 1a71cc719d Merge pull request #215 from kataras/patch-1
README.md: Add colly - Scraping Framework to the Related Projects
2017-10-07 10:12:28 -04:00
Gerasimos (Makis) MaropoulosandGitHub 4b3a6d701e README.md: Add colly - Scraping Framework to the Related Projects 2017-10-07 07:57:40 +03:00
Martin AngersandGitHub 63e7f870c2 Merge pull request #211 from 38elements/fix-typo
Fix typo
2017-09-29 06:56:09 -04:00
38elementsandGitHub 109d3a6a56 Fix typo 2017-09-29 18:51:07 +09:00
Martin Angers ce645ea5e6 use svg travis badge, master branch 2017-08-16 09:38:31 -04:00
Martin Angers 8806ada2a4 update README with "related projects" section instead of just "downstream" 2017-06-23 12:21:02 -04:00
Martin AngersandGitHub 2dc93891ab Merge pull request #183 from andrewstuart/master
Add goquery marshaling library as a downstream project in Readme
2017-05-20 15:45:20 -04:00
Andrew Stuart 4a7586a83f Update readme with some downstream projects 2017-05-15 16:49:20 -07:00
Martin Angers ed7d758e9a add 1.8, test on latest patches 2017-03-24 09:54:48 -04:00
Martin AngersandGitHub c641b87978 Merge pull request #162 from rohanpai/master
Added Sourcegraph Badge
2017-03-08 14:48:52 -05:00
Rohan PaiandGitHub 28076b7c97 Added Sourcegraph Badge 2017-03-08 11:27:24 -08:00
Martin Angers e1271ee34c Merge branch 'glebtv-setHtml' 2017-02-12 11:36:42 -05:00
Martin Angers f5c5de8530 update readme for new release 2017-02-12 11:36:10 -05:00
Martin Angers 46c32cef0e unexport SetNodes, add SetHtml tests 2017-02-12 11:34:10 -05:00
Gleb Tv 755fd592bb implement SetHtml and SetText, fixes #123 2017-02-08 16:27:36 +03:00
Martin AngersandGitHub 3cb3b86568 Update README.md 2016-12-29 22:41:54 -05:00
Martin AngersandGitHub 2ee2e49ac5 Merge pull request #142 from radovskyb/master
Optimizes the *Selection.Text method and reduces allocations.
2016-12-29 22:37:29 -05:00
Benjamin Radovsky c0b805c7dc greatly optimize the *Selection.Text method and highly reduce it's amount of allocations per op. 2016-12-27 17:37:31 +11:00
Martin Angers 152b1a2c8f remove wiki section on big docs, cascadia is now almost as fast as workarounds 2016-08-31 12:22:11 -04:00
Martin Angers 9043fe2599 add tip for large documents 2016-08-30 20:10:13 -04:00
Martin Angers 56a198aba8 add go1.7 to travis 2016-08-28 21:21:01 -04:00
Martin Angers 5dccaa115b optimize winnowNodes and ClosestNodes in the same way 2016-08-28 11:55:36 -04:00
Martin Angers 26f4f54c91 simplify string search expressions, remove unused code 2016-08-28 11:09:29 -04:00
Martin Angers e89fe653fd update README for v1.0.1 2016-08-28 11:06:06 -04:00
Martin Angers ab2bb46123 update pre-commit hook with other static checks 2016-08-28 11:04:26 -04:00
Martin Angers b91267330e add tests and benchmark for appendWithoutDuplicates optimization 2016-08-28 11:03:18 -04:00
Martin Angers d85ad7e2ed remove var declaration 2016-08-27 22:44:10 -04:00
Martin Angers 6841d28c6f comment and explain the appendWithoutDuplicates logic 2016-08-27 22:29:53 -04:00
Martin Angers 7bd630023a reuse the deduplication set map in mapNodes 2016-08-27 22:25:02 -04:00
Martin Angers c7533ba696 maintain a set of existing target nodes to deduplicate
The old implementation used nested loops to check if one of the
new nodes was already in the existing nodes. This commit
maintains a map[*html.Node]bool so that a single loop over
the new elements is required. This is slower for small
slices though, and allocates more. The next step is for
appendWithoutDuplicates to accept a map as argument so
that the set is not recreated on each call from mapNodes.
2016-08-27 18:55:21 -04:00
Martin Angers ad8b3d8222 remove logging from benchmarks, validate expected result instead 2016-08-27 18:37:55 -04:00
Martin Angers 7f4291566c tag v1.0.0, closes #124 2016-07-27 21:43:13 -04:00
Martin Angers f0d75731e0 fix example, closes #120 2016-06-29 08:59:28 -04:00
Martin AngersandGitHub 77cbaef46a Merge pull request #115 from PuerkitoBio/i114
Treat invalid selector strings as a Matcher that doesn't match anything
2016-06-15 09:48:05 -04:00
Martin Angers 2441483128 update doc 2016-06-15 09:40:19 -04:00
Martin Angers 2ab590df05 use a failing matcher if selector fails to compile, adjust tests 2016-06-15 09:27:17 -04:00
Martin Angers 9cc531274b implement tests for selection methods with invalid selector strings 2016-06-15 08:42:27 -04:00
Martin Angers 2cd8b4e49a test all go versions since 1.1 2016-06-15 07:50:27 -04:00
Martin Angers aeecb06b1e doc: better explain the arguments to the function passed to Each and Map, closes #107 2016-05-26 08:12:20 -04:00
Martin Angers 2e29ea41f0 add a test for issue #103 2016-04-08 07:41:18 -04:00
Martin Angers 5cc721859e edit example to follow Go conventions 2016-04-08 07:39:02 -04:00
Martin Angers 4126223a86 exclude testdata/ files from github language stats 2016-02-16 20:35:17 -05:00
Martin Angers 81e6517758 doc: fix list for godoc 2016-02-02 12:40:27 -05:00
Martin Angers bb16855614 Merge pull request #102 from PuerkitoBio/helper-funcs
add NodeName and OuterHtml utility functions, closes #96 and #101 .
2016-02-02 12:37:00 -05:00
39 changed files with 1206 additions and 248 deletions
+1
View File
@@ -0,0 +1 @@
testdata/* linguist-vendored
+10 -1
View File
@@ -1,7 +1,16 @@
# editor temporary files
*.sublime-*
.DS_Store
*.swp
#*.*#
tags
goquery.test
# direnv config
.env*
# test binaries
*.test
# coverage and profilte outputs
*.out
+9 -3
View File
@@ -1,7 +1,13 @@
language: go
go:
- 1.1
- 1.2
- 1.3
- 1.1.x
- 1.2.x
- 1.3.x
- 1.4.x
- 1.5.x
- 1.6.x
- 1.7.x
- 1.8.x
- 1.9.x
- tip
+1 -1
View File
@@ -1,4 +1,4 @@
Copyright (c) 2012-2014, Martin Angers & Contributors
Copyright (c) 2012-2016, Martin Angers & Contributors
All rights reserved.
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
+39 -9
View File
@@ -1,8 +1,6 @@
# goquery - a little like that j-thing, only in Go
[![build status](https://secure.travis-ci.org/PuerkitoBio/goquery.svg?branch=master)](http://travis-ci.org/PuerkitoBio/goquery) [![GoDoc](https://godoc.org/github.com/PuerkitoBio/goquery?status.png)](http://godoc.org/github.com/PuerkitoBio/goquery) [![Sourcegraph Badge](https://sourcegraph.com/github.com/PuerkitoBio/goquery/-/badge.svg)](https://sourcegraph.com/github.com/PuerkitoBio/goquery?badge)
[![build status](https://secure.travis-ci.org/PuerkitoBio/goquery.png)](http://travis-ci.org/PuerkitoBio/goquery)
[![GoDoc](https://godoc.org/github.com/PuerkitoBio/goquery?status.png)](http://godoc.org/github.com/PuerkitoBio/goquery)
goquery brings a syntax and a set of features similar to [jQuery][] to the [Go language][go]. It is based on Go's [net/html package][html] and the CSS Selector library [cascadia][]. Since the net/html parser returns nodes, and not a full-featured DOM tree, jQuery's stateful manipulation functions (like height(), css(), detach()) have been left off.
@@ -10,6 +8,15 @@ Also, because the net/html parser requires UTF-8 encoding, so does goquery: it i
Syntax-wise, it is as close as possible to jQuery, with the same function names when possible, and that warm and fuzzy chainable interface. jQuery being the ultra-popular library that it is, I felt that writing a similar HTML-manipulating library was better to follow its API than to start anew (in the same spirit as Go's `fmt` package), even though some of its methods are less than intuitive (looking at you, [index()][index]...).
## Table of Contents
* [Installation](#installation)
* [Changelog](#changelog)
* [API](#api)
* [Examples](#examples)
* [Related Projects](#related-projects)
* [License](#license)
## Installation
Please note that because of the net/html dependency, goquery requires Go1.1+.
@@ -17,7 +24,7 @@ Please note that because of the net/html dependency, goquery requires Go1.1+.
$ go get github.com/PuerkitoBio/goquery
(optional) To run unit tests:
$ cd $GOPATH/src/github.com/PuerkitoBio/goquery
$ go test
@@ -30,6 +37,13 @@ Please note that because of the net/html dependency, goquery requires Go1.1+.
**Note that goquery's API is now stable, and will not break.**
* **2018-01-28 (v1.3.0)** : Add `ToEnd` constant to `Slice` until the end of the selection (thanks to @davidjwilkins for raising the issue).
* **2018-01-11 (v1.2.0)** : Add `AddBack*` and deprecate `AndSelf` (thanks to @davidjwilkins).
* **2017-02-12 (v1.1.0)** : Add `SetHtml` and `SetText` (thanks to @glebtv).
* **2016-12-29 (v1.0.2)** : Optimize allocations for `Selection.Text` (thanks to @radovskyb).
* **2016-08-28 (v1.0.1)** : Optimize performance for large documents.
* **2016-07-27 (v1.0.0)** : Tag version 1.0.0.
* **2016-06-15** : Invalid selector strings internally compile to a `Matcher` implementation that never matches any node (instead of a panic). So for example, `doc.Find("~")` returns an empty `*Selection` object.
* **2016-02-02** : Add `NodeName` utility function similar to the DOM's `nodeName` property. It returns the tag name of the first element in a selection, and other relevant values of non-element nodes (see godoc for details). Add `OuterHtml` utility function similar to the DOM's `outerHTML` property (named `OuterHtml` in small caps for consistency with the existing `Html` method on the `Selection`).
* **2015-04-20** : Add `AttrOr` helper method to return the attribute's value or a default value if absent. Thanks to [piotrkowalczuk][piotr].
* **2015-02-04** : Add more manipulation functions - Prepend* - thanks again to [Andrew Stone][thatguystone].
@@ -61,13 +75,18 @@ Utility functions that are not in jQuery but are useful in Go are implemented as
The complete [godoc reference documentation can be found here][doc].
Please note that Cascadia's selectors do not necessarily match all supported selectors of jQuery (Sizzle). See the [cascadia project][cascadia] for details.
Please note that Cascadia's selectors do not necessarily match all supported selectors of jQuery (Sizzle). See the [cascadia project][cascadia] for details. Invalid selector strings compile to a `Matcher` that fails to match any node. Behaviour of the various functions that take a selector string as argument follows from that fact, e.g. (where `~` is an invalid selector string):
* `Find("~")` returns an empty selection because the selector string doesn't match anything.
* `Add("~")` returns a new selection that holds the same nodes as the original selection, because it didn't add any node (selector string didn't match anything).
* `ParentsFiltered("~")` returns an empty selection because the selector string doesn't match anything.
* `ParentsUntil("~")` returns all parents of the selection because the selector string didn't match any element to stop before the top element.
## Examples
See some tips and tricks in the [wiki][].
Taken (and adapted as if executed from outside the goquery package) from example_test.go:
Adapted from example_test.go:
```Go
package main
@@ -80,13 +99,15 @@ import (
)
func ExampleScrape() {
doc, err := goquery.NewDocument("http://metalsucks.net")
doc, err := goquery.NewDocument("http://metalsucks.net")
if err != nil {
log.Fatal(err)
}
doc.Find(".reviews-wrap article .review-rhs").Each(func(i int, s *goquery.Selection) {
band := s.Find("h3").Text()
// Find the review items
doc.Find(".sidebar-reviews article .content-block").Each(func(i int, s *goquery.Selection) {
// For each item found, get the band and title
band := s.Find("a").Text()
title := s.Find("i").Text()
fmt.Printf("Review %d: %s - %s\n", i, band, title)
})
@@ -97,6 +118,13 @@ func main() {
}
```
## Related Projects
- [Goq][goq], an HTML deserialization and scraping library based on goquery and struct tags.
- [andybalholm/cascadia][cascadia], the CSS selector library used by goquery.
- [suntong/cascadia][cascadiacli], a command-line interface to the cascadia CSS selector library, useful to test selectors.
- [asciimoo/colly](https://github.com/asciimoo/colly), a lightning fast and elegant Scraping Framework
## License
The [BSD 3-Clause license][bsd], the same as the [Go language][golic]. Cascadia's license is [here][caslic].
@@ -104,6 +132,7 @@ The [BSD 3-Clause license][bsd], the same as the [Go language][golic]. Cascadia'
[jquery]: http://jquery.com/
[go]: http://golang.org/
[cascadia]: https://github.com/andybalholm/cascadia
[cascadiacli]: https://github.com/suntong/cascadia
[bsd]: http://opensource.org/licenses/BSD-3-Clause
[golic]: http://golang.org/LICENSE
[caslic]: https://github.com/andybalholm/cascadia/blob/master/LICENSE
@@ -114,3 +143,4 @@ The [BSD 3-Clause license][bsd], the same as the [Go language][golic]. Cascadia'
[wiki]: https://github.com/PuerkitoBio/goquery/wiki/Tips-and-tricks
[thatguystone]: https://github.com/thatguystone
[piotr]: https://github.com/piotrkowalczuk
[goq]: https://github.com/andrewstuart/goq
+23 -2
View File
@@ -4,6 +4,16 @@ import (
"golang.org/x/net/html"
)
const (
maxUint = ^uint(0)
maxInt = int(maxUint >> 1)
// ToEnd is a special index value that can be used as end index in a call
// to Slice so that all elements are selected until the end of the Selection.
// It is equivalent to passing (*Selection).Length().
ToEnd = maxInt
)
// First reduces the set of matched elements to the first in the set.
// It returns a new Selection object, and an empty Selection object if the
// the selection is empty.
@@ -35,12 +45,23 @@ func (s *Selection) Eq(index int) *Selection {
}
// Slice reduces the set of matched elements to a subset specified by a range
// of indices.
// of indices. The start index is 0-based and indicates the index of the first
// element to select. The end index is 0-based and indicates the index at which
// the elements stop being selected (the end index is not selected).
//
// The indices may be negative, in which case they represent an offset from the
// end of the selection.
//
// The special value ToEnd may be specified as end index, in which case all elements
// until the end are selected. This works both for a positive and negative start
// index.
func (s *Selection) Slice(start, end int) *Selection {
if start < 0 {
start += len(s.Nodes)
}
if end < 0 {
if end == ToEnd {
end = len(s.Nodes)
} else if end < 0 {
end += len(s.Nodes)
}
return pushStack(s, s.Nodes[start:end])
+54
View File
@@ -14,6 +14,11 @@ func TestFirstEmpty(t *testing.T) {
assertLength(t, sel.Nodes, 0)
}
func TestFirstInvalid(t *testing.T) {
sel := Doc().Find("").First()
assertLength(t, sel.Nodes, 0)
}
func TestFirstRollback(t *testing.T) {
sel := Doc().Find(".pvk-content")
sel2 := sel.First().End()
@@ -36,6 +41,11 @@ func TestLastEmpty(t *testing.T) {
assertLength(t, sel.Nodes, 0)
}
func TestLastInvalid(t *testing.T) {
sel := Doc().Find("").Last()
assertLength(t, sel.Nodes, 0)
}
func TestLastRollback(t *testing.T) {
sel := Doc().Find(".pvk-content")
sel2 := sel.Last().End()
@@ -63,6 +73,11 @@ func TestEqEmpty(t *testing.T) {
assertLength(t, sel.Nodes, 0)
}
func TestEqInvalid(t *testing.T) {
sel := Doc().Find("").Eq(0)
assertLength(t, sel.Nodes, 0)
}
func TestEqInvalidPositive(t *testing.T) {
sel := Doc().Find(".pvk-content").Eq(3)
assertLength(t, sel.Nodes, 0)
@@ -83,6 +98,32 @@ func TestSlice(t *testing.T) {
sel := Doc().Find(".pvk-content").Slice(0, 2)
assertLength(t, sel.Nodes, 2)
assertSelectionIs(t, sel, "#pc1", "#pc2")
}
func TestSliceToEnd(t *testing.T) {
sel := Doc().Find(".pvk-content").Slice(1, ToEnd)
assertLength(t, sel.Nodes, 2)
assertSelectionIs(t, sel.Eq(0), "#pc2")
if _, ok := sel.Eq(1).Attr("id"); ok {
t.Error("Want no attribute ID, got one")
}
}
func TestSliceEmpty(t *testing.T) {
defer assertPanic(t)
Doc().Find("x").Slice(0, 2)
}
func TestSliceInvalid(t *testing.T) {
defer assertPanic(t)
Doc().Find("").Slice(0, 2)
}
func TestSliceInvalidToEnd(t *testing.T) {
defer assertPanic(t)
Doc().Find("").Slice(2, ToEnd)
}
func TestSliceOutOfBounds(t *testing.T) {
@@ -110,6 +151,12 @@ func TestNegativeSliceBoth(t *testing.T) {
assertSelectionIs(t, sel.Eq(1), "#cf3")
}
func TestNegativeSliceToEnd(t *testing.T) {
sel := Doc().Find(".container-fluid").Slice(-3, ToEnd)
assertLength(t, sel.Nodes, 3)
assertSelectionIs(t, sel, "#cf2", "#cf3", "#cf4")
}
func TestNegativeSliceOutOfBounds(t *testing.T) {
defer assertPanic(t)
Doc().Find(".container-fluid").Slice(-12, -7)
@@ -157,6 +204,13 @@ func TestIndexSelector(t *testing.T) {
}
}
func TestIndexSelectorInvalid(t *testing.T) {
sel := Doc().Find(".hero-unit")
if i := sel.IndexSelector(""); i != -1 {
t.Errorf("Expected index of -1, got %v.", i)
}
}
func TestIndexOfNode(t *testing.T) {
sel := Doc().Find("div.pvk-gutter")
if i := sel.IndexOfNode(sel.Nodes[1]); i != 1 {
+85
View File
@@ -0,0 +1,85 @@
BenchmarkFirst-4 30000000 50.7 ns/op 48 B/op 1 allocs/op
BenchmarkLast-4 30000000 50.9 ns/op 48 B/op 1 allocs/op
BenchmarkEq-4 30000000 55.7 ns/op 48 B/op 1 allocs/op
BenchmarkSlice-4 500000000 3.45 ns/op 0 B/op 0 allocs/op
BenchmarkGet-4 2000000000 1.68 ns/op 0 B/op 0 allocs/op
BenchmarkIndex-4 3000000 541 ns/op 248 B/op 10 allocs/op
BenchmarkIndexSelector-4 200000 10749 ns/op 2464 B/op 17 allocs/op
BenchmarkIndexOfNode-4 200000000 6.47 ns/op 0 B/op 0 allocs/op
BenchmarkIndexOfSelection-4 200000000 7.27 ns/op 0 B/op 0 allocs/op
BenchmarkMetalReviewExample-4 10000 138426 ns/op 12240 B/op 319 allocs/op
BenchmarkAdd-4 200000 10192 ns/op 208 B/op 9 allocs/op
BenchmarkAddSelection-4 10000000 158 ns/op 48 B/op 1 allocs/op
BenchmarkAddNodes-4 10000000 156 ns/op 48 B/op 1 allocs/op
BenchmarkAndSelf-4 1000000 1588 ns/op 1008 B/op 5 allocs/op
BenchmarkFilter-4 100000 20427 ns/op 360 B/op 8 allocs/op
BenchmarkNot-4 100000 23508 ns/op 136 B/op 5 allocs/op
BenchmarkFilterFunction-4 50000 34178 ns/op 22976 B/op 755 allocs/op
BenchmarkNotFunction-4 50000 38173 ns/op 29120 B/op 757 allocs/op
BenchmarkFilterNodes-4 50000 34001 ns/op 20960 B/op 749 allocs/op
BenchmarkNotNodes-4 30000 40344 ns/op 29120 B/op 757 allocs/op
BenchmarkFilterSelection-4 50000 33308 ns/op 20960 B/op 749 allocs/op
BenchmarkNotSelection-4 30000 40748 ns/op 29120 B/op 757 allocs/op
BenchmarkHas-4 5000 263346 ns/op 1816 B/op 48 allocs/op
BenchmarkHasNodes-4 10000 160840 ns/op 21184 B/op 752 allocs/op
BenchmarkHasSelection-4 10000 165410 ns/op 21184 B/op 752 allocs/op
BenchmarkEnd-4 2000000000 1.01 ns/op 0 B/op 0 allocs/op
BenchmarkEach-4 300000 4664 ns/op 3304 B/op 118 allocs/op
BenchmarkMap-4 200000 8286 ns/op 5572 B/op 184 allocs/op
BenchmarkEachWithBreak-4 2000000 806 ns/op 560 B/op 20 allocs/op
BenchmarkAttr-4 100000000 21.6 ns/op 0 B/op 0 allocs/op
BenchmarkText-4 200000 8909 ns/op 7536 B/op 110 allocs/op
BenchmarkLength-4 2000000000 0.34 ns/op 0 B/op 0 allocs/op
BenchmarkHtml-4 3000000 422 ns/op 120 B/op 2 allocs/op
BenchmarkIs-4 100000 22615 ns/op 88 B/op 4 allocs/op
BenchmarkIsPositional-4 50000 26655 ns/op 1112 B/op 10 allocs/op
BenchmarkIsFunction-4 1000000 1208 ns/op 784 B/op 28 allocs/op
BenchmarkIsSelection-4 50000 33497 ns/op 20960 B/op 749 allocs/op
BenchmarkIsNodes-4 50000 33572 ns/op 20960 B/op 749 allocs/op
BenchmarkHasClass-4 10000 232802 ns/op 14944 B/op 976 allocs/op
BenchmarkContains-4 200000000 7.33 ns/op 0 B/op 0 allocs/op
BenchmarkFind-4 200000 10715 ns/op 2464 B/op 17 allocs/op
BenchmarkFindWithinSelection-4 50000 35878 ns/op 2176 B/op 78 allocs/op
BenchmarkFindSelection-4 10000 194356 ns/op 2672 B/op 82 allocs/op
BenchmarkFindNodes-4 10000 195510 ns/op 2672 B/op 82 allocs/op
BenchmarkContents-4 1000000 2252 ns/op 864 B/op 34 allocs/op
BenchmarkContentsFiltered-4 500000 3015 ns/op 1016 B/op 39 allocs/op
BenchmarkChildren-4 5000000 364 ns/op 152 B/op 7 allocs/op
BenchmarkChildrenFiltered-4 1000000 2212 ns/op 352 B/op 15 allocs/op
BenchmarkParent-4 50000 24643 ns/op 4048 B/op 381 allocs/op
BenchmarkParentFiltered-4 50000 25967 ns/op 4248 B/op 388 allocs/op
BenchmarkParents-4 30000 50000 ns/op 27776 B/op 830 allocs/op
BenchmarkParentsFiltered-4 30000 53107 ns/op 28360 B/op 838 allocs/op
BenchmarkParentsUntil-4 100000 22423 ns/op 10352 B/op 353 allocs/op
BenchmarkParentsUntilSelection-4 20000 86925 ns/op 51144 B/op 1516 allocs/op
BenchmarkParentsUntilNodes-4 20000 87597 ns/op 51144 B/op 1516 allocs/op
BenchmarkParentsFilteredUntil-4 300000 5568 ns/op 2232 B/op 86 allocs/op
BenchmarkParentsFilteredUntilSelection-4 200000 10966 ns/op 5440 B/op 190 allocs/op
BenchmarkParentsFilteredUntilNodes-4 200000 10919 ns/op 5440 B/op 190 allocs/op
BenchmarkSiblings-4 30000 46018 ns/op 15400 B/op 204 allocs/op
BenchmarkSiblingsFiltered-4 30000 50566 ns/op 16496 B/op 213 allocs/op
BenchmarkNext-4 200000 7921 ns/op 3216 B/op 112 allocs/op
BenchmarkNextFiltered-4 200000 8804 ns/op 3416 B/op 118 allocs/op
BenchmarkNextAll-4 50000 31098 ns/op 9912 B/op 138 allocs/op
BenchmarkNextAllFiltered-4 50000 34677 ns/op 11008 B/op 147 allocs/op
BenchmarkPrev-4 200000 7920 ns/op 3216 B/op 112 allocs/op
BenchmarkPrevFiltered-4 200000 8913 ns/op 3416 B/op 118 allocs/op
BenchmarkPrevAll-4 200000 10845 ns/op 4376 B/op 113 allocs/op
BenchmarkPrevAllFiltered-4 100000 12030 ns/op 4576 B/op 119 allocs/op
BenchmarkNextUntil-4 100000 19193 ns/op 5760 B/op 260 allocs/op
BenchmarkNextUntilSelection-4 50000 34829 ns/op 18480 B/op 542 allocs/op
BenchmarkNextUntilNodes-4 100000 14459 ns/op 7944 B/op 248 allocs/op
BenchmarkPrevUntil-4 20000 66296 ns/op 12856 B/op 448 allocs/op
BenchmarkPrevUntilSelection-4 30000 45037 ns/op 23432 B/op 689 allocs/op
BenchmarkPrevUntilNodes-4 200000 11525 ns/op 6152 B/op 203 allocs/op
BenchmarkNextFilteredUntil-4 100000 12940 ns/op 4512 B/op 173 allocs/op
BenchmarkNextFilteredUntilSelection-4 50000 38924 ns/op 19160 B/op 567 allocs/op
BenchmarkNextFilteredUntilNodes-4 50000 38528 ns/op 19160 B/op 567 allocs/op
BenchmarkPrevFilteredUntil-4 100000 12980 ns/op 4664 B/op 175 allocs/op
BenchmarkPrevFilteredUntilSelection-4 50000 39671 ns/op 19936 B/op 587 allocs/op
BenchmarkPrevFilteredUntilNodes-4 50000 39484 ns/op 19936 B/op 587 allocs/op
BenchmarkClosest-4 500000 3310 ns/op 160 B/op 8 allocs/op
BenchmarkClosestSelection-4 5000000 361 ns/op 96 B/op 6 allocs/op
BenchmarkClosestNodes-4 5000000 359 ns/op 96 B/op 6 allocs/op
PASS
ok github.com/PuerkitoBio/goquery 163.718s
+85
View File
@@ -0,0 +1,85 @@
BenchmarkFirst-4 30000000 50.9 ns/op 48 B/op 1 allocs/op
BenchmarkLast-4 30000000 50.0 ns/op 48 B/op 1 allocs/op
BenchmarkEq-4 30000000 50.5 ns/op 48 B/op 1 allocs/op
BenchmarkSlice-4 500000000 3.53 ns/op 0 B/op 0 allocs/op
BenchmarkGet-4 2000000000 1.66 ns/op 0 B/op 0 allocs/op
BenchmarkIndex-4 2000000 832 ns/op 248 B/op 10 allocs/op
BenchmarkIndexSelector-4 100000 16073 ns/op 3839 B/op 21 allocs/op
BenchmarkIndexOfNode-4 200000000 6.38 ns/op 0 B/op 0 allocs/op
BenchmarkIndexOfSelection-4 200000000 7.14 ns/op 0 B/op 0 allocs/op
BenchmarkMetalReviewExample-4 10000 140737 ns/op 12418 B/op 320 allocs/op
BenchmarkAdd-4 100000 13162 ns/op 974 B/op 10 allocs/op
BenchmarkAddSelection-4 500000 3160 ns/op 814 B/op 2 allocs/op
BenchmarkAddNodes-4 500000 3159 ns/op 814 B/op 2 allocs/op
BenchmarkAndSelf-4 200000 7423 ns/op 2404 B/op 9 allocs/op
BenchmarkFilter-4 100000 19671 ns/op 360 B/op 8 allocs/op
BenchmarkNot-4 100000 22577 ns/op 136 B/op 5 allocs/op
BenchmarkFilterFunction-4 50000 33960 ns/op 22976 B/op 755 allocs/op
BenchmarkNotFunction-4 50000 37909 ns/op 29120 B/op 757 allocs/op
BenchmarkFilterNodes-4 50000 34196 ns/op 20960 B/op 749 allocs/op
BenchmarkNotNodes-4 30000 40446 ns/op 29120 B/op 757 allocs/op
BenchmarkFilterSelection-4 50000 33091 ns/op 20960 B/op 749 allocs/op
BenchmarkNotSelection-4 30000 40609 ns/op 29120 B/op 757 allocs/op
BenchmarkHas-4 5000 262936 ns/op 2371 B/op 50 allocs/op
BenchmarkHasNodes-4 10000 148631 ns/op 21184 B/op 752 allocs/op
BenchmarkHasSelection-4 10000 153117 ns/op 21184 B/op 752 allocs/op
BenchmarkEnd-4 2000000000 1.02 ns/op 0 B/op 0 allocs/op
BenchmarkEach-4 300000 4653 ns/op 3304 B/op 118 allocs/op
BenchmarkMap-4 200000 8257 ns/op 5572 B/op 184 allocs/op
BenchmarkEachWithBreak-4 2000000 806 ns/op 560 B/op 20 allocs/op
BenchmarkAttr-4 100000000 22.0 ns/op 0 B/op 0 allocs/op
BenchmarkText-4 200000 8913 ns/op 7536 B/op 110 allocs/op
BenchmarkLength-4 2000000000 0.35 ns/op 0 B/op 0 allocs/op
BenchmarkHtml-4 5000000 398 ns/op 120 B/op 2 allocs/op
BenchmarkIs-4 100000 22392 ns/op 88 B/op 4 allocs/op
BenchmarkIsPositional-4 50000 26259 ns/op 1112 B/op 10 allocs/op
BenchmarkIsFunction-4 1000000 1212 ns/op 784 B/op 28 allocs/op
BenchmarkIsSelection-4 50000 33222 ns/op 20960 B/op 749 allocs/op
BenchmarkIsNodes-4 50000 33408 ns/op 20960 B/op 749 allocs/op
BenchmarkHasClass-4 10000 233208 ns/op 14944 B/op 976 allocs/op
BenchmarkContains-4 200000000 7.57 ns/op 0 B/op 0 allocs/op
BenchmarkFind-4 100000 16121 ns/op 3839 B/op 21 allocs/op
BenchmarkFindWithinSelection-4 20000 68019 ns/op 11521 B/op 97 allocs/op
BenchmarkFindSelection-4 5000 387582 ns/op 59787 B/op 176 allocs/op
BenchmarkFindNodes-4 5000 389246 ns/op 59797 B/op 176 allocs/op
BenchmarkContents-4 200000 11475 ns/op 2878 B/op 42 allocs/op
BenchmarkContentsFiltered-4 200000 11222 ns/op 2498 B/op 46 allocs/op
BenchmarkChildren-4 2000000 650 ns/op 152 B/op 7 allocs/op
BenchmarkChildrenFiltered-4 500000 2568 ns/op 352 B/op 15 allocs/op
BenchmarkParent-4 2000 702513 ns/op 194478 B/op 828 allocs/op
BenchmarkParentFiltered-4 2000 690778 ns/op 194658 B/op 835 allocs/op
BenchmarkParents-4 10000 124855 ns/op 49869 B/op 868 allocs/op
BenchmarkParentsFiltered-4 10000 128535 ns/op 50456 B/op 876 allocs/op
BenchmarkParentsUntil-4 20000 72982 ns/op 23802 B/op 388 allocs/op
BenchmarkParentsUntilSelection-4 10000 156099 ns/op 72453 B/op 1549 allocs/op
BenchmarkParentsUntilNodes-4 10000 156610 ns/op 72455 B/op 1549 allocs/op
BenchmarkParentsFilteredUntil-4 100000 15549 ns/op 4068 B/op 94 allocs/op
BenchmarkParentsFilteredUntilSelection-4 100000 20564 ns/op 7276 B/op 198 allocs/op
BenchmarkParentsFilteredUntilNodes-4 100000 20635 ns/op 7276 B/op 198 allocs/op
BenchmarkSiblings-4 3000 565114 ns/op 205910 B/op 336 allocs/op
BenchmarkSiblingsFiltered-4 3000 580264 ns/op 206993 B/op 345 allocs/op
BenchmarkNext-4 20000 93177 ns/op 26810 B/op 169 allocs/op
BenchmarkNextFiltered-4 20000 94171 ns/op 27013 B/op 175 allocs/op
BenchmarkNextAll-4 5000 270320 ns/op 89289 B/op 237 allocs/op
BenchmarkNextAllFiltered-4 5000 275283 ns/op 90375 B/op 246 allocs/op
BenchmarkPrev-4 20000 92777 ns/op 26810 B/op 169 allocs/op
BenchmarkPrevFiltered-4 20000 95577 ns/op 27007 B/op 175 allocs/op
BenchmarkPrevAll-4 20000 86339 ns/op 27515 B/op 151 allocs/op
BenchmarkPrevAllFiltered-4 20000 87759 ns/op 27715 B/op 157 allocs/op
BenchmarkNextUntil-4 10000 163930 ns/op 48541 B/op 330 allocs/op
BenchmarkNextUntilSelection-4 30000 56382 ns/op 23880 B/op 556 allocs/op
BenchmarkNextUntilNodes-4 100000 18883 ns/op 8703 B/op 252 allocs/op
BenchmarkPrevUntil-4 3000 484668 ns/op 145402 B/op 611 allocs/op
BenchmarkPrevUntilSelection-4 20000 72125 ns/op 28865 B/op 705 allocs/op
BenchmarkPrevUntilNodes-4 100000 14722 ns/op 6510 B/op 205 allocs/op
BenchmarkNextFilteredUntil-4 50000 39006 ns/op 10990 B/op 192 allocs/op
BenchmarkNextFilteredUntilSelection-4 20000 66048 ns/op 25641 B/op 586 allocs/op
BenchmarkNextFilteredUntilNodes-4 20000 65314 ns/op 25640 B/op 586 allocs/op
BenchmarkPrevFilteredUntil-4 50000 33312 ns/op 9709 B/op 189 allocs/op
BenchmarkPrevFilteredUntilSelection-4 20000 64197 ns/op 24981 B/op 601 allocs/op
BenchmarkPrevFilteredUntilNodes-4 20000 64505 ns/op 24982 B/op 601 allocs/op
BenchmarkClosest-4 500000 4065 ns/op 160 B/op 8 allocs/op
BenchmarkClosestSelection-4 2000000 756 ns/op 96 B/op 6 allocs/op
BenchmarkClosestNodes-4 2000000 753 ns/op 96 B/op 6 allocs/op
PASS
ok github.com/PuerkitoBio/goquery 162.053s
+85
View File
@@ -0,0 +1,85 @@
BenchmarkFirst-4 30000000 51.8 ns/op 48 B/op 1 allocs/op
BenchmarkLast-4 30000000 50.1 ns/op 48 B/op 1 allocs/op
BenchmarkEq-4 30000000 51.4 ns/op 48 B/op 1 allocs/op
BenchmarkSlice-4 500000000 3.52 ns/op 0 B/op 0 allocs/op
BenchmarkGet-4 2000000000 1.65 ns/op 0 B/op 0 allocs/op
BenchmarkIndex-4 2000000 787 ns/op 248 B/op 10 allocs/op
BenchmarkIndexSelector-4 100000 16952 ns/op 3839 B/op 21 allocs/op
BenchmarkIndexOfNode-4 200000000 6.42 ns/op 0 B/op 0 allocs/op
BenchmarkIndexOfSelection-4 200000000 7.12 ns/op 0 B/op 0 allocs/op
BenchmarkMetalReviewExample-4 10000 141994 ns/op 12418 B/op 320 allocs/op
BenchmarkAdd-4 200000 10367 ns/op 208 B/op 9 allocs/op
BenchmarkAddSelection-4 10000000 152 ns/op 48 B/op 1 allocs/op
BenchmarkAddNodes-4 10000000 147 ns/op 48 B/op 1 allocs/op
BenchmarkAndSelf-4 1000000 1647 ns/op 1008 B/op 5 allocs/op
BenchmarkFilter-4 100000 19522 ns/op 360 B/op 8 allocs/op
BenchmarkNot-4 100000 22546 ns/op 136 B/op 5 allocs/op
BenchmarkFilterFunction-4 50000 35087 ns/op 22976 B/op 755 allocs/op
BenchmarkNotFunction-4 50000 39123 ns/op 29120 B/op 757 allocs/op
BenchmarkFilterNodes-4 50000 34890 ns/op 20960 B/op 749 allocs/op
BenchmarkNotNodes-4 30000 41145 ns/op 29120 B/op 757 allocs/op
BenchmarkFilterSelection-4 50000 33735 ns/op 20960 B/op 749 allocs/op
BenchmarkNotSelection-4 30000 41334 ns/op 29120 B/op 757 allocs/op
BenchmarkHas-4 5000 264058 ns/op 2370 B/op 50 allocs/op
BenchmarkHasNodes-4 10000 151718 ns/op 21184 B/op 752 allocs/op
BenchmarkHasSelection-4 10000 156955 ns/op 21184 B/op 752 allocs/op
BenchmarkEnd-4 2000000000 1.01 ns/op 0 B/op 0 allocs/op
BenchmarkEach-4 300000 4660 ns/op 3304 B/op 118 allocs/op
BenchmarkMap-4 200000 8404 ns/op 5572 B/op 184 allocs/op
BenchmarkEachWithBreak-4 2000000 806 ns/op 560 B/op 20 allocs/op
BenchmarkAttr-4 100000000 21.6 ns/op 0 B/op 0 allocs/op
BenchmarkText-4 200000 8911 ns/op 7536 B/op 110 allocs/op
BenchmarkLength-4 2000000000 0.34 ns/op 0 B/op 0 allocs/op
BenchmarkHtml-4 3000000 405 ns/op 120 B/op 2 allocs/op
BenchmarkIs-4 100000 22228 ns/op 88 B/op 4 allocs/op
BenchmarkIsPositional-4 50000 26469 ns/op 1112 B/op 10 allocs/op
BenchmarkIsFunction-4 1000000 1240 ns/op 784 B/op 28 allocs/op
BenchmarkIsSelection-4 50000 33709 ns/op 20960 B/op 749 allocs/op
BenchmarkIsNodes-4 50000 33711 ns/op 20960 B/op 749 allocs/op
BenchmarkHasClass-4 10000 236005 ns/op 14944 B/op 976 allocs/op
BenchmarkContains-4 200000000 7.47 ns/op 0 B/op 0 allocs/op
BenchmarkFind-4 100000 16075 ns/op 3839 B/op 21 allocs/op
BenchmarkFindWithinSelection-4 30000 41418 ns/op 3539 B/op 82 allocs/op
BenchmarkFindSelection-4 10000 209490 ns/op 5616 B/op 89 allocs/op
BenchmarkFindNodes-4 10000 208206 ns/op 5614 B/op 89 allocs/op
BenchmarkContents-4 300000 4751 ns/op 1420 B/op 36 allocs/op
BenchmarkContentsFiltered-4 300000 5454 ns/op 1570 B/op 41 allocs/op
BenchmarkChildren-4 3000000 527 ns/op 152 B/op 7 allocs/op
BenchmarkChildrenFiltered-4 1000000 2484 ns/op 352 B/op 15 allocs/op
BenchmarkParent-4 50000 34724 ns/op 6940 B/op 387 allocs/op
BenchmarkParentFiltered-4 50000 35596 ns/op 7141 B/op 394 allocs/op
BenchmarkParents-4 20000 62094 ns/op 30720 B/op 837 allocs/op
BenchmarkParentsFiltered-4 20000 63223 ns/op 31304 B/op 845 allocs/op
BenchmarkParentsUntil-4 50000 30391 ns/op 11828 B/op 358 allocs/op
BenchmarkParentsUntilSelection-4 20000 99962 ns/op 54075 B/op 1523 allocs/op
BenchmarkParentsUntilNodes-4 20000 98763 ns/op 54073 B/op 1523 allocs/op
BenchmarkParentsFilteredUntil-4 200000 7982 ns/op 2787 B/op 88 allocs/op
BenchmarkParentsFilteredUntilSelection-4 100000 13618 ns/op 5995 B/op 192 allocs/op
BenchmarkParentsFilteredUntilNodes-4 100000 13639 ns/op 5994 B/op 192 allocs/op
BenchmarkSiblings-4 20000 75287 ns/op 28453 B/op 225 allocs/op
BenchmarkSiblingsFiltered-4 20000 80139 ns/op 29543 B/op 234 allocs/op
BenchmarkNext-4 100000 14270 ns/op 4659 B/op 117 allocs/op
BenchmarkNextFiltered-4 100000 15352 ns/op 4860 B/op 123 allocs/op
BenchmarkNextAll-4 20000 60811 ns/op 22771 B/op 157 allocs/op
BenchmarkNextAllFiltered-4 20000 69079 ns/op 23871 B/op 166 allocs/op
BenchmarkPrev-4 100000 14417 ns/op 4659 B/op 117 allocs/op
BenchmarkPrevFiltered-4 100000 15443 ns/op 4859 B/op 123 allocs/op
BenchmarkPrevAll-4 100000 22008 ns/op 7346 B/op 120 allocs/op
BenchmarkPrevAllFiltered-4 100000 23212 ns/op 7544 B/op 126 allocs/op
BenchmarkNextUntil-4 50000 30589 ns/op 8767 B/op 267 allocs/op
BenchmarkNextUntilSelection-4 30000 40875 ns/op 19862 B/op 546 allocs/op
BenchmarkNextUntilNodes-4 100000 15987 ns/op 8134 B/op 249 allocs/op
BenchmarkPrevUntil-4 20000 98799 ns/op 25727 B/op 467 allocs/op
BenchmarkPrevUntilSelection-4 30000 51874 ns/op 24875 B/op 694 allocs/op
BenchmarkPrevUntilNodes-4 100000 12901 ns/op 6334 B/op 204 allocs/op
BenchmarkNextFilteredUntil-4 100000 19869 ns/op 5909 B/op 177 allocs/op
BenchmarkNextFilteredUntilSelection-4 30000 45412 ns/op 20557 B/op 571 allocs/op
BenchmarkNextFilteredUntilNodes-4 30000 45363 ns/op 20557 B/op 571 allocs/op
BenchmarkPrevFilteredUntil-4 100000 19357 ns/op 6033 B/op 179 allocs/op
BenchmarkPrevFilteredUntilSelection-4 30000 46396 ns/op 21305 B/op 591 allocs/op
BenchmarkPrevFilteredUntilNodes-4 30000 46133 ns/op 21305 B/op 591 allocs/op
BenchmarkClosest-4 500000 3448 ns/op 160 B/op 8 allocs/op
BenchmarkClosestSelection-4 3000000 528 ns/op 96 B/op 6 allocs/op
BenchmarkClosestNodes-4 3000000 523 ns/op 96 B/op 6 allocs/op
PASS
ok github.com/PuerkitoBio/goquery 162.012s
+86
View File
@@ -0,0 +1,86 @@
BenchmarkFirst-4 30000000 51.7 ns/op 48 B/op 1 allocs/op
BenchmarkLast-4 30000000 51.9 ns/op 48 B/op 1 allocs/op
BenchmarkEq-4 30000000 50.0 ns/op 48 B/op 1 allocs/op
BenchmarkSlice-4 500000000 3.47 ns/op 0 B/op 0 allocs/op
BenchmarkGet-4 2000000000 1.68 ns/op 0 B/op 0 allocs/op
BenchmarkIndex-4 2000000 804 ns/op 248 B/op 10 allocs/op
BenchmarkIndexSelector-4 100000 16285 ns/op 3839 B/op 21 allocs/op
BenchmarkIndexOfNode-4 200000000 6.50 ns/op 0 B/op 0 allocs/op
BenchmarkIndexOfSelection-4 200000000 7.02 ns/op 0 B/op 0 allocs/op
BenchmarkMetalReviewExample-4 10000 143160 ns/op 12417 B/op 320 allocs/op
BenchmarkAdd-4 200000 10326 ns/op 208 B/op 9 allocs/op
BenchmarkAddSelection-4 10000000 155 ns/op 48 B/op 1 allocs/op
BenchmarkAddNodes-4 10000000 156 ns/op 48 B/op 1 allocs/op
BenchmarkAddNodesBig-4 20000 94439 ns/op 21847 B/op 37 allocs/op
BenchmarkAndSelf-4 1000000 1791 ns/op 1008 B/op 5 allocs/op
BenchmarkFilter-4 100000 19470 ns/op 360 B/op 8 allocs/op
BenchmarkNot-4 100000 22500 ns/op 136 B/op 5 allocs/op
BenchmarkFilterFunction-4 50000 34578 ns/op 22976 B/op 755 allocs/op
BenchmarkNotFunction-4 50000 38703 ns/op 29120 B/op 757 allocs/op
BenchmarkFilterNodes-4 50000 34486 ns/op 20960 B/op 749 allocs/op
BenchmarkNotNodes-4 30000 41094 ns/op 29120 B/op 757 allocs/op
BenchmarkFilterSelection-4 50000 33623 ns/op 20960 B/op 749 allocs/op
BenchmarkNotSelection-4 30000 41483 ns/op 29120 B/op 757 allocs/op
BenchmarkHas-4 5000 266628 ns/op 2371 B/op 50 allocs/op
BenchmarkHasNodes-4 10000 152617 ns/op 21184 B/op 752 allocs/op
BenchmarkHasSelection-4 10000 156682 ns/op 21184 B/op 752 allocs/op
BenchmarkEnd-4 2000000000 1.00 ns/op 0 B/op 0 allocs/op
BenchmarkEach-4 300000 4712 ns/op 3304 B/op 118 allocs/op
BenchmarkMap-4 200000 8434 ns/op 5572 B/op 184 allocs/op
BenchmarkEachWithBreak-4 2000000 819 ns/op 560 B/op 20 allocs/op
BenchmarkAttr-4 100000000 21.7 ns/op 0 B/op 0 allocs/op
BenchmarkText-4 200000 9376 ns/op 7536 B/op 110 allocs/op
BenchmarkLength-4 2000000000 0.35 ns/op 0 B/op 0 allocs/op
BenchmarkHtml-4 5000000 401 ns/op 120 B/op 2 allocs/op
BenchmarkIs-4 100000 22214 ns/op 88 B/op 4 allocs/op
BenchmarkIsPositional-4 50000 26559 ns/op 1112 B/op 10 allocs/op
BenchmarkIsFunction-4 1000000 1228 ns/op 784 B/op 28 allocs/op
BenchmarkIsSelection-4 50000 33471 ns/op 20960 B/op 749 allocs/op
BenchmarkIsNodes-4 50000 34461 ns/op 20960 B/op 749 allocs/op
BenchmarkHasClass-4 10000 232429 ns/op 14944 B/op 976 allocs/op
BenchmarkContains-4 200000000 7.62 ns/op 0 B/op 0 allocs/op
BenchmarkFind-4 100000 16114 ns/op 3839 B/op 21 allocs/op
BenchmarkFindWithinSelection-4 30000 42520 ns/op 3540 B/op 82 allocs/op
BenchmarkFindSelection-4 10000 209801 ns/op 5615 B/op 89 allocs/op
BenchmarkFindNodes-4 10000 209082 ns/op 5614 B/op 89 allocs/op
BenchmarkContents-4 300000 4836 ns/op 1420 B/op 36 allocs/op
BenchmarkContentsFiltered-4 200000 5495 ns/op 1570 B/op 41 allocs/op
BenchmarkChildren-4 3000000 527 ns/op 152 B/op 7 allocs/op
BenchmarkChildrenFiltered-4 500000 2499 ns/op 352 B/op 15 allocs/op
BenchmarkParent-4 50000 34072 ns/op 6942 B/op 387 allocs/op
BenchmarkParentFiltered-4 50000 36077 ns/op 7141 B/op 394 allocs/op
BenchmarkParents-4 20000 64118 ns/op 30719 B/op 837 allocs/op
BenchmarkParentsFiltered-4 20000 63432 ns/op 31303 B/op 845 allocs/op
BenchmarkParentsUntil-4 50000 29589 ns/op 11829 B/op 358 allocs/op
BenchmarkParentsUntilSelection-4 10000 101033 ns/op 54076 B/op 1523 allocs/op
BenchmarkParentsUntilNodes-4 10000 100584 ns/op 54076 B/op 1523 allocs/op
BenchmarkParentsFilteredUntil-4 200000 8061 ns/op 2787 B/op 88 allocs/op
BenchmarkParentsFilteredUntilSelection-4 100000 13848 ns/op 5995 B/op 192 allocs/op
BenchmarkParentsFilteredUntilNodes-4 100000 13766 ns/op 5995 B/op 192 allocs/op
BenchmarkSiblings-4 20000 75135 ns/op 28453 B/op 225 allocs/op
BenchmarkSiblingsFiltered-4 20000 80532 ns/op 29544 B/op 234 allocs/op
BenchmarkNext-4 100000 14200 ns/op 4660 B/op 117 allocs/op
BenchmarkNextFiltered-4 100000 15284 ns/op 4859 B/op 123 allocs/op
BenchmarkNextAll-4 20000 60889 ns/op 22774 B/op 157 allocs/op
BenchmarkNextAllFiltered-4 20000 65125 ns/op 23869 B/op 166 allocs/op
BenchmarkPrev-4 100000 14448 ns/op 4659 B/op 117 allocs/op
BenchmarkPrevFiltered-4 100000 15444 ns/op 4859 B/op 123 allocs/op
BenchmarkPrevAll-4 100000 22019 ns/op 7344 B/op 120 allocs/op
BenchmarkPrevAllFiltered-4 100000 23307 ns/op 7545 B/op 126 allocs/op
BenchmarkNextUntil-4 50000 30287 ns/op 8766 B/op 267 allocs/op
BenchmarkNextUntilSelection-4 30000 41476 ns/op 19862 B/op 546 allocs/op
BenchmarkNextUntilNodes-4 100000 16106 ns/op 8133 B/op 249 allocs/op
BenchmarkPrevUntil-4 20000 98951 ns/op 25728 B/op 467 allocs/op
BenchmarkPrevUntilSelection-4 30000 52390 ns/op 24875 B/op 694 allocs/op
BenchmarkPrevUntilNodes-4 100000 12986 ns/op 6334 B/op 204 allocs/op
BenchmarkNextFilteredUntil-4 100000 19365 ns/op 5908 B/op 177 allocs/op
BenchmarkNextFilteredUntilSelection-4 30000 45334 ns/op 20555 B/op 571 allocs/op
BenchmarkNextFilteredUntilNodes-4 30000 45292 ns/op 20556 B/op 571 allocs/op
BenchmarkPrevFilteredUntil-4 100000 19412 ns/op 6032 B/op 179 allocs/op
BenchmarkPrevFilteredUntilSelection-4 30000 46286 ns/op 21304 B/op 591 allocs/op
BenchmarkPrevFilteredUntilNodes-4 30000 46554 ns/op 21305 B/op 591 allocs/op
BenchmarkClosest-4 500000 3480 ns/op 160 B/op 8 allocs/op
BenchmarkClosestSelection-4 2000000 722 ns/op 96 B/op 6 allocs/op
BenchmarkClosestNodes-4 2000000 719 ns/op 96 B/op 6 allocs/op
PASS
ok github.com/PuerkitoBio/goquery 160.565s
+12 -4
View File
@@ -70,7 +70,9 @@ func BenchmarkIndex(b *testing.B) {
for i := 0; i < b.N; i++ {
j = sel.Index()
}
b.Logf("Index=%d", j)
if j != 3 {
b.Fatalf("want 3, got %d", j)
}
}
func BenchmarkIndexSelector(b *testing.B) {
@@ -82,7 +84,9 @@ func BenchmarkIndexSelector(b *testing.B) {
for i := 0; i < b.N; i++ {
j = sel.IndexSelector("dd")
}
b.Logf("IndexSelector=%d", j)
if j != 4 {
b.Fatalf("want 4, got %d", j)
}
}
func BenchmarkIndexOfNode(b *testing.B) {
@@ -96,7 +100,9 @@ func BenchmarkIndexOfNode(b *testing.B) {
for i := 0; i < b.N; i++ {
j = sel.IndexOfNode(n)
}
b.Logf("IndexOfNode=%d", j)
if j != 2 {
b.Fatalf("want 2, got %d", j)
}
}
func BenchmarkIndexOfSelection(b *testing.B) {
@@ -108,5 +114,7 @@ func BenchmarkIndexOfSelection(b *testing.B) {
for i := 0; i < b.N; i++ {
j = sel.IndexOfSelection(sel2)
}
b.Logf("IndexOfSelection=%d", j)
if j != 2 {
b.Fatalf("want 2, got %d", j)
}
}
-2
View File
@@ -37,6 +37,4 @@ func BenchmarkMetalReviewExample(b *testing.B) {
}
})
}
b.Log(buf.String())
b.Logf("MetalReviewExample=%d", n)
}
+36 -4
View File
@@ -17,7 +17,9 @@ func BenchmarkAdd(b *testing.B) {
sel.Add("h2[title]")
}
}
b.Logf("Add=%d", n)
if n != 43 {
b.Fatalf("want 43, got %d", n)
}
}
func BenchmarkAddSelection(b *testing.B) {
@@ -34,7 +36,9 @@ func BenchmarkAddSelection(b *testing.B) {
sel.AddSelection(sel2)
}
}
b.Logf("AddSelection=%d", n)
if n != 43 {
b.Fatalf("want 43, got %d", n)
}
}
func BenchmarkAddNodes(b *testing.B) {
@@ -52,7 +56,33 @@ func BenchmarkAddNodes(b *testing.B) {
sel.AddNodes(nodes...)
}
}
b.Logf("AddNodes=%d", n)
if n != 43 {
b.Fatalf("want 43, got %d", n)
}
}
func BenchmarkAddNodesBig(b *testing.B) {
var n int
doc := DocW()
sel := doc.Find("li")
// make nodes > 1000
nodes := sel.Nodes
nodes = append(nodes, nodes...)
nodes = append(nodes, nodes...)
sel = doc.Find("xyz")
b.ResetTimer()
for i := 0; i < b.N; i++ {
if n == 0 {
n = sel.AddNodes(nodes...).Length()
} else {
sel.AddNodes(nodes...)
}
}
if n != 373 {
b.Fatalf("want 373, got %d", n)
}
}
func BenchmarkAndSelf(b *testing.B) {
@@ -68,5 +98,7 @@ func BenchmarkAndSelf(b *testing.B) {
sel.AndSelf()
}
}
b.Logf("AndSelf=%d", n)
if n != 44 {
b.Fatalf("want 44, got %d", n)
}
}
+36 -12
View File
@@ -17,7 +17,9 @@ func BenchmarkFilter(b *testing.B) {
sel.Filter(".toclevel-1")
}
}
b.Logf("Filter=%d", n)
if n != 13 {
b.Fatalf("want 13, got %d", n)
}
}
func BenchmarkNot(b *testing.B) {
@@ -33,7 +35,9 @@ func BenchmarkNot(b *testing.B) {
sel.Filter(".toclevel-2")
}
}
b.Logf("Not=%d", n)
if n != 371 {
b.Fatalf("want 371, got %d", n)
}
}
func BenchmarkFilterFunction(b *testing.B) {
@@ -52,7 +56,9 @@ func BenchmarkFilterFunction(b *testing.B) {
sel.FilterFunction(f)
}
}
b.Logf("FilterFunction=%d", n)
if n != 112 {
b.Fatalf("want 112, got %d", n)
}
}
func BenchmarkNotFunction(b *testing.B) {
@@ -71,7 +77,9 @@ func BenchmarkNotFunction(b *testing.B) {
sel.NotFunction(f)
}
}
b.Logf("NotFunction=%d", n)
if n != 261 {
b.Fatalf("want 261, got %d", n)
}
}
func BenchmarkFilterNodes(b *testing.B) {
@@ -89,7 +97,9 @@ func BenchmarkFilterNodes(b *testing.B) {
sel.FilterNodes(nodes...)
}
}
b.Logf("FilterNodes=%d", n)
if n != 2 {
b.Fatalf("want 2, got %d", n)
}
}
func BenchmarkNotNodes(b *testing.B) {
@@ -107,7 +117,9 @@ func BenchmarkNotNodes(b *testing.B) {
sel.NotNodes(nodes...)
}
}
b.Logf("NotNodes=%d", n)
if n != 360 {
b.Fatalf("want 360, got %d", n)
}
}
func BenchmarkFilterSelection(b *testing.B) {
@@ -124,7 +136,9 @@ func BenchmarkFilterSelection(b *testing.B) {
sel.FilterSelection(sel2)
}
}
b.Logf("FilterSelection=%d", n)
if n != 2 {
b.Fatalf("want 2, got %d", n)
}
}
func BenchmarkNotSelection(b *testing.B) {
@@ -141,7 +155,9 @@ func BenchmarkNotSelection(b *testing.B) {
sel.NotSelection(sel2)
}
}
b.Logf("NotSelection=%d", n)
if n != 360 {
b.Fatalf("want 360, got %d", n)
}
}
func BenchmarkHas(b *testing.B) {
@@ -157,7 +173,9 @@ func BenchmarkHas(b *testing.B) {
sel.Has(".editsection")
}
}
b.Logf("Has=%d", n)
if n != 13 {
b.Fatalf("want 13, got %d", n)
}
}
func BenchmarkHasNodes(b *testing.B) {
@@ -175,7 +193,9 @@ func BenchmarkHasNodes(b *testing.B) {
sel.HasNodes(nodes...)
}
}
b.Logf("HasNodes=%d", n)
if n != 15 {
b.Fatalf("want 15, got %d", n)
}
}
func BenchmarkHasSelection(b *testing.B) {
@@ -192,7 +212,9 @@ func BenchmarkHasSelection(b *testing.B) {
sel.HasSelection(sel2)
}
}
b.Logf("HasSelection=%d", n)
if n != 15 {
b.Fatalf("want 15, got %d", n)
}
}
func BenchmarkEnd(b *testing.B) {
@@ -208,5 +230,7 @@ func BenchmarkEnd(b *testing.B) {
sel.End()
}
}
b.Logf("End=%d", n)
if n != 373 {
b.Fatalf("want 373, got %d", n)
}
}
+9 -3
View File
@@ -19,7 +19,9 @@ func BenchmarkEach(b *testing.B) {
n = tmp
}
}
b.Logf("Each=%d", n)
if n != 59 {
b.Fatalf("want 59, got %d", n)
}
}
func BenchmarkMap(b *testing.B) {
@@ -38,7 +40,9 @@ func BenchmarkMap(b *testing.B) {
n = tmp
}
}
b.Logf("Map=%d", n)
if n != 59 {
b.Fatalf("want 59, got %d", n)
}
}
func BenchmarkEachWithBreak(b *testing.B) {
@@ -58,5 +62,7 @@ func BenchmarkEachWithBreak(b *testing.B) {
n = tmp
}
}
b.Logf("Each=%d", n)
if n != 10 {
b.Fatalf("want 10, got %d", n)
}
}
+6 -2
View File
@@ -13,7 +13,9 @@ func BenchmarkAttr(b *testing.B) {
for i := 0; i < b.N; i++ {
s, _ = sel.Attr("id")
}
b.Logf("Attr=%s", s)
if s != "firstHeading" {
b.Fatalf("want firstHeading, got %q", s)
}
}
func BenchmarkText(b *testing.B) {
@@ -34,7 +36,9 @@ func BenchmarkLength(b *testing.B) {
for i := 0; i < b.N; i++ {
n = sel.Length()
}
b.Logf("Length=%d", n)
if n != 14 {
b.Fatalf("want 14, got %d", n)
}
}
func BenchmarkHtml(b *testing.B) {
+21 -7
View File
@@ -13,7 +13,9 @@ func BenchmarkIs(b *testing.B) {
for i := 0; i < b.N; i++ {
y = sel.Is(".toclevel-2")
}
b.Logf("Is=%v", y)
if !y {
b.Fatal("want true")
}
}
func BenchmarkIsPositional(b *testing.B) {
@@ -25,7 +27,9 @@ func BenchmarkIsPositional(b *testing.B) {
for i := 0; i < b.N; i++ {
y = sel.Is("li:nth-child(2)")
}
b.Logf("IsPositional=%v", y)
if !y {
b.Fatal("want true")
}
}
func BenchmarkIsFunction(b *testing.B) {
@@ -40,7 +44,9 @@ func BenchmarkIsFunction(b *testing.B) {
for i := 0; i < b.N; i++ {
y = sel.IsFunction(f)
}
b.Logf("IsFunction=%v", y)
if !y {
b.Fatal("want true")
}
}
func BenchmarkIsSelection(b *testing.B) {
@@ -53,7 +59,9 @@ func BenchmarkIsSelection(b *testing.B) {
for i := 0; i < b.N; i++ {
y = sel.IsSelection(sel2)
}
b.Logf("IsSelection=%v", y)
if !y {
b.Fatal("want true")
}
}
func BenchmarkIsNodes(b *testing.B) {
@@ -67,7 +75,9 @@ func BenchmarkIsNodes(b *testing.B) {
for i := 0; i < b.N; i++ {
y = sel.IsNodes(nodes...)
}
b.Logf("IsNodes=%v", y)
if !y {
b.Fatal("want true")
}
}
func BenchmarkHasClass(b *testing.B) {
@@ -79,7 +89,9 @@ func BenchmarkHasClass(b *testing.B) {
for i := 0; i < b.N; i++ {
y = sel.HasClass("official")
}
b.Logf("HasClass=%v", y)
if !y {
b.Fatal("want true")
}
}
func BenchmarkContains(b *testing.B) {
@@ -93,5 +105,7 @@ func BenchmarkContains(b *testing.B) {
for i := 0; i < b.N; i++ {
y = sel.Contains(node)
}
b.Logf("Contains=%v", y)
if !y {
b.Fatal("want true")
}
}
+129 -43
View File
@@ -15,7 +15,9 @@ func BenchmarkFind(b *testing.B) {
DocB().Find("dd")
}
}
b.Logf("Find=%d", n)
if n != 41 {
b.Fatalf("want 41, got %d", n)
}
}
func BenchmarkFindWithinSelection(b *testing.B) {
@@ -31,7 +33,9 @@ func BenchmarkFindWithinSelection(b *testing.B) {
sel.Find("a[class]")
}
}
b.Logf("FindWithinSelection=%d", n)
if n != 39 {
b.Fatalf("want 39, got %d", n)
}
}
func BenchmarkFindSelection(b *testing.B) {
@@ -48,7 +52,9 @@ func BenchmarkFindSelection(b *testing.B) {
sel.FindSelection(sel2)
}
}
b.Logf("FindSelection=%d", n)
if n != 73 {
b.Fatalf("want 73, got %d", n)
}
}
func BenchmarkFindNodes(b *testing.B) {
@@ -66,7 +72,9 @@ func BenchmarkFindNodes(b *testing.B) {
sel.FindNodes(nodes...)
}
}
b.Logf("FindNodes=%d", n)
if n != 73 {
b.Fatalf("want 73, got %d", n)
}
}
func BenchmarkContents(b *testing.B) {
@@ -82,7 +90,9 @@ func BenchmarkContents(b *testing.B) {
sel.Contents()
}
}
b.Logf("Contents=%d", n)
if n != 16 {
b.Fatalf("want 16, got %d", n)
}
}
func BenchmarkContentsFiltered(b *testing.B) {
@@ -98,7 +108,9 @@ func BenchmarkContentsFiltered(b *testing.B) {
sel.ContentsFiltered("a[href=\"#Examples\"]")
}
}
b.Logf("ContentsFiltered=%d", n)
if n != 1 {
b.Fatalf("want 1, got %d", n)
}
}
func BenchmarkChildren(b *testing.B) {
@@ -114,7 +126,9 @@ func BenchmarkChildren(b *testing.B) {
sel.Children()
}
}
b.Logf("Children=%d", n)
if n != 2 {
b.Fatalf("want 2, got %d", n)
}
}
func BenchmarkChildrenFiltered(b *testing.B) {
@@ -130,7 +144,9 @@ func BenchmarkChildrenFiltered(b *testing.B) {
sel.ChildrenFiltered(".editsection")
}
}
b.Logf("ChildrenFiltered=%d", n)
if n != 2 {
b.Fatalf("want 2, got %d", n)
}
}
func BenchmarkParent(b *testing.B) {
@@ -146,7 +162,9 @@ func BenchmarkParent(b *testing.B) {
sel.Parent()
}
}
b.Logf("Parent=%d", n)
if n != 55 {
b.Fatalf("want 55, got %d", n)
}
}
func BenchmarkParentFiltered(b *testing.B) {
@@ -162,7 +180,9 @@ func BenchmarkParentFiltered(b *testing.B) {
sel.ParentFiltered("ul[id]")
}
}
b.Logf("ParentFiltered=%d", n)
if n != 4 {
b.Fatalf("want 4, got %d", n)
}
}
func BenchmarkParents(b *testing.B) {
@@ -178,7 +198,9 @@ func BenchmarkParents(b *testing.B) {
sel.Parents()
}
}
b.Logf("Parents=%d", n)
if n != 73 {
b.Fatalf("want 73, got %d", n)
}
}
func BenchmarkParentsFiltered(b *testing.B) {
@@ -194,7 +216,9 @@ func BenchmarkParentsFiltered(b *testing.B) {
sel.ParentsFiltered("tr")
}
}
b.Logf("ParentsFiltered=%d", n)
if n != 18 {
b.Fatalf("want 18, got %d", n)
}
}
func BenchmarkParentsUntil(b *testing.B) {
@@ -210,7 +234,9 @@ func BenchmarkParentsUntil(b *testing.B) {
sel.ParentsUntil("table")
}
}
b.Logf("ParentsUntil=%d", n)
if n != 52 {
b.Fatalf("want 52, got %d", n)
}
}
func BenchmarkParentsUntilSelection(b *testing.B) {
@@ -227,7 +253,9 @@ func BenchmarkParentsUntilSelection(b *testing.B) {
sel.ParentsUntilSelection(sel2)
}
}
b.Logf("ParentsUntilSelection=%d", n)
if n != 70 {
b.Fatalf("want 70, got %d", n)
}
}
func BenchmarkParentsUntilNodes(b *testing.B) {
@@ -245,7 +273,9 @@ func BenchmarkParentsUntilNodes(b *testing.B) {
sel.ParentsUntilNodes(nodes...)
}
}
b.Logf("ParentsUntilNodes=%d", n)
if n != 70 {
b.Fatalf("want 70, got %d", n)
}
}
func BenchmarkParentsFilteredUntil(b *testing.B) {
@@ -261,7 +291,9 @@ func BenchmarkParentsFilteredUntil(b *testing.B) {
sel.ParentsFilteredUntil(":nth-child(1)", "ul")
}
}
b.Logf("ParentsFilteredUntil=%d", n)
if n != 2 {
b.Fatalf("want 2, got %d", n)
}
}
func BenchmarkParentsFilteredUntilSelection(b *testing.B) {
@@ -278,7 +310,9 @@ func BenchmarkParentsFilteredUntilSelection(b *testing.B) {
sel.ParentsFilteredUntilSelection(":nth-child(1)", sel2)
}
}
b.Logf("ParentsFilteredUntilSelection=%d", n)
if n != 2 {
b.Fatalf("want 2, got %d", n)
}
}
func BenchmarkParentsFilteredUntilNodes(b *testing.B) {
@@ -296,7 +330,9 @@ func BenchmarkParentsFilteredUntilNodes(b *testing.B) {
sel.ParentsFilteredUntilNodes(":nth-child(1)", nodes...)
}
}
b.Logf("ParentsFilteredUntilNodes=%d", n)
if n != 2 {
b.Fatalf("want 2, got %d", n)
}
}
func BenchmarkSiblings(b *testing.B) {
@@ -312,7 +348,9 @@ func BenchmarkSiblings(b *testing.B) {
sel.Siblings()
}
}
b.Logf("Siblings=%d", n)
if n != 293 {
b.Fatalf("want 293, got %d", n)
}
}
func BenchmarkSiblingsFiltered(b *testing.B) {
@@ -328,7 +366,9 @@ func BenchmarkSiblingsFiltered(b *testing.B) {
sel.SiblingsFiltered("[class]")
}
}
b.Logf("SiblingsFiltered=%d", n)
if n != 46 {
b.Fatalf("want 46, got %d", n)
}
}
func BenchmarkNext(b *testing.B) {
@@ -344,7 +384,9 @@ func BenchmarkNext(b *testing.B) {
sel.Next()
}
}
b.Logf("Next=%d", n)
if n != 49 {
b.Fatalf("want 49, got %d", n)
}
}
func BenchmarkNextFiltered(b *testing.B) {
@@ -360,7 +402,9 @@ func BenchmarkNextFiltered(b *testing.B) {
sel.NextFiltered("[class]")
}
}
b.Logf("NextFiltered=%d", n)
if n != 6 {
b.Fatalf("want 6, got %d", n)
}
}
func BenchmarkNextAll(b *testing.B) {
@@ -376,7 +420,9 @@ func BenchmarkNextAll(b *testing.B) {
sel.NextAll()
}
}
b.Logf("NextAll=%d", n)
if n != 234 {
b.Fatalf("want 234, got %d", n)
}
}
func BenchmarkNextAllFiltered(b *testing.B) {
@@ -392,7 +438,9 @@ func BenchmarkNextAllFiltered(b *testing.B) {
sel.NextAllFiltered("[class]")
}
}
b.Logf("NextAllFiltered=%d", n)
if n != 33 {
b.Fatalf("want 33, got %d", n)
}
}
func BenchmarkPrev(b *testing.B) {
@@ -408,7 +456,9 @@ func BenchmarkPrev(b *testing.B) {
sel.Prev()
}
}
b.Logf("Prev=%d", n)
if n != 49 {
b.Fatalf("want 49, got %d", n)
}
}
func BenchmarkPrevFiltered(b *testing.B) {
@@ -426,7 +476,9 @@ func BenchmarkPrevFiltered(b *testing.B) {
}
// There is one more Prev li with a class, compared to Next li with a class
// (confirmed by looking at the HTML, this is ok)
b.Logf("PrevFiltered=%d", n)
if n != 7 {
b.Fatalf("want 7, got %d", n)
}
}
func BenchmarkPrevAll(b *testing.B) {
@@ -442,7 +494,9 @@ func BenchmarkPrevAll(b *testing.B) {
sel.PrevAll()
}
}
b.Logf("PrevAll=%d", n)
if n != 78 {
b.Fatalf("want 78, got %d", n)
}
}
func BenchmarkPrevAllFiltered(b *testing.B) {
@@ -458,7 +512,9 @@ func BenchmarkPrevAllFiltered(b *testing.B) {
sel.PrevAllFiltered("[class]")
}
}
b.Logf("PrevAllFiltered=%d", n)
if n != 6 {
b.Fatalf("want 6, got %d", n)
}
}
func BenchmarkNextUntil(b *testing.B) {
@@ -474,7 +530,9 @@ func BenchmarkNextUntil(b *testing.B) {
sel.NextUntil(":nth-child(4)")
}
}
b.Logf("NextUntil=%d", n)
if n != 84 {
b.Fatalf("want 84, got %d", n)
}
}
func BenchmarkNextUntilSelection(b *testing.B) {
@@ -491,7 +549,9 @@ func BenchmarkNextUntilSelection(b *testing.B) {
sel.NextUntilSelection(sel2)
}
}
b.Logf("NextUntilSelection=%d", n)
if n != 42 {
b.Fatalf("want 42, got %d", n)
}
}
func BenchmarkNextUntilNodes(b *testing.B) {
@@ -509,7 +569,9 @@ func BenchmarkNextUntilNodes(b *testing.B) {
sel.NextUntilNodes(nodes...)
}
}
b.Logf("NextUntilNodes=%d", n)
if n != 12 {
b.Fatalf("want 12, got %d", n)
}
}
func BenchmarkPrevUntil(b *testing.B) {
@@ -525,7 +587,9 @@ func BenchmarkPrevUntil(b *testing.B) {
sel.PrevUntil(":nth-child(4)")
}
}
b.Logf("PrevUntil=%d", n)
if n != 238 {
b.Fatalf("want 238, got %d", n)
}
}
func BenchmarkPrevUntilSelection(b *testing.B) {
@@ -542,7 +606,9 @@ func BenchmarkPrevUntilSelection(b *testing.B) {
sel.PrevUntilSelection(sel2)
}
}
b.Logf("PrevUntilSelection=%d", n)
if n != 49 {
b.Fatalf("want 49, got %d", n)
}
}
func BenchmarkPrevUntilNodes(b *testing.B) {
@@ -560,7 +626,9 @@ func BenchmarkPrevUntilNodes(b *testing.B) {
sel.PrevUntilNodes(nodes...)
}
}
b.Logf("PrevUntilNodes=%d", n)
if n != 11 {
b.Fatalf("want 11, got %d", n)
}
}
func BenchmarkNextFilteredUntil(b *testing.B) {
@@ -576,7 +644,9 @@ func BenchmarkNextFilteredUntil(b *testing.B) {
sel.NextFilteredUntil("p", "div")
}
}
b.Logf("NextFilteredUntil=%d", n)
if n != 22 {
b.Fatalf("want 22, got %d", n)
}
}
func BenchmarkNextFilteredUntilSelection(b *testing.B) {
@@ -593,7 +663,9 @@ func BenchmarkNextFilteredUntilSelection(b *testing.B) {
sel.NextFilteredUntilSelection("p", sel2)
}
}
b.Logf("NextFilteredUntilSelection=%d", n)
if n != 22 {
b.Fatalf("want 22, got %d", n)
}
}
func BenchmarkNextFilteredUntilNodes(b *testing.B) {
@@ -611,7 +683,9 @@ func BenchmarkNextFilteredUntilNodes(b *testing.B) {
sel.NextFilteredUntilNodes("p", nodes...)
}
}
b.Logf("NextFilteredUntilNodes=%d", n)
if n != 22 {
b.Fatalf("want 22, got %d", n)
}
}
func BenchmarkPrevFilteredUntil(b *testing.B) {
@@ -627,7 +701,9 @@ func BenchmarkPrevFilteredUntil(b *testing.B) {
sel.PrevFilteredUntil("p", "div")
}
}
b.Logf("PrevFilteredUntil=%d", n)
if n != 20 {
b.Fatalf("want 20, got %d", n)
}
}
func BenchmarkPrevFilteredUntilSelection(b *testing.B) {
@@ -644,7 +720,9 @@ func BenchmarkPrevFilteredUntilSelection(b *testing.B) {
sel.PrevFilteredUntilSelection("p", sel2)
}
}
b.Logf("PrevFilteredUntilSelection=%d", n)
if n != 20 {
b.Fatalf("want 20, got %d", n)
}
}
func BenchmarkPrevFilteredUntilNodes(b *testing.B) {
@@ -662,7 +740,9 @@ func BenchmarkPrevFilteredUntilNodes(b *testing.B) {
sel.PrevFilteredUntilNodes("p", nodes...)
}
}
b.Logf("PrevFilteredUntilNodes=%d", n)
if n != 20 {
b.Fatalf("want 20, got %d", n)
}
}
func BenchmarkClosest(b *testing.B) {
@@ -678,7 +758,9 @@ func BenchmarkClosest(b *testing.B) {
sel.Closest(".pvk-content")
}
}
b.Logf("Closest=%d", n)
if n != 2 {
b.Fatalf("want 2, got %d", n)
}
}
func BenchmarkClosestSelection(b *testing.B) {
@@ -695,7 +777,9 @@ func BenchmarkClosestSelection(b *testing.B) {
sel.ClosestSelection(sel2)
}
}
b.Logf("ClosestSelection=%d", n)
if n != 2 {
b.Fatalf("want 2, got %d", n)
}
}
func BenchmarkClosestNodes(b *testing.B) {
@@ -712,5 +796,7 @@ func BenchmarkClosestNodes(b *testing.B) {
sel.ClosestNodes(nodes...)
}
}
b.Logf("ClosestNodes=%d", n)
if n != 2 {
b.Fatalf("want 2, got %d", n)
}
}
+1 -1
View File
@@ -1,4 +1,4 @@
// Copyright (c) 2012-2014, Martin Angers & Contributors
// Copyright (c) 2012-2016, Martin Angers & Contributors
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without modification,
+2 -2
View File
@@ -13,7 +13,7 @@ and then:
```
// Load the URL
res, err := http.Get(url)
if e != nil {
if err != nil {
// handle error
}
defer res.Body.Close()
@@ -65,4 +65,4 @@ Thanks to github user @jmoiron.
[otto]: https://github.com/robertkrimen/otto
[exotto]: https://gist.github.com/cryptix/87127f76a94183747b53
[iconv]: http://godoc.org/?q=iconv
[text]: http://godoc.org/code.google.com/p/go.text/encoding
[text]: https://godoc.org/golang.org/x/text/encoding
+8 -10
View File
@@ -1,26 +1,24 @@
package goquery
package goquery_test
import (
"fmt"
"log"
// In real use, this import would be required (not in this example, since it
// is part of the goquery package)
//"github.com/PuerkitoBio/goquery"
"github.com/PuerkitoBio/goquery"
)
// This example scrapes the reviews shown on the home page of metalsucks.net.
func ExampleScrape_MetalSucks() {
// Load the HTML document (in real use, the type would be *goquery.Document)
doc, err := NewDocument("http://metalsucks.net")
func Example() {
// Load the HTML document
doc, err := goquery.NewDocument("http://metalsucks.net")
if err != nil {
log.Fatal(err)
}
// Find the review items (the type of the Selection would be *goquery.Selection)
doc.Find(".reviews-wrap article .review-rhs").Each(func(i int, s *Selection) {
// Find the review items
doc.Find(".sidebar-reviews article .content-block").Each(func(i int, s *goquery.Selection) {
// For each item found, get the band and title
band := s.Find("h3").Text()
band := s.Find("a").Text()
title := s.Find("i").Text()
fmt.Printf("Review %d: %s - %s\n", i, band, title)
})
+27 -6
View File
@@ -1,16 +1,13 @@
package goquery
import (
"github.com/andybalholm/cascadia"
"golang.org/x/net/html"
)
import "golang.org/x/net/html"
// Add adds the selector string's matching nodes to those in the current
// selection and returns a new Selection object.
// The selector string is run in the context of the document of the current
// Selection object.
func (s *Selection) Add(selector string) *Selection {
return s.AddNodes(findWithMatcher([]*html.Node{s.document.rootNode}, cascadia.MustCompile(selector))...)
return s.AddNodes(findWithMatcher([]*html.Node{s.document.rootNode}, compileMatcher(selector))...)
}
// AddMatcher adds the matcher's matching nodes to those in the current
@@ -38,12 +35,36 @@ func (s *Selection) Union(sel *Selection) *Selection {
// AddNodes adds the specified nodes to those in the
// current selection and returns a new Selection object.
func (s *Selection) AddNodes(nodes ...*html.Node) *Selection {
return pushStack(s, appendWithoutDuplicates(s.Nodes, nodes))
return pushStack(s, appendWithoutDuplicates(s.Nodes, nodes, nil))
}
// AndSelf adds the previous set of elements on the stack to the current set.
// It returns a new Selection object containing the current Selection combined
// with the previous one.
// Deprecated: This function has been deprecated and is now an alias for AddBack().
func (s *Selection) AndSelf() *Selection {
return s.AddBack()
}
// AddBack adds the previous set of elements on the stack to the current set.
// It returns a new Selection object containing the current Selection combined
// with the previous one.
func (s *Selection) AddBack() *Selection {
return s.AddSelection(s.prevSel)
}
// AddBackFiltered reduces the previous set of elements on the stack to those that
// match the selector string, and adds them to the current set.
// It returns a new Selection object containing the current Selection combined
// with the filtered previous one
func (s *Selection) AddBackFiltered(selector string) *Selection {
return s.AddSelection(s.prevSel.Filter(selector))
}
// AddBackMatcher reduces the previous set of elements on the stack to those that match
// the mateher, and adds them to the curernt set.
// It returns a new Selection object containing the current Selection combined
// with the filtered previous one
func (s *Selection) AddBackMatcher(m Matcher) *Selection {
return s.AddSelection(s.prevSel.FilterMatcher(m))
}
+50
View File
@@ -9,6 +9,16 @@ func TestAdd(t *testing.T) {
assertLength(t, sel.Nodes, 19)
}
func TestAddInvalid(t *testing.T) {
sel1 := Doc().Find("div.row-fluid")
sel2 := sel1.Add("")
assertLength(t, sel1.Nodes, 9)
assertLength(t, sel2.Nodes, 9)
if sel1 == sel2 {
t.Errorf("selections should not be the same")
}
}
func TestAddRollback(t *testing.T) {
sel := Doc().Find(".pvk-content")
sel2 := sel.Add("a").End()
@@ -56,6 +66,24 @@ func TestAddNodesRollback(t *testing.T) {
assertEqual(t, sel, sel2)
}
func TestAddNodesBig(t *testing.T) {
doc := DocW()
sel := doc.Find("li")
assertLength(t, sel.Nodes, 373)
sel2 := doc.Find("xyz")
assertLength(t, sel2.Nodes, 0)
nodes := sel.Nodes
sel2 = sel2.AddNodes(nodes...)
assertLength(t, sel2.Nodes, 373)
nodes2 := append(nodes, nodes...)
sel2 = sel2.End().AddNodes(nodes2...)
assertLength(t, sel2.Nodes, 373)
nodes3 := append(nodes2, nodes...)
sel2 = sel2.End().AddNodes(nodes3...)
assertLength(t, sel2.Nodes, 373)
}
func TestAndSelf(t *testing.T) {
sel := Doc().Find(".span12").Last().AndSelf()
assertLength(t, sel.Nodes, 2)
@@ -66,3 +94,25 @@ func TestAndSelfRollback(t *testing.T) {
sel2 := sel.Find("a").AndSelf().End().End()
assertEqual(t, sel, sel2)
}
func TestAddBack(t *testing.T) {
sel := Doc().Find(".span12").Last().AddBack()
assertLength(t, sel.Nodes, 2)
}
func TestAddBackRollback(t *testing.T) {
sel := Doc().Find(".pvk-content")
sel2 := sel.Find("a").AddBack().End().End()
assertEqual(t, sel, sel2)
}
func TestAddBackFiltered(t *testing.T) {
sel := Doc().Find(".span12, .footer").Find("h1").AddBackFiltered(".footer")
assertLength(t, sel.Nodes, 2)
}
func TestAddBackFilteredRollback(t *testing.T) {
sel := Doc().Find(".span12, .footer")
sel2 := sel.Find("h1").AddBackFiltered(".footer").End().End()
assertEqual(t, sel, sel2)
}
+16 -9
View File
@@ -1,19 +1,16 @@
package goquery
import (
"github.com/andybalholm/cascadia"
"golang.org/x/net/html"
)
import "golang.org/x/net/html"
// Filter reduces the set of matched elements to those that match the selector string.
// It returns a new Selection object for this subset of matching elements.
func (s *Selection) Filter(selector string) *Selection {
return s.FilterMatcher(cascadia.MustCompile(selector))
return s.FilterMatcher(compileMatcher(selector))
}
// FilterMatcher reduces the set of matched elements to those that match
// the given matcher.
// It returns a new Selection object for this subset of matching elements.
// the given matcher. It returns a new Selection object for this subset
// of matching elements.
func (s *Selection) FilterMatcher(m Matcher) *Selection {
return pushStack(s, winnow(s, m, true))
}
@@ -21,7 +18,7 @@ func (s *Selection) FilterMatcher(m Matcher) *Selection {
// Not removes elements from the Selection that match the selector string.
// It returns a new Selection object with the matching elements removed.
func (s *Selection) Not(selector string) *Selection {
return s.NotMatcher(cascadia.MustCompile(selector))
return s.NotMatcher(compileMatcher(selector))
}
// NotMatcher removes elements from the Selection that match the given matcher.
@@ -142,8 +139,18 @@ func winnow(sel *Selection, m Matcher, keep bool) []*html.Node {
// Filter based on an array of nodes, and the indicator to keep (Filter) or
// to get rid of (Not) the matching elements.
func winnowNodes(sel *Selection, nodes []*html.Node, keep bool) []*html.Node {
if len(nodes)+len(sel.Nodes) < minNodesForSet {
return grep(sel, func(i int, s *Selection) bool {
return isInSlice(nodes, s.Get(0)) == keep
})
}
set := make(map[*html.Node]bool)
for _, n := range nodes {
set[n] = true
}
return grep(sel, func(i int, s *Selection) bool {
return isInSlice(nodes, s.Get(0)) == keep
return set[s.Get(0)] == keep
})
}
+15
View File
@@ -14,6 +14,11 @@ func TestFilterNone(t *testing.T) {
assertLength(t, sel.Nodes, 0)
}
func TestFilterInvalid(t *testing.T) {
sel := Doc().Find(".span12").Filter("")
assertLength(t, sel.Nodes, 0)
}
func TestFilterRollback(t *testing.T) {
sel := Doc().Find(".pvk-content")
sel2 := sel.Filter(".alert").End()
@@ -74,6 +79,11 @@ func TestNot(t *testing.T) {
assertLength(t, sel.Nodes, 1)
}
func TestNotInvalid(t *testing.T) {
sel := Doc().Find(".span12").Not("")
assertLength(t, sel.Nodes, 2)
}
func TestNotRollback(t *testing.T) {
sel := Doc().Find(".span12")
sel2 := sel.Not(".alert").End()
@@ -145,6 +155,11 @@ func TestHas(t *testing.T) {
// Has() returns the high-level .container-fluid div, and the one that is the immediate parent of center-content
}
func TestHasInvalid(t *testing.T) {
sel := Doc().Find(".container-fluid").Has("")
assertLength(t, sel.Nodes, 0)
}
func TestHasRollback(t *testing.T) {
sel := Doc().Find(".container-fluid")
sel2 := sel.Has(".center-content").End()
+8 -2
View File
@@ -1,7 +1,10 @@
package goquery
// Each iterates over a Selection object, executing a function for each
// matched element. It returns the current Selection object.
// matched element. It returns the current Selection object. The function
// f is called for each element in the selection with the index of the
// element in that selection starting at 0, and a *Selection that contains
// only that element.
func (s *Selection) Each(f func(int, *Selection)) *Selection {
for i, n := range s.Nodes {
f(i, newSingleSelection(n, s.document))
@@ -23,7 +26,10 @@ func (s *Selection) EachWithBreak(f func(int, *Selection) bool) *Selection {
}
// Map passes each element in the current matched set through a function,
// producing a slice of string holding the returned values.
// producing a slice of string holding the returned values. The function
// f is called for each element in the selection with the index of the
// element in that selection starting at 0, and a *Selection that contains
// only that element.
func (s *Selection) Map(f func(int, *Selection) string) (result []string) {
for i, n := range s.Nodes {
result = append(result, f(i, newSingleSelection(n, s.document)))
+33 -10
View File
@@ -3,7 +3,6 @@ package goquery
import (
"strings"
"github.com/andybalholm/cascadia"
"golang.org/x/net/html"
)
@@ -15,7 +14,7 @@ import (
//
// This follows the same rules as Selection.Append.
func (s *Selection) After(selector string) *Selection {
return s.AfterMatcher(cascadia.MustCompile(selector))
return s.AfterMatcher(compileMatcher(selector))
}
// AfterMatcher applies the matcher from the root document and inserts the matched elements
@@ -66,7 +65,7 @@ func (s *Selection) AfterNodes(ns ...*html.Node) *Selection {
// appended to all target locations except the last one, which will be moved
// as noted in (2).
func (s *Selection) Append(selector string) *Selection {
return s.AppendMatcher(cascadia.MustCompile(selector))
return s.AppendMatcher(compileMatcher(selector))
}
// AppendMatcher appends the elements specified by the matcher to the end of each element
@@ -103,7 +102,7 @@ func (s *Selection) AppendNodes(ns ...*html.Node) *Selection {
//
// This follows the same rules as Selection.Append.
func (s *Selection) Before(selector string) *Selection {
return s.BeforeMatcher(cascadia.MustCompile(selector))
return s.BeforeMatcher(compileMatcher(selector))
}
// BeforeMatcher inserts the matched elements before each element in the set of matched elements.
@@ -165,7 +164,7 @@ func (s *Selection) Empty() *Selection {
// Prepend prepends the elements specified by the selector to each element in
// the set of matched elements, following the same rules as Append.
func (s *Selection) Prepend(selector string) *Selection {
return s.PrependMatcher(cascadia.MustCompile(selector))
return s.PrependMatcher(compileMatcher(selector))
}
// PrependMatcher prepends the elements specified by the matcher to each
@@ -216,7 +215,7 @@ func (s *Selection) Remove() *Selection {
// RemoveFiltered removes the set of matched elements by selector.
// It returns the Selection of removed nodes.
func (s *Selection) RemoveFiltered(selector string) *Selection {
return s.RemoveMatcher(cascadia.MustCompile(selector))
return s.RemoveMatcher(compileMatcher(selector))
}
// RemoveMatcher removes the set of matched elements.
@@ -231,7 +230,7 @@ func (s *Selection) RemoveMatcher(m Matcher) *Selection {
//
// This follows the same rules as Selection.Append.
func (s *Selection) ReplaceWith(selector string) *Selection {
return s.ReplaceWithMatcher(cascadia.MustCompile(selector))
return s.ReplaceWithMatcher(compileMatcher(selector))
}
// ReplaceWithMatcher replaces each element in the set of matched elements with
@@ -271,6 +270,18 @@ func (s *Selection) ReplaceWithNodes(ns ...*html.Node) *Selection {
return s.Remove()
}
// SetHtml sets the html content of each element in the selection to
// specified html string.
func (s *Selection) SetHtml(html string) *Selection {
return setHtmlNodes(s, parseHtml(html)...)
}
// SetText sets the content of each element in the selection to specified content.
// The provided text string is escaped.
func (s *Selection) SetText(text string) *Selection {
return s.SetHtml(html.EscapeString(text))
}
// Unwrap removes the parents of the set of matched elements, leaving the matched
// elements (and their siblings, if any) in their place.
// It returns the original selection.
@@ -293,7 +304,7 @@ func (s *Selection) Unwrap() *Selection {
//
// It returns the original set of elements.
func (s *Selection) Wrap(selector string) *Selection {
return s.WrapMatcher(cascadia.MustCompile(selector))
return s.WrapMatcher(compileMatcher(selector))
}
// WrapMatcher wraps each element in the set of matched elements inside the
@@ -345,7 +356,7 @@ func (s *Selection) wrapNodes(ns ...*html.Node) *Selection {
//
// It returns the original set of elements.
func (s *Selection) WrapAll(selector string) *Selection {
return s.WrapAllMatcher(cascadia.MustCompile(selector))
return s.WrapAllMatcher(compileMatcher(selector))
}
// WrapAllMatcher wraps a single HTML structure, matched by the given Matcher,
@@ -415,7 +426,7 @@ func (s *Selection) WrapAllNode(n *html.Node) *Selection {
//
// It returns the original set of elements.
func (s *Selection) WrapInner(selector string) *Selection {
return s.WrapInnerMatcher(cascadia.MustCompile(selector))
return s.WrapInnerMatcher(compileMatcher(selector))
}
// WrapInnerMatcher wraps an HTML structure, matched by the given selector,
@@ -482,6 +493,18 @@ func parseHtml(h string) []*html.Node {
return nodes
}
func setHtmlNodes(s *Selection, ns ...*html.Node) *Selection {
for _, n := range s.Nodes {
for c := n.FirstChild; c != nil; c = n.FirstChild {
n.RemoveChild(c)
}
for _, c := range ns {
n.AppendChild(cloneNode(c))
}
}
return s
}
// Get the first child that is an ElementNode
func getFirstChildEl(n *html.Node) *html.Node {
c := n.FirstChild
+60
View File
@@ -278,6 +278,66 @@ func TestReplaceWithHtml(t *testing.T) {
printSel(t, doc.Selection)
}
func TestSetHtml(t *testing.T) {
doc := Doc2Clone()
q := doc.Find("#main, #foot")
q.SetHtml(`<div id="replace">test</div>`)
assertLength(t, doc.Find("#replace").Nodes, 2)
assertLength(t, doc.Find("#main, #foot").Nodes, 2)
if q.Text() != "testtest" {
t.Errorf("Expected text to be %v, found %v", "testtest", q.Text())
}
printSel(t, doc.Selection)
}
func TestSetHtmlNoMatch(t *testing.T) {
doc := Doc2Clone()
q := doc.Find("#notthere")
q.SetHtml(`<div id="replace">test</div>`)
assertLength(t, doc.Find("#replace").Nodes, 0)
printSel(t, doc.Selection)
}
func TestSetHtmlEmpty(t *testing.T) {
doc := Doc2Clone()
q := doc.Find("#main")
q.SetHtml(``)
assertLength(t, doc.Find("#main").Nodes, 1)
assertLength(t, doc.Find("#main").Children().Nodes, 0)
printSel(t, doc.Selection)
}
func TestSetText(t *testing.T) {
doc := Doc2Clone()
q := doc.Find("#main, #foot")
repl := "<div id=\"replace\">test</div>"
q.SetText(repl)
assertLength(t, doc.Find("#replace").Nodes, 0)
assertLength(t, doc.Find("#main, #foot").Nodes, 2)
if q.Text() != (repl + repl) {
t.Errorf("Expected text to be %v, found %v", (repl + repl), q.Text())
}
h, err := q.Html()
if err != nil {
t.Errorf("Error: %v", err)
}
esc := "&lt;div id=&#34;replace&#34;&gt;test&lt;/div&gt;"
if h != esc {
t.Errorf("Expected html to be %v, found %v", esc, h)
}
printSel(t, doc.Selection)
}
func TestReplaceWithSelection(t *testing.T) {
doc := Doc2Clone()
sel := doc.Find("#nf6").ReplaceWithSelection(doc.Find("#nf5"))
+27 -27
View File
@@ -1,37 +1,37 @@
#!/bin/sh
# Copyright 2012 The Go Authors. All rights reserved.
# Use of this source code is governed by a BSD-style
# license that can be found in the LICENSE file.
# git gofmt pre-commit hook
#
# To use, store as .git/hooks/pre-commit inside your repository and make sure
# it has execute permissions.
#
# This script does not handle file names that contain spaces.
echo ">>> golint"
for dir in $(go list ./... | grep -v /vendor/)
do
golint "${dir}"
done
echo "<<< golint"
echo
# golint is purely informational, it doesn't fail with exit code != 0 if it finds something,
# because it may find a lot of false positives. Just print out its result for information.
echo "lint result (informational only):"
golint .
echo ">>> go vet"
go vet $(go list ./... | grep -v /vendor/)
echo "<<< go vet"
echo
# go vet returns 1 if an error was found. Exit the hook with this exit code.
go vet ./...
vetres=$?
echo ">>> gosimple"
gosimple $(go list ./... | grep -v /vendor/)
echo "<<< gosimple"
echo
# Check for gofmt problems and report if any.
gofiles=$(git diff --cached --name-only --diff-filter=ACM | grep '.go$')
gofiles=$(git diff --cached --name-only --diff-filter=ACM | grep '.go$' | grep -v /vendor/)
[ -z "$gofiles" ] && echo "EXIT $vetres" && exit $vetres
unformatted=$(gofmt -l $gofiles)
[ -z "$unformatted" ] && echo "EXIT $vetres" && exit $vetres
if [ -n "$gofiles" ]; then
unformatted=$(gofmt -l $gofiles)
# Some files are not gofmt'd. Print message and fail.
if [ -n "$unformatted" ]; then
# Some files are not gofmt'd.
echo >&2 "Go files must be formatted with gofmt. Please run:"
for fn in $unformatted; do
echo >&2 " gofmt -w $PWD/$fn"
done
fi
fi
echo
echo >&2 "Go files must be formatted with gofmt. Please run:"
for fn in $unformatted; do
echo >&2 " gofmt -w $PWD/$fn"
done
echo "EXIT 1"
exit 1
+18 -21
View File
@@ -63,9 +63,22 @@ func (s *Selection) Text() string {
var buf bytes.Buffer
// Slightly optimized vs calling Each: no single selection object created
for _, n := range s.Nodes {
buf.WriteString(getNodeText(n))
var f func(*html.Node)
f = func(n *html.Node) {
if n.Type == html.TextNode {
// Keep newlines and spaces, like jQuery
buf.WriteString(n.Data)
}
if n.FirstChild != nil {
for c := n.FirstChild; c != nil; c = c.NextSibling {
f(c)
}
}
}
for _, n := range s.Nodes {
f(n)
}
return buf.String()
}
@@ -112,7 +125,7 @@ func (s *Selection) AddClass(class ...string) *Selection {
for _, n := range s.Nodes {
curClasses, attr := getClassesAndAttr(n, true)
for _, newClass := range tcls {
if strings.Index(curClasses, " "+newClass+" ") == -1 {
if !strings.Contains(curClasses, " "+newClass+" ") {
curClasses += newClass + " "
}
}
@@ -129,7 +142,7 @@ func (s *Selection) HasClass(class string) bool {
class = " " + class + " "
for _, n := range s.Nodes {
classes, _ := getClassesAndAttr(n, false)
if strings.Index(classes, class) > -1 {
if strings.Contains(classes, class) {
return true
}
}
@@ -179,7 +192,7 @@ func (s *Selection) ToggleClass(class ...string) *Selection {
for _, n := range s.Nodes {
classes, attr := getClassesAndAttr(n, true)
for _, tcl := range tcls {
if strings.Index(classes, " "+tcl+" ") != -1 {
if strings.Contains(classes, " "+tcl+" ") {
classes = strings.Replace(classes, " "+tcl+" ", " ", -1)
} else {
classes += tcl + " "
@@ -192,22 +205,6 @@ func (s *Selection) ToggleClass(class ...string) *Selection {
return s
}
// Get the specified node's text content.
func getNodeText(node *html.Node) string {
if node.Type == html.TextNode {
// Keep newlines and spaces, like jQuery
return node.Data
} else if node.FirstChild != nil {
var buf bytes.Buffer
for c := node.FirstChild; c != nil; c = c.NextSibling {
buf.WriteString(getNodeText(c))
}
return buf.String()
}
return ""
}
func getAttributePtr(attrName string, n *html.Node) *html.Attribute {
if n == nil {
return nil
+2 -9
View File
@@ -1,18 +1,11 @@
package goquery
import (
"github.com/andybalholm/cascadia"
"golang.org/x/net/html"
)
import "golang.org/x/net/html"
// Is checks the current matched set of elements against a selector and
// returns true if at least one of these elements matches.
func (s *Selection) Is(selector string) bool {
if len(s.Nodes) > 0 {
return s.IsMatcher(cascadia.MustCompile(selector))
}
return false
return s.IsMatcher(compileMatcher(selector))
}
// IsMatcher checks the current matched set of elements against a matcher and
+7
View File
@@ -11,6 +11,13 @@ func TestIs(t *testing.T) {
}
}
func TestIsInvalid(t *testing.T) {
sel := Doc().Find(".footer p:nth-child(1)")
if sel.Is("") {
t.Error("Is should not succeed with invalid selector string")
}
}
func TestIsPositional(t *testing.T) {
sel := Doc().Find(".footer p:nth-child(2)")
if !sel.Is("p:nth-child(2)") {
+31 -29
View File
@@ -1,9 +1,6 @@
package goquery
import (
"github.com/andybalholm/cascadia"
"golang.org/x/net/html"
)
import "golang.org/x/net/html"
type siblingType int
@@ -24,7 +21,7 @@ const (
// elements, filtered by a selector. It returns a new Selection object
// containing these matched elements.
func (s *Selection) Find(selector string) *Selection {
return pushStack(s, findWithMatcher(s.Nodes, cascadia.MustCompile(selector)))
return pushStack(s, findWithMatcher(s.Nodes, compileMatcher(selector)))
}
// FindMatcher gets the descendants of each element in the current set of matched
@@ -93,7 +90,7 @@ func (s *Selection) Children() *Selection {
// filtered by the specified selector. It returns a new
// Selection object containing these elements.
func (s *Selection) ChildrenFiltered(selector string) *Selection {
return filterAndPush(s, getChildrenNodes(s.Nodes, siblingAll), cascadia.MustCompile(selector))
return filterAndPush(s, getChildrenNodes(s.Nodes, siblingAll), compileMatcher(selector))
}
// ChildrenMatcher gets the child elements of each element in the Selection,
@@ -112,7 +109,7 @@ func (s *Selection) Parent() *Selection {
// ParentFiltered gets the parent of each element in the Selection filtered by a
// selector. It returns a new Selection object containing the matched elements.
func (s *Selection) ParentFiltered(selector string) *Selection {
return filterAndPush(s, getParentNodes(s.Nodes), cascadia.MustCompile(selector))
return filterAndPush(s, getParentNodes(s.Nodes), compileMatcher(selector))
}
// ParentMatcher gets the parent of each element in the Selection filtered by a
@@ -124,7 +121,7 @@ func (s *Selection) ParentMatcher(m Matcher) *Selection {
// Closest gets the first element that matches the selector by testing the
// element itself and traversing up through its ancestors in the DOM tree.
func (s *Selection) Closest(selector string) *Selection {
cs := cascadia.MustCompile(selector)
cs := compileMatcher(selector)
return s.ClosestMatcher(cs)
}
@@ -146,11 +143,15 @@ func (s *Selection) ClosestMatcher(m Matcher) *Selection {
// ClosestNodes gets the first element that matches one of the nodes by testing the
// element itself and traversing up through its ancestors in the DOM tree.
func (s *Selection) ClosestNodes(nodes ...*html.Node) *Selection {
set := make(map[*html.Node]bool)
for _, n := range nodes {
set[n] = true
}
return pushStack(s, mapNodes(s.Nodes, func(i int, n *html.Node) []*html.Node {
// For each node in the selection, test the node itself, then each parent
// until a match is found.
for ; n != nil; n = n.Parent {
if isInSlice(nodes, n) {
if set[n] {
return []*html.Node{n}
}
}
@@ -177,7 +178,7 @@ func (s *Selection) Parents() *Selection {
// ParentsFiltered gets the ancestors of each element in the current
// Selection. It returns a new Selection object with the matched elements.
func (s *Selection) ParentsFiltered(selector string) *Selection {
return filterAndPush(s, getParentsNodes(s.Nodes, nil, nil), cascadia.MustCompile(selector))
return filterAndPush(s, getParentsNodes(s.Nodes, nil, nil), compileMatcher(selector))
}
// ParentsMatcher gets the ancestors of each element in the current
@@ -190,7 +191,7 @@ func (s *Selection) ParentsMatcher(m Matcher) *Selection {
// not including the element matched by the selector. It returns a new Selection
// object containing the matched elements.
func (s *Selection) ParentsUntil(selector string) *Selection {
return pushStack(s, getParentsNodes(s.Nodes, cascadia.MustCompile(selector), nil))
return pushStack(s, getParentsNodes(s.Nodes, compileMatcher(selector), nil))
}
// ParentsUntilMatcher gets the ancestors of each element in the Selection, up to but
@@ -221,7 +222,7 @@ func (s *Selection) ParentsUntilNodes(nodes ...*html.Node) *Selection {
// results based on a selector string. It returns a new Selection
// object containing the matched elements.
func (s *Selection) ParentsFilteredUntil(filterSelector, untilSelector string) *Selection {
return filterAndPush(s, getParentsNodes(s.Nodes, cascadia.MustCompile(untilSelector), nil), cascadia.MustCompile(filterSelector))
return filterAndPush(s, getParentsNodes(s.Nodes, compileMatcher(untilSelector), nil), compileMatcher(filterSelector))
}
// ParentsFilteredUntilMatcher is like ParentsUntilMatcher, with the option to filter the
@@ -234,7 +235,7 @@ func (s *Selection) ParentsFilteredUntilMatcher(filter, until Matcher) *Selectio
// option to filter the results based on a selector string. It returns a new
// Selection object containing the matched elements.
func (s *Selection) ParentsFilteredUntilSelection(filterSelector string, sel *Selection) *Selection {
return s.ParentsMatcherUntilSelection(cascadia.MustCompile(filterSelector), sel)
return s.ParentsMatcherUntilSelection(compileMatcher(filterSelector), sel)
}
// ParentsMatcherUntilSelection is like ParentsUntilSelection, with the
@@ -251,7 +252,7 @@ func (s *Selection) ParentsMatcherUntilSelection(filter Matcher, sel *Selection)
// option to filter the results based on a selector string. It returns a new
// Selection object containing the matched elements.
func (s *Selection) ParentsFilteredUntilNodes(filterSelector string, nodes ...*html.Node) *Selection {
return filterAndPush(s, getParentsNodes(s.Nodes, nil, nodes), cascadia.MustCompile(filterSelector))
return filterAndPush(s, getParentsNodes(s.Nodes, nil, nodes), compileMatcher(filterSelector))
}
// ParentsMatcherUntilNodes is like ParentsUntilNodes, with the
@@ -271,7 +272,7 @@ func (s *Selection) Siblings() *Selection {
// filtered by a selector. It returns a new Selection object containing the
// matched elements.
func (s *Selection) SiblingsFiltered(selector string) *Selection {
return filterAndPush(s, getSiblingNodes(s.Nodes, siblingAll, nil, nil), cascadia.MustCompile(selector))
return filterAndPush(s, getSiblingNodes(s.Nodes, siblingAll, nil, nil), compileMatcher(selector))
}
// SiblingsMatcher gets the siblings of each element in the Selection
@@ -291,7 +292,7 @@ func (s *Selection) Next() *Selection {
// Selection filtered by a selector. It returns a new Selection object
// containing the matched elements.
func (s *Selection) NextFiltered(selector string) *Selection {
return filterAndPush(s, getSiblingNodes(s.Nodes, siblingNext, nil, nil), cascadia.MustCompile(selector))
return filterAndPush(s, getSiblingNodes(s.Nodes, siblingNext, nil, nil), compileMatcher(selector))
}
// NextMatcher gets the immediately following sibling of each element in the
@@ -311,7 +312,7 @@ func (s *Selection) NextAll() *Selection {
// Selection filtered by a selector. It returns a new Selection object
// containing the matched elements.
func (s *Selection) NextAllFiltered(selector string) *Selection {
return filterAndPush(s, getSiblingNodes(s.Nodes, siblingNextAll, nil, nil), cascadia.MustCompile(selector))
return filterAndPush(s, getSiblingNodes(s.Nodes, siblingNextAll, nil, nil), compileMatcher(selector))
}
// NextAllMatcher gets all the following siblings of each element in the
@@ -331,7 +332,7 @@ func (s *Selection) Prev() *Selection {
// Selection filtered by a selector. It returns a new Selection object
// containing the matched elements.
func (s *Selection) PrevFiltered(selector string) *Selection {
return filterAndPush(s, getSiblingNodes(s.Nodes, siblingPrev, nil, nil), cascadia.MustCompile(selector))
return filterAndPush(s, getSiblingNodes(s.Nodes, siblingPrev, nil, nil), compileMatcher(selector))
}
// PrevMatcher gets the immediately preceding sibling of each element in the
@@ -351,7 +352,7 @@ func (s *Selection) PrevAll() *Selection {
// Selection filtered by a selector. It returns a new Selection object
// containing the matched elements.
func (s *Selection) PrevAllFiltered(selector string) *Selection {
return filterAndPush(s, getSiblingNodes(s.Nodes, siblingPrevAll, nil, nil), cascadia.MustCompile(selector))
return filterAndPush(s, getSiblingNodes(s.Nodes, siblingPrevAll, nil, nil), compileMatcher(selector))
}
// PrevAllMatcher gets all the preceding siblings of each element in the
@@ -366,7 +367,7 @@ func (s *Selection) PrevAllMatcher(m Matcher) *Selection {
// object containing the matched elements.
func (s *Selection) NextUntil(selector string) *Selection {
return pushStack(s, getSiblingNodes(s.Nodes, siblingNextUntil,
cascadia.MustCompile(selector), nil))
compileMatcher(selector), nil))
}
// NextUntilMatcher gets all following siblings of each element up to but not
@@ -400,7 +401,7 @@ func (s *Selection) NextUntilNodes(nodes ...*html.Node) *Selection {
// object containing the matched elements.
func (s *Selection) PrevUntil(selector string) *Selection {
return pushStack(s, getSiblingNodes(s.Nodes, siblingPrevUntil,
cascadia.MustCompile(selector), nil))
compileMatcher(selector), nil))
}
// PrevUntilMatcher gets all preceding siblings of each element up to but not
@@ -434,7 +435,7 @@ func (s *Selection) PrevUntilNodes(nodes ...*html.Node) *Selection {
// It returns a new Selection object containing the matched elements.
func (s *Selection) NextFilteredUntil(filterSelector, untilSelector string) *Selection {
return filterAndPush(s, getSiblingNodes(s.Nodes, siblingNextUntil,
cascadia.MustCompile(untilSelector), nil), cascadia.MustCompile(filterSelector))
compileMatcher(untilSelector), nil), compileMatcher(filterSelector))
}
// NextFilteredUntilMatcher is like NextUntilMatcher, with the option to filter
@@ -449,7 +450,7 @@ func (s *Selection) NextFilteredUntilMatcher(filter, until Matcher) *Selection {
// option to filter the results based on a selector string. It returns a new
// Selection object containing the matched elements.
func (s *Selection) NextFilteredUntilSelection(filterSelector string, sel *Selection) *Selection {
return s.NextMatcherUntilSelection(cascadia.MustCompile(filterSelector), sel)
return s.NextMatcherUntilSelection(compileMatcher(filterSelector), sel)
}
// NextMatcherUntilSelection is like NextUntilSelection, with the
@@ -467,7 +468,7 @@ func (s *Selection) NextMatcherUntilSelection(filter Matcher, sel *Selection) *S
// Selection object containing the matched elements.
func (s *Selection) NextFilteredUntilNodes(filterSelector string, nodes ...*html.Node) *Selection {
return filterAndPush(s, getSiblingNodes(s.Nodes, siblingNextUntil,
nil, nodes), cascadia.MustCompile(filterSelector))
nil, nodes), compileMatcher(filterSelector))
}
// NextMatcherUntilNodes is like NextUntilNodes, with the
@@ -483,7 +484,7 @@ func (s *Selection) NextMatcherUntilNodes(filter Matcher, nodes ...*html.Node) *
// It returns a new Selection object containing the matched elements.
func (s *Selection) PrevFilteredUntil(filterSelector, untilSelector string) *Selection {
return filterAndPush(s, getSiblingNodes(s.Nodes, siblingPrevUntil,
cascadia.MustCompile(untilSelector), nil), cascadia.MustCompile(filterSelector))
compileMatcher(untilSelector), nil), compileMatcher(filterSelector))
}
// PrevFilteredUntilMatcher is like PrevUntilMatcher, with the option to filter
@@ -498,7 +499,7 @@ func (s *Selection) PrevFilteredUntilMatcher(filter, until Matcher) *Selection {
// option to filter the results based on a selector string. It returns a new
// Selection object containing the matched elements.
func (s *Selection) PrevFilteredUntilSelection(filterSelector string, sel *Selection) *Selection {
return s.PrevMatcherUntilSelection(cascadia.MustCompile(filterSelector), sel)
return s.PrevMatcherUntilSelection(compileMatcher(filterSelector), sel)
}
// PrevMatcherUntilSelection is like PrevUntilSelection, with the
@@ -516,7 +517,7 @@ func (s *Selection) PrevMatcherUntilSelection(filter Matcher, sel *Selection) *S
// Selection object containing the matched elements.
func (s *Selection) PrevFilteredUntilNodes(filterSelector string, nodes ...*html.Node) *Selection {
return filterAndPush(s, getSiblingNodes(s.Nodes, siblingPrevUntil,
nil, nodes), cascadia.MustCompile(filterSelector))
nil, nodes), compileMatcher(filterSelector))
}
// PrevMatcherUntilNodes is like PrevUntilNodes, with the
@@ -687,10 +688,11 @@ func getParentNodes(nodes []*html.Node) []*html.Node {
// Returns an array of nodes mapped by calling the callback function once for
// each node in the source nodes.
func mapNodes(nodes []*html.Node, f func(int, *html.Node) []*html.Node) (result []*html.Node) {
set := make(map[*html.Node]bool)
for i, n := range nodes {
if vals := f(i, n); len(vals) > 0 {
result = appendWithoutDuplicates(result, vals)
result = appendWithoutDuplicates(result, vals, set)
}
}
return
return result
}
+99 -3
View File
@@ -21,9 +21,19 @@ func TestFindNotSelf(t *testing.T) {
assertLength(t, sel.Nodes, 0)
}
func TestFindInvalidSelector(t *testing.T) {
defer assertPanic(t)
Doc().Find(":+ ^")
func TestFindInvalid(t *testing.T) {
sel := Doc().Find(":+ ^")
assertLength(t, sel.Nodes, 0)
}
func TestFindBig(t *testing.T) {
doc := DocW()
sel := doc.Find("li")
assertLength(t, sel.Nodes, 373)
sel2 := doc.Find("span")
assertLength(t, sel2.Nodes, 448)
sel3 := sel.FindSelection(sel2)
assertLength(t, sel3.Nodes, 248)
}
func TestChainedFind(t *testing.T) {
@@ -31,6 +41,11 @@ func TestChainedFind(t *testing.T) {
assertLength(t, sel.Nodes, 4)
}
func TestChainedFindInvalid(t *testing.T) {
sel := Doc().Find("div.hero-unit").Find("")
assertLength(t, sel.Nodes, 0)
}
func TestChildren(t *testing.T) {
sel := Doc().Find(".pvk-content").Children()
assertLength(t, sel.Nodes, 5)
@@ -58,6 +73,11 @@ func TestChildrenFiltered(t *testing.T) {
assertLength(t, sel.Nodes, 1)
}
func TestChildrenFilteredInvalid(t *testing.T) {
sel := Doc().Find(".pvk-content").ChildrenFiltered("")
assertLength(t, sel.Nodes, 0)
}
func TestChildrenFilteredRollback(t *testing.T) {
sel := Doc().Find(".pvk-content")
sel2 := sel.ChildrenFiltered(".hero-unit").End()
@@ -69,6 +89,11 @@ func TestContentsFiltered(t *testing.T) {
assertLength(t, sel.Nodes, 1)
}
func TestContentsFilteredInvalid(t *testing.T) {
sel := Doc().Find(".pvk-content").ContentsFiltered("~")
assertLength(t, sel.Nodes, 0)
}
func TestContentsFilteredRollback(t *testing.T) {
sel := Doc().Find(".pvk-content")
sel2 := sel.ContentsFiltered(".hero-unit").End()
@@ -102,6 +127,11 @@ func TestParentFiltered(t *testing.T) {
assertClass(t, sel, "hero-unit")
}
func TestParentFilteredInvalid(t *testing.T) {
sel := Doc().Find(".container-fluid").ParentFiltered("")
assertLength(t, sel.Nodes, 0)
}
func TestParentFilteredRollback(t *testing.T) {
sel := Doc().Find(".container-fluid")
sel2 := sel.ParentFiltered(".hero-unit").End()
@@ -130,6 +160,11 @@ func TestParentsFiltered(t *testing.T) {
assertLength(t, sel.Nodes, 1)
}
func TestParentsFilteredInvalid(t *testing.T) {
sel := Doc().Find(".container-fluid").ParentsFiltered("")
assertLength(t, sel.Nodes, 0)
}
func TestParentsFilteredRollback(t *testing.T) {
sel := Doc().Find(".container-fluid")
sel2 := sel.ParentsFiltered("body").End()
@@ -141,6 +176,11 @@ func TestParentsUntil(t *testing.T) {
assertLength(t, sel.Nodes, 6)
}
func TestParentsUntilInvalid(t *testing.T) {
sel := Doc().Find(".container-fluid").ParentsUntil("")
assertLength(t, sel.Nodes, 8)
}
func TestParentsUntilRollback(t *testing.T) {
sel := Doc().Find(".container-fluid")
sel2 := sel.ParentsUntil("body").End()
@@ -180,6 +220,11 @@ func TestParentsFilteredUntil(t *testing.T) {
assertLength(t, sel.Nodes, 2)
}
func TestParentsFilteredUntilInvalid(t *testing.T) {
sel := Doc().Find(".container-fluid").ParentsFilteredUntil("", "")
assertLength(t, sel.Nodes, 0)
}
func TestParentsFilteredUntilRollback(t *testing.T) {
sel := Doc().Find(".container-fluid")
sel2 := sel.ParentsFilteredUntil(".pvk-content", "body").End()
@@ -240,6 +285,11 @@ func TestSiblingsFiltered(t *testing.T) {
assertLength(t, sel.Nodes, 3)
}
func TestSiblingsFilteredInvalid(t *testing.T) {
sel := Doc().Find(".pvk-gutter").SiblingsFiltered("")
assertLength(t, sel.Nodes, 0)
}
func TestSiblingsFilteredRollback(t *testing.T) {
sel := Doc().Find(".pvk-gutter")
sel2 := sel.SiblingsFiltered(".pvk-content").End()
@@ -272,6 +322,11 @@ func TestNextFiltered(t *testing.T) {
assertLength(t, sel.Nodes, 2)
}
func TestNextFilteredInvalid(t *testing.T) {
sel := Doc().Find(".container-fluid").NextFiltered("")
assertLength(t, sel.Nodes, 0)
}
func TestNextFilteredRollback(t *testing.T) {
sel := Doc().Find(".container-fluid")
sel2 := sel.NextFiltered("div").End()
@@ -310,6 +365,11 @@ func TestPrevFiltered(t *testing.T) {
assertLength(t, sel.Nodes, 5)
}
func TestPrevFilteredInvalid(t *testing.T) {
sel := Doc().Find(".row-fluid").PrevFiltered("")
assertLength(t, sel.Nodes, 0)
}
func TestPrevFilteredRollback(t *testing.T) {
sel := Doc().Find(".row-fluid")
sel2 := sel.PrevFiltered(".row-fluid").End()
@@ -342,6 +402,11 @@ func TestNextAllFiltered(t *testing.T) {
assertLength(t, sel.Nodes, 2)
}
func TestNextAllFilteredInvalid(t *testing.T) {
sel := Doc().Find("#cf2 .row-fluid").NextAllFiltered("")
assertLength(t, sel.Nodes, 0)
}
func TestNextAllFilteredRollback(t *testing.T) {
sel := Doc().Find("#cf2 .row-fluid")
sel2 := sel.NextAllFiltered("[ng-cloak]").End()
@@ -380,6 +445,11 @@ func TestPrevAllFiltered(t *testing.T) {
assertLength(t, sel.Nodes, 3)
}
func TestPrevAllFilteredInvalid(t *testing.T) {
sel := Doc().Find(".pvk-gutter").PrevAllFiltered("")
assertLength(t, sel.Nodes, 0)
}
func TestPrevAllFilteredRollback(t *testing.T) {
sel := Doc().Find(".pvk-gutter")
sel2 := sel.PrevAllFiltered(".pvk-content").End()
@@ -392,6 +462,11 @@ func TestNextUntil(t *testing.T) {
assertSelectionIs(t, sel, "h4")
}
func TestNextUntilInvalid(t *testing.T) {
sel := Doc().Find(".alert a").NextUntil("")
assertLength(t, sel.Nodes, 2)
}
func TestNextUntil2(t *testing.T) {
sel := Doc().Find("#cf2-1").NextUntil("[ng-cloak]")
assertLength(t, sel.Nodes, 1)
@@ -446,6 +521,11 @@ func TestPrevUntil(t *testing.T) {
assertSelectionIs(t, sel, "h4")
}
func TestPrevUntilInvalid(t *testing.T) {
sel := Doc().Find(".alert p").PrevUntil("")
assertLength(t, sel.Nodes, 2)
}
func TestPrevUntil2(t *testing.T) {
sel := Doc().Find("[ng-cloak]").PrevUntil(":not([ng-cloak])")
assertLength(t, sel.Nodes, 1)
@@ -500,6 +580,11 @@ func TestNextFilteredUntil(t *testing.T) {
assertSelectionIs(t, sel, "#n3", "#n5", "#nf3", "#nf5")
}
func TestNextFilteredUntilInvalid(t *testing.T) {
sel := Doc2().Find(".two").NextFilteredUntil("", "")
assertLength(t, sel.Nodes, 0)
}
func TestNextFilteredUntilRollback(t *testing.T) {
sel := Doc2().Find(".two")
sel2 := sel.NextFilteredUntil(".even", ".six").End()
@@ -542,6 +627,11 @@ func TestPrevFilteredUntil(t *testing.T) {
assertSelectionIs(t, sel, "#n4", "#n2", "#nf4", "#nf2")
}
func TestPrevFilteredUntilInvalid(t *testing.T) {
sel := Doc2().Find(".five").PrevFilteredUntil("", "")
assertLength(t, sel.Nodes, 0)
}
func TestPrevFilteredUntilRollback(t *testing.T) {
sel := Doc2().Find(".four")
sel2 := sel.PrevFilteredUntil(".odd", ".one").End()
@@ -598,6 +688,12 @@ func TestClosestNone(t *testing.T) {
assertLength(t, sel2.Nodes, 0)
}
func TestClosestInvalid(t *testing.T) {
sel := Doc().Find("h4")
sel2 := sel.Closest("")
assertLength(t, sel2.Nodes, 0)
}
func TestClosestMany(t *testing.T) {
sel := Doc().Find(".container-fluid")
sel2 := sel.Closest(".pvk-content")
+20
View File
@@ -6,6 +6,8 @@ import (
"net/http"
"net/url"
"github.com/andybalholm/cascadia"
"golang.org/x/net/html"
)
@@ -113,3 +115,21 @@ type Matcher interface {
MatchAll(*html.Node) []*html.Node
Filter([]*html.Node) []*html.Node
}
// compileMatcher compiles the selector string s and returns
// the corresponding Matcher. If s is an invalid selector string,
// it returns a Matcher that fails all matches.
func compileMatcher(s string) Matcher {
cs, err := cascadia.Compile(s)
if err != nil {
return invalidMatcher{}
}
return cs
}
// invalidMatcher is a Matcher that always fails to match.
type invalidMatcher struct{}
func (invalidMatcher) Match(n *html.Node) bool { return false }
func (invalidMatcher) MatchAll(n *html.Node) []*html.Node { return nil }
func (invalidMatcher) Filter(ns []*html.Node) []*html.Node { return nil }
+19 -9
View File
@@ -4,6 +4,7 @@ import (
"bytes"
"fmt"
"os"
"strings"
"testing"
"golang.org/x/net/html"
@@ -22,45 +23,42 @@ func Doc() *Document {
}
return doc
}
func DocClone() *Document {
return CloneDocument(Doc())
}
func Doc2() *Document {
if doc2 == nil {
doc2 = loadDoc("page2.html")
}
return doc2
}
func Doc2Clone() *Document {
return CloneDocument(Doc2())
}
func Doc3() *Document {
if doc3 == nil {
doc3 = loadDoc("page3.html")
}
return doc3
}
func Doc3Clone() *Document {
return CloneDocument(Doc3())
}
func DocB() *Document {
if docB == nil {
docB = loadDoc("gotesting.html")
}
return docB
}
func DocBClone() *Document {
return CloneDocument(DocB())
}
func DocW() *Document {
if docW == nil {
docW = loadDoc("gowiki.html")
}
return docW
}
func DocWClone() *Document {
return CloneDocument(DocW())
}
func assertLength(t *testing.T, nodes []*html.Node, length int) {
if len(nodes) != length {
@@ -190,3 +188,15 @@ func TestNewDocumentFromResponseNil(t *testing.T) {
t.Error("Expected error, got none")
}
}
func TestIssue103(t *testing.T) {
d, err := NewDocumentFromReader(strings.NewReader("<html><title>Scientists Stored These Images in DNA—Then Flawlessly Retrieved Them</title></html>"))
if err != nil {
t.Error(err)
}
text := d.Find("title").Text()
for i, r := range text {
t.Logf("%d: %d - %q\n", i, r, string(r))
}
t.Log(text)
}
+35 -16
View File
@@ -6,6 +6,11 @@ import (
"golang.org/x/net/html"
)
// used to determine if a set (map[*html.Node]bool) should be used
// instead of iterating over a slice. The set uses more memory and
// is slower than slice iteration for small N.
const minNodesForSet = 1000
var nodeNames = []string{
html.ErrorNode: "#error",
html.TextNode: "#text",
@@ -20,12 +25,12 @@ var nodeNames = []string{
// Go's net/html package defines the following node types, listed with
// the corresponding returned value from this function:
//
// ErrorNode : #error
// TextNode : #text
// DocumentNode : #document
// ElementNode : the element's tag name
// CommentNode : #comment
// DoctypeNode : the name of the document type
// ErrorNode : #error
// TextNode : #text
// DocumentNode : #document
// ElementNode : the element's tag name
// CommentNode : #comment
// DoctypeNode : the name of the document type
//
func NodeName(s *Selection) string {
if s.Length() == 0 {
@@ -62,13 +67,6 @@ func OuterHtml(s *Selection) (string, error) {
return buf.String(), nil
}
func getChildren(n *html.Node) (result []*html.Node) {
for c := n.FirstChild; c != nil; c = c.NextSibling {
result = append(result, c)
}
return result
}
// Loop through all container nodes to search for the target node.
func sliceContains(container []*html.Node, contained *html.Node) bool {
for _, n := range container {
@@ -112,11 +110,32 @@ func indexInSlice(slice []*html.Node, node *html.Node) int {
// Appends the new nodes to the target slice, making sure no duplicate is added.
// There is no check to the original state of the target slice, so it may still
// contain duplicates. The target slice is returned because append() may create
// a new underlying array.
func appendWithoutDuplicates(target []*html.Node, nodes []*html.Node) []*html.Node {
// a new underlying array. If targetSet is nil, a local set is created with the
// target if len(target) + len(nodes) is greater than minNodesForSet.
func appendWithoutDuplicates(target []*html.Node, nodes []*html.Node, targetSet map[*html.Node]bool) []*html.Node {
// if there are not that many nodes, don't use the map, faster to just use nested loops
// (unless a non-nil targetSet is passed, in which case the caller knows better).
if targetSet == nil && len(target)+len(nodes) < minNodesForSet {
for _, n := range nodes {
if !isInSlice(target, n) {
target = append(target, n)
}
}
return target
}
// if a targetSet is passed, then assume it is reliable, otherwise create one
// and initialize it with the current target contents.
if targetSet == nil {
targetSet = make(map[*html.Node]bool, len(target))
for _, n := range target {
targetSet[n] = true
}
}
for _, n := range nodes {
if !isInSlice(target, n) {
if !targetSet[n] {
target = append(target, n)
targetSet[n] = true
}
}
+1 -1
View File
@@ -77,7 +77,7 @@ func TestNodeNameMultiSel(t *testing.T) {
sort.Strings(in)
sort.Strings(out)
if !reflect.DeepEqual(in, out) {
t.Error("want %v, got %v")
t.Errorf("want %v, got %v", in, out)
}
}