Added BoxTree

This commit is contained in:
tidwall 2018-08-02 19:57:11 -07:00
parent 39d44980c5
commit 1d78a41e41
70 changed files with 2836 additions and 11353 deletions

View File

@ -1,19 +0,0 @@
## Sponsors & Backers
Tile38 is an MIT-licensed open source project.
It's an independent project with its ongoing development made possible thanks to the support by these awesome [backers](https://github.com/tidwall/tile38/blob/master/BACKERS.md).
If you'd like to join them, please consider:
- [Become a backer or sponsor on Patreon](https://www.patreon.com/tidwall).
## Generous Backers via Patreon ($50+)
- gmonk63
## Backers via Patreon
- Aviv Klasquin Komissar
- Coen B
- Melbatoast
- Richard Law

225
Gopkg.lock generated
View File

@ -2,241 +2,414 @@
[[projects]]
digest = "1:db7100aae319b808f819ee6d1d2e400d234b0a335bff2b70392f1e3a79ccd63d"
name = "github.com/Shopify/sarama"
packages = ["."]
pruneopts = ""
revision = "bbdbe644099b7fdc8327d5cc69c030945188b2e9"
version = "v1.13.0"
[[projects]]
digest = "1:11c577e4f8478a8b397adb7976f1f201b31951a5473d7e794c6423a1224dab80"
name = "github.com/aws/aws-sdk-go"
packages = ["aws","aws/awserr","aws/awsutil","aws/client","aws/client/metadata","aws/corehandlers","aws/credentials","aws/credentials/ec2rolecreds","aws/credentials/endpointcreds","aws/credentials/stscreds","aws/defaults","aws/ec2metadata","aws/endpoints","aws/request","aws/session","aws/signer/v4","internal/shareddefaults","private/protocol","private/protocol/query","private/protocol/query/queryutil","private/protocol/rest","private/protocol/xml/xmlutil","service/sqs","service/sts"]
packages = [
"aws",
"aws/awserr",
"aws/awsutil",
"aws/client",
"aws/client/metadata",
"aws/corehandlers",
"aws/credentials",
"aws/credentials/ec2rolecreds",
"aws/credentials/endpointcreds",
"aws/credentials/stscreds",
"aws/defaults",
"aws/ec2metadata",
"aws/endpoints",
"aws/request",
"aws/session",
"aws/signer/v4",
"internal/shareddefaults",
"private/protocol",
"private/protocol/query",
"private/protocol/query/queryutil",
"private/protocol/rest",
"private/protocol/xml/xmlutil",
"service/sqs",
"service/sts",
]
pruneopts = ""
revision = "da415b5fa0ff3f91d4707348a8ea1be53f700c22"
version = "v1.12.6"
[[projects]]
digest = "1:56c130d885a4aacae1dd9c7b71cfe39912c7ebc1ff7d2b46083c8812996dc43b"
name = "github.com/davecgh/go-spew"
packages = ["spew"]
pruneopts = ""
revision = "346938d642f2ec3594ed81d874461961cd0faa76"
version = "v1.1.0"
[[projects]]
digest = "1:f714fa0ab4449a2fe13d156446ac1c1e16bc85334e9be320d42bf8bee362ba45"
name = "github.com/eapache/go-resiliency"
packages = ["breaker"]
pruneopts = ""
revision = "6800482f2c813e689c88b7ed3282262385011890"
version = "v1.0.0"
[[projects]]
branch = "master"
digest = "1:1f7503fa58a852a1416556ae2ddb219b49a1304fd408391948e2e3676514c48d"
name = "github.com/eapache/go-xerial-snappy"
packages = ["."]
pruneopts = ""
revision = "bb955e01b9346ac19dc29eb16586c90ded99a98c"
[[projects]]
digest = "1:c05dc14dd75a9697b8410ea13445ceb40669448f789afe955351ad34bc998cd0"
name = "github.com/eapache/queue"
packages = ["."]
pruneopts = ""
revision = "ded5959c0d4e360646dc9e9908cff48666781367"
version = "v1.0.2"
[[projects]]
digest = "1:eecb3e6cef98036972582ffff7d3e340aef15f075236da353aa3e7fb798fdb21"
name = "github.com/eclipse/paho.mqtt.golang"
packages = [".","packets"]
packages = [
".",
"packets",
]
pruneopts = ""
revision = "aff15770515e3c57fc6109da73d42b0d46f7f483"
version = "v1.1.0"
[[projects]]
digest = "1:c955959498f5eb42652958ac60bcd9a7fde78b8d6886f2e5b88513114c902a9c"
name = "github.com/garyburd/redigo"
packages = ["internal","redis"]
packages = [
"internal",
"redis",
]
pruneopts = ""
revision = "433969511232c397de61b1442f9fd49ec06ae9ba"
version = "v1.1.0"
[[projects]]
digest = "1:e26d0f8ccaf4087b93306d5e20e4816258116868ad6c6da0f2b4926c72fb92fa"
name = "github.com/go-ini/ini"
packages = ["."]
pruneopts = ""
revision = "20b96f641a5ea98f2f8619ff4f3e061cff4833bd"
version = "v1.28.2"
[[projects]]
branch = "master"
digest = "1:27854310d59099f8dcc61dd8af4a69f0a3597f001154b2fb4d1c41baf2e31ec1"
name = "github.com/golang/protobuf"
packages = ["proto","ptypes","ptypes/any","ptypes/duration","ptypes/timestamp"]
packages = [
"proto",
"ptypes",
"ptypes/any",
"ptypes/duration",
"ptypes/timestamp",
]
pruneopts = ""
revision = "130e6b02ab059e7b717a096f397c5b60111cae74"
[[projects]]
branch = "master"
digest = "1:09307dfb1aa3f49a2bf869dcfa4c6c06ecd3c207221bd1c1a1141f0e51f209eb"
name = "github.com/golang/snappy"
packages = ["."]
pruneopts = ""
revision = "553a641470496b2327abcac10b36396bd98e45c9"
[[projects]]
digest = "1:6f49eae0c1e5dab1dafafee34b207aeb7a42303105960944828c2079b92fc88e"
name = "github.com/jmespath/go-jmespath"
packages = ["."]
pruneopts = ""
revision = "0b12d6b5"
[[projects]]
branch = "master"
digest = "1:30f72985e574101b71666d6e601e7564bd02d95164da59ca17363ad194137969"
name = "github.com/peterh/liner"
packages = ["."]
pruneopts = ""
revision = "a37ad39843113264dae84a5d89fcee28f50b35c6"
[[projects]]
digest = "1:2118aac9bc7ff09544626d5d1c7f7d4fb92a558702b00da5572ccc80ae7caf2b"
name = "github.com/pierrec/lz4"
packages = ["."]
pruneopts = ""
revision = "08c27939df1bd95e881e2c2367a749964ad1fceb"
version = "v1.0.1"
[[projects]]
digest = "1:ff95a6c61f34f32e57833783059c80274d84e9c74e6e315c3dc2e93e9bf3dab9"
name = "github.com/pierrec/xxHash"
packages = ["xxHash32"]
pruneopts = ""
revision = "f051bb7f1d1aaf1b5a665d74fb6b0217712c69f7"
version = "v0.1.1"
[[projects]]
name = "github.com/pmezard/go-difflib"
packages = ["difflib"]
revision = "792786c7400a136282c1664665ae0a8db921c6c2"
version = "v1.0.0"
[[projects]]
branch = "master"
digest = "1:5e3f4c2357e1e95ede9f36dc027b4b5a2cca463c8899344c22ebeb7c0abab8d5"
name = "github.com/rcrowley/go-metrics"
packages = ["."]
pruneopts = ""
revision = "1f30fe9094a513ce4c700b9a54458bbb0c96996c"
[[projects]]
branch = "master"
digest = "1:10332fc7c87ee09af278e9c1899f1349f5c54dd22843ad2030e6556d05c5983e"
name = "github.com/streadway/amqp"
packages = ["."]
pruneopts = ""
revision = "cefed15a0bd808d13947f228770a81b06ebe8e45"
[[projects]]
name = "github.com/stretchr/testify"
packages = ["assert"]
revision = "69483b4bd14f5845b5a1e55bca19e954e827f1d0"
version = "v1.1.4"
branch = "master"
digest = "1:6e319cc90f8432f0ac3f78e6bb7be410e6939b6405e1ba84ae4e920de15476f3"
name = "github.com/tidwall/boxtree"
packages = ["d2"]
pruneopts = ""
revision = "a570caa42c5e4c65f50e6f7ca0e7fa1c5084981c"
[[projects]]
branch = "master"
digest = "1:b28f2f9253cbb1bf2bcb3c0ab7421d2f88a245386199a6668b0a66eb09ce3e1f"
name = "github.com/tidwall/btree"
packages = ["."]
pruneopts = ""
revision = "9876f1454cf0993a53d74c27196993e345f50dd1"
[[projects]]
branch = "master"
digest = "1:2fef6390e8d9118debd4937a699afad9e1629f2d5d3c965a58fc33afe04f9b46"
name = "github.com/tidwall/buntdb"
packages = ["."]
pruneopts = ""
revision = "b67b1b8c1658cb01502801c14e33c61e6c4cbb95"
[[projects]]
digest = "1:211773b67c5594aa92b1e8389c59558fa4927614507ea38237265e00c0ba6b81"
name = "github.com/tidwall/gjson"
packages = ["."]
pruneopts = ""
revision = "5a69e67cfd8f6f9b0044ed49f5079d0eeed28653"
version = "v1.0.1"
[[projects]]
branch = "master"
digest = "1:bf59f997bab72b8ecd044aed35d706edb6abd6128afe0502c94398b2374f1f3f"
name = "github.com/tidwall/grect"
packages = ["."]
pruneopts = ""
revision = "ba9a043346eba55344e40d66a5e74cfda3a9d293"
[[projects]]
branch = "master"
digest = "1:96eb1cfd440166e1313d61021adba328e0dfeeac426abf9cc8c9879019b99b59"
name = "github.com/tidwall/lotsa"
packages = ["."]
pruneopts = ""
revision = "a03631ac7f1cd37f159fca01ff6d600b3536d3cf"
[[projects]]
branch = "master"
digest = "1:4db4f92bb9cb04cfc4fccb36aba2598b02a988008c4cc0692b241214ad8ac96e"
name = "github.com/tidwall/match"
packages = ["."]
pruneopts = ""
revision = "1731857f09b1f38450e2c12409748407822dc6be"
[[projects]]
branch = "master"
digest = "1:630381558bc538e831db8468dd0dc2702d81789f79b8ddf665eeebc729e2a055"
name = "github.com/tidwall/redbench"
packages = ["."]
pruneopts = ""
revision = "637a608ebec1acbf049c2e4a5eda6c2d72aa3af1"
[[projects]]
branch = "master"
digest = "1:3d97df307101403f6647217a1af92bdb1dc15d7d4c2c92280faeeb98c4fce0f2"
name = "github.com/tidwall/redcon"
packages = ["."]
pruneopts = ""
revision = "3df12143a4fe57c9f0d7f0f37e29ad95bc37f9a7"
[[projects]]
branch = "master"
digest = "1:f0bb37e804b0c1901995b54f51a63f23ff0bb67747a1f8d37a666f394025bbc8"
name = "github.com/tidwall/resp"
packages = ["."]
pruneopts = ""
revision = "b2b1a7ca20e34ad839fdb81f78e67522c99959f0"
[[projects]]
branch = "master"
digest = "1:9384bff0cadcd196d5981923a738898c36d4ce049781152b9a9816791c8221b4"
name = "github.com/tidwall/rtree"
packages = ["."]
pruneopts = ""
revision = "d4a8a3d30d5729f85edfba1745241f3a621d0359"
[[projects]]
digest = "1:ed6a1c415a0bd35c9c18eec74bfd460a57ba21fb3bc0da629afc275096edffa4"
name = "github.com/tidwall/sjson"
packages = ["."]
pruneopts = ""
revision = "6a22caf2fd45d5e2119bfc3717e984f15a7eb7ee"
version = "v1.0.0"
[[projects]]
branch = "master"
name = "github.com/tidwall/tinyqueue"
packages = ["."]
revision = "1feaf062ef04a231c9126f99a68eaa579fd0e390"
[[projects]]
branch = "master"
digest = "1:9d71091ff8756d88318a4334be685d311b10e1a01c0290ce743187b3bfb1b3f6"
name = "github.com/yuin/gopher-lua"
packages = [".","ast","parse","pm"]
packages = [
".",
"ast",
"parse",
"pm",
]
pruneopts = ""
revision = "eb1c7299435cc746b72514f37f74a5154dfe460f"
[[projects]]
branch = "master"
digest = "1:d2438f1c85d855408197edcbac2ba137513738ccefbd12396b0af99d5449880b"
name = "golang.org/x/crypto"
packages = ["ssh/terminal"]
pruneopts = ""
revision = "9419663f5a44be8b34ca85f08abc5fe1be11f8a3"
[[projects]]
branch = "master"
digest = "1:898bc7c802c1e0c20cecd65811e90b7b9bc5651b4a07aefd159451bfb200b2b3"
name = "golang.org/x/net"
packages = ["context","http2","http2/hpack","idna","internal/timeseries","lex/httplex","proxy","trace","websocket"]
packages = [
"context",
"http2",
"http2/hpack",
"idna",
"internal/timeseries",
"lex/httplex",
"proxy",
"trace",
"websocket",
]
pruneopts = ""
revision = "a04bdaca5b32abe1c069418fb7088ae607de5bd0"
[[projects]]
branch = "master"
digest = "1:e9f555036bb1a2f61074131371313348581fa1b643c0e5f8c0a436bf7ce6db69"
name = "golang.org/x/sys"
packages = ["unix","windows"]
packages = [
"unix",
"windows",
]
pruneopts = ""
revision = "314a259e304ff91bd6985da2a7149bbf91237993"
[[projects]]
branch = "master"
digest = "1:de5f2b854c46e933cd32e9c6e1f4bcf79d38587037f9a35178dde028779d9067"
name = "golang.org/x/text"
packages = ["collate","collate/build","internal/colltab","internal/gen","internal/tag","internal/triegen","internal/ucd","language","secure/bidirule","transform","unicode/bidi","unicode/cldr","unicode/norm","unicode/rangetable"]
packages = [
"collate",
"collate/build",
"internal/colltab",
"internal/gen",
"internal/tag",
"internal/triegen",
"internal/ucd",
"language",
"secure/bidirule",
"transform",
"unicode/bidi",
"unicode/cldr",
"unicode/norm",
"unicode/rangetable",
]
pruneopts = ""
revision = "d82c1812e304abfeeabd31e995a115a2855bf642"
[[projects]]
branch = "master"
digest = "1:180913ea45cbe0072abce387a686b929908f8213106a735fe1d1273ae5239648"
name = "google.golang.org/genproto"
packages = ["googleapis/rpc/status"]
pruneopts = ""
revision = "f676e0f3ac6395ff1a529ae59a6670878a8371a6"
[[projects]]
digest = "1:b3c989821c14a572c49a8091fd1e832a5e53d3ae2fbf90ed3ea46cef4863aad9"
name = "google.golang.org/grpc"
packages = [".","codes","connectivity","credentials","grpclb/grpc_lb_v1/messages","grpclog","internal","keepalive","metadata","naming","peer","stats","status","tap","transport"]
packages = [
".",
"codes",
"connectivity",
"credentials",
"grpclb/grpc_lb_v1/messages",
"grpclog",
"internal",
"keepalive",
"metadata",
"naming",
"peer",
"stats",
"status",
"tap",
"transport",
]
pruneopts = ""
revision = "f92cdcd7dcdc69e81b2d7b338479a19a8723cfa3"
version = "v1.6.0"
[[projects]]
branch = "master"
digest = "1:d14d9e6f479cbbac36f556199034db558ec5755b59dcf9b44fa373611e97c7be"
name = "layeh.com/gopher-json"
packages = ["."]
pruneopts = ""
revision = "c128cc74278be889c4381681712931976fe0d88b"
[solve-meta]
analyzer-name = "dep"
analyzer-version = 1
inputs-digest = "815f293904a566198a4157c81abad0db57c378fad869347de42c2201795b2158"
input-imports = [
"github.com/Shopify/sarama",
"github.com/aws/aws-sdk-go/aws",
"github.com/aws/aws-sdk-go/aws/credentials",
"github.com/aws/aws-sdk-go/aws/session",
"github.com/aws/aws-sdk-go/service/sqs",
"github.com/eclipse/paho.mqtt.golang",
"github.com/garyburd/redigo/redis",
"github.com/golang/protobuf/proto",
"github.com/peterh/liner",
"github.com/streadway/amqp",
"github.com/tidwall/boxtree/d2",
"github.com/tidwall/btree",
"github.com/tidwall/buntdb",
"github.com/tidwall/gjson",
"github.com/tidwall/lotsa",
"github.com/tidwall/redbench",
"github.com/tidwall/redcon",
"github.com/tidwall/resp",
"github.com/tidwall/sjson",
"github.com/yuin/gopher-lua",
"golang.org/x/crypto/ssh/terminal",
"golang.org/x/net/context",
"google.golang.org/grpc",
"layeh.com/gopher-json",
]
solver-name = "gps-cdcl"
solver-version = 1

View File

@ -20,9 +20,12 @@
# name = "github.com/x/y"
# version = "2.4.0"
required = ["github.com/tidwall/lotsa"]
[[constraint]]
branch = "master"
name = "github.com/tidwall/boxtree"
[[constraint]]
name = "github.com/Shopify/sarama"
version = "1.13.0"

View File

@ -33,7 +33,6 @@ Tile38 is an open source (MIT licensed), in-memory geolocation data store, spati
- Leader / follower [replication](#replication).
- In-memory database that persists on disk.
- All coordinates are in [WGS 84 Web Mercator / EPSG:3857](#coordinate-system)
- Fast R-Tree indexes based on the [RBush](https://github.com/mourner/rbush) library by [Vladimir Agafonkin](https://github.com/mourner)
## Components
- `tile38-server ` - The server

View File

@ -1,19 +1,17 @@
package index
import (
rtree "github.com/tidwall/tile38/pkg/index/rtree"
"github.com/tidwall/boxtree/d2"
)
// Index is a geospatial index
type Index struct {
r *rtree.RTree
r d2.BoxTree
}
// New create a new index
func New() *Index {
return &Index{
r: rtree.New(),
}
return &Index{}
}
// Item represents an index item.
@ -40,13 +38,13 @@ func (item *FlexItem) Point() (x, y float64) {
// Insert inserts an item into the index
func (ix *Index) Insert(item Item) {
minX, minY, maxX, maxY := item.Rect()
ix.r.Insert([2]float64{minX, minY}, [2]float64{maxX, maxY}, item)
ix.r.Insert([]float64{minX, minY}, []float64{maxX, maxY}, item)
}
// Remove removed an item from the index
func (ix *Index) Remove(item Item) {
minX, minY, maxX, maxY := item.Rect()
ix.r.Remove([2]float64{minX, minY}, [2]float64{maxX, maxY}, item)
ix.r.Delete([]float64{minX, minY}, []float64{maxX, maxY}, item)
}
// Count counts all items in the index.
@ -63,22 +61,35 @@ func (ix *Index) Bounds() (MinX, MinY, MaxX, MaxY float64) {
// RemoveAll removes all items from the index.
func (ix *Index) RemoveAll() {
ix.r = rtree.New()
ix.r = d2.BoxTree{}
}
// KNN returns the nearsest neighbors
func (ix *Index) KNN(x, y float64, iterator func(item interface{}) bool) bool {
return ix.r.KNN([2]float64{x, y}, [2]float64{x, y}, true,
func(item interface{}, dist float64) bool {
return iterator(item)
res := true
ix.r.Nearby([]float64{x, y}, []float64{x, y},
func(_, _ []float64, item interface{}) bool {
if !iterator(item) {
res = false
return false
}
return true
})
return res
}
// Search returns all items that intersect the bounding box.
func (ix *Index) Search(minX, minY, maxX, maxY float64,
iterator func(item interface{}) bool,
) bool {
return ix.r.Search([2]float64{minX, minY}, [2]float64{maxX, maxY},
func(item interface{}) bool {
return iterator(item)
res := true
ix.r.Search([]float64{minX, minY}, []float64{maxX, maxY},
func(_, _ []float64, item interface{}) bool {
if !iterator(item) {
res = false
return false
}
return true
})
return res
}

View File

@ -1 +0,0 @@
*.png

View File

@ -1,660 +0,0 @@
// This package is adapted from the RBush project by Vladimir Agafonkin.
// https://github.com/mourner/rbush
package rtree
import (
"math"
"unsafe"
)
// D is the number of dimensions
const D = 2
const M = 13
// precalculate infinity
var mathInfNeg = math.Inf(-1)
var mathInfPos = math.Inf(+1)
type treeNode struct {
min, max [D]float64
children [M + 1]*treeNode
count int
height int
leaf bool
}
func (node *treeNode) unsafeItem() *treeItem {
return (*treeItem)(unsafe.Pointer(node))
}
func createNode(children []*treeNode) *treeNode {
n := &treeNode{
height: 1,
leaf: true,
}
if len(children) > 0 {
n.count = len(children)
copy(n.children[:n.count], children)
}
for i := 0; i < D; i++ {
n.min[i] = mathInfPos
n.max[i] = mathInfNeg
}
return n
}
func (node *treeNode) extend(b *treeNode) {
for i := 0; i < len(node.min); i++ {
if b.min[i] < node.min[i] {
node.min[i] = b.min[i]
}
if b.max[i] > node.max[i] {
node.max[i] = b.max[i]
}
}
}
func (node *treeNode) area() float64 {
area := node.max[0] - node.min[0]
for i := 1; i < len(node.min); i++ {
area *= node.max[i] - node.min[i]
}
return area
}
func (node *treeNode) enlargedAreaAxis(b *treeNode, axis int) float64 {
var max, min float64
if b.max[axis] > node.max[axis] {
max = b.max[axis]
} else {
max = node.max[axis]
}
if b.min[axis] < node.min[axis] {
min = b.min[axis]
} else {
min = node.min[axis]
}
return max - min
}
func (node *treeNode) enlargedArea(b *treeNode) float64 {
area := node.enlargedAreaAxis(b, 0)
for i := 1; i < len(node.min); i++ {
area *= node.enlargedAreaAxis(b, i)
}
return area
}
func (node *treeNode) intersectionAreaAxis(b *treeNode, axis int) float64 {
var max, min float64
if node.max[axis] < b.max[axis] {
max = node.max[axis]
} else {
max = b.max[axis]
}
if node.min[axis] > b.min[axis] {
min = node.min[axis]
} else {
min = b.min[axis]
}
if max > min {
return max - min
}
return 0
}
func (node *treeNode) intersectionArea(b *treeNode) float64 {
area := node.intersectionAreaAxis(b, 0)
for i := 1; i < len(node.min); i++ {
area *= node.intersectionAreaAxis(b, i)
}
return area
}
func (node *treeNode) margin() float64 {
margin := node.max[0] - node.min[0]
for i := 1; i < len(node.min); i++ {
margin += node.max[i] - node.min[i]
}
return margin
}
type result int
const (
not result = 0
intersects result = 1
contains result = 2
)
func (node *treeNode) overlaps(b *treeNode) result {
for i := 0; i < len(node.min); i++ {
if b.min[i] > node.max[i] || b.max[i] < node.min[i] {
return not
}
if node.min[i] > b.min[i] || b.max[i] > node.max[i] {
i++
for ; i < len(node.min); i++ {
if b.min[i] > node.max[i] || b.max[i] < node.min[i] {
return not
}
}
return intersects
}
}
return contains
}
func (node *treeNode) intersects(b *treeNode) bool {
for i := 0; i < len(node.min); i++ {
if b.min[i] > node.max[i] || b.max[i] < node.min[i] {
return false
}
}
return true
}
func (node *treeNode) findItem(item interface{}) int {
for i := 0; i < node.count; i++ {
if node.children[i].unsafeItem().item == item {
return i
}
}
return -1
}
func (node *treeNode) contains(b *treeNode) bool {
for i := 0; i < len(node.min); i++ {
if node.min[i] > b.min[i] || b.max[i] > node.max[i] {
return false
}
}
return true
}
func (node *treeNode) childCount() int {
if node.leaf {
return node.count
}
var n int
for i := 0; i < node.count; i++ {
n += node.children[i].childCount()
}
return n
}
type treeItem struct {
min, max [D]float64
item interface{}
}
func (item *treeItem) unsafeNode() *treeNode {
return (*treeNode)(unsafe.Pointer(item))
}
// RTree is an R-tree
type RTree struct {
maxEntries int
minEntries int
data *treeNode // root node
// resusable fields, these help performance of common mutable operations.
reuse struct {
path []*treeNode // for reinsertion path
indexes []int // for remove function
stack []int // for bulk loading
}
}
// New creates a new R-tree
func New() *RTree {
tr := &RTree{}
tr.maxEntries = int(math.Max(4, float64(M)))
tr.minEntries = int(math.Max(2, math.Ceil(float64(tr.maxEntries)*0.4)))
tr.data = createNode(nil)
return tr
}
// Insert inserts an item
func (tr *RTree) Insert(min, max [D]float64, item interface{}) {
if item == nil {
panic("nil item")
}
bbox := treeNode{min: min, max: max}
tr.insert(&bbox, item, tr.data.height-1, false)
}
func (tr *RTree) insert(bbox *treeNode, item interface{}, level int, isNode bool) {
tr.reuse.path = tr.reuse.path[:0]
node, insertPath := tr.chooseSubtree(bbox, tr.data, level, tr.reuse.path)
if item == nil {
// item is only nil when bulk loading a node
if node.leaf {
panic("loading node into leaf")
}
node.children[node.count] = bbox
node.count++
} else {
ti := &treeItem{min: bbox.min, max: bbox.max, item: item}
node.children[node.count] = ti.unsafeNode()
node.count++
}
node.extend(bbox)
for level >= 0 {
if insertPath[level].count > tr.maxEntries {
insertPath = tr.split(insertPath, level)
level--
} else {
break
}
}
tr.adjustParentBBoxes(bbox, insertPath, level)
tr.reuse.path = insertPath
}
func (tr *RTree) adjustParentBBoxes(bbox *treeNode, path []*treeNode, level int) {
// adjust bboxes along the given tree path
for i := level; i >= 0; i-- {
path[i].extend(bbox)
}
}
func (tr *RTree) chooseSubtree(bbox, node *treeNode, level int, path []*treeNode) (*treeNode, []*treeNode) {
var targetNode *treeNode
var area, enlargement, minArea, minEnlargement float64
for {
path = append(path, node)
if node.leaf || len(path)-1 == level {
break
}
minEnlargement = mathInfPos
minArea = minEnlargement
for i := 0; i < node.count; i++ {
child := node.children[i]
area = child.area()
enlargement = bbox.enlargedArea(child) - area
if enlargement < minEnlargement {
minEnlargement = enlargement
if area < minArea {
minArea = area
}
targetNode = child
} else if enlargement == minEnlargement {
if area < minArea {
minArea = area
targetNode = child
}
}
}
if targetNode != nil {
node = targetNode
} else if node.count > 0 {
node = (*treeNode)(node.children[0])
} else {
node = nil
}
}
return node, path
}
func (tr *RTree) split(insertPath []*treeNode, level int) []*treeNode {
var node = insertPath[level]
var M = node.count
var m = tr.minEntries
tr.chooseSplitAxis(node, m, M)
splitIndex := tr.chooseSplitIndex(node, m, M)
spliced := make([]*treeNode, node.count-splitIndex)
copy(spliced, node.children[splitIndex:])
node.count = splitIndex
newNode := createNode(spliced)
newNode.height = node.height
newNode.leaf = node.leaf
tr.calcBBox(node)
tr.calcBBox(newNode)
if level != 0 {
insertPath[level-1].children[insertPath[level-1].count] = newNode
insertPath[level-1].count++
} else {
tr.splitRoot(node, newNode)
}
return insertPath
}
func (tr *RTree) chooseSplitIndex(node *treeNode, m, M int) int {
var i int
var bbox1, bbox2 *treeNode
var overlap, area, minOverlap, minArea float64
var index int
minArea = mathInfPos
minOverlap = minArea
for i = m; i <= M-m; i++ {
bbox1 = tr.distBBox(node, 0, i, nil)
bbox2 = tr.distBBox(node, i, M, nil)
overlap = bbox1.intersectionArea(bbox2)
area = bbox1.area() + bbox2.area()
// choose distribution with minimum overlap
if overlap < minOverlap {
minOverlap = overlap
index = i
if area < minArea {
minArea = area
}
} else if overlap == minOverlap {
// otherwise choose distribution with minimum area
if area < minArea {
minArea = area
index = i
}
}
}
return index
}
func (tr *RTree) calcBBox(node *treeNode) {
tr.distBBox(node, 0, node.count, node)
}
func (tr *RTree) chooseSplitAxis(node *treeNode, m, M int) {
minMargin := tr.allDistMargin(node, m, M, 0)
var minAxis int
for axis := 1; axis < D; axis++ {
margin := tr.allDistMargin(node, m, M, axis)
if margin < minMargin {
minMargin = margin
minAxis = axis
}
}
if minAxis < D {
tr.sortNodes(node, minAxis)
}
}
func (tr *RTree) splitRoot(node, newNode *treeNode) {
tr.data = createNode([]*treeNode{node, newNode})
tr.data.height = node.height + 1
tr.data.leaf = false
tr.calcBBox(tr.data)
}
func (tr *RTree) distBBox(node *treeNode, k, p int, destNode *treeNode) *treeNode {
if destNode == nil {
destNode = createNode(nil)
} else {
for i := 0; i < D; i++ {
destNode.min[i] = mathInfPos
destNode.max[i] = mathInfNeg
}
}
for i := k; i < p; i++ {
if node.leaf {
destNode.extend(node.children[i])
} else {
destNode.extend((*treeNode)(node.children[i]))
}
}
return destNode
}
func (tr *RTree) allDistMargin(node *treeNode, m, M int, axis int) float64 {
tr.sortNodes(node, axis)
var leftBBox = tr.distBBox(node, 0, m, nil)
var rightBBox = tr.distBBox(node, M-m, M, nil)
var margin = leftBBox.margin() + rightBBox.margin()
var i int
if node.leaf {
for i = m; i < M-m; i++ {
leftBBox.extend(node.children[i])
margin += leftBBox.margin()
}
for i = M - m - 1; i >= m; i-- {
leftBBox.extend(node.children[i])
margin += rightBBox.margin()
}
} else {
for i = m; i < M-m; i++ {
child := (*treeNode)(node.children[i])
leftBBox.extend(child)
margin += leftBBox.margin()
}
for i = M - m - 1; i >= m; i-- {
child := (*treeNode)(node.children[i])
leftBBox.extend(child)
margin += rightBBox.margin()
}
}
return margin
}
func (tr *RTree) sortNodes(node *treeNode, axis int) {
sortByAxis(node.children[:node.count], axis)
}
func sortByAxis(items []*treeNode, axis int) {
if len(items) < 2 {
return
}
left, right := 0, len(items)-1
pivotIndex := len(items) / 2
items[pivotIndex], items[right] = items[right], items[pivotIndex]
for i := range items {
if items[i].min[axis] < items[right].min[axis] {
items[i], items[left] = items[left], items[i]
left++
}
}
items[left], items[right] = items[right], items[left]
sortByAxis(items[:left], axis)
sortByAxis(items[left+1:], axis)
}
// Search searches the tree for items in the input rectangle
func (tr *RTree) Search(min, max [D]float64, iter func(item interface{}) bool) bool {
bbox := &treeNode{min: min, max: max}
if !tr.data.intersects(bbox) {
return true
}
return tr.search(tr.data, bbox, iter)
}
func (tr *RTree) search(node, bbox *treeNode, iter func(item interface{}) bool) bool {
if node.leaf {
for i := 0; i < node.count; i++ {
if bbox.intersects(node.children[i]) {
if !iter(node.children[i].unsafeItem().item) {
return false
}
}
}
} else {
for i := 0; i < node.count; i++ {
r := bbox.overlaps(node.children[i])
if r == intersects {
if !tr.search(node.children[i], bbox, iter) {
return false
}
} else if r == contains {
if !scan(node.children[i], iter) {
return false
}
}
}
}
return true
}
// Remove removes an item from the R-tree.
func (tr *RTree) Remove(min, max [D]float64, item interface{}) {
bbox := &treeNode{min: min, max: max}
tr.remove(bbox, item)
}
func (tr *RTree) remove(bbox *treeNode, item interface{}) {
path := tr.reuse.path[:0]
indexes := tr.reuse.indexes[:0]
var node = tr.data
var i int
var parent *treeNode
var index int
var goingUp bool
for node != nil || len(path) != 0 {
if node == nil {
node = path[len(path)-1]
path = path[:len(path)-1]
if len(path) == 0 {
parent = nil
} else {
parent = path[len(path)-1]
}
i = indexes[len(indexes)-1]
indexes = indexes[:len(indexes)-1]
goingUp = true
}
if node.leaf {
index = node.findItem(item)
if index != -1 {
// item found, remove the item and condense tree upwards
copy(node.children[index:], node.children[index+1:])
node.children[node.count-1] = nil
node.count--
path = append(path, node)
tr.condense(path)
goto done
}
}
if !goingUp && !node.leaf && node.contains(bbox) { // go down
path = append(path, node)
indexes = append(indexes, i)
i = 0
parent = node
node = (*treeNode)(node.children[0])
} else if parent != nil { // go right
i++
if i == parent.count {
node = nil
} else {
node = (*treeNode)(parent.children[i])
}
goingUp = false
} else {
node = nil
}
}
done:
tr.reuse.path = path
tr.reuse.indexes = indexes
return
}
func (tr *RTree) condense(path []*treeNode) {
// go through the path, removing empty nodes and updating bboxes
var siblings []*treeNode
for i := len(path) - 1; i >= 0; i-- {
if path[i].count == 0 {
if i > 0 {
siblings = path[i-1].children[:path[i-1].count]
index := -1
for j := 0; j < len(siblings); j++ {
if siblings[j] == path[i] {
index = j
break
}
}
copy(siblings[index:], siblings[index+1:])
siblings[len(siblings)-1] = nil
path[i-1].count--
//siblings = siblings[:len(siblings)-1]
//path[i-1].children = siblings
} else {
tr.data = createNode(nil) // clear tree
}
} else {
tr.calcBBox(path[i])
}
}
}
// Count returns the number of items in the R-tree.
func (tr *RTree) Count() int {
return tr.data.childCount()
}
// Traverse iterates over the entire R-tree and includes all nodes and items.
func (tr *RTree) Traverse(iter func(min, max [D]float64, level int, item interface{}) bool) bool {
return tr.traverse(tr.data, iter)
}
func (tr *RTree) traverse(node *treeNode, iter func(min, max [D]float64, level int, item interface{}) bool) bool {
if !iter(node.min, node.max, int(node.height), nil) {
return false
}
if node.leaf {
for i := 0; i < node.count; i++ {
child := node.children[i]
if !iter(child.min, child.max, 0, child.unsafeItem().item) {
return false
}
}
} else {
for i := 0; i < node.count; i++ {
child := node.children[i]
if !tr.traverse(child, iter) {
return false
}
}
}
return true
}
// Scan iterates over the entire R-tree
func (tr *RTree) Scan(iter func(item interface{}) bool) bool {
return scan(tr.data, iter)
}
func scan(node *treeNode, iter func(item interface{}) bool) bool {
if node.leaf {
for i := 0; i < node.count; i++ {
child := node.children[i]
if !iter(child.unsafeItem().item) {
return false
}
}
} else {
for i := 0; i < node.count; i++ {
child := node.children[i]
if !scan(child, iter) {
return false
}
}
}
return true
}
// Bounds returns the bounding box of the entire R-tree
func (tr *RTree) Bounds() (min, max [D]float64) {
if tr.data.count > 0 {
min, max = tr.data.min, tr.data.max
}
return
}
// Complexity returns the complexity of the R-tree. The higher the value, the
// more complex the tree. The value of 1 is the lowest.
func (tr *RTree) Complexity() float64 {
var nodeCount int
var itemCount int
tr.Traverse(func(_, _ [D]float64, level int, _ interface{}) bool {
if level == 0 {
itemCount++
} else {
nodeCount++
}
return true
})
return float64(tr.maxEntries*nodeCount) / float64(itemCount)
}

View File

@ -1,554 +0,0 @@
package rtree
import (
"fmt"
"log"
"math"
"math/rand"
"runtime"
"sort"
"testing"
"time"
"github.com/stretchr/testify/assert"
)
type Rect struct {
min, max [D]float64
item interface{}
}
func ptrMakePoint(vals ...float64) *Rect {
var r Rect
for i := 0; i < D && i < len(vals); i++ {
r.min[i] = vals[i]
r.max[i] = vals[i]
}
r.item = &r
return &r
}
func ptrMakeRect(vals ...float64) *Rect {
var r Rect
for i := 0; i < D && i < len(vals); i++ {
r.min[i] = vals[i]
r.max[i] = vals[i+D]
}
r.item = &r
return &r
}
func TestRTree(t *testing.T) {
tr := New()
p := ptrMakePoint(10, 10)
tr.Insert(p.min, p.max, p.item)
}
func TestPtrBasic2D(t *testing.T) {
if D != 2 {
return
}
tr := New()
p1 := ptrMakePoint(-115, 33)
p2 := ptrMakePoint(-113, 35)
tr.Insert(p1.min, p1.max, p1.item)
tr.Insert(p2.min, p2.max, p2.item)
assert.Equal(t, 2, tr.Count())
var points []*Rect
bbox := ptrMakeRect(-116, 32, -114, 34)
tr.Search(bbox.min, bbox.max, func(item interface{}) bool {
points = append(points, item.(*Rect))
return true
})
assert.Equal(t, 1, len(points))
tr.Remove(p1.min, p1.max, p1.item)
assert.Equal(t, 1, tr.Count())
points = nil
bbox = ptrMakeRect(-116, 33, -114, 34)
tr.Search(bbox.min, bbox.max, func(item interface{}) bool {
points = append(points, item.(*Rect))
return true
})
assert.Equal(t, 0, len(points))
tr.Remove(p2.min, p2.max, p2.item)
assert.Equal(t, 0, tr.Count())
}
func getMemStats() runtime.MemStats {
runtime.GC()
time.Sleep(time.Millisecond)
runtime.GC()
var ms runtime.MemStats
runtime.ReadMemStats(&ms)
return ms
}
func ptrMakeRandom(what string) *Rect {
if what == "point" {
vals := make([]float64, D)
for i := 0; i < D; i++ {
if i == 0 {
vals[i] = rand.Float64()*360 - 180
} else if i == 1 {
vals[i] = rand.Float64()*180 - 90
} else {
vals[i] = rand.Float64()*100 - 50
}
}
return ptrMakePoint(vals...)
} else if what == "rect" {
vals := make([]float64, D)
for i := 0; i < D; i++ {
if i == 0 {
vals[i] = rand.Float64()*340 - 170
} else if i == 1 {
vals[i] = rand.Float64()*160 - 80
} else {
vals[i] = rand.Float64()*80 - 30
}
}
rvals := make([]float64, D*2)
for i := 0; i < D; i++ {
rvals[i] = vals[i] - rand.Float64()*10
rvals[D+i] = vals[i] + rand.Float64()*10
}
return ptrMakeRect(rvals...)
}
panic("??")
}
func TestPtrRandom(t *testing.T) {
t.Run(fmt.Sprintf("%dD", D), func(t *testing.T) {
t.Run("point", func(t *testing.T) { ptrTestRandom(t, "point", 10000) })
t.Run("rect", func(t *testing.T) { ptrTestRandom(t, "rect", 10000) })
})
}
func ptrTestRandom(t *testing.T, which string, n int) {
fmt.Println("-------------------------------------------------")
fmt.Printf("Testing Random %dD %ss\n", D, which)
fmt.Println("-------------------------------------------------")
rand.Seed(time.Now().UnixNano())
tr := New()
min, max := tr.Bounds()
assert.Equal(t, make([]float64, D), min[:])
assert.Equal(t, make([]float64, D), max[:])
// create random objects
m1 := getMemStats()
objs := make([]*Rect, n)
for i := 0; i < n; i++ {
objs[i] = ptrMakeRandom(which)
}
// insert the objects into tree
m2 := getMemStats()
start := time.Now()
for _, r := range objs {
tr.Insert(r.min, r.max, r.item)
}
durInsert := time.Since(start)
m3 := getMemStats()
assert.Equal(t, len(objs), tr.Count())
fmt.Printf("Inserted %d random %ss in %dms -- %d ops/sec\n",
len(objs), which, int(durInsert.Seconds()*1000),
int(float64(len(objs))/durInsert.Seconds()))
fmt.Printf(" total cost is %d bytes/%s\n", int(m3.HeapAlloc-m1.HeapAlloc)/len(objs), which)
fmt.Printf(" tree cost is %d bytes/%s\n", int(m3.HeapAlloc-m2.HeapAlloc)/len(objs), which)
fmt.Printf(" tree overhead %d%%\n", int((float64(m3.HeapAlloc-m2.HeapAlloc)/float64(len(objs)))/(float64(m3.HeapAlloc-m1.HeapAlloc)/float64(len(objs)))*100))
fmt.Printf(" complexity %f\n", tr.Complexity())
start = time.Now()
// count all nodes and leaves
var nodes int
var leaves int
var maxLevel int
tr.Traverse(func(min, max [D]float64, level int, item interface{}) bool {
if level != 0 {
nodes++
}
if level == 1 {
leaves++
}
if level > maxLevel {
maxLevel = level
}
return true
})
fmt.Printf(" nodes: %d, leaves: %d, level: %d\n", nodes, leaves, maxLevel)
// verify mbr
for i := 0; i < D; i++ {
min[i] = math.Inf(+1)
max[i] = math.Inf(-1)
}
for _, o := range objs {
for i := 0; i < D; i++ {
if o.min[i] < min[i] {
min[i] = o.min[i]
}
if o.max[i] > max[i] {
max[i] = o.max[i]
}
}
}
minb, maxb := tr.Bounds()
assert.Equal(t, min, minb)
assert.Equal(t, max, maxb)
// scan
var arr []*Rect
tr.Scan(func(item interface{}) bool {
arr = append(arr, item.(*Rect))
return true
})
assert.True(t, ptrTestHasSameItems(objs, arr))
// search
ptrTestSearch(t, tr, objs, 0.10, true)
ptrTestSearch(t, tr, objs, 0.50, true)
ptrTestSearch(t, tr, objs, 1.00, true)
// knn
ptrTestKNN(t, tr, objs, int(float64(len(objs))*0.01), true)
ptrTestKNN(t, tr, objs, int(float64(len(objs))*0.50), true)
ptrTestKNN(t, tr, objs, int(float64(len(objs))*1.00), true)
// remove all objects
indexes := rand.Perm(len(objs))
start = time.Now()
for _, i := range indexes {
tr.Remove(objs[i].min, objs[i].max, objs[i].item)
}
durRemove := time.Since(start)
assert.Equal(t, 0, tr.Count())
fmt.Printf("Removed %d random %ss in %dms -- %d ops/sec\n",
len(objs), which, int(durRemove.Seconds()*1000),
int(float64(len(objs))/durRemove.Seconds()))
min, max = tr.Bounds()
assert.Equal(t, make([]float64, D), min[:])
assert.Equal(t, make([]float64, D), max[:])
}
func ptrTestHasSameItems(a1, a2 []*Rect) bool {
if len(a1) != len(a2) {
return false
}
for _, p1 := range a1 {
var found bool
for _, p2 := range a2 {
if *p1 == *p2 {
found = true
break
}
}
if !found {
return false
}
}
return true
}
func ptrTestSearch(t *testing.T, tr *RTree, objs []*Rect, percent float64, check bool) {
var found int
var start time.Time
var stop time.Time
defer func() {
dur := stop.Sub(start)
fmt.Printf("Searched %.0f%% (%d/%d items) in %dms -- %d ops/sec\n",
percent*100, found, len(objs), int(dur.Seconds()*1000),
int(float64(1)/dur.Seconds()),
)
}()
min, max := tr.Bounds()
vals := make([]float64, D*2)
for i := 0; i < D; i++ {
vals[i] = ((max[i]+min[i])/2 - ((max[i]-min[i])*percent)/2)
vals[D+i] = ((max[i]+min[i])/2 + ((max[i]-min[i])*percent)/2)
}
var arr1 []*Rect
var box *Rect
if percent == 1 {
box = ptrMakeRect(append(append([]float64{}, min[:]...), max[:]...)...)
} else {
box = ptrMakeRect(vals...)
}
start = time.Now()
tr.Search(box.min, box.max, func(item interface{}) bool {
if check {
arr1 = append(arr1, item.(*Rect))
}
found++
return true
})
stop = time.Now()
if !check {
return
}
var arr2 []*Rect
for _, obj := range objs {
if ptrTestIntersects(obj, box) {
arr2 = append(arr2, obj)
}
}
assert.Equal(t, len(arr1), len(arr2))
for _, o1 := range arr1 {
var found bool
for _, o2 := range arr2 {
if *o2 == *o1 {
found = true
break
}
}
if !found {
t.Fatalf("not found")
}
}
}
func ptrTestKNN(t *testing.T, tr *RTree, objs []*Rect, n int, check bool) {
var start time.Time
var stop time.Time
defer func() {
dur := stop.Sub(start)
fmt.Printf("KNN %d items in %dms -- %d ops/sec\n",
n, int(dur.Seconds()*1000),
int(float64(1)/dur.Seconds()),
)
}()
min, max := tr.Bounds()
pvals := make([]float64, D)
for i := 0; i < D; i++ {
pvals[i] = (max[i] + min[i]) / 2
}
point := ptrMakePoint(pvals...)
// gather the results, make sure that is matches exactly
var arr1 []Rect
var dists1 []float64
pdist := math.Inf(-1)
start = time.Now()
tr.KNN(point.min, point.max, false, func(item interface{}, dist float64) bool {
if len(arr1) == n {
return false
}
arr1 = append(arr1, Rect{min: min, max: max, item: item})
dists1 = append(dists1, dist)
if dist < pdist {
panic("dist out of order")
}
pdist = dist
return true
})
stop = time.Now()
assert.True(t, n > len(objs) || n == len(arr1))
// get the KNN for the original array
nobjs := make([]*Rect, len(objs))
copy(nobjs, objs)
sort.Slice(nobjs, func(i, j int) bool {
idist := ptrTestBoxDist(pvals, nobjs[i].min, nobjs[i].max)
jdist := ptrTestBoxDist(pvals, nobjs[j].min, nobjs[j].max)
return idist < jdist
})
arr2 := nobjs[:len(arr1)]
var dists2 []float64
for i := 0; i < len(arr2); i++ {
dist := ptrTestBoxDist(pvals, arr2[i].min, arr2[i].max)
dists2 = append(dists2, dist)
}
// only compare the distances, not the objects because rectangles with
// a dist of zero will not be ordered.
assert.Equal(t, dists1, dists2)
}
func ptrTestBoxDist(point []float64, min, max [D]float64) float64 {
var dist float64
for i := 0; i < len(point); i++ {
d := ptrTestAxisDist(point[i], min[i], max[i])
dist += d * d
}
return dist
}
func ptrTestAxisDist(k, min, max float64) float64 {
if k < min {
return min - k
}
if k <= max {
return 0
}
return k - max
}
func ptrTestIntersects(obj, box *Rect) bool {
for i := 0; i < D; i++ {
if box.min[i] > obj.max[i] || box.max[i] < obj.min[i] {
return false
}
}
return true
}
// func TestPtrInsertFlatPNG2D(t *testing.T) {
// fmt.Println("-------------------------------------------------")
// fmt.Println("Generating Cities PNG 2D (flat-insert-2d.png)")
// fmt.Println("-------------------------------------------------")
// tr := New()
// var items []*Rect
// c := cities.Cities
// for i := 0; i < len(c); i++ {
// x := c[i].Longitude
// y := c[i].Latitude
// items = append(items, ptrMakePoint(x, y))
// }
// start := time.Now()
// for _, item := range items {
// tr.Insert(item.min, item.max, item.item)
// }
// dur := time.Since(start)
// fmt.Printf("wrote %d cities (flat) in %s (%.0f/ops)\n", len(c), dur, float64(len(c))/dur.Seconds())
// withGIF := os.Getenv("GIFOUTPUT") != ""
// if err := tr.SavePNG("ptr-flat-insert-2d.png", 1000, 1000, 1.25/360.0, 0, true, withGIF, os.Stdout); err != nil {
// t.Fatal(err)
// }
// if !withGIF {
// fmt.Println("use GIFOUTPUT=1 for animated gif")
// }
// }
// func TestPtrLoadFlatPNG2D(t *testing.T) {
// fmt.Println("-------------------------------------------------")
// fmt.Println("Generating Cities 2D PNG (flat-load-2d.png)")
// fmt.Println("-------------------------------------------------")
// tr := New()
// var items []*Rect
// c := cities.Cities
// for i := 0; i < len(c); i++ {
// x := c[i].Longitude
// y := c[i].Latitude
// items = append(items, ptrMakePoint(x, y))
// }
// var mins [][D]float64
// var maxs [][D]float64
// var ifs []interface{}
// for i := 0; i < len(items); i++ {
// mins = append(mins, items[i].min)
// maxs = append(maxs, items[i].max)
// ifs = append(ifs, items[i].item)
// }
// start := time.Now()
// tr.Load(mins, maxs, ifs)
// dur := time.Since(start)
// if true {
// var all []*Rect
// tr.Scan(func(min, max [D]float64, item interface{}) bool {
// all = append(all, &Rect{min: min, max: max, item: item})
// return true
// })
// assert.Equal(t, len(all), len(items))
// for len(all) > 0 {
// item := all[0]
// var found bool
// for _, city := range items {
// if *city == *item {
// found = true
// break
// }
// }
// if !found {
// t.Fatal("item not found")
// }
// all = all[1:]
// }
// }
// fmt.Printf("wrote %d cities (flat) in %s (%.0f/ops)\n", len(c), dur, float64(len(c))/dur.Seconds())
// withGIF := os.Getenv("GIFOUTPUT") != ""
// if err := tr.SavePNG("ptr-flat-load-2d.png", 1000, 1000, 1.25/360.0, 0, true, withGIF, os.Stdout); err != nil {
// t.Fatal(err)
// }
// if !withGIF {
// fmt.Println("use GIFOUTPUT=1 for animated gif")
// }
// }
func TestBenchmarks(t *testing.T) {
var points []*Rect
for i := 0; i < 2000000; i++ {
x := rand.Float64()*360 - 180
y := rand.Float64()*180 - 90
points = append(points, ptrMakePoint(x, y))
}
tr := New()
start := time.Now()
for i := len(points) / 2; i < len(points); i++ {
tr.Insert(points[i].min, points[i].max, points[i].item)
}
dur := time.Since(start)
log.Printf("insert 1M items one by one: %.3fs", dur.Seconds())
////
rarr := rand.Perm(len(points) / 2)
start = time.Now()
for i := 0; i < len(points)/2; i++ {
a := points[rarr[i]+len(points)/2]
b := points[rarr[i]]
tr.Remove(a.min, a.max, a.item)
tr.Insert(b.min, b.max, b.item)
}
dur = time.Since(start)
log.Printf("replaced 1M items one by one: %.3fs", dur.Seconds())
points = points[:len(points)/2]
////
start = time.Now()
for i := 0; i < 1000; i++ {
tr.Remove(points[i].min, points[i].max, points[i].item)
}
dur = time.Since(start)
log.Printf("remove 100 items one by one: %.3fs", dur.Seconds())
////
bbox := ptrMakeRect(0, 0, 0+(360*0.0001), 0+(180*0.0001))
start = time.Now()
for i := 0; i < 1000; i++ {
tr.Search(bbox.min, bbox.max, func(_ interface{}) bool { return true })
}
dur = time.Since(start)
log.Printf("1000 searches of 0.01%% area: %.3fs", dur.Seconds())
////
bbox = ptrMakeRect(0, 0, 0+(360*0.01), 0+(180*0.01))
start = time.Now()
for i := 0; i < 1000; i++ {
tr.Search(bbox.min, bbox.max, func(_ interface{}) bool { return true })
}
dur = time.Since(start)
log.Printf("1000 searches of 1%% area: %.3fs", dur.Seconds())
////
bbox = ptrMakeRect(0, 0, 0+(360*0.10), 0+(180*0.10))
start = time.Now()
for i := 0; i < 1000; i++ {
tr.Search(bbox.min, bbox.max, func(_ interface{}) bool { return true })
}
dur = time.Since(start)
log.Printf("1000 searches of 10%% area: %.3fs", dur.Seconds())
///
var mins [][D]float64
var maxs [][D]float64
var items []interface{}
for i := 0; i < len(points); i++ {
mins = append(mins, points[i].min)
maxs = append(maxs, points[i].max)
items = append(items, points[i].item)
}
tr = New()
start = time.Now()
tr.Load(mins, maxs, items)
dur = time.Since(start)
log.Printf("bulk-insert 1M items: %.3fs", dur.Seconds())
}

View File

@ -1,101 +0,0 @@
// This package is adapted from the RBush project by Vladimir Agafonkin.
// https://github.com/mourner/rbush
package rtree
import (
"github.com/tidwall/tinyqueue"
)
type queueItem struct {
node *treeNode
isItem bool
dist float64
}
func (item *queueItem) Less(b tinyqueue.Item) bool {
return item.dist < b.(*queueItem).dist
}
// KNN returns items nearest to farthest. The dist param is the "box distance".
func (tr *RTree) KNN(min, max [D]float64, center bool, iter func(item interface{}, dist float64) bool) bool {
var isBox bool
var knnPoint [D]float64
bbox := &treeNode{min: min, max: max}
for i := 0; i < D; i++ {
knnPoint[i] = (bbox.min[i] + bbox.max[i]) / 2
if !isBox && bbox.min[i] != bbox.max[i] {
isBox = true
}
}
node := tr.data
queue := tinyqueue.New(nil)
for node != nil {
for i := 0; i < node.count; i++ {
child := node.children[i]
var dist float64
if isBox {
dist = boxDistRect(bbox, child)
} else {
dist = boxDistPoint(knnPoint, child)
}
queue.Push(&queueItem{node: child, isItem: node.leaf, dist: dist})
}
for queue.Len() > 0 && queue.Peek().(*queueItem).isItem {
item := queue.Pop().(*queueItem)
if !iter(item.node.unsafeItem().item, item.dist) {
return false
}
}
last := queue.Pop()
if last != nil {
node = (*treeNode)(last.(*queueItem).node)
} else {
node = nil
}
}
return true
}
func boxDistRect(a, b *treeNode) float64 {
var dist float64
for i := 0; i < len(a.min); i++ {
var min, max float64
if a.min[i] > b.min[i] {
min = a.min[i]
} else {
min = b.min[i]
}
if a.max[i] < b.max[i] {
max = a.max[i]
} else {
max = b.max[i]
}
squared := min - max
if squared > 0 {
dist += squared * squared
}
}
return dist
}
func boxDistPoint(point [D]float64, childBox *treeNode) float64 {
var dist float64
for i := 0; i < len(point); i++ {
d := axisDist(point[i], childBox.min[i], childBox.max[i])
dist += d * d
}
return dist
}
func axisDist(k, min, max float64) float64 {
if k < min {
return min - k
}
if k <= max {
return 0
}
return k - max
}

View File

@ -1,102 +0,0 @@
// This package is adapted from the RBush project by Vladimir Agafonkin.
// https://github.com/mourner/rbush
package rtree
import (
"math"
)
// Load bulk load items into the R-tree.
func (tr *RTree) Load(mins, maxs [][D]float64, items []interface{}) {
if len(items) < tr.minEntries {
for i := 0; i < len(items); i++ {
tr.Insert(mins[i], maxs[i], items[i])
}
return
}
// prefill the items
fitems := make([]*treeNode, len(items))
for i := 0; i < len(items); i++ {
item := &treeItem{min: mins[i], max: maxs[i], item: items[i]}
fitems[i] = item.unsafeNode()
}
// following equations are defined in the paper describing OMT
N := len(fitems)
M := tr.maxEntries
h := int(math.Ceil(math.Log(float64(N)) / math.Log(float64(M))))
Nsubtree := int(math.Pow(float64(M), float64(h-1)))
S := int(math.Ceil(math.Sqrt(float64(N) / float64(Nsubtree))))
// sort by the initial axis
axis := 0
sortByAxis(fitems, axis)
// build the root node. it's split differently from the subtrees.
children := make([]*treeNode, 0, S)
for i := 0; i < S; i++ {
var part []*treeNode
if i == S-1 {
// last split
part = fitems[len(fitems)/S*i:]
} else {
part = fitems[len(fitems)/S*i : len(fitems)/S*(i+1)]
}
children = append(children, tr.omt(part, h-1, axis+1))
}
node := createNode(children)
node.leaf = false
node.height = h
tr.calcBBox(node)
if tr.data.count == 0 {
// save as is if tree is empty
tr.data = node
} else if tr.data.height == node.height {
// split root if trees have the same height
tr.splitRoot(tr.data, node)
} else {
if tr.data.height < node.height {
// swap trees if inserted one is bigger
tr.data, node = node, tr.data
}
// insert the small tree into the large tree at appropriate level
tr.insert(node, nil, tr.data.height-node.height-1, true)
}
}
func (tr *RTree) omt(fitems []*treeNode, h, axis int) *treeNode {
if len(fitems) <= tr.maxEntries {
// reached leaf level; return leaf
children := make([]*treeNode, len(fitems))
copy(children, fitems)
node := createNode(children)
node.height = h
tr.calcBBox(node)
return node
}
// sort the items on a different axis than the previous level.
sortByAxis(fitems, axis%D)
children := make([]*treeNode, 0, tr.maxEntries)
partsz := len(fitems) / tr.maxEntries
for i := 0; i < tr.maxEntries; i++ {
var part []*treeNode
if i == tr.maxEntries-1 {
// last part
part = fitems[partsz*i:]
} else {
part = fitems[partsz*i : partsz*(i+1)]
}
children = append(children, tr.omt(part, h-1, axis+1))
}
node := createNode(children)
node.height = h
node.leaf = false
tr.calcBBox(node)
return node
}

View File

@ -1,5 +0,0 @@
language: go
go:
- 1.5
- tip

View File

@ -1,27 +0,0 @@
Copyright (c) 2013, Patrick Mezard
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
The names of its contributors may not be used to endorse or promote
products derived from this software without specific prior written
permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

View File

@ -1,50 +0,0 @@
go-difflib
==========
[![Build Status](https://travis-ci.org/pmezard/go-difflib.png?branch=master)](https://travis-ci.org/pmezard/go-difflib)
[![GoDoc](https://godoc.org/github.com/pmezard/go-difflib/difflib?status.svg)](https://godoc.org/github.com/pmezard/go-difflib/difflib)
Go-difflib is a partial port of python 3 difflib package. Its main goal
was to make unified and context diff available in pure Go, mostly for
testing purposes.
The following class and functions (and related tests) have be ported:
* `SequenceMatcher`
* `unified_diff()`
* `context_diff()`
## Installation
```bash
$ go get github.com/pmezard/go-difflib/difflib
```
### Quick Start
Diffs are configured with Unified (or ContextDiff) structures, and can
be output to an io.Writer or returned as a string.
```Go
diff := UnifiedDiff{
A: difflib.SplitLines("foo\nbar\n"),
B: difflib.SplitLines("foo\nbaz\n"),
FromFile: "Original",
ToFile: "Current",
Context: 3,
}
text, _ := GetUnifiedDiffString(diff)
fmt.Printf(text)
```
would output:
```
--- Original
+++ Current
@@ -1,3 +1,3 @@
foo
-bar
+baz
```

View File

@ -1,772 +0,0 @@
// Package difflib is a partial port of Python difflib module.
//
// It provides tools to compare sequences of strings and generate textual diffs.
//
// The following class and functions have been ported:
//
// - SequenceMatcher
//
// - unified_diff
//
// - context_diff
//
// Getting unified diffs was the main goal of the port. Keep in mind this code
// is mostly suitable to output text differences in a human friendly way, there
// are no guarantees generated diffs are consumable by patch(1).
package difflib
import (
"bufio"
"bytes"
"fmt"
"io"
"strings"
)
func min(a, b int) int {
if a < b {
return a
}
return b
}
func max(a, b int) int {
if a > b {
return a
}
return b
}
func calculateRatio(matches, length int) float64 {
if length > 0 {
return 2.0 * float64(matches) / float64(length)
}
return 1.0
}
type Match struct {
A int
B int
Size int
}
type OpCode struct {
Tag byte
I1 int
I2 int
J1 int
J2 int
}
// SequenceMatcher compares sequence of strings. The basic
// algorithm predates, and is a little fancier than, an algorithm
// published in the late 1980's by Ratcliff and Obershelp under the
// hyperbolic name "gestalt pattern matching". The basic idea is to find
// the longest contiguous matching subsequence that contains no "junk"
// elements (R-O doesn't address junk). The same idea is then applied
// recursively to the pieces of the sequences to the left and to the right
// of the matching subsequence. This does not yield minimal edit
// sequences, but does tend to yield matches that "look right" to people.
//
// SequenceMatcher tries to compute a "human-friendly diff" between two
// sequences. Unlike e.g. UNIX(tm) diff, the fundamental notion is the
// longest *contiguous* & junk-free matching subsequence. That's what
// catches peoples' eyes. The Windows(tm) windiff has another interesting
// notion, pairing up elements that appear uniquely in each sequence.
// That, and the method here, appear to yield more intuitive difference
// reports than does diff. This method appears to be the least vulnerable
// to synching up on blocks of "junk lines", though (like blank lines in
// ordinary text files, or maybe "<P>" lines in HTML files). That may be
// because this is the only method of the 3 that has a *concept* of
// "junk" <wink>.
//
// Timing: Basic R-O is cubic time worst case and quadratic time expected
// case. SequenceMatcher is quadratic time for the worst case and has
// expected-case behavior dependent in a complicated way on how many
// elements the sequences have in common; best case time is linear.
type SequenceMatcher struct {
a []string
b []string
b2j map[string][]int
IsJunk func(string) bool
autoJunk bool
bJunk map[string]struct{}
matchingBlocks []Match
fullBCount map[string]int
bPopular map[string]struct{}
opCodes []OpCode
}
func NewMatcher(a, b []string) *SequenceMatcher {
m := SequenceMatcher{autoJunk: true}
m.SetSeqs(a, b)
return &m
}
func NewMatcherWithJunk(a, b []string, autoJunk bool,
isJunk func(string) bool) *SequenceMatcher {
m := SequenceMatcher{IsJunk: isJunk, autoJunk: autoJunk}
m.SetSeqs(a, b)
return &m
}
// Set two sequences to be compared.
func (m *SequenceMatcher) SetSeqs(a, b []string) {
m.SetSeq1(a)
m.SetSeq2(b)
}
// Set the first sequence to be compared. The second sequence to be compared is
// not changed.
//
// SequenceMatcher computes and caches detailed information about the second
// sequence, so if you want to compare one sequence S against many sequences,
// use .SetSeq2(s) once and call .SetSeq1(x) repeatedly for each of the other
// sequences.
//
// See also SetSeqs() and SetSeq2().
func (m *SequenceMatcher) SetSeq1(a []string) {
if &a == &m.a {
return
}
m.a = a
m.matchingBlocks = nil
m.opCodes = nil
}
// Set the second sequence to be compared. The first sequence to be compared is
// not changed.
func (m *SequenceMatcher) SetSeq2(b []string) {
if &b == &m.b {
return
}
m.b = b
m.matchingBlocks = nil
m.opCodes = nil
m.fullBCount = nil
m.chainB()
}
func (m *SequenceMatcher) chainB() {
// Populate line -> index mapping
b2j := map[string][]int{}
for i, s := range m.b {
indices := b2j[s]
indices = append(indices, i)
b2j[s] = indices
}
// Purge junk elements
m.bJunk = map[string]struct{}{}
if m.IsJunk != nil {
junk := m.bJunk
for s, _ := range b2j {
if m.IsJunk(s) {
junk[s] = struct{}{}
}
}
for s, _ := range junk {
delete(b2j, s)
}
}
// Purge remaining popular elements
popular := map[string]struct{}{}
n := len(m.b)
if m.autoJunk && n >= 200 {
ntest := n/100 + 1
for s, indices := range b2j {
if len(indices) > ntest {
popular[s] = struct{}{}
}
}
for s, _ := range popular {
delete(b2j, s)
}
}
m.bPopular = popular
m.b2j = b2j
}
func (m *SequenceMatcher) isBJunk(s string) bool {
_, ok := m.bJunk[s]
return ok
}
// Find longest matching block in a[alo:ahi] and b[blo:bhi].
//
// If IsJunk is not defined:
//
// Return (i,j,k) such that a[i:i+k] is equal to b[j:j+k], where
// alo <= i <= i+k <= ahi
// blo <= j <= j+k <= bhi
// and for all (i',j',k') meeting those conditions,
// k >= k'
// i <= i'
// and if i == i', j <= j'
//
// In other words, of all maximal matching blocks, return one that
// starts earliest in a, and of all those maximal matching blocks that
// start earliest in a, return the one that starts earliest in b.
//
// If IsJunk is defined, first the longest matching block is
// determined as above, but with the additional restriction that no
// junk element appears in the block. Then that block is extended as
// far as possible by matching (only) junk elements on both sides. So
// the resulting block never matches on junk except as identical junk
// happens to be adjacent to an "interesting" match.
//
// If no blocks match, return (alo, blo, 0).
func (m *SequenceMatcher) findLongestMatch(alo, ahi, blo, bhi int) Match {
// CAUTION: stripping common prefix or suffix would be incorrect.
// E.g.,
// ab
// acab
// Longest matching block is "ab", but if common prefix is
// stripped, it's "a" (tied with "b"). UNIX(tm) diff does so
// strip, so ends up claiming that ab is changed to acab by
// inserting "ca" in the middle. That's minimal but unintuitive:
// "it's obvious" that someone inserted "ac" at the front.
// Windiff ends up at the same place as diff, but by pairing up
// the unique 'b's and then matching the first two 'a's.
besti, bestj, bestsize := alo, blo, 0
// find longest junk-free match
// during an iteration of the loop, j2len[j] = length of longest
// junk-free match ending with a[i-1] and b[j]
j2len := map[int]int{}
for i := alo; i != ahi; i++ {
// look at all instances of a[i] in b; note that because
// b2j has no junk keys, the loop is skipped if a[i] is junk
newj2len := map[int]int{}
for _, j := range m.b2j[m.a[i]] {
// a[i] matches b[j]
if j < blo {
continue
}
if j >= bhi {
break
}
k := j2len[j-1] + 1
newj2len[j] = k
if k > bestsize {
besti, bestj, bestsize = i-k+1, j-k+1, k
}
}
j2len = newj2len
}
// Extend the best by non-junk elements on each end. In particular,
// "popular" non-junk elements aren't in b2j, which greatly speeds
// the inner loop above, but also means "the best" match so far
// doesn't contain any junk *or* popular non-junk elements.
for besti > alo && bestj > blo && !m.isBJunk(m.b[bestj-1]) &&
m.a[besti-1] == m.b[bestj-1] {
besti, bestj, bestsize = besti-1, bestj-1, bestsize+1
}
for besti+bestsize < ahi && bestj+bestsize < bhi &&
!m.isBJunk(m.b[bestj+bestsize]) &&
m.a[besti+bestsize] == m.b[bestj+bestsize] {
bestsize += 1
}
// Now that we have a wholly interesting match (albeit possibly
// empty!), we may as well suck up the matching junk on each
// side of it too. Can't think of a good reason not to, and it
// saves post-processing the (possibly considerable) expense of
// figuring out what to do with it. In the case of an empty
// interesting match, this is clearly the right thing to do,
// because no other kind of match is possible in the regions.
for besti > alo && bestj > blo && m.isBJunk(m.b[bestj-1]) &&
m.a[besti-1] == m.b[bestj-1] {
besti, bestj, bestsize = besti-1, bestj-1, bestsize+1
}
for besti+bestsize < ahi && bestj+bestsize < bhi &&
m.isBJunk(m.b[bestj+bestsize]) &&
m.a[besti+bestsize] == m.b[bestj+bestsize] {
bestsize += 1
}
return Match{A: besti, B: bestj, Size: bestsize}
}
// Return list of triples describing matching subsequences.
//
// Each triple is of the form (i, j, n), and means that
// a[i:i+n] == b[j:j+n]. The triples are monotonically increasing in
// i and in j. It's also guaranteed that if (i, j, n) and (i', j', n') are
// adjacent triples in the list, and the second is not the last triple in the
// list, then i+n != i' or j+n != j'. IOW, adjacent triples never describe
// adjacent equal blocks.
//
// The last triple is a dummy, (len(a), len(b), 0), and is the only
// triple with n==0.
func (m *SequenceMatcher) GetMatchingBlocks() []Match {
if m.matchingBlocks != nil {
return m.matchingBlocks
}
var matchBlocks func(alo, ahi, blo, bhi int, matched []Match) []Match
matchBlocks = func(alo, ahi, blo, bhi int, matched []Match) []Match {
match := m.findLongestMatch(alo, ahi, blo, bhi)
i, j, k := match.A, match.B, match.Size
if match.Size > 0 {
if alo < i && blo < j {
matched = matchBlocks(alo, i, blo, j, matched)
}
matched = append(matched, match)
if i+k < ahi && j+k < bhi {
matched = matchBlocks(i+k, ahi, j+k, bhi, matched)
}
}
return matched
}
matched := matchBlocks(0, len(m.a), 0, len(m.b), nil)
// It's possible that we have adjacent equal blocks in the
// matching_blocks list now.
nonAdjacent := []Match{}
i1, j1, k1 := 0, 0, 0
for _, b := range matched {
// Is this block adjacent to i1, j1, k1?
i2, j2, k2 := b.A, b.B, b.Size
if i1+k1 == i2 && j1+k1 == j2 {
// Yes, so collapse them -- this just increases the length of
// the first block by the length of the second, and the first
// block so lengthened remains the block to compare against.
k1 += k2
} else {
// Not adjacent. Remember the first block (k1==0 means it's
// the dummy we started with), and make the second block the
// new block to compare against.
if k1 > 0 {
nonAdjacent = append(nonAdjacent, Match{i1, j1, k1})
}
i1, j1, k1 = i2, j2, k2
}
}
if k1 > 0 {
nonAdjacent = append(nonAdjacent, Match{i1, j1, k1})
}
nonAdjacent = append(nonAdjacent, Match{len(m.a), len(m.b), 0})
m.matchingBlocks = nonAdjacent
return m.matchingBlocks
}
// Return list of 5-tuples describing how to turn a into b.
//
// Each tuple is of the form (tag, i1, i2, j1, j2). The first tuple
// has i1 == j1 == 0, and remaining tuples have i1 == the i2 from the
// tuple preceding it, and likewise for j1 == the previous j2.
//
// The tags are characters, with these meanings:
//
// 'r' (replace): a[i1:i2] should be replaced by b[j1:j2]
//
// 'd' (delete): a[i1:i2] should be deleted, j1==j2 in this case.
//
// 'i' (insert): b[j1:j2] should be inserted at a[i1:i1], i1==i2 in this case.
//
// 'e' (equal): a[i1:i2] == b[j1:j2]
func (m *SequenceMatcher) GetOpCodes() []OpCode {
if m.opCodes != nil {
return m.opCodes
}
i, j := 0, 0
matching := m.GetMatchingBlocks()
opCodes := make([]OpCode, 0, len(matching))
for _, m := range matching {
// invariant: we've pumped out correct diffs to change
// a[:i] into b[:j], and the next matching block is
// a[ai:ai+size] == b[bj:bj+size]. So we need to pump
// out a diff to change a[i:ai] into b[j:bj], pump out
// the matching block, and move (i,j) beyond the match
ai, bj, size := m.A, m.B, m.Size
tag := byte(0)
if i < ai && j < bj {
tag = 'r'
} else if i < ai {
tag = 'd'
} else if j < bj {
tag = 'i'
}
if tag > 0 {
opCodes = append(opCodes, OpCode{tag, i, ai, j, bj})
}
i, j = ai+size, bj+size
// the list of matching blocks is terminated by a
// sentinel with size 0
if size > 0 {
opCodes = append(opCodes, OpCode{'e', ai, i, bj, j})
}
}
m.opCodes = opCodes
return m.opCodes
}
// Isolate change clusters by eliminating ranges with no changes.
//
// Return a generator of groups with up to n lines of context.
// Each group is in the same format as returned by GetOpCodes().
func (m *SequenceMatcher) GetGroupedOpCodes(n int) [][]OpCode {
if n < 0 {
n = 3
}
codes := m.GetOpCodes()
if len(codes) == 0 {
codes = []OpCode{OpCode{'e', 0, 1, 0, 1}}
}
// Fixup leading and trailing groups if they show no changes.
if codes[0].Tag == 'e' {
c := codes[0]
i1, i2, j1, j2 := c.I1, c.I2, c.J1, c.J2
codes[0] = OpCode{c.Tag, max(i1, i2-n), i2, max(j1, j2-n), j2}
}
if codes[len(codes)-1].Tag == 'e' {
c := codes[len(codes)-1]
i1, i2, j1, j2 := c.I1, c.I2, c.J1, c.J2
codes[len(codes)-1] = OpCode{c.Tag, i1, min(i2, i1+n), j1, min(j2, j1+n)}
}
nn := n + n
groups := [][]OpCode{}
group := []OpCode{}
for _, c := range codes {
i1, i2, j1, j2 := c.I1, c.I2, c.J1, c.J2
// End the current group and start a new one whenever
// there is a large range with no changes.
if c.Tag == 'e' && i2-i1 > nn {
group = append(group, OpCode{c.Tag, i1, min(i2, i1+n),
j1, min(j2, j1+n)})
groups = append(groups, group)
group = []OpCode{}
i1, j1 = max(i1, i2-n), max(j1, j2-n)
}
group = append(group, OpCode{c.Tag, i1, i2, j1, j2})
}
if len(group) > 0 && !(len(group) == 1 && group[0].Tag == 'e') {
groups = append(groups, group)
}
return groups
}
// Return a measure of the sequences' similarity (float in [0,1]).
//
// Where T is the total number of elements in both sequences, and
// M is the number of matches, this is 2.0*M / T.
// Note that this is 1 if the sequences are identical, and 0 if
// they have nothing in common.
//
// .Ratio() is expensive to compute if you haven't already computed
// .GetMatchingBlocks() or .GetOpCodes(), in which case you may
// want to try .QuickRatio() or .RealQuickRation() first to get an
// upper bound.
func (m *SequenceMatcher) Ratio() float64 {
matches := 0
for _, m := range m.GetMatchingBlocks() {
matches += m.Size
}
return calculateRatio(matches, len(m.a)+len(m.b))
}
// Return an upper bound on ratio() relatively quickly.
//
// This isn't defined beyond that it is an upper bound on .Ratio(), and
// is faster to compute.
func (m *SequenceMatcher) QuickRatio() float64 {
// viewing a and b as multisets, set matches to the cardinality
// of their intersection; this counts the number of matches
// without regard to order, so is clearly an upper bound
if m.fullBCount == nil {
m.fullBCount = map[string]int{}
for _, s := range m.b {
m.fullBCount[s] = m.fullBCount[s] + 1
}
}
// avail[x] is the number of times x appears in 'b' less the
// number of times we've seen it in 'a' so far ... kinda
avail := map[string]int{}
matches := 0
for _, s := range m.a {
n, ok := avail[s]
if !ok {
n = m.fullBCount[s]
}
avail[s] = n - 1
if n > 0 {
matches += 1
}
}
return calculateRatio(matches, len(m.a)+len(m.b))
}
// Return an upper bound on ratio() very quickly.
//
// This isn't defined beyond that it is an upper bound on .Ratio(), and
// is faster to compute than either .Ratio() or .QuickRatio().
func (m *SequenceMatcher) RealQuickRatio() float64 {
la, lb := len(m.a), len(m.b)
return calculateRatio(min(la, lb), la+lb)
}
// Convert range to the "ed" format
func formatRangeUnified(start, stop int) string {
// Per the diff spec at http://www.unix.org/single_unix_specification/
beginning := start + 1 // lines start numbering with one
length := stop - start
if length == 1 {
return fmt.Sprintf("%d", beginning)
}
if length == 0 {
beginning -= 1 // empty ranges begin at line just before the range
}
return fmt.Sprintf("%d,%d", beginning, length)
}
// Unified diff parameters
type UnifiedDiff struct {
A []string // First sequence lines
FromFile string // First file name
FromDate string // First file time
B []string // Second sequence lines
ToFile string // Second file name
ToDate string // Second file time
Eol string // Headers end of line, defaults to LF
Context int // Number of context lines
}
// Compare two sequences of lines; generate the delta as a unified diff.
//
// Unified diffs are a compact way of showing line changes and a few
// lines of context. The number of context lines is set by 'n' which
// defaults to three.
//
// By default, the diff control lines (those with ---, +++, or @@) are
// created with a trailing newline. This is helpful so that inputs
// created from file.readlines() result in diffs that are suitable for
// file.writelines() since both the inputs and outputs have trailing
// newlines.
//
// For inputs that do not have trailing newlines, set the lineterm
// argument to "" so that the output will be uniformly newline free.
//
// The unidiff format normally has a header for filenames and modification
// times. Any or all of these may be specified using strings for
// 'fromfile', 'tofile', 'fromfiledate', and 'tofiledate'.
// The modification times are normally expressed in the ISO 8601 format.
func WriteUnifiedDiff(writer io.Writer, diff UnifiedDiff) error {
buf := bufio.NewWriter(writer)
defer buf.Flush()
wf := func(format string, args ...interface{}) error {
_, err := buf.WriteString(fmt.Sprintf(format, args...))
return err
}
ws := func(s string) error {
_, err := buf.WriteString(s)
return err
}
if len(diff.Eol) == 0 {
diff.Eol = "\n"
}
started := false
m := NewMatcher(diff.A, diff.B)
for _, g := range m.GetGroupedOpCodes(diff.Context) {
if !started {
started = true
fromDate := ""
if len(diff.FromDate) > 0 {
fromDate = "\t" + diff.FromDate
}
toDate := ""
if len(diff.ToDate) > 0 {
toDate = "\t" + diff.ToDate
}
if diff.FromFile != "" || diff.ToFile != "" {
err := wf("--- %s%s%s", diff.FromFile, fromDate, diff.Eol)
if err != nil {
return err
}
err = wf("+++ %s%s%s", diff.ToFile, toDate, diff.Eol)
if err != nil {
return err
}
}
}
first, last := g[0], g[len(g)-1]
range1 := formatRangeUnified(first.I1, last.I2)
range2 := formatRangeUnified(first.J1, last.J2)
if err := wf("@@ -%s +%s @@%s", range1, range2, diff.Eol); err != nil {
return err
}
for _, c := range g {
i1, i2, j1, j2 := c.I1, c.I2, c.J1, c.J2
if c.Tag == 'e' {
for _, line := range diff.A[i1:i2] {
if err := ws(" " + line); err != nil {
return err
}
}
continue
}
if c.Tag == 'r' || c.Tag == 'd' {
for _, line := range diff.A[i1:i2] {
if err := ws("-" + line); err != nil {
return err
}
}
}
if c.Tag == 'r' || c.Tag == 'i' {
for _, line := range diff.B[j1:j2] {
if err := ws("+" + line); err != nil {
return err
}
}
}
}
}
return nil
}
// Like WriteUnifiedDiff but returns the diff a string.
func GetUnifiedDiffString(diff UnifiedDiff) (string, error) {
w := &bytes.Buffer{}
err := WriteUnifiedDiff(w, diff)
return string(w.Bytes()), err
}
// Convert range to the "ed" format.
func formatRangeContext(start, stop int) string {
// Per the diff spec at http://www.unix.org/single_unix_specification/
beginning := start + 1 // lines start numbering with one
length := stop - start
if length == 0 {
beginning -= 1 // empty ranges begin at line just before the range
}
if length <= 1 {
return fmt.Sprintf("%d", beginning)
}
return fmt.Sprintf("%d,%d", beginning, beginning+length-1)
}
type ContextDiff UnifiedDiff
// Compare two sequences of lines; generate the delta as a context diff.
//
// Context diffs are a compact way of showing line changes and a few
// lines of context. The number of context lines is set by diff.Context
// which defaults to three.
//
// By default, the diff control lines (those with *** or ---) are
// created with a trailing newline.
//
// For inputs that do not have trailing newlines, set the diff.Eol
// argument to "" so that the output will be uniformly newline free.
//
// The context diff format normally has a header for filenames and
// modification times. Any or all of these may be specified using
// strings for diff.FromFile, diff.ToFile, diff.FromDate, diff.ToDate.
// The modification times are normally expressed in the ISO 8601 format.
// If not specified, the strings default to blanks.
func WriteContextDiff(writer io.Writer, diff ContextDiff) error {
buf := bufio.NewWriter(writer)
defer buf.Flush()
var diffErr error
wf := func(format string, args ...interface{}) {
_, err := buf.WriteString(fmt.Sprintf(format, args...))
if diffErr == nil && err != nil {
diffErr = err
}
}
ws := func(s string) {
_, err := buf.WriteString(s)
if diffErr == nil && err != nil {
diffErr = err
}
}
if len(diff.Eol) == 0 {
diff.Eol = "\n"
}
prefix := map[byte]string{
'i': "+ ",
'd': "- ",
'r': "! ",
'e': " ",
}
started := false
m := NewMatcher(diff.A, diff.B)
for _, g := range m.GetGroupedOpCodes(diff.Context) {
if !started {
started = true
fromDate := ""
if len(diff.FromDate) > 0 {
fromDate = "\t" + diff.FromDate
}
toDate := ""
if len(diff.ToDate) > 0 {
toDate = "\t" + diff.ToDate
}
if diff.FromFile != "" || diff.ToFile != "" {
wf("*** %s%s%s", diff.FromFile, fromDate, diff.Eol)
wf("--- %s%s%s", diff.ToFile, toDate, diff.Eol)
}
}
first, last := g[0], g[len(g)-1]
ws("***************" + diff.Eol)
range1 := formatRangeContext(first.I1, last.I2)
wf("*** %s ****%s", range1, diff.Eol)
for _, c := range g {
if c.Tag == 'r' || c.Tag == 'd' {
for _, cc := range g {
if cc.Tag == 'i' {
continue
}
for _, line := range diff.A[cc.I1:cc.I2] {
ws(prefix[cc.Tag] + line)
}
}
break
}
}
range2 := formatRangeContext(first.J1, last.J2)
wf("--- %s ----%s", range2, diff.Eol)
for _, c := range g {
if c.Tag == 'r' || c.Tag == 'i' {
for _, cc := range g {
if cc.Tag == 'd' {
continue
}
for _, line := range diff.B[cc.J1:cc.J2] {
ws(prefix[cc.Tag] + line)
}
}
break
}
}
}
return diffErr
}
// Like WriteContextDiff but returns the diff a string.
func GetContextDiffString(diff ContextDiff) (string, error) {
w := &bytes.Buffer{}
err := WriteContextDiff(w, diff)
return string(w.Bytes()), err
}
// Split a string on "\n" while preserving them. The output can be used
// as input for UnifiedDiff and ContextDiff structures.
func SplitLines(s string) []string {
lines := strings.SplitAfter(s, "\n")
lines[len(lines)-1] += "\n"
return lines
}

View File

@ -1,426 +0,0 @@
package difflib
import (
"bytes"
"fmt"
"math"
"reflect"
"strings"
"testing"
)
func assertAlmostEqual(t *testing.T, a, b float64, places int) {
if math.Abs(a-b) > math.Pow10(-places) {
t.Errorf("%.7f != %.7f", a, b)
}
}
func assertEqual(t *testing.T, a, b interface{}) {
if !reflect.DeepEqual(a, b) {
t.Errorf("%v != %v", a, b)
}
}
func splitChars(s string) []string {
chars := make([]string, 0, len(s))
// Assume ASCII inputs
for i := 0; i != len(s); i++ {
chars = append(chars, string(s[i]))
}
return chars
}
func TestSequenceMatcherRatio(t *testing.T) {
s := NewMatcher(splitChars("abcd"), splitChars("bcde"))
assertEqual(t, s.Ratio(), 0.75)
assertEqual(t, s.QuickRatio(), 0.75)
assertEqual(t, s.RealQuickRatio(), 1.0)
}
func TestGetOptCodes(t *testing.T) {
a := "qabxcd"
b := "abycdf"
s := NewMatcher(splitChars(a), splitChars(b))
w := &bytes.Buffer{}
for _, op := range s.GetOpCodes() {
fmt.Fprintf(w, "%s a[%d:%d], (%s) b[%d:%d] (%s)\n", string(op.Tag),
op.I1, op.I2, a[op.I1:op.I2], op.J1, op.J2, b[op.J1:op.J2])
}
result := string(w.Bytes())
expected := `d a[0:1], (q) b[0:0] ()
e a[1:3], (ab) b[0:2] (ab)
r a[3:4], (x) b[2:3] (y)
e a[4:6], (cd) b[3:5] (cd)
i a[6:6], () b[5:6] (f)
`
if expected != result {
t.Errorf("unexpected op codes: \n%s", result)
}
}
func TestGroupedOpCodes(t *testing.T) {
a := []string{}
for i := 0; i != 39; i++ {
a = append(a, fmt.Sprintf("%02d", i))
}
b := []string{}
b = append(b, a[:8]...)
b = append(b, " i")
b = append(b, a[8:19]...)
b = append(b, " x")
b = append(b, a[20:22]...)
b = append(b, a[27:34]...)
b = append(b, " y")
b = append(b, a[35:]...)
s := NewMatcher(a, b)
w := &bytes.Buffer{}
for _, g := range s.GetGroupedOpCodes(-1) {
fmt.Fprintf(w, "group\n")
for _, op := range g {
fmt.Fprintf(w, " %s, %d, %d, %d, %d\n", string(op.Tag),
op.I1, op.I2, op.J1, op.J2)
}
}
result := string(w.Bytes())
expected := `group
e, 5, 8, 5, 8
i, 8, 8, 8, 9
e, 8, 11, 9, 12
group
e, 16, 19, 17, 20
r, 19, 20, 20, 21
e, 20, 22, 21, 23
d, 22, 27, 23, 23
e, 27, 30, 23, 26
group
e, 31, 34, 27, 30
r, 34, 35, 30, 31
e, 35, 38, 31, 34
`
if expected != result {
t.Errorf("unexpected op codes: \n%s", result)
}
}
func ExampleGetUnifiedDiffCode() {
a := `one
two
three
four
fmt.Printf("%s,%T",a,b)`
b := `zero
one
three
four`
diff := UnifiedDiff{
A: SplitLines(a),
B: SplitLines(b),
FromFile: "Original",
FromDate: "2005-01-26 23:30:50",
ToFile: "Current",
ToDate: "2010-04-02 10:20:52",
Context: 3,
}
result, _ := GetUnifiedDiffString(diff)
fmt.Println(strings.Replace(result, "\t", " ", -1))
// Output:
// --- Original 2005-01-26 23:30:50
// +++ Current 2010-04-02 10:20:52
// @@ -1,5 +1,4 @@
// +zero
// one
// -two
// three
// four
// -fmt.Printf("%s,%T",a,b)
}
func ExampleGetContextDiffCode() {
a := `one
two
three
four
fmt.Printf("%s,%T",a,b)`
b := `zero
one
tree
four`
diff := ContextDiff{
A: SplitLines(a),
B: SplitLines(b),
FromFile: "Original",
ToFile: "Current",
Context: 3,
Eol: "\n",
}
result, _ := GetContextDiffString(diff)
fmt.Print(strings.Replace(result, "\t", " ", -1))
// Output:
// *** Original
// --- Current
// ***************
// *** 1,5 ****
// one
// ! two
// ! three
// four
// - fmt.Printf("%s,%T",a,b)
// --- 1,4 ----
// + zero
// one
// ! tree
// four
}
func ExampleGetContextDiffString() {
a := `one
two
three
four`
b := `zero
one
tree
four`
diff := ContextDiff{
A: SplitLines(a),
B: SplitLines(b),
FromFile: "Original",
ToFile: "Current",
Context: 3,
Eol: "\n",
}
result, _ := GetContextDiffString(diff)
fmt.Printf(strings.Replace(result, "\t", " ", -1))
// Output:
// *** Original
// --- Current
// ***************
// *** 1,4 ****
// one
// ! two
// ! three
// four
// --- 1,4 ----
// + zero
// one
// ! tree
// four
}
func rep(s string, count int) string {
return strings.Repeat(s, count)
}
func TestWithAsciiOneInsert(t *testing.T) {
sm := NewMatcher(splitChars(rep("b", 100)),
splitChars("a"+rep("b", 100)))
assertAlmostEqual(t, sm.Ratio(), 0.995, 3)
assertEqual(t, sm.GetOpCodes(),
[]OpCode{{'i', 0, 0, 0, 1}, {'e', 0, 100, 1, 101}})
assertEqual(t, len(sm.bPopular), 0)
sm = NewMatcher(splitChars(rep("b", 100)),
splitChars(rep("b", 50)+"a"+rep("b", 50)))
assertAlmostEqual(t, sm.Ratio(), 0.995, 3)
assertEqual(t, sm.GetOpCodes(),
[]OpCode{{'e', 0, 50, 0, 50}, {'i', 50, 50, 50, 51}, {'e', 50, 100, 51, 101}})
assertEqual(t, len(sm.bPopular), 0)
}
func TestWithAsciiOnDelete(t *testing.T) {
sm := NewMatcher(splitChars(rep("a", 40)+"c"+rep("b", 40)),
splitChars(rep("a", 40)+rep("b", 40)))
assertAlmostEqual(t, sm.Ratio(), 0.994, 3)
assertEqual(t, sm.GetOpCodes(),
[]OpCode{{'e', 0, 40, 0, 40}, {'d', 40, 41, 40, 40}, {'e', 41, 81, 40, 80}})
}
func TestWithAsciiBJunk(t *testing.T) {
isJunk := func(s string) bool {
return s == " "
}
sm := NewMatcherWithJunk(splitChars(rep("a", 40)+rep("b", 40)),
splitChars(rep("a", 44)+rep("b", 40)), true, isJunk)
assertEqual(t, sm.bJunk, map[string]struct{}{})
sm = NewMatcherWithJunk(splitChars(rep("a", 40)+rep("b", 40)),
splitChars(rep("a", 44)+rep("b", 40)+rep(" ", 20)), false, isJunk)
assertEqual(t, sm.bJunk, map[string]struct{}{" ": struct{}{}})
isJunk = func(s string) bool {
return s == " " || s == "b"
}
sm = NewMatcherWithJunk(splitChars(rep("a", 40)+rep("b", 40)),
splitChars(rep("a", 44)+rep("b", 40)+rep(" ", 20)), false, isJunk)
assertEqual(t, sm.bJunk, map[string]struct{}{" ": struct{}{}, "b": struct{}{}})
}
func TestSFBugsRatioForNullSeqn(t *testing.T) {
sm := NewMatcher(nil, nil)
assertEqual(t, sm.Ratio(), 1.0)
assertEqual(t, sm.QuickRatio(), 1.0)
assertEqual(t, sm.RealQuickRatio(), 1.0)
}
func TestSFBugsComparingEmptyLists(t *testing.T) {
groups := NewMatcher(nil, nil).GetGroupedOpCodes(-1)
assertEqual(t, len(groups), 0)
diff := UnifiedDiff{
FromFile: "Original",
ToFile: "Current",
Context: 3,
}
result, err := GetUnifiedDiffString(diff)
assertEqual(t, err, nil)
assertEqual(t, result, "")
}
func TestOutputFormatRangeFormatUnified(t *testing.T) {
// Per the diff spec at http://www.unix.org/single_unix_specification/
//
// Each <range> field shall be of the form:
// %1d", <beginning line number> if the range contains exactly one line,
// and:
// "%1d,%1d", <beginning line number>, <number of lines> otherwise.
// If a range is empty, its beginning line number shall be the number of
// the line just before the range, or 0 if the empty range starts the file.
fm := formatRangeUnified
assertEqual(t, fm(3, 3), "3,0")
assertEqual(t, fm(3, 4), "4")
assertEqual(t, fm(3, 5), "4,2")
assertEqual(t, fm(3, 6), "4,3")
assertEqual(t, fm(0, 0), "0,0")
}
func TestOutputFormatRangeFormatContext(t *testing.T) {
// Per the diff spec at http://www.unix.org/single_unix_specification/
//
// The range of lines in file1 shall be written in the following format
// if the range contains two or more lines:
// "*** %d,%d ****\n", <beginning line number>, <ending line number>
// and the following format otherwise:
// "*** %d ****\n", <ending line number>
// The ending line number of an empty range shall be the number of the preceding line,
// or 0 if the range is at the start of the file.
//
// Next, the range of lines in file2 shall be written in the following format
// if the range contains two or more lines:
// "--- %d,%d ----\n", <beginning line number>, <ending line number>
// and the following format otherwise:
// "--- %d ----\n", <ending line number>
fm := formatRangeContext
assertEqual(t, fm(3, 3), "3")
assertEqual(t, fm(3, 4), "4")
assertEqual(t, fm(3, 5), "4,5")
assertEqual(t, fm(3, 6), "4,6")
assertEqual(t, fm(0, 0), "0")
}
func TestOutputFormatTabDelimiter(t *testing.T) {
diff := UnifiedDiff{
A: splitChars("one"),
B: splitChars("two"),
FromFile: "Original",
FromDate: "2005-01-26 23:30:50",
ToFile: "Current",
ToDate: "2010-04-12 10:20:52",
Eol: "\n",
}
ud, err := GetUnifiedDiffString(diff)
assertEqual(t, err, nil)
assertEqual(t, SplitLines(ud)[:2], []string{
"--- Original\t2005-01-26 23:30:50\n",
"+++ Current\t2010-04-12 10:20:52\n",
})
cd, err := GetContextDiffString(ContextDiff(diff))
assertEqual(t, err, nil)
assertEqual(t, SplitLines(cd)[:2], []string{
"*** Original\t2005-01-26 23:30:50\n",
"--- Current\t2010-04-12 10:20:52\n",
})
}
func TestOutputFormatNoTrailingTabOnEmptyFiledate(t *testing.T) {
diff := UnifiedDiff{
A: splitChars("one"),
B: splitChars("two"),
FromFile: "Original",
ToFile: "Current",
Eol: "\n",
}
ud, err := GetUnifiedDiffString(diff)
assertEqual(t, err, nil)
assertEqual(t, SplitLines(ud)[:2], []string{"--- Original\n", "+++ Current\n"})
cd, err := GetContextDiffString(ContextDiff(diff))
assertEqual(t, err, nil)
assertEqual(t, SplitLines(cd)[:2], []string{"*** Original\n", "--- Current\n"})
}
func TestOmitFilenames(t *testing.T) {
diff := UnifiedDiff{
A: SplitLines("o\nn\ne\n"),
B: SplitLines("t\nw\no\n"),
Eol: "\n",
}
ud, err := GetUnifiedDiffString(diff)
assertEqual(t, err, nil)
assertEqual(t, SplitLines(ud), []string{
"@@ -0,0 +1,2 @@\n",
"+t\n",
"+w\n",
"@@ -2,2 +3,0 @@\n",
"-n\n",
"-e\n",
"\n",
})
cd, err := GetContextDiffString(ContextDiff(diff))
assertEqual(t, err, nil)
assertEqual(t, SplitLines(cd), []string{
"***************\n",
"*** 0 ****\n",
"--- 1,2 ----\n",
"+ t\n",
"+ w\n",
"***************\n",
"*** 2,3 ****\n",
"- n\n",
"- e\n",
"--- 3 ----\n",
"\n",
})
}
func TestSplitLines(t *testing.T) {
allTests := []struct {
input string
want []string
}{
{"foo", []string{"foo\n"}},
{"foo\nbar", []string{"foo\n", "bar\n"}},
{"foo\nbar\n", []string{"foo\n", "bar\n", "\n"}},
}
for _, test := range allTests {
assertEqual(t, SplitLines(test.input), test.want)
}
}
func benchmarkSplitLines(b *testing.B, count int) {
str := strings.Repeat("foo\n", count)
b.ResetTimer()
n := 0
for i := 0; i < b.N; i++ {
n += len(SplitLines(str))
}
}
func BenchmarkSplitLines100(b *testing.B) {
benchmarkSplitLines(b, 100)
}
func BenchmarkSplitLines10000(b *testing.B) {
benchmarkSplitLines(b, 10000)
}

View File

@ -1,24 +0,0 @@
# Compiled Object files, Static and Dynamic libs (Shared Objects)
*.o
*.a
*.so
# Folders
_obj
_test
# Architecture specific extensions/prefixes
*.[568vq]
[568vq].out
*.cgo1.go
*.cgo2.c
_cgo_defun.c
_cgo_gotypes.go
_cgo_export.*
_testmain.go
*.exe
.DS_Store

View File

@ -1,16 +0,0 @@
language: go
sudo: false
go:
- 1.1
- 1.2
- 1.3
- 1.4
- 1.5
- 1.6
- 1.7
- tip
script:
- go test -v ./...

View File

@ -1,23 +0,0 @@
{
"ImportPath": "github.com/stretchr/testify",
"GoVersion": "go1.5",
"GodepVersion": "v74",
"Packages": [
"./..."
],
"Deps": [
{
"ImportPath": "github.com/davecgh/go-spew/spew",
"Comment": "v1.0.0-3-g6d21280",
"Rev": "6d212800a42e8ab5c146b8ace3490ee17e5225f9"
},
{
"ImportPath": "github.com/pmezard/go-difflib/difflib",
"Rev": "d8ed2627bdf02c080bf22230dbb337003b7aba2d"
},
{
"ImportPath": "github.com/stretchr/objx",
"Rev": "cbeaeb16a013161a98496fad62933b1d21786672"
}
]
}

View File

@ -1,5 +0,0 @@
This directory tree is generated automatically by godep.
Please do not edit.
See https://github.com/tools/godep for more information.

View File

@ -1,22 +0,0 @@
Copyright (c) 2012 - 2013 Mat Ryer and Tyler Bunnell
Please consider promoting this project if you find it useful.
Permission is hereby granted, free of charge, to any person
obtaining a copy of this software and associated documentation
files (the "Software"), to deal in the Software without restriction,
including without limitation the rights to use, copy, modify, merge,
publish, distribute, sublicense, and/or sell copies of the Software,
and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:
The above copyright notice and this permission notice shall be included
in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT
OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

View File

@ -1,22 +0,0 @@
Copyright (c) 2012 - 2013 Mat Ryer and Tyler Bunnell
Please consider promoting this project if you find it useful.
Permission is hereby granted, free of charge, to any person
obtaining a copy of this software and associated documentation
files (the "Software"), to deal in the Software without restriction,
including without limitation the rights to use, copy, modify, merge,
publish, distribute, sublicense, and/or sell copies of the Software,
and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:
The above copyright notice and this permission notice shall be included
in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT
OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

View File

@ -1,332 +0,0 @@
Testify - Thou Shalt Write Tests
================================
[![Build Status](https://travis-ci.org/stretchr/testify.svg)](https://travis-ci.org/stretchr/testify) [![Go Report Card](https://goreportcard.com/badge/github.com/stretchr/testify)](https://goreportcard.com/report/github.com/stretchr/testify) [![GoDoc](https://godoc.org/github.com/stretchr/testify?status.svg)](https://godoc.org/github.com/stretchr/testify)
Go code (golang) set of packages that provide many tools for testifying that your code will behave as you intend.
Features include:
* [Easy assertions](#assert-package)
* [Mocking](#mock-package)
* [HTTP response trapping](#http-package)
* [Testing suite interfaces and functions](#suite-package)
Get started:
* Install testify with [one line of code](#installation), or [update it with another](#staying-up-to-date)
* For an introduction to writing test code in Go, see http://golang.org/doc/code.html#Testing
* Check out the API Documentation http://godoc.org/github.com/stretchr/testify
* To make your testing life easier, check out our other project, [gorc](http://github.com/stretchr/gorc)
* A little about [Test-Driven Development (TDD)](http://en.wikipedia.org/wiki/Test-driven_development)
[`assert`](http://godoc.org/github.com/stretchr/testify/assert "API documentation") package
-------------------------------------------------------------------------------------------
The `assert` package provides some helpful methods that allow you to write better test code in Go.
* Prints friendly, easy to read failure descriptions
* Allows for very readable code
* Optionally annotate each assertion with a message
See it in action:
```go
package yours
import (
"testing"
"github.com/stretchr/testify/assert"
)
func TestSomething(t *testing.T) {
// assert equality
assert.Equal(t, 123, 123, "they should be equal")
// assert inequality
assert.NotEqual(t, 123, 456, "they should not be equal")
// assert for nil (good for errors)
assert.Nil(t, object)
// assert for not nil (good when you expect something)
if assert.NotNil(t, object) {
// now we know that object isn't nil, we are safe to make
// further assertions without causing any errors
assert.Equal(t, "Something", object.Value)
}
}
```
* Every assert func takes the `testing.T` object as the first argument. This is how it writes the errors out through the normal `go test` capabilities.
* Every assert func returns a bool indicating whether the assertion was successful or not, this is useful for if you want to go on making further assertions under certain conditions.
if you assert many times, use the below:
```go
package yours
import (
"testing"
"github.com/stretchr/testify/assert"
)
func TestSomething(t *testing.T) {
assert := assert.New(t)
// assert equality
assert.Equal(123, 123, "they should be equal")
// assert inequality
assert.NotEqual(123, 456, "they should not be equal")
// assert for nil (good for errors)
assert.Nil(object)
// assert for not nil (good when you expect something)
if assert.NotNil(object) {
// now we know that object isn't nil, we are safe to make
// further assertions without causing any errors
assert.Equal("Something", object.Value)
}
}
```
[`require`](http://godoc.org/github.com/stretchr/testify/require "API documentation") package
---------------------------------------------------------------------------------------------
The `require` package provides same global functions as the `assert` package, but instead of returning a boolean result they terminate current test.
See [t.FailNow](http://golang.org/pkg/testing/#T.FailNow) for details.
[`http`](http://godoc.org/github.com/stretchr/testify/http "API documentation") package
---------------------------------------------------------------------------------------
The `http` package contains test objects useful for testing code that relies on the `net/http` package. Check out the [(deprecated) API documentation for the `http` package](http://godoc.org/github.com/stretchr/testify/http).
We recommend you use [httptest](http://golang.org/pkg/net/http/httptest) instead.
[`mock`](http://godoc.org/github.com/stretchr/testify/mock "API documentation") package
----------------------------------------------------------------------------------------
The `mock` package provides a mechanism for easily writing mock objects that can be used in place of real objects when writing test code.
An example test function that tests a piece of code that relies on an external object `testObj`, can setup expectations (testify) and assert that they indeed happened:
```go
package yours
import (
"testing"
"github.com/stretchr/testify/mock"
)
/*
Test objects
*/
// MyMockedObject is a mocked object that implements an interface
// that describes an object that the code I am testing relies on.
type MyMockedObject struct{
mock.Mock
}
// DoSomething is a method on MyMockedObject that implements some interface
// and just records the activity, and returns what the Mock object tells it to.
//
// In the real object, this method would do something useful, but since this
// is a mocked object - we're just going to stub it out.
//
// NOTE: This method is not being tested here, code that uses this object is.
func (m *MyMockedObject) DoSomething(number int) (bool, error) {
args := m.Called(number)
return args.Bool(0), args.Error(1)
}
/*
Actual test functions
*/
// TestSomething is an example of how to use our test object to
// make assertions about some target code we are testing.
func TestSomething(t *testing.T) {
// create an instance of our test object
testObj := new(MyMockedObject)
// setup expectations
testObj.On("DoSomething", 123).Return(true, nil)
// call the code we are testing
targetFuncThatDoesSomethingWithObj(testObj)
// assert that the expectations were met
testObj.AssertExpectations(t)
}
```
For more information on how to write mock code, check out the [API documentation for the `mock` package](http://godoc.org/github.com/stretchr/testify/mock).
You can use the [mockery tool](http://github.com/vektra/mockery) to autogenerate the mock code against an interface as well, making using mocks much quicker.
[`suite`](http://godoc.org/github.com/stretchr/testify/suite "API documentation") package
-----------------------------------------------------------------------------------------
The `suite` package provides functionality that you might be used to from more common object oriented languages. With it, you can build a testing suite as a struct, build setup/teardown methods and testing methods on your struct, and run them with 'go test' as per normal.
An example suite is shown below:
```go
// Basic imports
import (
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/suite"
)
// Define the suite, and absorb the built-in basic suite
// functionality from testify - including a T() method which
// returns the current testing context
type ExampleTestSuite struct {
suite.Suite
VariableThatShouldStartAtFive int
}
// Make sure that VariableThatShouldStartAtFive is set to five
// before each test
func (suite *ExampleTestSuite) SetupTest() {
suite.VariableThatShouldStartAtFive = 5
}
// All methods that begin with "Test" are run as tests within a
// suite.
func (suite *ExampleTestSuite) TestExample() {
assert.Equal(suite.T(), 5, suite.VariableThatShouldStartAtFive)
}
// In order for 'go test' to run this suite, we need to create
// a normal test function and pass our suite to suite.Run
func TestExampleTestSuite(t *testing.T) {
suite.Run(t, new(ExampleTestSuite))
}
```
For a more complete example, using all of the functionality provided by the suite package, look at our [example testing suite](https://github.com/stretchr/testify/blob/master/suite/suite_test.go)
For more information on writing suites, check out the [API documentation for the `suite` package](http://godoc.org/github.com/stretchr/testify/suite).
`Suite` object has assertion methods:
```go
// Basic imports
import (
"testing"
"github.com/stretchr/testify/suite"
)
// Define the suite, and absorb the built-in basic suite
// functionality from testify - including assertion methods.
type ExampleTestSuite struct {
suite.Suite
VariableThatShouldStartAtFive int
}
// Make sure that VariableThatShouldStartAtFive is set to five
// before each test
func (suite *ExampleTestSuite) SetupTest() {
suite.VariableThatShouldStartAtFive = 5
}
// All methods that begin with "Test" are run as tests within a
// suite.
func (suite *ExampleTestSuite) TestExample() {
suite.Equal(suite.VariableThatShouldStartAtFive, 5)
}
// In order for 'go test' to run this suite, we need to create
// a normal test function and pass our suite to suite.Run
func TestExampleTestSuite(t *testing.T) {
suite.Run(t, new(ExampleTestSuite))
}
```
------
Installation
============
To install Testify, use `go get`:
* Latest version: go get github.com/stretchr/testify
* Specific version: go get gopkg.in/stretchr/testify.v1
This will then make the following packages available to you:
github.com/stretchr/testify/assert
github.com/stretchr/testify/mock
github.com/stretchr/testify/http
Import the `testify/assert` package into your code using this template:
```go
package yours
import (
"testing"
"github.com/stretchr/testify/assert"
)
func TestSomething(t *testing.T) {
assert.True(t, true, "True is true!")
}
```
------
Staying up to date
==================
To update Testify to the latest version, use `go get -u github.com/stretchr/testify`.
------
Version History
===============
* 1.0 - New package versioning strategy adopted.
------
Contributing
============
Please feel free to submit issues, fork the repository and send pull requests!
When submitting an issue, we ask that you please include a complete test function that demonstrates the issue. Extra credit for those using Testify to write the test code that demonstrates it.
------
Licence
=======
Copyright (c) 2012 - 2013 Mat Ryer and Tyler Bunnell
Please consider promoting this project if you find it useful.
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

View File

@ -1,287 +0,0 @@
// This program reads all assertion functions from the assert package and
// automatically generates the corersponding requires and forwarded assertions
package main
import (
"bytes"
"flag"
"fmt"
"go/ast"
"go/build"
"go/doc"
"go/importer"
"go/parser"
"go/token"
"go/types"
"io"
"io/ioutil"
"log"
"os"
"path"
"strings"
"text/template"
"github.com/ernesto-jimenez/gogen/imports"
)
var (
pkg = flag.String("assert-path", "github.com/stretchr/testify/assert", "Path to the assert package")
outputPkg = flag.String("output-package", "", "package for the resulting code")
tmplFile = flag.String("template", "", "What file to load the function template from")
out = flag.String("out", "", "What file to write the source code to")
)
func main() {
flag.Parse()
scope, docs, err := parsePackageSource(*pkg)
if err != nil {
log.Fatal(err)
}
importer, funcs, err := analyzeCode(scope, docs)
if err != nil {
log.Fatal(err)
}
if err := generateCode(importer, funcs); err != nil {
log.Fatal(err)
}
}
func generateCode(importer imports.Importer, funcs []testFunc) error {
buff := bytes.NewBuffer(nil)
tmplHead, tmplFunc, err := parseTemplates()
if err != nil {
return err
}
// Generate header
if err := tmplHead.Execute(buff, struct {
Name string
Imports map[string]string
}{
*outputPkg,
importer.Imports(),
}); err != nil {
return err
}
// Generate funcs
for _, fn := range funcs {
buff.Write([]byte("\n\n"))
if err := tmplFunc.Execute(buff, &fn); err != nil {
return err
}
}
// Write file
output, err := outputFile()
if err != nil {
return err
}
defer output.Close()
_, err = io.Copy(output, buff)
return err
}
func parseTemplates() (*template.Template, *template.Template, error) {
tmplHead, err := template.New("header").Parse(headerTemplate)
if err != nil {
return nil, nil, err
}
if *tmplFile != "" {
f, err := ioutil.ReadFile(*tmplFile)
if err != nil {
return nil, nil, err
}
funcTemplate = string(f)
}
tmpl, err := template.New("function").Parse(funcTemplate)
if err != nil {
return nil, nil, err
}
return tmplHead, tmpl, nil
}
func outputFile() (*os.File, error) {
filename := *out
if filename == "-" || (filename == "" && *tmplFile == "") {
return os.Stdout, nil
}
if filename == "" {
filename = strings.TrimSuffix(strings.TrimSuffix(*tmplFile, ".tmpl"), ".go") + ".go"
}
return os.Create(filename)
}
// analyzeCode takes the types scope and the docs and returns the import
// information and information about all the assertion functions.
func analyzeCode(scope *types.Scope, docs *doc.Package) (imports.Importer, []testFunc, error) {
testingT := scope.Lookup("TestingT").Type().Underlying().(*types.Interface)
importer := imports.New(*outputPkg)
var funcs []testFunc
// Go through all the top level functions
for _, fdocs := range docs.Funcs {
// Find the function
obj := scope.Lookup(fdocs.Name)
fn, ok := obj.(*types.Func)
if !ok {
continue
}
// Check function signatuer has at least two arguments
sig := fn.Type().(*types.Signature)
if sig.Params().Len() < 2 {
continue
}
// Check first argument is of type testingT
first, ok := sig.Params().At(0).Type().(*types.Named)
if !ok {
continue
}
firstType, ok := first.Underlying().(*types.Interface)
if !ok {
continue
}
if !types.Implements(firstType, testingT) {
continue
}
funcs = append(funcs, testFunc{*outputPkg, fdocs, fn})
importer.AddImportsFrom(sig.Params())
}
return importer, funcs, nil
}
// parsePackageSource returns the types scope and the package documentation from the pa
func parsePackageSource(pkg string) (*types.Scope, *doc.Package, error) {
pd, err := build.Import(pkg, ".", 0)
if err != nil {
return nil, nil, err
}
fset := token.NewFileSet()
files := make(map[string]*ast.File)
fileList := make([]*ast.File, len(pd.GoFiles))
for i, fname := range pd.GoFiles {
src, err := ioutil.ReadFile(path.Join(pd.SrcRoot, pd.ImportPath, fname))
if err != nil {
return nil, nil, err
}
f, err := parser.ParseFile(fset, fname, src, parser.ParseComments|parser.AllErrors)
if err != nil {
return nil, nil, err
}
files[fname] = f
fileList[i] = f
}
cfg := types.Config{
Importer: importer.Default(),
}
info := types.Info{
Defs: make(map[*ast.Ident]types.Object),
}
tp, err := cfg.Check(pkg, fset, fileList, &info)
if err != nil {
return nil, nil, err
}
scope := tp.Scope()
ap, _ := ast.NewPackage(fset, files, nil, nil)
docs := doc.New(ap, pkg, 0)
return scope, docs, nil
}
type testFunc struct {
CurrentPkg string
DocInfo *doc.Func
TypeInfo *types.Func
}
func (f *testFunc) Qualifier(p *types.Package) string {
if p == nil || p.Name() == f.CurrentPkg {
return ""
}
return p.Name()
}
func (f *testFunc) Params() string {
sig := f.TypeInfo.Type().(*types.Signature)
params := sig.Params()
p := ""
comma := ""
to := params.Len()
var i int
if sig.Variadic() {
to--
}
for i = 1; i < to; i++ {
param := params.At(i)
p += fmt.Sprintf("%s%s %s", comma, param.Name(), types.TypeString(param.Type(), f.Qualifier))
comma = ", "
}
if sig.Variadic() {
param := params.At(params.Len() - 1)
p += fmt.Sprintf("%s%s ...%s", comma, param.Name(), types.TypeString(param.Type().(*types.Slice).Elem(), f.Qualifier))
}
return p
}
func (f *testFunc) ForwardedParams() string {
sig := f.TypeInfo.Type().(*types.Signature)
params := sig.Params()
p := ""
comma := ""
to := params.Len()
var i int
if sig.Variadic() {
to--
}
for i = 1; i < to; i++ {
param := params.At(i)
p += fmt.Sprintf("%s%s", comma, param.Name())
comma = ", "
}
if sig.Variadic() {
param := params.At(params.Len() - 1)
p += fmt.Sprintf("%s%s...", comma, param.Name())
}
return p
}
func (f *testFunc) Comment() string {
return "// " + strings.Replace(strings.TrimSpace(f.DocInfo.Doc), "\n", "\n// ", -1)
}
func (f *testFunc) CommentWithoutT(receiver string) string {
search := fmt.Sprintf("assert.%s(t, ", f.DocInfo.Name)
replace := fmt.Sprintf("%s.%s(", receiver, f.DocInfo.Name)
return strings.Replace(f.Comment(), search, replace, -1)
}
var headerTemplate = `/*
* CODE GENERATED AUTOMATICALLY WITH github.com/stretchr/testify/_codegen
* THIS FILE MUST NOT BE EDITED BY HAND
*/
package {{.Name}}
import (
{{range $path, $name := .Imports}}
{{$name}} "{{$path}}"{{end}}
)
`
var funcTemplate = `{{.Comment}}
func (fwd *AssertionsForwarder) {{.DocInfo.Name}}({{.Params}}) bool {
return assert.{{.DocInfo.Name}}({{.ForwardedParams}})
}`

View File

@ -1,387 +0,0 @@
/*
* CODE GENERATED AUTOMATICALLY WITH github.com/stretchr/testify/_codegen
* THIS FILE MUST NOT BE EDITED BY HAND
*/
package assert
import (
http "net/http"
url "net/url"
time "time"
)
// Condition uses a Comparison to assert a complex condition.
func (a *Assertions) Condition(comp Comparison, msgAndArgs ...interface{}) bool {
return Condition(a.t, comp, msgAndArgs...)
}
// Contains asserts that the specified string, list(array, slice...) or map contains the
// specified substring or element.
//
// a.Contains("Hello World", "World", "But 'Hello World' does contain 'World'")
// a.Contains(["Hello", "World"], "World", "But ["Hello", "World"] does contain 'World'")
// a.Contains({"Hello": "World"}, "Hello", "But {'Hello': 'World'} does contain 'Hello'")
//
// Returns whether the assertion was successful (true) or not (false).
func (a *Assertions) Contains(s interface{}, contains interface{}, msgAndArgs ...interface{}) bool {
return Contains(a.t, s, contains, msgAndArgs...)
}
// Empty asserts that the specified object is empty. I.e. nil, "", false, 0 or either
// a slice or a channel with len == 0.
//
// a.Empty(obj)
//
// Returns whether the assertion was successful (true) or not (false).
func (a *Assertions) Empty(object interface{}, msgAndArgs ...interface{}) bool {
return Empty(a.t, object, msgAndArgs...)
}
// Equal asserts that two objects are equal.
//
// a.Equal(123, 123, "123 and 123 should be equal")
//
// Returns whether the assertion was successful (true) or not (false).
func (a *Assertions) Equal(expected interface{}, actual interface{}, msgAndArgs ...interface{}) bool {
return Equal(a.t, expected, actual, msgAndArgs...)
}
// EqualError asserts that a function returned an error (i.e. not `nil`)
// and that it is equal to the provided error.
//
// actualObj, err := SomeFunction()
// if assert.Error(t, err, "An error was expected") {
// assert.Equal(t, err, expectedError)
// }
//
// Returns whether the assertion was successful (true) or not (false).
func (a *Assertions) EqualError(theError error, errString string, msgAndArgs ...interface{}) bool {
return EqualError(a.t, theError, errString, msgAndArgs...)
}
// EqualValues asserts that two objects are equal or convertable to the same types
// and equal.
//
// a.EqualValues(uint32(123), int32(123), "123 and 123 should be equal")
//
// Returns whether the assertion was successful (true) or not (false).
func (a *Assertions) EqualValues(expected interface{}, actual interface{}, msgAndArgs ...interface{}) bool {
return EqualValues(a.t, expected, actual, msgAndArgs...)
}
// Error asserts that a function returned an error (i.e. not `nil`).
//
// actualObj, err := SomeFunction()
// if a.Error(err, "An error was expected") {
// assert.Equal(t, err, expectedError)
// }
//
// Returns whether the assertion was successful (true) or not (false).
func (a *Assertions) Error(err error, msgAndArgs ...interface{}) bool {
return Error(a.t, err, msgAndArgs...)
}
// Exactly asserts that two objects are equal is value and type.
//
// a.Exactly(int32(123), int64(123), "123 and 123 should NOT be equal")
//
// Returns whether the assertion was successful (true) or not (false).
func (a *Assertions) Exactly(expected interface{}, actual interface{}, msgAndArgs ...interface{}) bool {
return Exactly(a.t, expected, actual, msgAndArgs...)
}
// Fail reports a failure through
func (a *Assertions) Fail(failureMessage string, msgAndArgs ...interface{}) bool {
return Fail(a.t, failureMessage, msgAndArgs...)
}
// FailNow fails test
func (a *Assertions) FailNow(failureMessage string, msgAndArgs ...interface{}) bool {
return FailNow(a.t, failureMessage, msgAndArgs...)
}
// False asserts that the specified value is false.
//
// a.False(myBool, "myBool should be false")
//
// Returns whether the assertion was successful (true) or not (false).
func (a *Assertions) False(value bool, msgAndArgs ...interface{}) bool {
return False(a.t, value, msgAndArgs...)
}
// HTTPBodyContains asserts that a specified handler returns a
// body that contains a string.
//
// a.HTTPBodyContains(myHandler, "www.google.com", nil, "I'm Feeling Lucky")
//
// Returns whether the assertion was successful (true) or not (false).
func (a *Assertions) HTTPBodyContains(handler http.HandlerFunc, method string, url string, values url.Values, str interface{}) bool {
return HTTPBodyContains(a.t, handler, method, url, values, str)
}
// HTTPBodyNotContains asserts that a specified handler returns a
// body that does not contain a string.
//
// a.HTTPBodyNotContains(myHandler, "www.google.com", nil, "I'm Feeling Lucky")
//
// Returns whether the assertion was successful (true) or not (false).
func (a *Assertions) HTTPBodyNotContains(handler http.HandlerFunc, method string, url string, values url.Values, str interface{}) bool {
return HTTPBodyNotContains(a.t, handler, method, url, values, str)
}
// HTTPError asserts that a specified handler returns an error status code.
//
// a.HTTPError(myHandler, "POST", "/a/b/c", url.Values{"a": []string{"b", "c"}}
//
// Returns whether the assertion was successful (true) or not (false).
func (a *Assertions) HTTPError(handler http.HandlerFunc, method string, url string, values url.Values) bool {
return HTTPError(a.t, handler, method, url, values)
}
// HTTPRedirect asserts that a specified handler returns a redirect status code.
//
// a.HTTPRedirect(myHandler, "GET", "/a/b/c", url.Values{"a": []string{"b", "c"}}
//
// Returns whether the assertion was successful (true) or not (false).
func (a *Assertions) HTTPRedirect(handler http.HandlerFunc, method string, url string, values url.Values) bool {
return HTTPRedirect(a.t, handler, method, url, values)
}
// HTTPSuccess asserts that a specified handler returns a success status code.
//
// a.HTTPSuccess(myHandler, "POST", "http://www.google.com", nil)
//
// Returns whether the assertion was successful (true) or not (false).
func (a *Assertions) HTTPSuccess(handler http.HandlerFunc, method string, url string, values url.Values) bool {
return HTTPSuccess(a.t, handler, method, url, values)
}
// Implements asserts that an object is implemented by the specified interface.
//
// a.Implements((*MyInterface)(nil), new(MyObject), "MyObject")
func (a *Assertions) Implements(interfaceObject interface{}, object interface{}, msgAndArgs ...interface{}) bool {
return Implements(a.t, interfaceObject, object, msgAndArgs...)
}
// InDelta asserts that the two numerals are within delta of each other.
//
// a.InDelta(math.Pi, (22 / 7.0), 0.01)
//
// Returns whether the assertion was successful (true) or not (false).
func (a *Assertions) InDelta(expected interface{}, actual interface{}, delta float64, msgAndArgs ...interface{}) bool {
return InDelta(a.t, expected, actual, delta, msgAndArgs...)
}
// InDeltaSlice is the same as InDelta, except it compares two slices.
func (a *Assertions) InDeltaSlice(expected interface{}, actual interface{}, delta float64, msgAndArgs ...interface{}) bool {
return InDeltaSlice(a.t, expected, actual, delta, msgAndArgs...)
}
// InEpsilon asserts that expected and actual have a relative error less than epsilon
//
// Returns whether the assertion was successful (true) or not (false).
func (a *Assertions) InEpsilon(expected interface{}, actual interface{}, epsilon float64, msgAndArgs ...interface{}) bool {
return InEpsilon(a.t, expected, actual, epsilon, msgAndArgs...)
}
// InEpsilonSlice is the same as InEpsilon, except it compares two slices.
func (a *Assertions) InEpsilonSlice(expected interface{}, actual interface{}, delta float64, msgAndArgs ...interface{}) bool {
return InEpsilonSlice(a.t, expected, actual, delta, msgAndArgs...)
}
// IsType asserts that the specified objects are of the same type.
func (a *Assertions) IsType(expectedType interface{}, object interface{}, msgAndArgs ...interface{}) bool {
return IsType(a.t, expectedType, object, msgAndArgs...)
}
// JSONEq asserts that two JSON strings are equivalent.
//
// a.JSONEq(`{"hello": "world", "foo": "bar"}`, `{"foo": "bar", "hello": "world"}`)
//
// Returns whether the assertion was successful (true) or not (false).
func (a *Assertions) JSONEq(expected string, actual string, msgAndArgs ...interface{}) bool {
return JSONEq(a.t, expected, actual, msgAndArgs...)
}
// Len asserts that the specified object has specific length.
// Len also fails if the object has a type that len() not accept.
//
// a.Len(mySlice, 3, "The size of slice is not 3")
//
// Returns whether the assertion was successful (true) or not (false).
func (a *Assertions) Len(object interface{}, length int, msgAndArgs ...interface{}) bool {
return Len(a.t, object, length, msgAndArgs...)
}
// Nil asserts that the specified object is nil.
//
// a.Nil(err, "err should be nothing")
//
// Returns whether the assertion was successful (true) or not (false).
func (a *Assertions) Nil(object interface{}, msgAndArgs ...interface{}) bool {
return Nil(a.t, object, msgAndArgs...)
}
// NoError asserts that a function returned no error (i.e. `nil`).
//
// actualObj, err := SomeFunction()
// if a.NoError(err) {
// assert.Equal(t, actualObj, expectedObj)
// }
//
// Returns whether the assertion was successful (true) or not (false).
func (a *Assertions) NoError(err error, msgAndArgs ...interface{}) bool {
return NoError(a.t, err, msgAndArgs...)
}
// NotContains asserts that the specified string, list(array, slice...) or map does NOT contain the
// specified substring or element.
//
// a.NotContains("Hello World", "Earth", "But 'Hello World' does NOT contain 'Earth'")
// a.NotContains(["Hello", "World"], "Earth", "But ['Hello', 'World'] does NOT contain 'Earth'")
// a.NotContains({"Hello": "World"}, "Earth", "But {'Hello': 'World'} does NOT contain 'Earth'")
//
// Returns whether the assertion was successful (true) or not (false).
func (a *Assertions) NotContains(s interface{}, contains interface{}, msgAndArgs ...interface{}) bool {
return NotContains(a.t, s, contains, msgAndArgs...)
}
// NotEmpty asserts that the specified object is NOT empty. I.e. not nil, "", false, 0 or either
// a slice or a channel with len == 0.
//
// if a.NotEmpty(obj) {
// assert.Equal(t, "two", obj[1])
// }
//
// Returns whether the assertion was successful (true) or not (false).
func (a *Assertions) NotEmpty(object interface{}, msgAndArgs ...interface{}) bool {
return NotEmpty(a.t, object, msgAndArgs...)
}
// NotEqual asserts that the specified values are NOT equal.
//
// a.NotEqual(obj1, obj2, "two objects shouldn't be equal")
//
// Returns whether the assertion was successful (true) or not (false).
func (a *Assertions) NotEqual(expected interface{}, actual interface{}, msgAndArgs ...interface{}) bool {
return NotEqual(a.t, expected, actual, msgAndArgs...)
}
// NotNil asserts that the specified object is not nil.
//
// a.NotNil(err, "err should be something")
//
// Returns whether the assertion was successful (true) or not (false).
func (a *Assertions) NotNil(object interface{}, msgAndArgs ...interface{}) bool {
return NotNil(a.t, object, msgAndArgs...)
}
// NotPanics asserts that the code inside the specified PanicTestFunc does NOT panic.
//
// a.NotPanics(func(){
// RemainCalm()
// }, "Calling RemainCalm() should NOT panic")
//
// Returns whether the assertion was successful (true) or not (false).
func (a *Assertions) NotPanics(f PanicTestFunc, msgAndArgs ...interface{}) bool {
return NotPanics(a.t, f, msgAndArgs...)
}
// NotRegexp asserts that a specified regexp does not match a string.
//
// a.NotRegexp(regexp.MustCompile("starts"), "it's starting")
// a.NotRegexp("^start", "it's not starting")
//
// Returns whether the assertion was successful (true) or not (false).
func (a *Assertions) NotRegexp(rx interface{}, str interface{}, msgAndArgs ...interface{}) bool {
return NotRegexp(a.t, rx, str, msgAndArgs...)
}
// NotZero asserts that i is not the zero value for its type and returns the truth.
func (a *Assertions) NotZero(i interface{}, msgAndArgs ...interface{}) bool {
return NotZero(a.t, i, msgAndArgs...)
}
// Panics asserts that the code inside the specified PanicTestFunc panics.
//
// a.Panics(func(){
// GoCrazy()
// }, "Calling GoCrazy() should panic")
//
// Returns whether the assertion was successful (true) or not (false).
func (a *Assertions) Panics(f PanicTestFunc, msgAndArgs ...interface{}) bool {
return Panics(a.t, f, msgAndArgs...)
}
// Regexp asserts that a specified regexp matches a string.
//
// a.Regexp(regexp.MustCompile("start"), "it's starting")
// a.Regexp("start...$", "it's not starting")
//
// Returns whether the assertion was successful (true) or not (false).
func (a *Assertions) Regexp(rx interface{}, str interface{}, msgAndArgs ...interface{}) bool {
return Regexp(a.t, rx, str, msgAndArgs...)
}
// True asserts that the specified value is true.
//
// a.True(myBool, "myBool should be true")
//
// Returns whether the assertion was successful (true) or not (false).
func (a *Assertions) True(value bool, msgAndArgs ...interface{}) bool {
return True(a.t, value, msgAndArgs...)
}
// WithinDuration asserts that the two times are within duration delta of each other.
//
// a.WithinDuration(time.Now(), time.Now(), 10*time.Second, "The difference should not be more than 10s")
//
// Returns whether the assertion was successful (true) or not (false).
func (a *Assertions) WithinDuration(expected time.Time, actual time.Time, delta time.Duration, msgAndArgs ...interface{}) bool {
return WithinDuration(a.t, expected, actual, delta, msgAndArgs...)
}
// Zero asserts that i is the zero value for its type and returns the truth.
func (a *Assertions) Zero(i interface{}, msgAndArgs ...interface{}) bool {
return Zero(a.t, i, msgAndArgs...)
}

View File

@ -1,4 +0,0 @@
{{.CommentWithoutT "a"}}
func (a *Assertions) {{.DocInfo.Name}}({{.Params}}) bool {
return {{.DocInfo.Name}}(a.t, {{.ForwardedParams}})
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -1,45 +0,0 @@
// Package assert provides a set of comprehensive testing tools for use with the normal Go testing system.
//
// Example Usage
//
// The following is a complete example using assert in a standard test function:
// import (
// "testing"
// "github.com/stretchr/testify/assert"
// )
//
// func TestSomething(t *testing.T) {
//
// var a string = "Hello"
// var b string = "Hello"
//
// assert.Equal(t, a, b, "The two words should be the same.")
//
// }
//
// if you assert many times, use the format below:
//
// import (
// "testing"
// "github.com/stretchr/testify/assert"
// )
//
// func TestSomething(t *testing.T) {
// assert := assert.New(t)
//
// var a string = "Hello"
// var b string = "Hello"
//
// assert.Equal(a, b, "The two words should be the same.")
// }
//
// Assertions
//
// Assertions allow you to easily write test code, and are global funcs in the `assert` package.
// All assertion functions take, as the first argument, the `*testing.T` object provided by the
// testing framework. This allows the assertion funcs to write the failings and other details to
// the correct place.
//
// Every assertion function also takes an optional string message as the final argument,
// allowing custom error messages to be appended to the message the assertion method outputs.
package assert

View File

@ -1,10 +0,0 @@
package assert
import (
"errors"
)
// AnError is an error instance useful for testing. If the code does not care
// about error specifics, and only needs to return the error for example, this
// error should be used to make the test code more readable.
var AnError = errors.New("assert.AnError general error for testing")

View File

@ -1,16 +0,0 @@
package assert
// Assertions provides assertion methods around the
// TestingT interface.
type Assertions struct {
t TestingT
}
// New makes a new Assertions object for the specified TestingT.
func New(t TestingT) *Assertions {
return &Assertions{
t: t,
}
}
//go:generate go run ../_codegen/main.go -output-package=assert -template=assertion_forward.go.tmpl

View File

@ -1,611 +0,0 @@
package assert
import (
"errors"
"regexp"
"testing"
"time"
)
func TestImplementsWrapper(t *testing.T) {
assert := New(new(testing.T))
if !assert.Implements((*AssertionTesterInterface)(nil), new(AssertionTesterConformingObject)) {
t.Error("Implements method should return true: AssertionTesterConformingObject implements AssertionTesterInterface")
}
if assert.Implements((*AssertionTesterInterface)(nil), new(AssertionTesterNonConformingObject)) {
t.Error("Implements method should return false: AssertionTesterNonConformingObject does not implements AssertionTesterInterface")
}
}
func TestIsTypeWrapper(t *testing.T) {
assert := New(new(testing.T))
if !assert.IsType(new(AssertionTesterConformingObject), new(AssertionTesterConformingObject)) {
t.Error("IsType should return true: AssertionTesterConformingObject is the same type as AssertionTesterConformingObject")
}
if assert.IsType(new(AssertionTesterConformingObject), new(AssertionTesterNonConformingObject)) {
t.Error("IsType should return false: AssertionTesterConformingObject is not the same type as AssertionTesterNonConformingObject")
}
}
func TestEqualWrapper(t *testing.T) {
assert := New(new(testing.T))
if !assert.Equal("Hello World", "Hello World") {
t.Error("Equal should return true")
}
if !assert.Equal(123, 123) {
t.Error("Equal should return true")
}
if !assert.Equal(123.5, 123.5) {
t.Error("Equal should return true")
}
if !assert.Equal([]byte("Hello World"), []byte("Hello World")) {
t.Error("Equal should return true")
}
if !assert.Equal(nil, nil) {
t.Error("Equal should return true")
}
}
func TestEqualValuesWrapper(t *testing.T) {
assert := New(new(testing.T))
if !assert.EqualValues(uint32(10), int32(10)) {
t.Error("EqualValues should return true")
}
}
func TestNotNilWrapper(t *testing.T) {
assert := New(new(testing.T))
if !assert.NotNil(new(AssertionTesterConformingObject)) {
t.Error("NotNil should return true: object is not nil")
}
if assert.NotNil(nil) {
t.Error("NotNil should return false: object is nil")
}
}
func TestNilWrapper(t *testing.T) {
assert := New(new(testing.T))
if !assert.Nil(nil) {
t.Error("Nil should return true: object is nil")
}
if assert.Nil(new(AssertionTesterConformingObject)) {
t.Error("Nil should return false: object is not nil")
}
}
func TestTrueWrapper(t *testing.T) {
assert := New(new(testing.T))
if !assert.True(true) {
t.Error("True should return true")
}
if assert.True(false) {
t.Error("True should return false")
}
}
func TestFalseWrapper(t *testing.T) {
assert := New(new(testing.T))
if !assert.False(false) {
t.Error("False should return true")
}
if assert.False(true) {
t.Error("False should return false")
}
}
func TestExactlyWrapper(t *testing.T) {
assert := New(new(testing.T))
a := float32(1)
b := float64(1)
c := float32(1)
d := float32(2)
if assert.Exactly(a, b) {
t.Error("Exactly should return false")
}
if assert.Exactly(a, d) {
t.Error("Exactly should return false")
}
if !assert.Exactly(a, c) {
t.Error("Exactly should return true")
}
if assert.Exactly(nil, a) {
t.Error("Exactly should return false")
}
if assert.Exactly(a, nil) {
t.Error("Exactly should return false")
}
}
func TestNotEqualWrapper(t *testing.T) {
assert := New(new(testing.T))
if !assert.NotEqual("Hello World", "Hello World!") {
t.Error("NotEqual should return true")
}
if !assert.NotEqual(123, 1234) {
t.Error("NotEqual should return true")
}
if !assert.NotEqual(123.5, 123.55) {
t.Error("NotEqual should return true")
}
if !assert.NotEqual([]byte("Hello World"), []byte("Hello World!")) {
t.Error("NotEqual should return true")
}
if !assert.NotEqual(nil, new(AssertionTesterConformingObject)) {
t.Error("NotEqual should return true")
}
}
func TestContainsWrapper(t *testing.T) {
assert := New(new(testing.T))
list := []string{"Foo", "Bar"}
if !assert.Contains("Hello World", "Hello") {
t.Error("Contains should return true: \"Hello World\" contains \"Hello\"")
}
if assert.Contains("Hello World", "Salut") {
t.Error("Contains should return false: \"Hello World\" does not contain \"Salut\"")
}
if !assert.Contains(list, "Foo") {
t.Error("Contains should return true: \"[\"Foo\", \"Bar\"]\" contains \"Foo\"")
}
if assert.Contains(list, "Salut") {
t.Error("Contains should return false: \"[\"Foo\", \"Bar\"]\" does not contain \"Salut\"")
}
}
func TestNotContainsWrapper(t *testing.T) {
assert := New(new(testing.T))
list := []string{"Foo", "Bar"}
if !assert.NotContains("Hello World", "Hello!") {
t.Error("NotContains should return true: \"Hello World\" does not contain \"Hello!\"")
}
if assert.NotContains("Hello World", "Hello") {
t.Error("NotContains should return false: \"Hello World\" contains \"Hello\"")
}
if !assert.NotContains(list, "Foo!") {
t.Error("NotContains should return true: \"[\"Foo\", \"Bar\"]\" does not contain \"Foo!\"")
}
if assert.NotContains(list, "Foo") {
t.Error("NotContains should return false: \"[\"Foo\", \"Bar\"]\" contains \"Foo\"")
}
}
func TestConditionWrapper(t *testing.T) {
assert := New(new(testing.T))
if !assert.Condition(func() bool { return true }, "Truth") {
t.Error("Condition should return true")
}
if assert.Condition(func() bool { return false }, "Lie") {
t.Error("Condition should return false")
}
}
func TestDidPanicWrapper(t *testing.T) {
if funcDidPanic, _ := didPanic(func() {
panic("Panic!")
}); !funcDidPanic {
t.Error("didPanic should return true")
}
if funcDidPanic, _ := didPanic(func() {
}); funcDidPanic {
t.Error("didPanic should return false")
}
}
func TestPanicsWrapper(t *testing.T) {
assert := New(new(testing.T))
if !assert.Panics(func() {
panic("Panic!")
}) {
t.Error("Panics should return true")
}
if assert.Panics(func() {
}) {
t.Error("Panics should return false")
}
}
func TestNotPanicsWrapper(t *testing.T) {
assert := New(new(testing.T))
if !assert.NotPanics(func() {
}) {
t.Error("NotPanics should return true")
}
if assert.NotPanics(func() {
panic("Panic!")
}) {
t.Error("NotPanics should return false")
}
}
func TestNoErrorWrapper(t *testing.T) {
assert := New(t)
mockAssert := New(new(testing.T))
// start with a nil error
var err error
assert.True(mockAssert.NoError(err), "NoError should return True for nil arg")
// now set an error
err = errors.New("Some error")
assert.False(mockAssert.NoError(err), "NoError with error should return False")
}
func TestErrorWrapper(t *testing.T) {
assert := New(t)
mockAssert := New(new(testing.T))
// start with a nil error
var err error
assert.False(mockAssert.Error(err), "Error should return False for nil arg")
// now set an error
err = errors.New("Some error")
assert.True(mockAssert.Error(err), "Error with error should return True")
}
func TestEqualErrorWrapper(t *testing.T) {
assert := New(t)
mockAssert := New(new(testing.T))
// start with a nil error
var err error
assert.False(mockAssert.EqualError(err, ""),
"EqualError should return false for nil arg")
// now set an error
err = errors.New("some error")
assert.False(mockAssert.EqualError(err, "Not some error"),
"EqualError should return false for different error string")
assert.True(mockAssert.EqualError(err, "some error"),
"EqualError should return true")
}
func TestEmptyWrapper(t *testing.T) {
assert := New(t)
mockAssert := New(new(testing.T))
assert.True(mockAssert.Empty(""), "Empty string is empty")
assert.True(mockAssert.Empty(nil), "Nil is empty")
assert.True(mockAssert.Empty([]string{}), "Empty string array is empty")
assert.True(mockAssert.Empty(0), "Zero int value is empty")
assert.True(mockAssert.Empty(false), "False value is empty")
assert.False(mockAssert.Empty("something"), "Non Empty string is not empty")
assert.False(mockAssert.Empty(errors.New("something")), "Non nil object is not empty")
assert.False(mockAssert.Empty([]string{"something"}), "Non empty string array is not empty")
assert.False(mockAssert.Empty(1), "Non-zero int value is not empty")
assert.False(mockAssert.Empty(true), "True value is not empty")
}
func TestNotEmptyWrapper(t *testing.T) {
assert := New(t)
mockAssert := New(new(testing.T))
assert.False(mockAssert.NotEmpty(""), "Empty string is empty")
assert.False(mockAssert.NotEmpty(nil), "Nil is empty")
assert.False(mockAssert.NotEmpty([]string{}), "Empty string array is empty")
assert.False(mockAssert.NotEmpty(0), "Zero int value is empty")
assert.False(mockAssert.NotEmpty(false), "False value is empty")
assert.True(mockAssert.NotEmpty("something"), "Non Empty string is not empty")
assert.True(mockAssert.NotEmpty(errors.New("something")), "Non nil object is not empty")
assert.True(mockAssert.NotEmpty([]string{"something"}), "Non empty string array is not empty")
assert.True(mockAssert.NotEmpty(1), "Non-zero int value is not empty")
assert.True(mockAssert.NotEmpty(true), "True value is not empty")
}
func TestLenWrapper(t *testing.T) {
assert := New(t)
mockAssert := New(new(testing.T))
assert.False(mockAssert.Len(nil, 0), "nil does not have length")
assert.False(mockAssert.Len(0, 0), "int does not have length")
assert.False(mockAssert.Len(true, 0), "true does not have length")
assert.False(mockAssert.Len(false, 0), "false does not have length")
assert.False(mockAssert.Len('A', 0), "Rune does not have length")
assert.False(mockAssert.Len(struct{}{}, 0), "Struct does not have length")
ch := make(chan int, 5)
ch <- 1
ch <- 2
ch <- 3
cases := []struct {
v interface{}
l int
}{
{[]int{1, 2, 3}, 3},
{[...]int{1, 2, 3}, 3},
{"ABC", 3},
{map[int]int{1: 2, 2: 4, 3: 6}, 3},
{ch, 3},
{[]int{}, 0},
{map[int]int{}, 0},
{make(chan int), 0},
{[]int(nil), 0},
{map[int]int(nil), 0},
{(chan int)(nil), 0},
}
for _, c := range cases {
assert.True(mockAssert.Len(c.v, c.l), "%#v have %d items", c.v, c.l)
}
}
func TestWithinDurationWrapper(t *testing.T) {
assert := New(t)
mockAssert := New(new(testing.T))
a := time.Now()
b := a.Add(10 * time.Second)
assert.True(mockAssert.WithinDuration(a, b, 10*time.Second), "A 10s difference is within a 10s time difference")
assert.True(mockAssert.WithinDuration(b, a, 10*time.Second), "A 10s difference is within a 10s time difference")
assert.False(mockAssert.WithinDuration(a, b, 9*time.Second), "A 10s difference is not within a 9s time difference")
assert.False(mockAssert.WithinDuration(b, a, 9*time.Second), "A 10s difference is not within a 9s time difference")
assert.False(mockAssert.WithinDuration(a, b, -9*time.Second), "A 10s difference is not within a 9s time difference")
assert.False(mockAssert.WithinDuration(b, a, -9*time.Second), "A 10s difference is not within a 9s time difference")
assert.False(mockAssert.WithinDuration(a, b, -11*time.Second), "A 10s difference is not within a 9s time difference")
assert.False(mockAssert.WithinDuration(b, a, -11*time.Second), "A 10s difference is not within a 9s time difference")
}
func TestInDeltaWrapper(t *testing.T) {
assert := New(new(testing.T))
True(t, assert.InDelta(1.001, 1, 0.01), "|1.001 - 1| <= 0.01")
True(t, assert.InDelta(1, 1.001, 0.01), "|1 - 1.001| <= 0.01")
True(t, assert.InDelta(1, 2, 1), "|1 - 2| <= 1")
False(t, assert.InDelta(1, 2, 0.5), "Expected |1 - 2| <= 0.5 to fail")
False(t, assert.InDelta(2, 1, 0.5), "Expected |2 - 1| <= 0.5 to fail")
False(t, assert.InDelta("", nil, 1), "Expected non numerals to fail")
cases := []struct {
a, b interface{}
delta float64
}{
{uint8(2), uint8(1), 1},
{uint16(2), uint16(1), 1},
{uint32(2), uint32(1), 1},
{uint64(2), uint64(1), 1},
{int(2), int(1), 1},
{int8(2), int8(1), 1},
{int16(2), int16(1), 1},
{int32(2), int32(1), 1},
{int64(2), int64(1), 1},
{float32(2), float32(1), 1},
{float64(2), float64(1), 1},
}
for _, tc := range cases {
True(t, assert.InDelta(tc.a, tc.b, tc.delta), "Expected |%V - %V| <= %v", tc.a, tc.b, tc.delta)
}
}
func TestInEpsilonWrapper(t *testing.T) {
assert := New(new(testing.T))
cases := []struct {
a, b interface{}
epsilon float64
}{
{uint8(2), uint16(2), .001},
{2.1, 2.2, 0.1},
{2.2, 2.1, 0.1},
{-2.1, -2.2, 0.1},
{-2.2, -2.1, 0.1},
{uint64(100), uint8(101), 0.01},
{0.1, -0.1, 2},
}
for _, tc := range cases {
True(t, assert.InEpsilon(tc.a, tc.b, tc.epsilon, "Expected %V and %V to have a relative difference of %v", tc.a, tc.b, tc.epsilon))
}
cases = []struct {
a, b interface{}
epsilon float64
}{
{uint8(2), int16(-2), .001},
{uint64(100), uint8(102), 0.01},
{2.1, 2.2, 0.001},
{2.2, 2.1, 0.001},
{2.1, -2.2, 1},
{2.1, "bla-bla", 0},
{0.1, -0.1, 1.99},
}
for _, tc := range cases {
False(t, assert.InEpsilon(tc.a, tc.b, tc.epsilon, "Expected %V and %V to have a relative difference of %v", tc.a, tc.b, tc.epsilon))
}
}
func TestRegexpWrapper(t *testing.T) {
assert := New(new(testing.T))
cases := []struct {
rx, str string
}{
{"^start", "start of the line"},
{"end$", "in the end"},
{"[0-9]{3}[.-]?[0-9]{2}[.-]?[0-9]{2}", "My phone number is 650.12.34"},
}
for _, tc := range cases {
True(t, assert.Regexp(tc.rx, tc.str))
True(t, assert.Regexp(regexp.MustCompile(tc.rx), tc.str))
False(t, assert.NotRegexp(tc.rx, tc.str))
False(t, assert.NotRegexp(regexp.MustCompile(tc.rx), tc.str))
}
cases = []struct {
rx, str string
}{
{"^asdfastart", "Not the start of the line"},
{"end$", "in the end."},
{"[0-9]{3}[.-]?[0-9]{2}[.-]?[0-9]{2}", "My phone number is 650.12a.34"},
}
for _, tc := range cases {
False(t, assert.Regexp(tc.rx, tc.str), "Expected \"%s\" to not match \"%s\"", tc.rx, tc.str)
False(t, assert.Regexp(regexp.MustCompile(tc.rx), tc.str))
True(t, assert.NotRegexp(tc.rx, tc.str))
True(t, assert.NotRegexp(regexp.MustCompile(tc.rx), tc.str))
}
}
func TestZeroWrapper(t *testing.T) {
assert := New(t)
mockAssert := New(new(testing.T))
for _, test := range zeros {
assert.True(mockAssert.Zero(test), "Zero should return true for %v", test)
}
for _, test := range nonZeros {
assert.False(mockAssert.Zero(test), "Zero should return false for %v", test)
}
}
func TestNotZeroWrapper(t *testing.T) {
assert := New(t)
mockAssert := New(new(testing.T))
for _, test := range zeros {
assert.False(mockAssert.NotZero(test), "Zero should return true for %v", test)
}
for _, test := range nonZeros {
assert.True(mockAssert.NotZero(test), "Zero should return false for %v", test)
}
}
func TestJSONEqWrapper_EqualSONString(t *testing.T) {
assert := New(new(testing.T))
if !assert.JSONEq(`{"hello": "world", "foo": "bar"}`, `{"hello": "world", "foo": "bar"}`) {
t.Error("JSONEq should return true")
}
}
func TestJSONEqWrapper_EquivalentButNotEqual(t *testing.T) {
assert := New(new(testing.T))
if !assert.JSONEq(`{"hello": "world", "foo": "bar"}`, `{"foo": "bar", "hello": "world"}`) {
t.Error("JSONEq should return true")
}
}
func TestJSONEqWrapper_HashOfArraysAndHashes(t *testing.T) {
assert := New(new(testing.T))
if !assert.JSONEq("{\r\n\t\"numeric\": 1.5,\r\n\t\"array\": [{\"foo\": \"bar\"}, 1, \"string\", [\"nested\", \"array\", 5.5]],\r\n\t\"hash\": {\"nested\": \"hash\", \"nested_slice\": [\"this\", \"is\", \"nested\"]},\r\n\t\"string\": \"foo\"\r\n}",
"{\r\n\t\"numeric\": 1.5,\r\n\t\"hash\": {\"nested\": \"hash\", \"nested_slice\": [\"this\", \"is\", \"nested\"]},\r\n\t\"string\": \"foo\",\r\n\t\"array\": [{\"foo\": \"bar\"}, 1, \"string\", [\"nested\", \"array\", 5.5]]\r\n}") {
t.Error("JSONEq should return true")
}
}
func TestJSONEqWrapper_Array(t *testing.T) {
assert := New(new(testing.T))
if !assert.JSONEq(`["foo", {"hello": "world", "nested": "hash"}]`, `["foo", {"nested": "hash", "hello": "world"}]`) {
t.Error("JSONEq should return true")
}
}
func TestJSONEqWrapper_HashAndArrayNotEquivalent(t *testing.T) {
assert := New(new(testing.T))
if assert.JSONEq(`["foo", {"hello": "world", "nested": "hash"}]`, `{"foo": "bar", {"nested": "hash", "hello": "world"}}`) {
t.Error("JSONEq should return false")
}
}
func TestJSONEqWrapper_HashesNotEquivalent(t *testing.T) {
assert := New(new(testing.T))
if assert.JSONEq(`{"foo": "bar"}`, `{"foo": "bar", "hello": "world"}`) {
t.Error("JSONEq should return false")
}
}
func TestJSONEqWrapper_ActualIsNotJSON(t *testing.T) {
assert := New(new(testing.T))
if assert.JSONEq(`{"foo": "bar"}`, "Not JSON") {
t.Error("JSONEq should return false")
}
}
func TestJSONEqWrapper_ExpectedIsNotJSON(t *testing.T) {
assert := New(new(testing.T))
if assert.JSONEq("Not JSON", `{"foo": "bar", "hello": "world"}`) {
t.Error("JSONEq should return false")
}
}
func TestJSONEqWrapper_ExpectedAndActualNotJSON(t *testing.T) {
assert := New(new(testing.T))
if assert.JSONEq("Not JSON", "Not JSON") {
t.Error("JSONEq should return false")
}
}
func TestJSONEqWrapper_ArraysOfDifferentOrder(t *testing.T) {
assert := New(new(testing.T))
if assert.JSONEq(`["foo", {"hello": "world", "nested": "hash"}]`, `[{ "hello": "world", "nested": "hash"}, "foo"]`) {
t.Error("JSONEq should return false")
}
}

View File

@ -1,106 +0,0 @@
package assert
import (
"fmt"
"net/http"
"net/http/httptest"
"net/url"
"strings"
)
// httpCode is a helper that returns HTTP code of the response. It returns -1
// if building a new request fails.
func httpCode(handler http.HandlerFunc, method, url string, values url.Values) int {
w := httptest.NewRecorder()
req, err := http.NewRequest(method, url+"?"+values.Encode(), nil)
if err != nil {
return -1
}
handler(w, req)
return w.Code
}
// HTTPSuccess asserts that a specified handler returns a success status code.
//
// assert.HTTPSuccess(t, myHandler, "POST", "http://www.google.com", nil)
//
// Returns whether the assertion was successful (true) or not (false).
func HTTPSuccess(t TestingT, handler http.HandlerFunc, method, url string, values url.Values) bool {
code := httpCode(handler, method, url, values)
if code == -1 {
return false
}
return code >= http.StatusOK && code <= http.StatusPartialContent
}
// HTTPRedirect asserts that a specified handler returns a redirect status code.
//
// assert.HTTPRedirect(t, myHandler, "GET", "/a/b/c", url.Values{"a": []string{"b", "c"}}
//
// Returns whether the assertion was successful (true) or not (false).
func HTTPRedirect(t TestingT, handler http.HandlerFunc, method, url string, values url.Values) bool {
code := httpCode(handler, method, url, values)
if code == -1 {
return false
}
return code >= http.StatusMultipleChoices && code <= http.StatusTemporaryRedirect
}
// HTTPError asserts that a specified handler returns an error status code.
//
// assert.HTTPError(t, myHandler, "POST", "/a/b/c", url.Values{"a": []string{"b", "c"}}
//
// Returns whether the assertion was successful (true) or not (false).
func HTTPError(t TestingT, handler http.HandlerFunc, method, url string, values url.Values) bool {
code := httpCode(handler, method, url, values)
if code == -1 {
return false
}
return code >= http.StatusBadRequest
}
// HTTPBody is a helper that returns HTTP body of the response. It returns
// empty string if building a new request fails.
func HTTPBody(handler http.HandlerFunc, method, url string, values url.Values) string {
w := httptest.NewRecorder()
req, err := http.NewRequest(method, url+"?"+values.Encode(), nil)
if err != nil {
return ""
}
handler(w, req)
return w.Body.String()
}
// HTTPBodyContains asserts that a specified handler returns a
// body that contains a string.
//
// assert.HTTPBodyContains(t, myHandler, "www.google.com", nil, "I'm Feeling Lucky")
//
// Returns whether the assertion was successful (true) or not (false).
func HTTPBodyContains(t TestingT, handler http.HandlerFunc, method, url string, values url.Values, str interface{}) bool {
body := HTTPBody(handler, method, url, values)
contains := strings.Contains(body, fmt.Sprint(str))
if !contains {
Fail(t, fmt.Sprintf("Expected response body for \"%s\" to contain \"%s\" but found \"%s\"", url+"?"+values.Encode(), str, body))
}
return contains
}
// HTTPBodyNotContains asserts that a specified handler returns a
// body that does not contain a string.
//
// assert.HTTPBodyNotContains(t, myHandler, "www.google.com", nil, "I'm Feeling Lucky")
//
// Returns whether the assertion was successful (true) or not (false).
func HTTPBodyNotContains(t TestingT, handler http.HandlerFunc, method, url string, values url.Values, str interface{}) bool {
body := HTTPBody(handler, method, url, values)
contains := strings.Contains(body, fmt.Sprint(str))
if contains {
Fail(t, fmt.Sprintf("Expected response body for \"%s\" to NOT contain \"%s\" but found \"%s\"", url+"?"+values.Encode(), str, body))
}
return !contains
}

View File

@ -1,86 +0,0 @@
package assert
import (
"fmt"
"net/http"
"net/url"
"testing"
)
func httpOK(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusOK)
}
func httpRedirect(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusTemporaryRedirect)
}
func httpError(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusInternalServerError)
}
func TestHTTPStatuses(t *testing.T) {
assert := New(t)
mockT := new(testing.T)
assert.Equal(HTTPSuccess(mockT, httpOK, "GET", "/", nil), true)
assert.Equal(HTTPSuccess(mockT, httpRedirect, "GET", "/", nil), false)
assert.Equal(HTTPSuccess(mockT, httpError, "GET", "/", nil), false)
assert.Equal(HTTPRedirect(mockT, httpOK, "GET", "/", nil), false)
assert.Equal(HTTPRedirect(mockT, httpRedirect, "GET", "/", nil), true)
assert.Equal(HTTPRedirect(mockT, httpError, "GET", "/", nil), false)
assert.Equal(HTTPError(mockT, httpOK, "GET", "/", nil), false)
assert.Equal(HTTPError(mockT, httpRedirect, "GET", "/", nil), false)
assert.Equal(HTTPError(mockT, httpError, "GET", "/", nil), true)
}
func TestHTTPStatusesWrapper(t *testing.T) {
assert := New(t)
mockAssert := New(new(testing.T))
assert.Equal(mockAssert.HTTPSuccess(httpOK, "GET", "/", nil), true)
assert.Equal(mockAssert.HTTPSuccess(httpRedirect, "GET", "/", nil), false)
assert.Equal(mockAssert.HTTPSuccess(httpError, "GET", "/", nil), false)
assert.Equal(mockAssert.HTTPRedirect(httpOK, "GET", "/", nil), false)
assert.Equal(mockAssert.HTTPRedirect(httpRedirect, "GET", "/", nil), true)
assert.Equal(mockAssert.HTTPRedirect(httpError, "GET", "/", nil), false)
assert.Equal(mockAssert.HTTPError(httpOK, "GET", "/", nil), false)
assert.Equal(mockAssert.HTTPError(httpRedirect, "GET", "/", nil), false)
assert.Equal(mockAssert.HTTPError(httpError, "GET", "/", nil), true)
}
func httpHelloName(w http.ResponseWriter, r *http.Request) {
name := r.FormValue("name")
w.Write([]byte(fmt.Sprintf("Hello, %s!", name)))
}
func TestHttpBody(t *testing.T) {
assert := New(t)
mockT := new(testing.T)
assert.True(HTTPBodyContains(mockT, httpHelloName, "GET", "/", url.Values{"name": []string{"World"}}, "Hello, World!"))
assert.True(HTTPBodyContains(mockT, httpHelloName, "GET", "/", url.Values{"name": []string{"World"}}, "World"))
assert.False(HTTPBodyContains(mockT, httpHelloName, "GET", "/", url.Values{"name": []string{"World"}}, "world"))
assert.False(HTTPBodyNotContains(mockT, httpHelloName, "GET", "/", url.Values{"name": []string{"World"}}, "Hello, World!"))
assert.False(HTTPBodyNotContains(mockT, httpHelloName, "GET", "/", url.Values{"name": []string{"World"}}, "World"))
assert.True(HTTPBodyNotContains(mockT, httpHelloName, "GET", "/", url.Values{"name": []string{"World"}}, "world"))
}
func TestHttpBodyWrappers(t *testing.T) {
assert := New(t)
mockAssert := New(new(testing.T))
assert.True(mockAssert.HTTPBodyContains(httpHelloName, "GET", "/", url.Values{"name": []string{"World"}}, "Hello, World!"))
assert.True(mockAssert.HTTPBodyContains(httpHelloName, "GET", "/", url.Values{"name": []string{"World"}}, "World"))
assert.False(mockAssert.HTTPBodyContains(httpHelloName, "GET", "/", url.Values{"name": []string{"World"}}, "world"))
assert.False(mockAssert.HTTPBodyNotContains(httpHelloName, "GET", "/", url.Values{"name": []string{"World"}}, "Hello, World!"))
assert.False(mockAssert.HTTPBodyNotContains(httpHelloName, "GET", "/", url.Values{"name": []string{"World"}}, "World"))
assert.True(mockAssert.HTTPBodyNotContains(httpHelloName, "GET", "/", url.Values{"name": []string{"World"}}, "world"))
}

View File

@ -1,22 +0,0 @@
// Package testify is a set of packages that provide many tools for testifying that your code will behave as you intend.
//
// testify contains the following packages:
//
// The assert package provides a comprehensive set of assertion functions that tie in to the Go testing system.
//
// The http package contains tools to make it easier to test http activity using the Go testing system.
//
// The mock package provides a system by which it is possible to mock your objects and verify calls are happening as expected.
//
// The suite package provides a basic structure for using structs as testing suites, and methods on those structs as tests. It includes setup/teardown functionality in the way of interfaces.
package testify
// blank imports help docs.
import (
// assert package
_ "github.com/stretchr/testify/assert"
// http package
_ "github.com/stretchr/testify/http"
// mock package
_ "github.com/stretchr/testify/mock"
)

View File

@ -1,2 +0,0 @@
// Package http DEPRECATED USE net/http/httptest
package http

View File

@ -1,49 +0,0 @@
package http
import (
"net/http"
)
// TestResponseWriter DEPRECATED: We recommend you use http://golang.org/pkg/net/http/httptest instead.
type TestResponseWriter struct {
// StatusCode is the last int written by the call to WriteHeader(int)
StatusCode int
// Output is a string containing the written bytes using the Write([]byte) func.
Output string
// header is the internal storage of the http.Header object
header http.Header
}
// Header DEPRECATED: We recommend you use http://golang.org/pkg/net/http/httptest instead.
func (rw *TestResponseWriter) Header() http.Header {
if rw.header == nil {
rw.header = make(http.Header)
}
return rw.header
}
// Write DEPRECATED: We recommend you use http://golang.org/pkg/net/http/httptest instead.
func (rw *TestResponseWriter) Write(bytes []byte) (int, error) {
// assume 200 success if no header has been set
if rw.StatusCode == 0 {
rw.WriteHeader(200)
}
// add these bytes to the output string
rw.Output = rw.Output + string(bytes)
// return normal values
return 0, nil
}
// WriteHeader DEPRECATED: We recommend you use http://golang.org/pkg/net/http/httptest instead.
func (rw *TestResponseWriter) WriteHeader(i int) {
rw.StatusCode = i
}

View File

@ -1,17 +0,0 @@
package http
import (
"github.com/stretchr/testify/mock"
"net/http"
)
// TestRoundTripper DEPRECATED USE net/http/httptest
type TestRoundTripper struct {
mock.Mock
}
// RoundTrip DEPRECATED USE net/http/httptest
func (t *TestRoundTripper) RoundTrip(req *http.Request) (*http.Response, error) {
args := t.Called(req)
return args.Get(0).(*http.Response), args.Error(1)
}

View File

@ -1,44 +0,0 @@
// Package mock provides a system by which it is possible to mock your objects
// and verify calls are happening as expected.
//
// Example Usage
//
// The mock package provides an object, Mock, that tracks activity on another object. It is usually
// embedded into a test object as shown below:
//
// type MyTestObject struct {
// // add a Mock object instance
// mock.Mock
//
// // other fields go here as normal
// }
//
// When implementing the methods of an interface, you wire your functions up
// to call the Mock.Called(args...) method, and return the appropriate values.
//
// For example, to mock a method that saves the name and age of a person and returns
// the year of their birth or an error, you might write this:
//
// func (o *MyTestObject) SavePersonDetails(firstname, lastname string, age int) (int, error) {
// args := o.Called(firstname, lastname, age)
// return args.Int(0), args.Error(1)
// }
//
// The Int, Error and Bool methods are examples of strongly typed getters that take the argument
// index position. Given this argument list:
//
// (12, true, "Something")
//
// You could read them out strongly typed like this:
//
// args.Int(0)
// args.Bool(1)
// args.String(2)
//
// For objects of your own type, use the generic Arguments.Get(index) method and make a type assertion:
//
// return args.Get(0).(*MyObject), args.Get(1).(*AnotherObjectOfMine)
//
// This may cause a panic if the object you are getting is nil (the type assertion will fail), in those
// cases you should check for nil first.
package mock

View File

@ -1,763 +0,0 @@
package mock
import (
"fmt"
"reflect"
"regexp"
"runtime"
"strings"
"sync"
"time"
"github.com/davecgh/go-spew/spew"
"github.com/pmezard/go-difflib/difflib"
"github.com/stretchr/objx"
"github.com/stretchr/testify/assert"
)
func inin() {
spew.Config.SortKeys = true
}
// TestingT is an interface wrapper around *testing.T
type TestingT interface {
Logf(format string, args ...interface{})
Errorf(format string, args ...interface{})
FailNow()
}
/*
Call
*/
// Call represents a method call and is used for setting expectations,
// as well as recording activity.
type Call struct {
Parent *Mock
// The name of the method that was or will be called.
Method string
// Holds the arguments of the method.
Arguments Arguments
// Holds the arguments that should be returned when
// this method is called.
ReturnArguments Arguments
// The number of times to return the return arguments when setting
// expectations. 0 means to always return the value.
Repeatability int
// Amount of times this call has been called
totalCalls int
// Holds a channel that will be used to block the Return until it either
// receives a message or is closed. nil means it returns immediately.
WaitFor <-chan time.Time
// Holds a handler used to manipulate arguments content that are passed by
// reference. It's useful when mocking methods such as unmarshalers or
// decoders.
RunFn func(Arguments)
}
func newCall(parent *Mock, methodName string, methodArguments ...interface{}) *Call {
return &Call{
Parent: parent,
Method: methodName,
Arguments: methodArguments,
ReturnArguments: make([]interface{}, 0),
Repeatability: 0,
WaitFor: nil,
RunFn: nil,
}
}
func (c *Call) lock() {
c.Parent.mutex.Lock()
}
func (c *Call) unlock() {
c.Parent.mutex.Unlock()
}
// Return specifies the return arguments for the expectation.
//
// Mock.On("DoSomething").Return(errors.New("failed"))
func (c *Call) Return(returnArguments ...interface{}) *Call {
c.lock()
defer c.unlock()
c.ReturnArguments = returnArguments
return c
}
// Once indicates that that the mock should only return the value once.
//
// Mock.On("MyMethod", arg1, arg2).Return(returnArg1, returnArg2).Once()
func (c *Call) Once() *Call {
return c.Times(1)
}
// Twice indicates that that the mock should only return the value twice.
//
// Mock.On("MyMethod", arg1, arg2).Return(returnArg1, returnArg2).Twice()
func (c *Call) Twice() *Call {
return c.Times(2)
}
// Times indicates that that the mock should only return the indicated number
// of times.
//
// Mock.On("MyMethod", arg1, arg2).Return(returnArg1, returnArg2).Times(5)
func (c *Call) Times(i int) *Call {
c.lock()
defer c.unlock()
c.Repeatability = i
return c
}
// WaitUntil sets the channel that will block the mock's return until its closed
// or a message is received.
//
// Mock.On("MyMethod", arg1, arg2).WaitUntil(time.After(time.Second))
func (c *Call) WaitUntil(w <-chan time.Time) *Call {
c.lock()
defer c.unlock()
c.WaitFor = w
return c
}
// After sets how long to block until the call returns
//
// Mock.On("MyMethod", arg1, arg2).After(time.Second)
func (c *Call) After(d time.Duration) *Call {
return c.WaitUntil(time.After(d))
}
// Run sets a handler to be called before returning. It can be used when
// mocking a method such as unmarshalers that takes a pointer to a struct and
// sets properties in such struct
//
// Mock.On("Unmarshal", AnythingOfType("*map[string]interface{}").Return().Run(func(args Arguments) {
// arg := args.Get(0).(*map[string]interface{})
// arg["foo"] = "bar"
// })
func (c *Call) Run(fn func(Arguments)) *Call {
c.lock()
defer c.unlock()
c.RunFn = fn
return c
}
// On chains a new expectation description onto the mocked interface. This
// allows syntax like.
//
// Mock.
// On("MyMethod", 1).Return(nil).
// On("MyOtherMethod", 'a', 'b', 'c').Return(errors.New("Some Error"))
func (c *Call) On(methodName string, arguments ...interface{}) *Call {
return c.Parent.On(methodName, arguments...)
}
// Mock is the workhorse used to track activity on another object.
// For an example of its usage, refer to the "Example Usage" section at the top
// of this document.
type Mock struct {
// Represents the calls that are expected of
// an object.
ExpectedCalls []*Call
// Holds the calls that were made to this mocked object.
Calls []Call
// TestData holds any data that might be useful for testing. Testify ignores
// this data completely allowing you to do whatever you like with it.
testData objx.Map
mutex sync.Mutex
}
// TestData holds any data that might be useful for testing. Testify ignores
// this data completely allowing you to do whatever you like with it.
func (m *Mock) TestData() objx.Map {
if m.testData == nil {
m.testData = make(objx.Map)
}
return m.testData
}
/*
Setting expectations
*/
// On starts a description of an expectation of the specified method
// being called.
//
// Mock.On("MyMethod", arg1, arg2)
func (m *Mock) On(methodName string, arguments ...interface{}) *Call {
for _, arg := range arguments {
if v := reflect.ValueOf(arg); v.Kind() == reflect.Func {
panic(fmt.Sprintf("cannot use Func in expectations. Use mock.AnythingOfType(\"%T\")", arg))
}
}
m.mutex.Lock()
defer m.mutex.Unlock()
c := newCall(m, methodName, arguments...)
m.ExpectedCalls = append(m.ExpectedCalls, c)
return c
}
// /*
// Recording and responding to activity
// */
func (m *Mock) findExpectedCall(method string, arguments ...interface{}) (int, *Call) {
m.mutex.Lock()
defer m.mutex.Unlock()
for i, call := range m.ExpectedCalls {
if call.Method == method && call.Repeatability > -1 {
_, diffCount := call.Arguments.Diff(arguments)
if diffCount == 0 {
return i, call
}
}
}
return -1, nil
}
func (m *Mock) findClosestCall(method string, arguments ...interface{}) (bool, *Call) {
diffCount := 0
var closestCall *Call
for _, call := range m.expectedCalls() {
if call.Method == method {
_, tempDiffCount := call.Arguments.Diff(arguments)
if tempDiffCount < diffCount || diffCount == 0 {
diffCount = tempDiffCount
closestCall = call
}
}
}
if closestCall == nil {
return false, nil
}
return true, closestCall
}
func callString(method string, arguments Arguments, includeArgumentValues bool) string {
var argValsString string
if includeArgumentValues {
var argVals []string
for argIndex, arg := range arguments {
argVals = append(argVals, fmt.Sprintf("%d: %#v", argIndex, arg))
}
argValsString = fmt.Sprintf("\n\t\t%s", strings.Join(argVals, "\n\t\t"))
}
return fmt.Sprintf("%s(%s)%s", method, arguments.String(), argValsString)
}
// Called tells the mock object that a method has been called, and gets an array
// of arguments to return. Panics if the call is unexpected (i.e. not preceded by
// appropriate .On .Return() calls)
// If Call.WaitFor is set, blocks until the channel is closed or receives a message.
func (m *Mock) Called(arguments ...interface{}) Arguments {
// get the calling function's name
pc, _, _, ok := runtime.Caller(1)
if !ok {
panic("Couldn't get the caller information")
}
functionPath := runtime.FuncForPC(pc).Name()
//Next four lines are required to use GCCGO function naming conventions.
//For Ex: github_com_docker_libkv_store_mock.WatchTree.pN39_github_com_docker_libkv_store_mock.Mock
//uses inteface information unlike golang github.com/docker/libkv/store/mock.(*Mock).WatchTree
//With GCCGO we need to remove interface information starting from pN<dd>.
re := regexp.MustCompile("\\.pN\\d+_")
if re.MatchString(functionPath) {
functionPath = re.Split(functionPath, -1)[0]
}
parts := strings.Split(functionPath, ".")
functionName := parts[len(parts)-1]
found, call := m.findExpectedCall(functionName, arguments...)
if found < 0 {
// we have to fail here - because we don't know what to do
// as the return arguments. This is because:
//
// a) this is a totally unexpected call to this method,
// b) the arguments are not what was expected, or
// c) the developer has forgotten to add an accompanying On...Return pair.
closestFound, closestCall := m.findClosestCall(functionName, arguments...)
if closestFound {
panic(fmt.Sprintf("\n\nmock: Unexpected Method Call\n-----------------------------\n\n%s\n\nThe closest call I have is: \n\n%s\n\n%s\n", callString(functionName, arguments, true), callString(functionName, closestCall.Arguments, true), diffArguments(arguments, closestCall.Arguments)))
} else {
panic(fmt.Sprintf("\nassert: mock: I don't know what to return because the method call was unexpected.\n\tEither do Mock.On(\"%s\").Return(...) first, or remove the %s() call.\n\tThis method was unexpected:\n\t\t%s\n\tat: %s", functionName, functionName, callString(functionName, arguments, true), assert.CallerInfo()))
}
} else {
m.mutex.Lock()
switch {
case call.Repeatability == 1:
call.Repeatability = -1
call.totalCalls++
case call.Repeatability > 1:
call.Repeatability--
call.totalCalls++
case call.Repeatability == 0:
call.totalCalls++
}
m.mutex.Unlock()
}
// add the call
m.mutex.Lock()
m.Calls = append(m.Calls, *newCall(m, functionName, arguments...))
m.mutex.Unlock()
// block if specified
if call.WaitFor != nil {
<-call.WaitFor
}
if call.RunFn != nil {
call.RunFn(arguments)
}
return call.ReturnArguments
}
/*
Assertions
*/
type assertExpectationser interface {
AssertExpectations(TestingT) bool
}
// AssertExpectationsForObjects asserts that everything specified with On and Return
// of the specified objects was in fact called as expected.
//
// Calls may have occurred in any order.
func AssertExpectationsForObjects(t TestingT, testObjects ...interface{}) bool {
for _, obj := range testObjects {
if m, ok := obj.(Mock); ok {
t.Logf("Deprecated mock.AssertExpectationsForObjects(myMock.Mock) use mock.AssertExpectationsForObjects(myMock)")
obj = &m
}
m := obj.(assertExpectationser)
if !m.AssertExpectations(t) {
return false
}
}
return true
}
// AssertExpectations asserts that everything specified with On and Return was
// in fact called as expected. Calls may have occurred in any order.
func (m *Mock) AssertExpectations(t TestingT) bool {
var somethingMissing bool
var failedExpectations int
// iterate through each expectation
expectedCalls := m.expectedCalls()
for _, expectedCall := range expectedCalls {
if !m.methodWasCalled(expectedCall.Method, expectedCall.Arguments) && expectedCall.totalCalls == 0 {
somethingMissing = true
failedExpectations++
t.Logf("\u274C\t%s(%s)", expectedCall.Method, expectedCall.Arguments.String())
} else {
m.mutex.Lock()
if expectedCall.Repeatability > 0 {
somethingMissing = true
failedExpectations++
} else {
t.Logf("\u2705\t%s(%s)", expectedCall.Method, expectedCall.Arguments.String())
}
m.mutex.Unlock()
}
}
if somethingMissing {
t.Errorf("FAIL: %d out of %d expectation(s) were met.\n\tThe code you are testing needs to make %d more call(s).\n\tat: %s", len(expectedCalls)-failedExpectations, len(expectedCalls), failedExpectations, assert.CallerInfo())
}
return !somethingMissing
}
// AssertNumberOfCalls asserts that the method was called expectedCalls times.
func (m *Mock) AssertNumberOfCalls(t TestingT, methodName string, expectedCalls int) bool {
var actualCalls int
for _, call := range m.calls() {
if call.Method == methodName {
actualCalls++
}
}
return assert.Equal(t, expectedCalls, actualCalls, fmt.Sprintf("Expected number of calls (%d) does not match the actual number of calls (%d).", expectedCalls, actualCalls))
}
// AssertCalled asserts that the method was called.
// It can produce a false result when an argument is a pointer type and the underlying value changed after calling the mocked method.
func (m *Mock) AssertCalled(t TestingT, methodName string, arguments ...interface{}) bool {
if !assert.True(t, m.methodWasCalled(methodName, arguments), fmt.Sprintf("The \"%s\" method should have been called with %d argument(s), but was not.", methodName, len(arguments))) {
t.Logf("%v", m.expectedCalls())
return false
}
return true
}
// AssertNotCalled asserts that the method was not called.
// It can produce a false result when an argument is a pointer type and the underlying value changed after calling the mocked method.
func (m *Mock) AssertNotCalled(t TestingT, methodName string, arguments ...interface{}) bool {
if !assert.False(t, m.methodWasCalled(methodName, arguments), fmt.Sprintf("The \"%s\" method was called with %d argument(s), but should NOT have been.", methodName, len(arguments))) {
t.Logf("%v", m.expectedCalls())
return false
}
return true
}
func (m *Mock) methodWasCalled(methodName string, expected []interface{}) bool {
for _, call := range m.calls() {
if call.Method == methodName {
_, differences := Arguments(expected).Diff(call.Arguments)
if differences == 0 {
// found the expected call
return true
}
}
}
// we didn't find the expected call
return false
}
func (m *Mock) expectedCalls() []*Call {
m.mutex.Lock()
defer m.mutex.Unlock()
return append([]*Call{}, m.ExpectedCalls...)
}
func (m *Mock) calls() []Call {
m.mutex.Lock()
defer m.mutex.Unlock()
return append([]Call{}, m.Calls...)
}
/*
Arguments
*/
// Arguments holds an array of method arguments or return values.
type Arguments []interface{}
const (
// Anything is used in Diff and Assert when the argument being tested
// shouldn't be taken into consideration.
Anything string = "mock.Anything"
)
// AnythingOfTypeArgument is a string that contains the type of an argument
// for use when type checking. Used in Diff and Assert.
type AnythingOfTypeArgument string
// AnythingOfType returns an AnythingOfTypeArgument object containing the
// name of the type to check for. Used in Diff and Assert.
//
// For example:
// Assert(t, AnythingOfType("string"), AnythingOfType("int"))
func AnythingOfType(t string) AnythingOfTypeArgument {
return AnythingOfTypeArgument(t)
}
// argumentMatcher performs custom argument matching, returning whether or
// not the argument is matched by the expectation fixture function.
type argumentMatcher struct {
// fn is a function which accepts one argument, and returns a bool.
fn reflect.Value
}
func (f argumentMatcher) Matches(argument interface{}) bool {
expectType := f.fn.Type().In(0)
if reflect.TypeOf(argument).AssignableTo(expectType) {
result := f.fn.Call([]reflect.Value{reflect.ValueOf(argument)})
return result[0].Bool()
}
return false
}
func (f argumentMatcher) String() string {
return fmt.Sprintf("func(%s) bool", f.fn.Type().In(0).Name())
}
// MatchedBy can be used to match a mock call based on only certain properties
// from a complex struct or some calculation. It takes a function that will be
// evaluated with the called argument and will return true when there's a match
// and false otherwise.
//
// Example:
// m.On("Do", MatchedBy(func(req *http.Request) bool { return req.Host == "example.com" }))
//
// |fn|, must be a function accepting a single argument (of the expected type)
// which returns a bool. If |fn| doesn't match the required signature,
// MathedBy() panics.
func MatchedBy(fn interface{}) argumentMatcher {
fnType := reflect.TypeOf(fn)
if fnType.Kind() != reflect.Func {
panic(fmt.Sprintf("assert: arguments: %s is not a func", fn))
}
if fnType.NumIn() != 1 {
panic(fmt.Sprintf("assert: arguments: %s does not take exactly one argument", fn))
}
if fnType.NumOut() != 1 || fnType.Out(0).Kind() != reflect.Bool {
panic(fmt.Sprintf("assert: arguments: %s does not return a bool", fn))
}
return argumentMatcher{fn: reflect.ValueOf(fn)}
}
// Get Returns the argument at the specified index.
func (args Arguments) Get(index int) interface{} {
if index+1 > len(args) {
panic(fmt.Sprintf("assert: arguments: Cannot call Get(%d) because there are %d argument(s).", index, len(args)))
}
return args[index]
}
// Is gets whether the objects match the arguments specified.
func (args Arguments) Is(objects ...interface{}) bool {
for i, obj := range args {
if obj != objects[i] {
return false
}
}
return true
}
// Diff gets a string describing the differences between the arguments
// and the specified objects.
//
// Returns the diff string and number of differences found.
func (args Arguments) Diff(objects []interface{}) (string, int) {
var output = "\n"
var differences int
var maxArgCount = len(args)
if len(objects) > maxArgCount {
maxArgCount = len(objects)
}
for i := 0; i < maxArgCount; i++ {
var actual, expected interface{}
if len(objects) <= i {
actual = "(Missing)"
} else {
actual = objects[i]
}
if len(args) <= i {
expected = "(Missing)"
} else {
expected = args[i]
}
if matcher, ok := expected.(argumentMatcher); ok {
if matcher.Matches(actual) {
output = fmt.Sprintf("%s\t%d: \u2705 %s matched by %s\n", output, i, actual, matcher)
} else {
differences++
output = fmt.Sprintf("%s\t%d: \u2705 %s not matched by %s\n", output, i, actual, matcher)
}
} else if reflect.TypeOf(expected) == reflect.TypeOf((*AnythingOfTypeArgument)(nil)).Elem() {
// type checking
if reflect.TypeOf(actual).Name() != string(expected.(AnythingOfTypeArgument)) && reflect.TypeOf(actual).String() != string(expected.(AnythingOfTypeArgument)) {
// not match
differences++
output = fmt.Sprintf("%s\t%d: \u274C type %s != type %s - %s\n", output, i, expected, reflect.TypeOf(actual).Name(), actual)
}
} else {
// normal checking
if assert.ObjectsAreEqual(expected, Anything) || assert.ObjectsAreEqual(actual, Anything) || assert.ObjectsAreEqual(actual, expected) {
// match
output = fmt.Sprintf("%s\t%d: \u2705 %s == %s\n", output, i, actual, expected)
} else {
// not match
differences++
output = fmt.Sprintf("%s\t%d: \u274C %s != %s\n", output, i, actual, expected)
}
}
}
if differences == 0 {
return "No differences.", differences
}
return output, differences
}
// Assert compares the arguments with the specified objects and fails if
// they do not exactly match.
func (args Arguments) Assert(t TestingT, objects ...interface{}) bool {
// get the differences
diff, diffCount := args.Diff(objects)
if diffCount == 0 {
return true
}
// there are differences... report them...
t.Logf(diff)
t.Errorf("%sArguments do not match.", assert.CallerInfo())
return false
}
// String gets the argument at the specified index. Panics if there is no argument, or
// if the argument is of the wrong type.
//
// If no index is provided, String() returns a complete string representation
// of the arguments.
func (args Arguments) String(indexOrNil ...int) string {
if len(indexOrNil) == 0 {
// normal String() method - return a string representation of the args
var argsStr []string
for _, arg := range args {
argsStr = append(argsStr, fmt.Sprintf("%s", reflect.TypeOf(arg)))
}
return strings.Join(argsStr, ",")
} else if len(indexOrNil) == 1 {
// Index has been specified - get the argument at that index
var index = indexOrNil[0]
var s string
var ok bool
if s, ok = args.Get(index).(string); !ok {
panic(fmt.Sprintf("assert: arguments: String(%d) failed because object wasn't correct type: %s", index, args.Get(index)))
}
return s
}
panic(fmt.Sprintf("assert: arguments: Wrong number of arguments passed to String. Must be 0 or 1, not %d", len(indexOrNil)))
}
// Int gets the argument at the specified index. Panics if there is no argument, or
// if the argument is of the wrong type.
func (args Arguments) Int(index int) int {
var s int
var ok bool
if s, ok = args.Get(index).(int); !ok {
panic(fmt.Sprintf("assert: arguments: Int(%d) failed because object wasn't correct type: %v", index, args.Get(index)))
}
return s
}
// Error gets the argument at the specified index. Panics if there is no argument, or
// if the argument is of the wrong type.
func (args Arguments) Error(index int) error {
obj := args.Get(index)
var s error
var ok bool
if obj == nil {
return nil
}
if s, ok = obj.(error); !ok {
panic(fmt.Sprintf("assert: arguments: Error(%d) failed because object wasn't correct type: %v", index, args.Get(index)))
}
return s
}
// Bool gets the argument at the specified index. Panics if there is no argument, or
// if the argument is of the wrong type.
func (args Arguments) Bool(index int) bool {
var s bool
var ok bool
if s, ok = args.Get(index).(bool); !ok {
panic(fmt.Sprintf("assert: arguments: Bool(%d) failed because object wasn't correct type: %v", index, args.Get(index)))
}
return s
}
func typeAndKind(v interface{}) (reflect.Type, reflect.Kind) {
t := reflect.TypeOf(v)
k := t.Kind()
if k == reflect.Ptr {
t = t.Elem()
k = t.Kind()
}
return t, k
}
func diffArguments(expected Arguments, actual Arguments) string {
for x := range expected {
if diffString := diff(expected[x], actual[x]); diffString != "" {
return fmt.Sprintf("Difference found in argument %v:\n\n%s", x, diffString)
}
}
return ""
}
// diff returns a diff of both values as long as both are of the same type and
// are a struct, map, slice or array. Otherwise it returns an empty string.
func diff(expected interface{}, actual interface{}) string {
if expected == nil || actual == nil {
return ""
}
et, ek := typeAndKind(expected)
at, _ := typeAndKind(actual)
if et != at {
return ""
}
if ek != reflect.Struct && ek != reflect.Map && ek != reflect.Slice && ek != reflect.Array {
return ""
}
e := spew.Sdump(expected)
a := spew.Sdump(actual)
diff, _ := difflib.GetUnifiedDiffString(difflib.UnifiedDiff{
A: difflib.SplitLines(e),
B: difflib.SplitLines(a),
FromFile: "Expected",
FromDate: "",
ToFile: "Actual",
ToDate: "",
Context: 1,
})
return diff
}

File diff suppressed because it is too large Load Diff

View File

@ -1,12 +0,0 @@
package testify
import (
"github.com/stretchr/testify/assert"
"testing"
)
func TestImports(t *testing.T) {
if assert.Equal(t, 1, 1) != true {
t.Error("Something is wrong.")
}
}

View File

@ -1,28 +0,0 @@
// Package require implements the same assertions as the `assert` package but
// stops test execution when a test fails.
//
// Example Usage
//
// The following is a complete example using require in a standard test function:
// import (
// "testing"
// "github.com/stretchr/testify/require"
// )
//
// func TestSomething(t *testing.T) {
//
// var a string = "Hello"
// var b string = "Hello"
//
// require.Equal(t, a, b, "The two words should be the same.")
//
// }
//
// Assertions
//
// The `require` package have same global functions as in the `assert` package,
// but instead of returning a boolean result they call `t.FailNow()`.
//
// Every assertion function also takes an optional string message as the final argument,
// allowing custom error messages to be appended to the message the assertion method outputs.
package require

View File

@ -1,16 +0,0 @@
package require
// Assertions provides assertion methods around the
// TestingT interface.
type Assertions struct {
t TestingT
}
// New makes a new Assertions object for the specified TestingT.
func New(t TestingT) *Assertions {
return &Assertions{
t: t,
}
}
//go:generate go run ../_codegen/main.go -output-package=require -template=require_forward.go.tmpl

View File

@ -1,385 +0,0 @@
package require
import (
"errors"
"testing"
"time"
)
func TestImplementsWrapper(t *testing.T) {
require := New(t)
require.Implements((*AssertionTesterInterface)(nil), new(AssertionTesterConformingObject))
mockT := new(MockT)
mockRequire := New(mockT)
mockRequire.Implements((*AssertionTesterInterface)(nil), new(AssertionTesterNonConformingObject))
if !mockT.Failed {
t.Error("Check should fail")
}
}
func TestIsTypeWrapper(t *testing.T) {
require := New(t)
require.IsType(new(AssertionTesterConformingObject), new(AssertionTesterConformingObject))
mockT := new(MockT)
mockRequire := New(mockT)
mockRequire.IsType(new(AssertionTesterConformingObject), new(AssertionTesterNonConformingObject))
if !mockT.Failed {
t.Error("Check should fail")
}
}
func TestEqualWrapper(t *testing.T) {
require := New(t)
require.Equal(1, 1)
mockT := new(MockT)
mockRequire := New(mockT)
mockRequire.Equal(1, 2)
if !mockT.Failed {
t.Error("Check should fail")
}
}
func TestNotEqualWrapper(t *testing.T) {
require := New(t)
require.NotEqual(1, 2)
mockT := new(MockT)
mockRequire := New(mockT)
mockRequire.NotEqual(2, 2)
if !mockT.Failed {
t.Error("Check should fail")
}
}
func TestExactlyWrapper(t *testing.T) {
require := New(t)
a := float32(1)
b := float32(1)
c := float64(1)
require.Exactly(a, b)
mockT := new(MockT)
mockRequire := New(mockT)
mockRequire.Exactly(a, c)
if !mockT.Failed {
t.Error("Check should fail")
}
}
func TestNotNilWrapper(t *testing.T) {
require := New(t)
require.NotNil(t, new(AssertionTesterConformingObject))
mockT := new(MockT)
mockRequire := New(mockT)
mockRequire.NotNil(nil)
if !mockT.Failed {
t.Error("Check should fail")
}
}
func TestNilWrapper(t *testing.T) {
require := New(t)
require.Nil(nil)
mockT := new(MockT)
mockRequire := New(mockT)
mockRequire.Nil(new(AssertionTesterConformingObject))
if !mockT.Failed {
t.Error("Check should fail")
}
}
func TestTrueWrapper(t *testing.T) {
require := New(t)
require.True(true)
mockT := new(MockT)
mockRequire := New(mockT)
mockRequire.True(false)
if !mockT.Failed {
t.Error("Check should fail")
}
}
func TestFalseWrapper(t *testing.T) {
require := New(t)
require.False(false)
mockT := new(MockT)
mockRequire := New(mockT)
mockRequire.False(true)
if !mockT.Failed {
t.Error("Check should fail")
}
}
func TestContainsWrapper(t *testing.T) {
require := New(t)
require.Contains("Hello World", "Hello")
mockT := new(MockT)
mockRequire := New(mockT)
mockRequire.Contains("Hello World", "Salut")
if !mockT.Failed {
t.Error("Check should fail")
}
}
func TestNotContainsWrapper(t *testing.T) {
require := New(t)
require.NotContains("Hello World", "Hello!")
mockT := new(MockT)
mockRequire := New(mockT)
mockRequire.NotContains("Hello World", "Hello")
if !mockT.Failed {
t.Error("Check should fail")
}
}
func TestPanicsWrapper(t *testing.T) {
require := New(t)
require.Panics(func() {
panic("Panic!")
})
mockT := new(MockT)
mockRequire := New(mockT)
mockRequire.Panics(func() {})
if !mockT.Failed {
t.Error("Check should fail")
}
}
func TestNotPanicsWrapper(t *testing.T) {
require := New(t)
require.NotPanics(func() {})
mockT := new(MockT)
mockRequire := New(mockT)
mockRequire.NotPanics(func() {
panic("Panic!")
})
if !mockT.Failed {
t.Error("Check should fail")
}
}
func TestNoErrorWrapper(t *testing.T) {
require := New(t)
require.NoError(nil)
mockT := new(MockT)
mockRequire := New(mockT)
mockRequire.NoError(errors.New("some error"))
if !mockT.Failed {
t.Error("Check should fail")
}
}
func TestErrorWrapper(t *testing.T) {
require := New(t)
require.Error(errors.New("some error"))
mockT := new(MockT)
mockRequire := New(mockT)
mockRequire.Error(nil)
if !mockT.Failed {
t.Error("Check should fail")
}
}
func TestEqualErrorWrapper(t *testing.T) {
require := New(t)
require.EqualError(errors.New("some error"), "some error")
mockT := new(MockT)
mockRequire := New(mockT)
mockRequire.EqualError(errors.New("some error"), "Not some error")
if !mockT.Failed {
t.Error("Check should fail")
}
}
func TestEmptyWrapper(t *testing.T) {
require := New(t)
require.Empty("")
mockT := new(MockT)
mockRequire := New(mockT)
mockRequire.Empty("x")
if !mockT.Failed {
t.Error("Check should fail")
}
}
func TestNotEmptyWrapper(t *testing.T) {
require := New(t)
require.NotEmpty("x")
mockT := new(MockT)
mockRequire := New(mockT)
mockRequire.NotEmpty("")
if !mockT.Failed {
t.Error("Check should fail")
}
}
func TestWithinDurationWrapper(t *testing.T) {
require := New(t)
a := time.Now()
b := a.Add(10 * time.Second)
require.WithinDuration(a, b, 15*time.Second)
mockT := new(MockT)
mockRequire := New(mockT)
mockRequire.WithinDuration(a, b, 5*time.Second)
if !mockT.Failed {
t.Error("Check should fail")
}
}
func TestInDeltaWrapper(t *testing.T) {
require := New(t)
require.InDelta(1.001, 1, 0.01)
mockT := new(MockT)
mockRequire := New(mockT)
mockRequire.InDelta(1, 2, 0.5)
if !mockT.Failed {
t.Error("Check should fail")
}
}
func TestZeroWrapper(t *testing.T) {
require := New(t)
require.Zero(0)
mockT := new(MockT)
mockRequire := New(mockT)
mockRequire.Zero(1)
if !mockT.Failed {
t.Error("Check should fail")
}
}
func TestNotZeroWrapper(t *testing.T) {
require := New(t)
require.NotZero(1)
mockT := new(MockT)
mockRequire := New(mockT)
mockRequire.NotZero(0)
if !mockT.Failed {
t.Error("Check should fail")
}
}
func TestJSONEqWrapper_EqualSONString(t *testing.T) {
mockT := new(MockT)
mockRequire := New(mockT)
mockRequire.JSONEq(`{"hello": "world", "foo": "bar"}`, `{"hello": "world", "foo": "bar"}`)
if mockT.Failed {
t.Error("Check should pass")
}
}
func TestJSONEqWrapper_EquivalentButNotEqual(t *testing.T) {
mockT := new(MockT)
mockRequire := New(mockT)
mockRequire.JSONEq(`{"hello": "world", "foo": "bar"}`, `{"foo": "bar", "hello": "world"}`)
if mockT.Failed {
t.Error("Check should pass")
}
}
func TestJSONEqWrapper_HashOfArraysAndHashes(t *testing.T) {
mockT := new(MockT)
mockRequire := New(mockT)
mockRequire.JSONEq("{\r\n\t\"numeric\": 1.5,\r\n\t\"array\": [{\"foo\": \"bar\"}, 1, \"string\", [\"nested\", \"array\", 5.5]],\r\n\t\"hash\": {\"nested\": \"hash\", \"nested_slice\": [\"this\", \"is\", \"nested\"]},\r\n\t\"string\": \"foo\"\r\n}",
"{\r\n\t\"numeric\": 1.5,\r\n\t\"hash\": {\"nested\": \"hash\", \"nested_slice\": [\"this\", \"is\", \"nested\"]},\r\n\t\"string\": \"foo\",\r\n\t\"array\": [{\"foo\": \"bar\"}, 1, \"string\", [\"nested\", \"array\", 5.5]]\r\n}")
if mockT.Failed {
t.Error("Check should pass")
}
}
func TestJSONEqWrapper_Array(t *testing.T) {
mockT := new(MockT)
mockRequire := New(mockT)
mockRequire.JSONEq(`["foo", {"hello": "world", "nested": "hash"}]`, `["foo", {"nested": "hash", "hello": "world"}]`)
if mockT.Failed {
t.Error("Check should pass")
}
}
func TestJSONEqWrapper_HashAndArrayNotEquivalent(t *testing.T) {
mockT := new(MockT)
mockRequire := New(mockT)
mockRequire.JSONEq(`["foo", {"hello": "world", "nested": "hash"}]`, `{"foo": "bar", {"nested": "hash", "hello": "world"}}`)
if !mockT.Failed {
t.Error("Check should fail")
}
}
func TestJSONEqWrapper_HashesNotEquivalent(t *testing.T) {
mockT := new(MockT)
mockRequire := New(mockT)
mockRequire.JSONEq(`{"foo": "bar"}`, `{"foo": "bar", "hello": "world"}`)
if !mockT.Failed {
t.Error("Check should fail")
}
}
func TestJSONEqWrapper_ActualIsNotJSON(t *testing.T) {
mockT := new(MockT)
mockRequire := New(mockT)
mockRequire.JSONEq(`{"foo": "bar"}`, "Not JSON")
if !mockT.Failed {
t.Error("Check should fail")
}
}
func TestJSONEqWrapper_ExpectedIsNotJSON(t *testing.T) {
mockT := new(MockT)
mockRequire := New(mockT)
mockRequire.JSONEq("Not JSON", `{"foo": "bar", "hello": "world"}`)
if !mockT.Failed {
t.Error("Check should fail")
}
}
func TestJSONEqWrapper_ExpectedAndActualNotJSON(t *testing.T) {
mockT := new(MockT)
mockRequire := New(mockT)
mockRequire.JSONEq("Not JSON", "Not JSON")
if !mockT.Failed {
t.Error("Check should fail")
}
}
func TestJSONEqWrapper_ArraysOfDifferentOrder(t *testing.T) {
mockT := new(MockT)
mockRequire := New(mockT)
mockRequire.JSONEq(`["foo", {"hello": "world", "nested": "hash"}]`, `[{ "hello": "world", "nested": "hash"}, "foo"]`)
if !mockT.Failed {
t.Error("Check should fail")
}
}

View File

@ -1,464 +0,0 @@
/*
* CODE GENERATED AUTOMATICALLY WITH github.com/stretchr/testify/_codegen
* THIS FILE MUST NOT BE EDITED BY HAND
*/
package require
import (
assert "github.com/stretchr/testify/assert"
http "net/http"
url "net/url"
time "time"
)
// Condition uses a Comparison to assert a complex condition.
func Condition(t TestingT, comp assert.Comparison, msgAndArgs ...interface{}) {
if !assert.Condition(t, comp, msgAndArgs...) {
t.FailNow()
}
}
// Contains asserts that the specified string, list(array, slice...) or map contains the
// specified substring or element.
//
// assert.Contains(t, "Hello World", "World", "But 'Hello World' does contain 'World'")
// assert.Contains(t, ["Hello", "World"], "World", "But ["Hello", "World"] does contain 'World'")
// assert.Contains(t, {"Hello": "World"}, "Hello", "But {'Hello': 'World'} does contain 'Hello'")
//
// Returns whether the assertion was successful (true) or not (false).
func Contains(t TestingT, s interface{}, contains interface{}, msgAndArgs ...interface{}) {
if !assert.Contains(t, s, contains, msgAndArgs...) {
t.FailNow()
}
}
// Empty asserts that the specified object is empty. I.e. nil, "", false, 0 or either
// a slice or a channel with len == 0.
//
// assert.Empty(t, obj)
//
// Returns whether the assertion was successful (true) or not (false).
func Empty(t TestingT, object interface{}, msgAndArgs ...interface{}) {
if !assert.Empty(t, object, msgAndArgs...) {
t.FailNow()
}
}
// Equal asserts that two objects are equal.
//
// assert.Equal(t, 123, 123, "123 and 123 should be equal")
//
// Returns whether the assertion was successful (true) or not (false).
func Equal(t TestingT, expected interface{}, actual interface{}, msgAndArgs ...interface{}) {
if !assert.Equal(t, expected, actual, msgAndArgs...) {
t.FailNow()
}
}
// EqualError asserts that a function returned an error (i.e. not `nil`)
// and that it is equal to the provided error.
//
// actualObj, err := SomeFunction()
// if assert.Error(t, err, "An error was expected") {
// assert.Equal(t, err, expectedError)
// }
//
// Returns whether the assertion was successful (true) or not (false).
func EqualError(t TestingT, theError error, errString string, msgAndArgs ...interface{}) {
if !assert.EqualError(t, theError, errString, msgAndArgs...) {
t.FailNow()
}
}
// EqualValues asserts that two objects are equal or convertable to the same types
// and equal.
//
// assert.EqualValues(t, uint32(123), int32(123), "123 and 123 should be equal")
//
// Returns whether the assertion was successful (true) or not (false).
func EqualValues(t TestingT, expected interface{}, actual interface{}, msgAndArgs ...interface{}) {
if !assert.EqualValues(t, expected, actual, msgAndArgs...) {
t.FailNow()
}
}
// Error asserts that a function returned an error (i.e. not `nil`).
//
// actualObj, err := SomeFunction()
// if assert.Error(t, err, "An error was expected") {
// assert.Equal(t, err, expectedError)
// }
//
// Returns whether the assertion was successful (true) or not (false).
func Error(t TestingT, err error, msgAndArgs ...interface{}) {
if !assert.Error(t, err, msgAndArgs...) {
t.FailNow()
}
}
// Exactly asserts that two objects are equal is value and type.
//
// assert.Exactly(t, int32(123), int64(123), "123 and 123 should NOT be equal")
//
// Returns whether the assertion was successful (true) or not (false).
func Exactly(t TestingT, expected interface{}, actual interface{}, msgAndArgs ...interface{}) {
if !assert.Exactly(t, expected, actual, msgAndArgs...) {
t.FailNow()
}
}
// Fail reports a failure through
func Fail(t TestingT, failureMessage string, msgAndArgs ...interface{}) {
if !assert.Fail(t, failureMessage, msgAndArgs...) {
t.FailNow()
}
}
// FailNow fails test
func FailNow(t TestingT, failureMessage string, msgAndArgs ...interface{}) {
if !assert.FailNow(t, failureMessage, msgAndArgs...) {
t.FailNow()
}
}
// False asserts that the specified value is false.
//
// assert.False(t, myBool, "myBool should be false")
//
// Returns whether the assertion was successful (true) or not (false).
func False(t TestingT, value bool, msgAndArgs ...interface{}) {
if !assert.False(t, value, msgAndArgs...) {
t.FailNow()
}
}
// HTTPBodyContains asserts that a specified handler returns a
// body that contains a string.
//
// assert.HTTPBodyContains(t, myHandler, "www.google.com", nil, "I'm Feeling Lucky")
//
// Returns whether the assertion was successful (true) or not (false).
func HTTPBodyContains(t TestingT, handler http.HandlerFunc, method string, url string, values url.Values, str interface{}) {
if !assert.HTTPBodyContains(t, handler, method, url, values, str) {
t.FailNow()
}
}
// HTTPBodyNotContains asserts that a specified handler returns a
// body that does not contain a string.
//
// assert.HTTPBodyNotContains(t, myHandler, "www.google.com", nil, "I'm Feeling Lucky")
//
// Returns whether the assertion was successful (true) or not (false).
func HTTPBodyNotContains(t TestingT, handler http.HandlerFunc, method string, url string, values url.Values, str interface{}) {
if !assert.HTTPBodyNotContains(t, handler, method, url, values, str) {
t.FailNow()
}
}
// HTTPError asserts that a specified handler returns an error status code.
//
// assert.HTTPError(t, myHandler, "POST", "/a/b/c", url.Values{"a": []string{"b", "c"}}
//
// Returns whether the assertion was successful (true) or not (false).
func HTTPError(t TestingT, handler http.HandlerFunc, method string, url string, values url.Values) {
if !assert.HTTPError(t, handler, method, url, values) {
t.FailNow()
}
}
// HTTPRedirect asserts that a specified handler returns a redirect status code.
//
// assert.HTTPRedirect(t, myHandler, "GET", "/a/b/c", url.Values{"a": []string{"b", "c"}}
//
// Returns whether the assertion was successful (true) or not (false).
func HTTPRedirect(t TestingT, handler http.HandlerFunc, method string, url string, values url.Values) {
if !assert.HTTPRedirect(t, handler, method, url, values) {
t.FailNow()
}
}
// HTTPSuccess asserts that a specified handler returns a success status code.
//
// assert.HTTPSuccess(t, myHandler, "POST", "http://www.google.com", nil)
//
// Returns whether the assertion was successful (true) or not (false).
func HTTPSuccess(t TestingT, handler http.HandlerFunc, method string, url string, values url.Values) {
if !assert.HTTPSuccess(t, handler, method, url, values) {
t.FailNow()
}
}
// Implements asserts that an object is implemented by the specified interface.
//
// assert.Implements(t, (*MyInterface)(nil), new(MyObject), "MyObject")
func Implements(t TestingT, interfaceObject interface{}, object interface{}, msgAndArgs ...interface{}) {
if !assert.Implements(t, interfaceObject, object, msgAndArgs...) {
t.FailNow()
}
}
// InDelta asserts that the two numerals are within delta of each other.
//
// assert.InDelta(t, math.Pi, (22 / 7.0), 0.01)
//
// Returns whether the assertion was successful (true) or not (false).
func InDelta(t TestingT, expected interface{}, actual interface{}, delta float64, msgAndArgs ...interface{}) {
if !assert.InDelta(t, expected, actual, delta, msgAndArgs...) {
t.FailNow()
}
}
// InDeltaSlice is the same as InDelta, except it compares two slices.
func InDeltaSlice(t TestingT, expected interface{}, actual interface{}, delta float64, msgAndArgs ...interface{}) {
if !assert.InDeltaSlice(t, expected, actual, delta, msgAndArgs...) {
t.FailNow()
}
}
// InEpsilon asserts that expected and actual have a relative error less than epsilon
//
// Returns whether the assertion was successful (true) or not (false).
func InEpsilon(t TestingT, expected interface{}, actual interface{}, epsilon float64, msgAndArgs ...interface{}) {
if !assert.InEpsilon(t, expected, actual, epsilon, msgAndArgs...) {
t.FailNow()
}
}
// InEpsilonSlice is the same as InEpsilon, except it compares two slices.
func InEpsilonSlice(t TestingT, expected interface{}, actual interface{}, delta float64, msgAndArgs ...interface{}) {
if !assert.InEpsilonSlice(t, expected, actual, delta, msgAndArgs...) {
t.FailNow()
}
}
// IsType asserts that the specified objects are of the same type.
func IsType(t TestingT, expectedType interface{}, object interface{}, msgAndArgs ...interface{}) {
if !assert.IsType(t, expectedType, object, msgAndArgs...) {
t.FailNow()
}
}
// JSONEq asserts that two JSON strings are equivalent.
//
// assert.JSONEq(t, `{"hello": "world", "foo": "bar"}`, `{"foo": "bar", "hello": "world"}`)
//
// Returns whether the assertion was successful (true) or not (false).
func JSONEq(t TestingT, expected string, actual string, msgAndArgs ...interface{}) {
if !assert.JSONEq(t, expected, actual, msgAndArgs...) {
t.FailNow()
}
}
// Len asserts that the specified object has specific length.
// Len also fails if the object has a type that len() not accept.
//
// assert.Len(t, mySlice, 3, "The size of slice is not 3")
//
// Returns whether the assertion was successful (true) or not (false).
func Len(t TestingT, object interface{}, length int, msgAndArgs ...interface{}) {
if !assert.Len(t, object, length, msgAndArgs...) {
t.FailNow()
}
}
// Nil asserts that the specified object is nil.
//
// assert.Nil(t, err, "err should be nothing")
//
// Returns whether the assertion was successful (true) or not (false).
func Nil(t TestingT, object interface{}, msgAndArgs ...interface{}) {
if !assert.Nil(t, object, msgAndArgs...) {
t.FailNow()
}
}
// NoError asserts that a function returned no error (i.e. `nil`).
//
// actualObj, err := SomeFunction()
// if assert.NoError(t, err) {
// assert.Equal(t, actualObj, expectedObj)
// }
//
// Returns whether the assertion was successful (true) or not (false).
func NoError(t TestingT, err error, msgAndArgs ...interface{}) {
if !assert.NoError(t, err, msgAndArgs...) {
t.FailNow()
}
}
// NotContains asserts that the specified string, list(array, slice...) or map does NOT contain the
// specified substring or element.
//
// assert.NotContains(t, "Hello World", "Earth", "But 'Hello World' does NOT contain 'Earth'")
// assert.NotContains(t, ["Hello", "World"], "Earth", "But ['Hello', 'World'] does NOT contain 'Earth'")
// assert.NotContains(t, {"Hello": "World"}, "Earth", "But {'Hello': 'World'} does NOT contain 'Earth'")
//
// Returns whether the assertion was successful (true) or not (false).
func NotContains(t TestingT, s interface{}, contains interface{}, msgAndArgs ...interface{}) {
if !assert.NotContains(t, s, contains, msgAndArgs...) {
t.FailNow()
}
}
// NotEmpty asserts that the specified object is NOT empty. I.e. not nil, "", false, 0 or either
// a slice or a channel with len == 0.
//
// if assert.NotEmpty(t, obj) {
// assert.Equal(t, "two", obj[1])
// }
//
// Returns whether the assertion was successful (true) or not (false).
func NotEmpty(t TestingT, object interface{}, msgAndArgs ...interface{}) {
if !assert.NotEmpty(t, object, msgAndArgs...) {
t.FailNow()
}
}
// NotEqual asserts that the specified values are NOT equal.
//
// assert.NotEqual(t, obj1, obj2, "two objects shouldn't be equal")
//
// Returns whether the assertion was successful (true) or not (false).
func NotEqual(t TestingT, expected interface{}, actual interface{}, msgAndArgs ...interface{}) {
if !assert.NotEqual(t, expected, actual, msgAndArgs...) {
t.FailNow()
}
}
// NotNil asserts that the specified object is not nil.
//
// assert.NotNil(t, err, "err should be something")
//
// Returns whether the assertion was successful (true) or not (false).
func NotNil(t TestingT, object interface{}, msgAndArgs ...interface{}) {
if !assert.NotNil(t, object, msgAndArgs...) {
t.FailNow()
}
}
// NotPanics asserts that the code inside the specified PanicTestFunc does NOT panic.
//
// assert.NotPanics(t, func(){
// RemainCalm()
// }, "Calling RemainCalm() should NOT panic")
//
// Returns whether the assertion was successful (true) or not (false).
func NotPanics(t TestingT, f assert.PanicTestFunc, msgAndArgs ...interface{}) {
if !assert.NotPanics(t, f, msgAndArgs...) {
t.FailNow()
}
}
// NotRegexp asserts that a specified regexp does not match a string.
//
// assert.NotRegexp(t, regexp.MustCompile("starts"), "it's starting")
// assert.NotRegexp(t, "^start", "it's not starting")
//
// Returns whether the assertion was successful (true) or not (false).
func NotRegexp(t TestingT, rx interface{}, str interface{}, msgAndArgs ...interface{}) {
if !assert.NotRegexp(t, rx, str, msgAndArgs...) {
t.FailNow()
}
}
// NotZero asserts that i is not the zero value for its type and returns the truth.
func NotZero(t TestingT, i interface{}, msgAndArgs ...interface{}) {
if !assert.NotZero(t, i, msgAndArgs...) {
t.FailNow()
}
}
// Panics asserts that the code inside the specified PanicTestFunc panics.
//
// assert.Panics(t, func(){
// GoCrazy()
// }, "Calling GoCrazy() should panic")
//
// Returns whether the assertion was successful (true) or not (false).
func Panics(t TestingT, f assert.PanicTestFunc, msgAndArgs ...interface{}) {
if !assert.Panics(t, f, msgAndArgs...) {
t.FailNow()
}
}
// Regexp asserts that a specified regexp matches a string.
//
// assert.Regexp(t, regexp.MustCompile("start"), "it's starting")
// assert.Regexp(t, "start...$", "it's not starting")
//
// Returns whether the assertion was successful (true) or not (false).
func Regexp(t TestingT, rx interface{}, str interface{}, msgAndArgs ...interface{}) {
if !assert.Regexp(t, rx, str, msgAndArgs...) {
t.FailNow()
}
}
// True asserts that the specified value is true.
//
// assert.True(t, myBool, "myBool should be true")
//
// Returns whether the assertion was successful (true) or not (false).
func True(t TestingT, value bool, msgAndArgs ...interface{}) {
if !assert.True(t, value, msgAndArgs...) {
t.FailNow()
}
}
// WithinDuration asserts that the two times are within duration delta of each other.
//
// assert.WithinDuration(t, time.Now(), time.Now(), 10*time.Second, "The difference should not be more than 10s")
//
// Returns whether the assertion was successful (true) or not (false).
func WithinDuration(t TestingT, expected time.Time, actual time.Time, delta time.Duration, msgAndArgs ...interface{}) {
if !assert.WithinDuration(t, expected, actual, delta, msgAndArgs...) {
t.FailNow()
}
}
// Zero asserts that i is the zero value for its type and returns the truth.
func Zero(t TestingT, i interface{}, msgAndArgs ...interface{}) {
if !assert.Zero(t, i, msgAndArgs...) {
t.FailNow()
}
}

View File

@ -1,6 +0,0 @@
{{.Comment}}
func {{.DocInfo.Name}}(t TestingT, {{.Params}}) {
if !assert.{{.DocInfo.Name}}(t, {{.ForwardedParams}}) {
t.FailNow()
}
}

View File

@ -1,388 +0,0 @@
/*
* CODE GENERATED AUTOMATICALLY WITH github.com/stretchr/testify/_codegen
* THIS FILE MUST NOT BE EDITED BY HAND
*/
package require
import (
assert "github.com/stretchr/testify/assert"
http "net/http"
url "net/url"
time "time"
)
// Condition uses a Comparison to assert a complex condition.
func (a *Assertions) Condition(comp assert.Comparison, msgAndArgs ...interface{}) {
Condition(a.t, comp, msgAndArgs...)
}
// Contains asserts that the specified string, list(array, slice...) or map contains the
// specified substring or element.
//
// a.Contains("Hello World", "World", "But 'Hello World' does contain 'World'")
// a.Contains(["Hello", "World"], "World", "But ["Hello", "World"] does contain 'World'")
// a.Contains({"Hello": "World"}, "Hello", "But {'Hello': 'World'} does contain 'Hello'")
//
// Returns whether the assertion was successful (true) or not (false).
func (a *Assertions) Contains(s interface{}, contains interface{}, msgAndArgs ...interface{}) {
Contains(a.t, s, contains, msgAndArgs...)
}
// Empty asserts that the specified object is empty. I.e. nil, "", false, 0 or either
// a slice or a channel with len == 0.
//
// a.Empty(obj)
//
// Returns whether the assertion was successful (true) or not (false).
func (a *Assertions) Empty(object interface{}, msgAndArgs ...interface{}) {
Empty(a.t, object, msgAndArgs...)
}
// Equal asserts that two objects are equal.
//
// a.Equal(123, 123, "123 and 123 should be equal")
//
// Returns whether the assertion was successful (true) or not (false).
func (a *Assertions) Equal(expected interface{}, actual interface{}, msgAndArgs ...interface{}) {
Equal(a.t, expected, actual, msgAndArgs...)
}
// EqualError asserts that a function returned an error (i.e. not `nil`)
// and that it is equal to the provided error.
//
// actualObj, err := SomeFunction()
// if assert.Error(t, err, "An error was expected") {
// assert.Equal(t, err, expectedError)
// }
//
// Returns whether the assertion was successful (true) or not (false).
func (a *Assertions) EqualError(theError error, errString string, msgAndArgs ...interface{}) {
EqualError(a.t, theError, errString, msgAndArgs...)
}
// EqualValues asserts that two objects are equal or convertable to the same types
// and equal.
//
// a.EqualValues(uint32(123), int32(123), "123 and 123 should be equal")
//
// Returns whether the assertion was successful (true) or not (false).
func (a *Assertions) EqualValues(expected interface{}, actual interface{}, msgAndArgs ...interface{}) {
EqualValues(a.t, expected, actual, msgAndArgs...)
}
// Error asserts that a function returned an error (i.e. not `nil`).
//
// actualObj, err := SomeFunction()
// if a.Error(err, "An error was expected") {
// assert.Equal(t, err, expectedError)
// }
//
// Returns whether the assertion was successful (true) or not (false).
func (a *Assertions) Error(err error, msgAndArgs ...interface{}) {
Error(a.t, err, msgAndArgs...)
}
// Exactly asserts that two objects are equal is value and type.
//
// a.Exactly(int32(123), int64(123), "123 and 123 should NOT be equal")
//
// Returns whether the assertion was successful (true) or not (false).
func (a *Assertions) Exactly(expected interface{}, actual interface{}, msgAndArgs ...interface{}) {
Exactly(a.t, expected, actual, msgAndArgs...)
}
// Fail reports a failure through
func (a *Assertions) Fail(failureMessage string, msgAndArgs ...interface{}) {
Fail(a.t, failureMessage, msgAndArgs...)
}
// FailNow fails test
func (a *Assertions) FailNow(failureMessage string, msgAndArgs ...interface{}) {
FailNow(a.t, failureMessage, msgAndArgs...)
}
// False asserts that the specified value is false.
//
// a.False(myBool, "myBool should be false")
//
// Returns whether the assertion was successful (true) or not (false).
func (a *Assertions) False(value bool, msgAndArgs ...interface{}) {
False(a.t, value, msgAndArgs...)
}
// HTTPBodyContains asserts that a specified handler returns a
// body that contains a string.
//
// a.HTTPBodyContains(myHandler, "www.google.com", nil, "I'm Feeling Lucky")
//
// Returns whether the assertion was successful (true) or not (false).
func (a *Assertions) HTTPBodyContains(handler http.HandlerFunc, method string, url string, values url.Values, str interface{}) {
HTTPBodyContains(a.t, handler, method, url, values, str)
}
// HTTPBodyNotContains asserts that a specified handler returns a
// body that does not contain a string.
//
// a.HTTPBodyNotContains(myHandler, "www.google.com", nil, "I'm Feeling Lucky")
//
// Returns whether the assertion was successful (true) or not (false).
func (a *Assertions) HTTPBodyNotContains(handler http.HandlerFunc, method string, url string, values url.Values, str interface{}) {
HTTPBodyNotContains(a.t, handler, method, url, values, str)
}
// HTTPError asserts that a specified handler returns an error status code.
//
// a.HTTPError(myHandler, "POST", "/a/b/c", url.Values{"a": []string{"b", "c"}}
//
// Returns whether the assertion was successful (true) or not (false).
func (a *Assertions) HTTPError(handler http.HandlerFunc, method string, url string, values url.Values) {
HTTPError(a.t, handler, method, url, values)
}
// HTTPRedirect asserts that a specified handler returns a redirect status code.
//
// a.HTTPRedirect(myHandler, "GET", "/a/b/c", url.Values{"a": []string{"b", "c"}}
//
// Returns whether the assertion was successful (true) or not (false).
func (a *Assertions) HTTPRedirect(handler http.HandlerFunc, method string, url string, values url.Values) {
HTTPRedirect(a.t, handler, method, url, values)
}
// HTTPSuccess asserts that a specified handler returns a success status code.
//
// a.HTTPSuccess(myHandler, "POST", "http://www.google.com", nil)
//
// Returns whether the assertion was successful (true) or not (false).
func (a *Assertions) HTTPSuccess(handler http.HandlerFunc, method string, url string, values url.Values) {
HTTPSuccess(a.t, handler, method, url, values)
}
// Implements asserts that an object is implemented by the specified interface.
//
// a.Implements((*MyInterface)(nil), new(MyObject), "MyObject")
func (a *Assertions) Implements(interfaceObject interface{}, object interface{}, msgAndArgs ...interface{}) {
Implements(a.t, interfaceObject, object, msgAndArgs...)
}
// InDelta asserts that the two numerals are within delta of each other.
//
// a.InDelta(math.Pi, (22 / 7.0), 0.01)
//
// Returns whether the assertion was successful (true) or not (false).
func (a *Assertions) InDelta(expected interface{}, actual interface{}, delta float64, msgAndArgs ...interface{}) {
InDelta(a.t, expected, actual, delta, msgAndArgs...)
}
// InDeltaSlice is the same as InDelta, except it compares two slices.
func (a *Assertions) InDeltaSlice(expected interface{}, actual interface{}, delta float64, msgAndArgs ...interface{}) {
InDeltaSlice(a.t, expected, actual, delta, msgAndArgs...)
}
// InEpsilon asserts that expected and actual have a relative error less than epsilon
//
// Returns whether the assertion was successful (true) or not (false).
func (a *Assertions) InEpsilon(expected interface{}, actual interface{}, epsilon float64, msgAndArgs ...interface{}) {
InEpsilon(a.t, expected, actual, epsilon, msgAndArgs...)
}
// InEpsilonSlice is the same as InEpsilon, except it compares two slices.
func (a *Assertions) InEpsilonSlice(expected interface{}, actual interface{}, delta float64, msgAndArgs ...interface{}) {
InEpsilonSlice(a.t, expected, actual, delta, msgAndArgs...)
}
// IsType asserts that the specified objects are of the same type.
func (a *Assertions) IsType(expectedType interface{}, object interface{}, msgAndArgs ...interface{}) {
IsType(a.t, expectedType, object, msgAndArgs...)
}
// JSONEq asserts that two JSON strings are equivalent.
//
// a.JSONEq(`{"hello": "world", "foo": "bar"}`, `{"foo": "bar", "hello": "world"}`)
//
// Returns whether the assertion was successful (true) or not (false).
func (a *Assertions) JSONEq(expected string, actual string, msgAndArgs ...interface{}) {
JSONEq(a.t, expected, actual, msgAndArgs...)
}
// Len asserts that the specified object has specific length.
// Len also fails if the object has a type that len() not accept.
//
// a.Len(mySlice, 3, "The size of slice is not 3")
//
// Returns whether the assertion was successful (true) or not (false).
func (a *Assertions) Len(object interface{}, length int, msgAndArgs ...interface{}) {
Len(a.t, object, length, msgAndArgs...)
}
// Nil asserts that the specified object is nil.
//
// a.Nil(err, "err should be nothing")
//
// Returns whether the assertion was successful (true) or not (false).
func (a *Assertions) Nil(object interface{}, msgAndArgs ...interface{}) {
Nil(a.t, object, msgAndArgs...)
}
// NoError asserts that a function returned no error (i.e. `nil`).
//
// actualObj, err := SomeFunction()
// if a.NoError(err) {
// assert.Equal(t, actualObj, expectedObj)
// }
//
// Returns whether the assertion was successful (true) or not (false).
func (a *Assertions) NoError(err error, msgAndArgs ...interface{}) {
NoError(a.t, err, msgAndArgs...)
}
// NotContains asserts that the specified string, list(array, slice...) or map does NOT contain the
// specified substring or element.
//
// a.NotContains("Hello World", "Earth", "But 'Hello World' does NOT contain 'Earth'")
// a.NotContains(["Hello", "World"], "Earth", "But ['Hello', 'World'] does NOT contain 'Earth'")
// a.NotContains({"Hello": "World"}, "Earth", "But {'Hello': 'World'} does NOT contain 'Earth'")
//
// Returns whether the assertion was successful (true) or not (false).
func (a *Assertions) NotContains(s interface{}, contains interface{}, msgAndArgs ...interface{}) {
NotContains(a.t, s, contains, msgAndArgs...)
}
// NotEmpty asserts that the specified object is NOT empty. I.e. not nil, "", false, 0 or either
// a slice or a channel with len == 0.
//
// if a.NotEmpty(obj) {
// assert.Equal(t, "two", obj[1])
// }
//
// Returns whether the assertion was successful (true) or not (false).
func (a *Assertions) NotEmpty(object interface{}, msgAndArgs ...interface{}) {
NotEmpty(a.t, object, msgAndArgs...)
}
// NotEqual asserts that the specified values are NOT equal.
//
// a.NotEqual(obj1, obj2, "two objects shouldn't be equal")
//
// Returns whether the assertion was successful (true) or not (false).
func (a *Assertions) NotEqual(expected interface{}, actual interface{}, msgAndArgs ...interface{}) {
NotEqual(a.t, expected, actual, msgAndArgs...)
}
// NotNil asserts that the specified object is not nil.
//
// a.NotNil(err, "err should be something")
//
// Returns whether the assertion was successful (true) or not (false).
func (a *Assertions) NotNil(object interface{}, msgAndArgs ...interface{}) {
NotNil(a.t, object, msgAndArgs...)
}
// NotPanics asserts that the code inside the specified PanicTestFunc does NOT panic.
//
// a.NotPanics(func(){
// RemainCalm()
// }, "Calling RemainCalm() should NOT panic")
//
// Returns whether the assertion was successful (true) or not (false).
func (a *Assertions) NotPanics(f assert.PanicTestFunc, msgAndArgs ...interface{}) {
NotPanics(a.t, f, msgAndArgs...)
}
// NotRegexp asserts that a specified regexp does not match a string.
//
// a.NotRegexp(regexp.MustCompile("starts"), "it's starting")
// a.NotRegexp("^start", "it's not starting")
//
// Returns whether the assertion was successful (true) or not (false).
func (a *Assertions) NotRegexp(rx interface{}, str interface{}, msgAndArgs ...interface{}) {
NotRegexp(a.t, rx, str, msgAndArgs...)
}
// NotZero asserts that i is not the zero value for its type and returns the truth.
func (a *Assertions) NotZero(i interface{}, msgAndArgs ...interface{}) {
NotZero(a.t, i, msgAndArgs...)
}
// Panics asserts that the code inside the specified PanicTestFunc panics.
//
// a.Panics(func(){
// GoCrazy()
// }, "Calling GoCrazy() should panic")
//
// Returns whether the assertion was successful (true) or not (false).
func (a *Assertions) Panics(f assert.PanicTestFunc, msgAndArgs ...interface{}) {
Panics(a.t, f, msgAndArgs...)
}
// Regexp asserts that a specified regexp matches a string.
//
// a.Regexp(regexp.MustCompile("start"), "it's starting")
// a.Regexp("start...$", "it's not starting")
//
// Returns whether the assertion was successful (true) or not (false).
func (a *Assertions) Regexp(rx interface{}, str interface{}, msgAndArgs ...interface{}) {
Regexp(a.t, rx, str, msgAndArgs...)
}
// True asserts that the specified value is true.
//
// a.True(myBool, "myBool should be true")
//
// Returns whether the assertion was successful (true) or not (false).
func (a *Assertions) True(value bool, msgAndArgs ...interface{}) {
True(a.t, value, msgAndArgs...)
}
// WithinDuration asserts that the two times are within duration delta of each other.
//
// a.WithinDuration(time.Now(), time.Now(), 10*time.Second, "The difference should not be more than 10s")
//
// Returns whether the assertion was successful (true) or not (false).
func (a *Assertions) WithinDuration(expected time.Time, actual time.Time, delta time.Duration, msgAndArgs ...interface{}) {
WithinDuration(a.t, expected, actual, delta, msgAndArgs...)
}
// Zero asserts that i is the zero value for its type and returns the truth.
func (a *Assertions) Zero(i interface{}, msgAndArgs ...interface{}) {
Zero(a.t, i, msgAndArgs...)
}

View File

@ -1,4 +0,0 @@
{{.CommentWithoutT "a"}}
func (a *Assertions) {{.DocInfo.Name}}({{.Params}}) {
{{.DocInfo.Name}}(a.t, {{.ForwardedParams}})
}

View File

@ -1,9 +0,0 @@
package require
// TestingT is an interface wrapper around *testing.T
type TestingT interface {
Errorf(format string, args ...interface{})
FailNow()
}
//go:generate go run ../_codegen/main.go -output-package=require -template=require.go.tmpl

View File

@ -1,369 +0,0 @@
package require
import (
"errors"
"testing"
"time"
)
// AssertionTesterInterface defines an interface to be used for testing assertion methods
type AssertionTesterInterface interface {
TestMethod()
}
// AssertionTesterConformingObject is an object that conforms to the AssertionTesterInterface interface
type AssertionTesterConformingObject struct {
}
func (a *AssertionTesterConformingObject) TestMethod() {
}
// AssertionTesterNonConformingObject is an object that does not conform to the AssertionTesterInterface interface
type AssertionTesterNonConformingObject struct {
}
type MockT struct {
Failed bool
}
func (t *MockT) FailNow() {
t.Failed = true
}
func (t *MockT) Errorf(format string, args ...interface{}) {
_, _ = format, args
}
func TestImplements(t *testing.T) {
Implements(t, (*AssertionTesterInterface)(nil), new(AssertionTesterConformingObject))
mockT := new(MockT)
Implements(mockT, (*AssertionTesterInterface)(nil), new(AssertionTesterNonConformingObject))
if !mockT.Failed {
t.Error("Check should fail")
}
}
func TestIsType(t *testing.T) {
IsType(t, new(AssertionTesterConformingObject), new(AssertionTesterConformingObject))
mockT := new(MockT)
IsType(mockT, new(AssertionTesterConformingObject), new(AssertionTesterNonConformingObject))
if !mockT.Failed {
t.Error("Check should fail")
}
}
func TestEqual(t *testing.T) {
Equal(t, 1, 1)
mockT := new(MockT)
Equal(mockT, 1, 2)
if !mockT.Failed {
t.Error("Check should fail")
}
}
func TestNotEqual(t *testing.T) {
NotEqual(t, 1, 2)
mockT := new(MockT)
NotEqual(mockT, 2, 2)
if !mockT.Failed {
t.Error("Check should fail")
}
}
func TestExactly(t *testing.T) {
a := float32(1)
b := float32(1)
c := float64(1)
Exactly(t, a, b)
mockT := new(MockT)
Exactly(mockT, a, c)
if !mockT.Failed {
t.Error("Check should fail")
}
}
func TestNotNil(t *testing.T) {
NotNil(t, new(AssertionTesterConformingObject))
mockT := new(MockT)
NotNil(mockT, nil)
if !mockT.Failed {
t.Error("Check should fail")
}
}
func TestNil(t *testing.T) {
Nil(t, nil)
mockT := new(MockT)
Nil(mockT, new(AssertionTesterConformingObject))
if !mockT.Failed {
t.Error("Check should fail")
}
}
func TestTrue(t *testing.T) {
True(t, true)
mockT := new(MockT)
True(mockT, false)
if !mockT.Failed {
t.Error("Check should fail")
}
}
func TestFalse(t *testing.T) {
False(t, false)
mockT := new(MockT)
False(mockT, true)
if !mockT.Failed {
t.Error("Check should fail")
}
}
func TestContains(t *testing.T) {
Contains(t, "Hello World", "Hello")
mockT := new(MockT)
Contains(mockT, "Hello World", "Salut")
if !mockT.Failed {
t.Error("Check should fail")
}
}
func TestNotContains(t *testing.T) {
NotContains(t, "Hello World", "Hello!")
mockT := new(MockT)
NotContains(mockT, "Hello World", "Hello")
if !mockT.Failed {
t.Error("Check should fail")
}
}
func TestPanics(t *testing.T) {
Panics(t, func() {
panic("Panic!")
})
mockT := new(MockT)
Panics(mockT, func() {})
if !mockT.Failed {
t.Error("Check should fail")
}
}
func TestNotPanics(t *testing.T) {
NotPanics(t, func() {})
mockT := new(MockT)
NotPanics(mockT, func() {
panic("Panic!")
})
if !mockT.Failed {
t.Error("Check should fail")
}
}
func TestNoError(t *testing.T) {
NoError(t, nil)
mockT := new(MockT)
NoError(mockT, errors.New("some error"))
if !mockT.Failed {
t.Error("Check should fail")
}
}
func TestError(t *testing.T) {
Error(t, errors.New("some error"))
mockT := new(MockT)
Error(mockT, nil)
if !mockT.Failed {
t.Error("Check should fail")
}
}
func TestEqualError(t *testing.T) {
EqualError(t, errors.New("some error"), "some error")
mockT := new(MockT)
EqualError(mockT, errors.New("some error"), "Not some error")
if !mockT.Failed {
t.Error("Check should fail")
}
}
func TestEmpty(t *testing.T) {
Empty(t, "")
mockT := new(MockT)
Empty(mockT, "x")
if !mockT.Failed {
t.Error("Check should fail")
}
}
func TestNotEmpty(t *testing.T) {
NotEmpty(t, "x")
mockT := new(MockT)
NotEmpty(mockT, "")
if !mockT.Failed {
t.Error("Check should fail")
}
}
func TestWithinDuration(t *testing.T) {
a := time.Now()
b := a.Add(10 * time.Second)
WithinDuration(t, a, b, 15*time.Second)
mockT := new(MockT)
WithinDuration(mockT, a, b, 5*time.Second)
if !mockT.Failed {
t.Error("Check should fail")
}
}
func TestInDelta(t *testing.T) {
InDelta(t, 1.001, 1, 0.01)
mockT := new(MockT)
InDelta(mockT, 1, 2, 0.5)
if !mockT.Failed {
t.Error("Check should fail")
}
}
func TestZero(t *testing.T) {
Zero(t, "")
mockT := new(MockT)
Zero(mockT, "x")
if !mockT.Failed {
t.Error("Check should fail")
}
}
func TestNotZero(t *testing.T) {
NotZero(t, "x")
mockT := new(MockT)
NotZero(mockT, "")
if !mockT.Failed {
t.Error("Check should fail")
}
}
func TestJSONEq_EqualSONString(t *testing.T) {
mockT := new(MockT)
JSONEq(mockT, `{"hello": "world", "foo": "bar"}`, `{"hello": "world", "foo": "bar"}`)
if mockT.Failed {
t.Error("Check should pass")
}
}
func TestJSONEq_EquivalentButNotEqual(t *testing.T) {
mockT := new(MockT)
JSONEq(mockT, `{"hello": "world", "foo": "bar"}`, `{"foo": "bar", "hello": "world"}`)
if mockT.Failed {
t.Error("Check should pass")
}
}
func TestJSONEq_HashOfArraysAndHashes(t *testing.T) {
mockT := new(MockT)
JSONEq(mockT, "{\r\n\t\"numeric\": 1.5,\r\n\t\"array\": [{\"foo\": \"bar\"}, 1, \"string\", [\"nested\", \"array\", 5.5]],\r\n\t\"hash\": {\"nested\": \"hash\", \"nested_slice\": [\"this\", \"is\", \"nested\"]},\r\n\t\"string\": \"foo\"\r\n}",
"{\r\n\t\"numeric\": 1.5,\r\n\t\"hash\": {\"nested\": \"hash\", \"nested_slice\": [\"this\", \"is\", \"nested\"]},\r\n\t\"string\": \"foo\",\r\n\t\"array\": [{\"foo\": \"bar\"}, 1, \"string\", [\"nested\", \"array\", 5.5]]\r\n}")
if mockT.Failed {
t.Error("Check should pass")
}
}
func TestJSONEq_Array(t *testing.T) {
mockT := new(MockT)
JSONEq(mockT, `["foo", {"hello": "world", "nested": "hash"}]`, `["foo", {"nested": "hash", "hello": "world"}]`)
if mockT.Failed {
t.Error("Check should pass")
}
}
func TestJSONEq_HashAndArrayNotEquivalent(t *testing.T) {
mockT := new(MockT)
JSONEq(mockT, `["foo", {"hello": "world", "nested": "hash"}]`, `{"foo": "bar", {"nested": "hash", "hello": "world"}}`)
if !mockT.Failed {
t.Error("Check should fail")
}
}
func TestJSONEq_HashesNotEquivalent(t *testing.T) {
mockT := new(MockT)
JSONEq(mockT, `{"foo": "bar"}`, `{"foo": "bar", "hello": "world"}`)
if !mockT.Failed {
t.Error("Check should fail")
}
}
func TestJSONEq_ActualIsNotJSON(t *testing.T) {
mockT := new(MockT)
JSONEq(mockT, `{"foo": "bar"}`, "Not JSON")
if !mockT.Failed {
t.Error("Check should fail")
}
}
func TestJSONEq_ExpectedIsNotJSON(t *testing.T) {
mockT := new(MockT)
JSONEq(mockT, "Not JSON", `{"foo": "bar", "hello": "world"}`)
if !mockT.Failed {
t.Error("Check should fail")
}
}
func TestJSONEq_ExpectedAndActualNotJSON(t *testing.T) {
mockT := new(MockT)
JSONEq(mockT, "Not JSON", "Not JSON")
if !mockT.Failed {
t.Error("Check should fail")
}
}
func TestJSONEq_ArraysOfDifferentOrder(t *testing.T) {
mockT := new(MockT)
JSONEq(mockT, `["foo", {"hello": "world", "nested": "hash"}]`, `[{ "hello": "world", "nested": "hash"}, "foo"]`)
if !mockT.Failed {
t.Error("Check should fail")
}
}

View File

@ -1,65 +0,0 @@
// Package suite contains logic for creating testing suite structs
// and running the methods on those structs as tests. The most useful
// piece of this package is that you can create setup/teardown methods
// on your testing suites, which will run before/after the whole suite
// or individual tests (depending on which interface(s) you
// implement).
//
// A testing suite is usually built by first extending the built-in
// suite functionality from suite.Suite in testify. Alternatively,
// you could reproduce that logic on your own if you wanted (you
// just need to implement the TestingSuite interface from
// suite/interfaces.go).
//
// After that, you can implement any of the interfaces in
// suite/interfaces.go to add setup/teardown functionality to your
// suite, and add any methods that start with "Test" to add tests.
// Methods that do not match any suite interfaces and do not begin
// with "Test" will not be run by testify, and can safely be used as
// helper methods.
//
// Once you've built your testing suite, you need to run the suite
// (using suite.Run from testify) inside any function that matches the
// identity that "go test" is already looking for (i.e.
// func(*testing.T)).
//
// Regular expression to select test suites specified command-line
// argument "-run". Regular expression to select the methods
// of test suites specified command-line argument "-m".
// Suite object has assertion methods.
//
// A crude example:
// // Basic imports
// import (
// "testing"
// "github.com/stretchr/testify/assert"
// "github.com/stretchr/testify/suite"
// )
//
// // Define the suite, and absorb the built-in basic suite
// // functionality from testify - including a T() method which
// // returns the current testing context
// type ExampleTestSuite struct {
// suite.Suite
// VariableThatShouldStartAtFive int
// }
//
// // Make sure that VariableThatShouldStartAtFive is set to five
// // before each test
// func (suite *ExampleTestSuite) SetupTest() {
// suite.VariableThatShouldStartAtFive = 5
// }
//
// // All methods that begin with "Test" are run as tests within a
// // suite.
// func (suite *ExampleTestSuite) TestExample() {
// assert.Equal(suite.T(), 5, suite.VariableThatShouldStartAtFive)
// suite.Equal(5, suite.VariableThatShouldStartAtFive)
// }
//
// // In order for 'go test' to run this suite, we need to create
// // a normal test function and pass our suite to suite.Run
// func TestExampleTestSuite(t *testing.T) {
// suite.Run(t, new(ExampleTestSuite))
// }
package suite

View File

@ -1,34 +0,0 @@
package suite
import "testing"
// TestingSuite can store and return the current *testing.T context
// generated by 'go test'.
type TestingSuite interface {
T() *testing.T
SetT(*testing.T)
}
// SetupAllSuite has a SetupSuite method, which will run before the
// tests in the suite are run.
type SetupAllSuite interface {
SetupSuite()
}
// SetupTestSuite has a SetupTest method, which will run before each
// test in the suite.
type SetupTestSuite interface {
SetupTest()
}
// TearDownAllSuite has a TearDownSuite method, which will run after
// all the tests in the suite have been run.
type TearDownAllSuite interface {
TearDownSuite()
}
// TearDownTestSuite has a TearDownTest method, which will run after
// each test in the suite.
type TearDownTestSuite interface {
TearDownTest()
}

View File

@ -1,115 +0,0 @@
package suite
import (
"flag"
"fmt"
"os"
"reflect"
"regexp"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
var matchMethod = flag.String("testify.m", "", "regular expression to select tests of the testify suite to run")
// Suite is a basic testing suite with methods for storing and
// retrieving the current *testing.T context.
type Suite struct {
*assert.Assertions
require *require.Assertions
t *testing.T
}
// T retrieves the current *testing.T context.
func (suite *Suite) T() *testing.T {
return suite.t
}
// SetT sets the current *testing.T context.
func (suite *Suite) SetT(t *testing.T) {
suite.t = t
suite.Assertions = assert.New(t)
suite.require = require.New(t)
}
// Require returns a require context for suite.
func (suite *Suite) Require() *require.Assertions {
if suite.require == nil {
suite.require = require.New(suite.T())
}
return suite.require
}
// Assert returns an assert context for suite. Normally, you can call
// `suite.NoError(expected, actual)`, but for situations where the embedded
// methods are overridden (for example, you might want to override
// assert.Assertions with require.Assertions), this method is provided so you
// can call `suite.Assert().NoError()`.
func (suite *Suite) Assert() *assert.Assertions {
if suite.Assertions == nil {
suite.Assertions = assert.New(suite.T())
}
return suite.Assertions
}
// Run takes a testing suite and runs all of the tests attached
// to it.
func Run(t *testing.T, suite TestingSuite) {
suite.SetT(t)
if setupAllSuite, ok := suite.(SetupAllSuite); ok {
setupAllSuite.SetupSuite()
}
defer func() {
if tearDownAllSuite, ok := suite.(TearDownAllSuite); ok {
tearDownAllSuite.TearDownSuite()
}
}()
methodFinder := reflect.TypeOf(suite)
tests := []testing.InternalTest{}
for index := 0; index < methodFinder.NumMethod(); index++ {
method := methodFinder.Method(index)
ok, err := methodFilter(method.Name)
if err != nil {
fmt.Fprintf(os.Stderr, "testify: invalid regexp for -m: %s\n", err)
os.Exit(1)
}
if ok {
test := testing.InternalTest{
Name: method.Name,
F: func(t *testing.T) {
parentT := suite.T()
suite.SetT(t)
if setupTestSuite, ok := suite.(SetupTestSuite); ok {
setupTestSuite.SetupTest()
}
defer func() {
if tearDownTestSuite, ok := suite.(TearDownTestSuite); ok {
tearDownTestSuite.TearDownTest()
}
suite.SetT(parentT)
}()
method.Func.Call([]reflect.Value{reflect.ValueOf(suite)})
},
}
tests = append(tests, test)
}
}
if !testing.RunTests(func(_, _ string) (bool, error) { return true, nil },
tests) {
t.Fail()
}
}
// Filtering method according to set regular expression
// specified command-line argument -m
func methodFilter(name string) (bool, error) {
if ok, _ := regexp.MatchString("^Test", name); !ok {
return false, nil
}
return regexp.MatchString(*matchMethod, name)
}

View File

@ -1,239 +0,0 @@
package suite
import (
"errors"
"io/ioutil"
"os"
"testing"
"github.com/stretchr/testify/assert"
)
// SuiteRequireTwice is intended to test the usage of suite.Require in two
// different tests
type SuiteRequireTwice struct{ Suite }
// TestSuiteRequireTwice checks for regressions of issue #149 where
// suite.requirements was not initialised in suite.SetT()
// A regression would result on these tests panicking rather than failing.
func TestSuiteRequireTwice(t *testing.T) {
ok := testing.RunTests(
func(_, _ string) (bool, error) { return true, nil },
[]testing.InternalTest{{
Name: "TestSuiteRequireTwice",
F: func(t *testing.T) {
suite := new(SuiteRequireTwice)
Run(t, suite)
},
}},
)
assert.Equal(t, false, ok)
}
func (s *SuiteRequireTwice) TestRequireOne() {
r := s.Require()
r.Equal(1, 2)
}
func (s *SuiteRequireTwice) TestRequireTwo() {
r := s.Require()
r.Equal(1, 2)
}
// This suite is intended to store values to make sure that only
// testing-suite-related methods are run. It's also a fully
// functional example of a testing suite, using setup/teardown methods
// and a helper method that is ignored by testify. To make this look
// more like a real world example, all tests in the suite perform some
// type of assertion.
type SuiteTester struct {
// Include our basic suite logic.
Suite
// Keep counts of how many times each method is run.
SetupSuiteRunCount int
TearDownSuiteRunCount int
SetupTestRunCount int
TearDownTestRunCount int
TestOneRunCount int
TestTwoRunCount int
NonTestMethodRunCount int
}
type SuiteSkipTester struct {
// Include our basic suite logic.
Suite
// Keep counts of how many times each method is run.
SetupSuiteRunCount int
TearDownSuiteRunCount int
}
// The SetupSuite method will be run by testify once, at the very
// start of the testing suite, before any tests are run.
func (suite *SuiteTester) SetupSuite() {
suite.SetupSuiteRunCount++
}
func (suite *SuiteSkipTester) SetupSuite() {
suite.SetupSuiteRunCount++
suite.T().Skip()
}
// The TearDownSuite method will be run by testify once, at the very
// end of the testing suite, after all tests have been run.
func (suite *SuiteTester) TearDownSuite() {
suite.TearDownSuiteRunCount++
}
func (suite *SuiteSkipTester) TearDownSuite() {
suite.TearDownSuiteRunCount++
}
// The SetupTest method will be run before every test in the suite.
func (suite *SuiteTester) SetupTest() {
suite.SetupTestRunCount++
}
// The TearDownTest method will be run after every test in the suite.
func (suite *SuiteTester) TearDownTest() {
suite.TearDownTestRunCount++
}
// Every method in a testing suite that begins with "Test" will be run
// as a test. TestOne is an example of a test. For the purposes of
// this example, we've included assertions in the tests, since most
// tests will issue assertions.
func (suite *SuiteTester) TestOne() {
beforeCount := suite.TestOneRunCount
suite.TestOneRunCount++
assert.Equal(suite.T(), suite.TestOneRunCount, beforeCount+1)
suite.Equal(suite.TestOneRunCount, beforeCount+1)
}
// TestTwo is another example of a test.
func (suite *SuiteTester) TestTwo() {
beforeCount := suite.TestTwoRunCount
suite.TestTwoRunCount++
assert.NotEqual(suite.T(), suite.TestTwoRunCount, beforeCount)
suite.NotEqual(suite.TestTwoRunCount, beforeCount)
}
func (suite *SuiteTester) TestSkip() {
suite.T().Skip()
}
// NonTestMethod does not begin with "Test", so it will not be run by
// testify as a test in the suite. This is useful for creating helper
// methods for your tests.
func (suite *SuiteTester) NonTestMethod() {
suite.NonTestMethodRunCount++
}
// TestRunSuite will be run by the 'go test' command, so within it, we
// can run our suite using the Run(*testing.T, TestingSuite) function.
func TestRunSuite(t *testing.T) {
suiteTester := new(SuiteTester)
Run(t, suiteTester)
// Normally, the test would end here. The following are simply
// some assertions to ensure that the Run function is working as
// intended - they are not part of the example.
// The suite was only run once, so the SetupSuite and TearDownSuite
// methods should have each been run only once.
assert.Equal(t, suiteTester.SetupSuiteRunCount, 1)
assert.Equal(t, suiteTester.TearDownSuiteRunCount, 1)
// There are three test methods (TestOne, TestTwo, and TestSkip), so
// the SetupTest and TearDownTest methods (which should be run once for
// each test) should have been run three times.
assert.Equal(t, suiteTester.SetupTestRunCount, 3)
assert.Equal(t, suiteTester.TearDownTestRunCount, 3)
// Each test should have been run once.
assert.Equal(t, suiteTester.TestOneRunCount, 1)
assert.Equal(t, suiteTester.TestTwoRunCount, 1)
// Methods that don't match the test method identifier shouldn't
// have been run at all.
assert.Equal(t, suiteTester.NonTestMethodRunCount, 0)
suiteSkipTester := new(SuiteSkipTester)
Run(t, suiteSkipTester)
// The suite was only run once, so the SetupSuite and TearDownSuite
// methods should have each been run only once, even though SetupSuite
// called Skip()
assert.Equal(t, suiteSkipTester.SetupSuiteRunCount, 1)
assert.Equal(t, suiteSkipTester.TearDownSuiteRunCount, 1)
}
func TestSuiteGetters(t *testing.T) {
suite := new(SuiteTester)
suite.SetT(t)
assert.NotNil(t, suite.Assert())
assert.Equal(t, suite.Assertions, suite.Assert())
assert.NotNil(t, suite.Require())
assert.Equal(t, suite.require, suite.Require())
}
type SuiteLoggingTester struct {
Suite
}
func (s *SuiteLoggingTester) TestLoggingPass() {
s.T().Log("TESTLOGPASS")
}
func (s *SuiteLoggingTester) TestLoggingFail() {
s.T().Log("TESTLOGFAIL")
assert.NotNil(s.T(), nil) // expected to fail
}
type StdoutCapture struct {
oldStdout *os.File
readPipe *os.File
}
func (sc *StdoutCapture) StartCapture() {
sc.oldStdout = os.Stdout
sc.readPipe, os.Stdout, _ = os.Pipe()
}
func (sc *StdoutCapture) StopCapture() (string, error) {
if sc.oldStdout == nil || sc.readPipe == nil {
return "", errors.New("StartCapture not called before StopCapture")
}
os.Stdout.Close()
os.Stdout = sc.oldStdout
bytes, err := ioutil.ReadAll(sc.readPipe)
if err != nil {
return "", err
}
return string(bytes), nil
}
func TestSuiteLogging(t *testing.T) {
testT := testing.T{}
suiteLoggingTester := new(SuiteLoggingTester)
capture := StdoutCapture{}
capture.StartCapture()
Run(&testT, suiteLoggingTester)
output, err := capture.StopCapture()
assert.Nil(t, err, "Got an error trying to capture stdout!")
// Failed tests' output is always printed
assert.Contains(t, output, "TESTLOGFAIL")
if testing.Verbose() {
// In verbose mode, output from successful tests is also printed
assert.Contains(t, output, "TESTLOGPASS")
} else {
assert.NotContains(t, output, "TESTLOGPASS")
}
}

19
vendor/github.com/tidwall/boxtree/LICENSE generated vendored Normal file
View File

@ -0,0 +1,19 @@
Copyright (c) 2018 Josh Baker
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

94
vendor/github.com/tidwall/boxtree/README.md generated vendored Normal file
View File

@ -0,0 +1,94 @@
# `BoxTree`
[![GoDoc](https://godoc.org/github.com/tidwall/boxtree?status.svg)](https://godoc.org/github.com/tidwall/boxtree)
**EXPERIMENTAL**
This package provides an in-memory R-Tree implementation for Go. It's designed
for [Tile38](https://github.com/tidwall/tile38).
<img src="/res/cities.png" width="512" height="256" border="0" alt="Cities">
## Features
- Support for 2 and 3 dimensions
- Optimized for fast box inserts and replacements.
## Usage
### Installing
To start using BoxTree, install Go and run `go get`:
```sh
$ go get -u github.com/tidwall/boxtree
```
### Basic operations
```go
// create a 2D BoxTree
tr := boxtree.New(2)
// insert a point
tr.Insert([]float64{-112.0078, 33.4373}, nil, "PHX")
// insert a box
tr.Insert([]float64{10, 10}, []float64{20, 20}, "rect")
// search
tr.Search([]float64{-112.1, 33.4}, []float64{-112.0, 33.5},
func(min, max []float64, value interface{}) bool {
println(value.(string)) // prints "PHX"
},
)
// delete
tr.Delete([]float64{-112.0078, 33.4373}, []float64{-112.0078, 33.4373}, "PHX")
```
## Algorithms
This implementation is a variant of the original paper:
[R-TREES. A DYNAMIC INDEX STRUCTURE FOR SPATIAL SEARCHING](http://www-db.deis.unibo.it/courses/SI-LS/papers/Gut84.pdf)
### Inserting
Same as the original algorithm. From the root to the leaf, the boxes which will incur the least enlargment are chosen. Ties go to boxes with the smallest area.
### Deleting
Same as the original algorithm. A target box is deleted directly. When the number of children in a box falls below it's minumum entries, it is removed from the tree and it's items are re-inserted.
### Splitting
This is a custom algorithm.
It attempts to minimize intensive operations such as pre-sorting the children and comparing overlaps & area sizes.
The desire is to do simple single axis distance calculations each child only once, with a target 50/50 chance that the child might be moved in-memory.
When a box has reached it's max number of entries it's largest axis is calculated and the box is split into two smaller boxes, named `left` and `right`.
Each child boxes is then evaluated to determine which smaller box it should be placed into.
Two values, `min-dist` and `max-dist`, are calcuated for each child.
- `min-dist` is the distance from the parent's minumum value of it's largest axis to the child's minumum value of the parent largest axis.
- `max-dist` is the distance from the parent's maximum value of it's largest axis to the child's maximum value of the parent largest axis.
When the `min-dist` is less than `max-dist` then the child is placed into the `left` box.
When the `max-dist` is less than `min-dist` then the child is placed into the `right` box.
When the `min-dist` is equal to `max-dist` then the child is placed into an `equal` bucket until all of the children are evaluated.
Each `equal` box is then one-by-one placed in either `left` or `right`, whichever has less children.
## Performance
In my testing:
- Insert show similar performance as the quadratic R-tree and ~1.2x - 1.5x faster than R*tree.
- Search and Delete is ~1.5x - 2x faster than quadratic and about the same as R*tree.
I hope to provide more details in the future.
## License
`BoxTree` source code is available under the MIT License.

36
vendor/github.com/tidwall/boxtree/boxtree.go generated vendored Normal file
View File

@ -0,0 +1,36 @@
package boxtree
import (
"github.com/tidwall/boxtree/d2"
"github.com/tidwall/boxtree/d3"
)
// BoxTree is an rtree by a different name
type BoxTree interface {
Insert(min, max []float64, value interface{})
Delete(min, max []float64, value interface{})
Search(min, max []float64,
iter func(min, max []float64, value interface{}) bool,
)
TotalOverlapArea() float64
Traverse(iter func(min, max []float64, height, level int,
value interface{}) int)
Scan(iter func(min, max []float64, value interface{}) bool)
Nearby(min, max []float64,
iter func(min, max []float64, item interface{}) bool,
)
Bounds() (min, max []float64)
Count() int
}
// New returns are new BoxTree, only 2 dims are allows
func New(dims int) BoxTree {
switch dims {
default:
panic("invalid dimensions")
case 2:
return new(d2.BoxTree)
case 3:
return new(d3.BoxTree)
}
}

125
vendor/github.com/tidwall/boxtree/boxtree_test.go generated vendored Normal file
View File

@ -0,0 +1,125 @@
package boxtree
import (
"fmt"
"math/rand"
"os"
"testing"
"time"
"github.com/tidwall/lotsa"
)
func TestBoxTree(t *testing.T) {
New(2)
New(3)
defer func() {
s := recover().(string)
if s != "invalid dimensions" {
t.Fatalf("expected '%s', got '%s'", "invalid dimensions", s)
}
}()
New(4)
// there are more test in the d2/d3 directories
}
func TestBenchInsert2D(t *testing.T) {
testBenchInsert(t, 100000, 2)
}
func TestBenchInsert3D(t *testing.T) {
testBenchInsert(t, 100000, 3)
}
func testBenchInsert(t *testing.T, N, D int) {
rand.Seed(time.Now().UnixNano())
points := make([]float64, N*D)
for i := 0; i < N; i++ {
for j := 0; j < D; j++ {
points[i*D+j] = rand.Float64()*100 - 50
}
}
tr := New(D)
lotsa.Output = os.Stdout
fmt.Printf("Insert(%dD): ", D)
lotsa.Ops(N, 1, func(i, _ int) {
tr.Insert(points[i*D+0:i*D+D], nil, i)
})
fmt.Printf("Search(%dD): ", D)
var count int
lotsa.Ops(N, 1, func(i, _ int) {
tr.Search(points[i*D+0:i*D+D], points[i*D+0:i*D+D],
func(min, max []float64, value interface{}) bool {
count++
return true
},
)
})
if count != N {
t.Fatalf("expected %d, got %d", N, count)
}
fmt.Printf("Delete(%dD): ", D)
lotsa.Ops(N, 1, func(i, _ int) {
tr.Delete(points[i*D+0:i*D+D], points[i*D+0:i*D+D], i)
})
if tr.Count() != 0 {
t.Fatalf("expected %d, got %d", N, tr.Count())
}
}
type tItem2 struct {
point [2]float64
}
func (item *tItem2) Point() (x, y float64) {
return item.point[0], item.point[1]
}
func (item *tItem2) Rect() (minX, minY, maxX, maxY float64) {
return item.point[0], item.point[1], item.point[0], item.point[1]
}
///////////////////////////////////////////////
// Old Tile38 Index < July 27, 2018
///////////////////////////////////////////////
// func TestBenchInsert2D_Old(t *testing.T) {
// // import "github.com/tidwall/tile38/pkg/index"
// N := 100000
// D := 2
// rand.Seed(time.Now().UnixNano())
// items := make([]*tItem2, N*D)
// for i := 0; i < N; i++ {
// items[i] = new(tItem2)
// for j := 0; j < D; j++ {
// items[i].point[j] = rand.Float64()*100 - 50
// }
// }
// tr := index.New()
// lotsa.Output = os.Stdout
// fmt.Printf("Insert(%dD): ", D)
// lotsa.Ops(N, 1, func(i, _ int) {
// tr.Insert(items[i])
// })
// fmt.Printf("Search(%dD): ", D)
// var count int
// lotsa.Ops(N, 1, func(i, _ int) {
// tr.Search(
// items[i].point[0], items[i].point[1],
// items[i].point[0], items[i].point[1],
// func(_ interface{}) bool {
// count++
// return true
// },
// )
// })
// if count != N {
// t.Fatalf("expected %d, got %d", N, count)
// }
// fmt.Printf("Delete(%dD): ", D)
// lotsa.Ops(N, 1, func(i, _ int) {
// tr.Remove(items[i])
// })
// if tr.Count() != 0 {
// t.Fatalf("expected %d, got %d", N, tr.Count())
// }
// }

707
vendor/github.com/tidwall/boxtree/d2/boxtree.go generated vendored Normal file
View File

@ -0,0 +1,707 @@
package d2
const dims = 2
const (
maxEntries = 16
minEntries = maxEntries * 40 / 100
)
type box struct {
data interface{}
min, max [dims]float64
}
type node struct {
count int
boxes [maxEntries + 1]box
}
// BoxTree ...
type BoxTree struct {
height int
root box
count int
reinsert []box
}
func (r *box) expand(b *box) {
for i := 0; i < dims; i++ {
if b.min[i] < r.min[i] {
r.min[i] = b.min[i]
}
if b.max[i] > r.max[i] {
r.max[i] = b.max[i]
}
}
}
func (r *box) area() float64 {
area := r.max[0] - r.min[0]
for i := 1; i < dims; i++ {
area *= r.max[i] - r.min[i]
}
return area
}
func (r *box) overlapArea(b *box) float64 {
area := 1.0
for i := 0; i < dims; i++ {
var max, min float64
if r.max[i] < b.max[i] {
max = r.max[i]
} else {
max = b.max[i]
}
if r.min[i] > b.min[i] {
min = r.min[i]
} else {
min = b.min[i]
}
if max > min {
area *= max - min
} else {
return 0
}
}
return area
}
func (r *box) enlargedArea(b *box) float64 {
area := 1.0
for i := 0; i < len(r.min); i++ {
if b.max[i] > r.max[i] {
if b.min[i] < r.min[i] {
area *= b.max[i] - b.min[i]
} else {
area *= b.max[i] - r.min[i]
}
} else {
if b.min[i] < r.min[i] {
area *= r.max[i] - b.min[i]
} else {
area *= r.max[i] - r.min[i]
}
}
}
return area
}
// Insert inserts an item into the RTree
func (tr *BoxTree) Insert(min, max []float64, value interface{}) {
var item box
fit(min, max, value, &item)
tr.insert(&item)
}
func (tr *BoxTree) insert(item *box) {
if tr.root.data == nil {
fit(item.min[:], item.max[:], new(node), &tr.root)
}
grown := tr.root.insert(item, tr.height)
if grown {
tr.root.expand(item)
}
if tr.root.data.(*node).count == maxEntries+1 {
newRoot := new(node)
tr.root.splitLargestAxisEdgeSnap(&newRoot.boxes[1])
newRoot.boxes[0] = tr.root
newRoot.count = 2
tr.root.data = newRoot
tr.root.recalc()
tr.height++
}
tr.count++
}
func (r *box) chooseLeastEnlargement(b *box) int {
j, jenlargement, jarea := -1, 0.0, 0.0
n := r.data.(*node)
for i := 0; i < n.count; i++ {
var area float64
if false {
area = n.boxes[i].area()
} else {
// force inline
area = n.boxes[i].max[0] - n.boxes[i].min[0]
for j := 1; j < dims; j++ {
area *= n.boxes[i].max[j] - n.boxes[i].min[j]
}
}
var enlargement float64
if false {
enlargement = n.boxes[i].enlargedArea(b) - area
} else {
// force inline
enlargedArea := 1.0
for j := 0; j < len(n.boxes[i].min); j++ {
if b.max[j] > n.boxes[i].max[j] {
if b.min[j] < n.boxes[i].min[j] {
enlargedArea *= b.max[j] - b.min[j]
} else {
enlargedArea *= b.max[j] - n.boxes[i].min[j]
}
} else {
if b.min[j] < n.boxes[i].min[j] {
enlargedArea *= n.boxes[i].max[j] - b.min[j]
} else {
enlargedArea *= n.boxes[i].max[j] - n.boxes[i].min[j]
}
}
}
enlargement = enlargedArea - area
}
if j == -1 || enlargement < jenlargement {
j, jenlargement, jarea = i, enlargement, area
} else if enlargement == jenlargement {
if area < jarea {
j, jenlargement, jarea = i, enlargement, area
}
}
}
return j
}
func (r *box) recalc() {
n := r.data.(*node)
r.min = n.boxes[0].min
r.max = n.boxes[0].max
for i := 1; i < n.count; i++ {
r.expand(&n.boxes[i])
}
}
// contains return struct when b is fully contained inside of n
func (r *box) contains(b *box) bool {
for i := 0; i < dims; i++ {
if b.min[i] < r.min[i] || b.max[i] > r.max[i] {
return false
}
}
return true
}
func (r *box) largestAxis() (axis int, size float64) {
j, jsz := 0, 0.0
for i := 0; i < dims; i++ {
sz := r.max[i] - r.min[i]
if i == 0 || sz > jsz {
j, jsz = i, sz
}
}
return j, jsz
}
func (r *box) splitLargestAxisEdgeSnap(right *box) {
axis, _ := r.largestAxis()
left := r
leftNode := left.data.(*node)
rightNode := new(node)
right.data = rightNode
var equals []box
for i := 0; i < leftNode.count; i++ {
minDist := leftNode.boxes[i].min[axis] - left.min[axis]
maxDist := left.max[axis] - leftNode.boxes[i].max[axis]
if minDist < maxDist {
// stay left
} else {
if minDist > maxDist {
// move to right
rightNode.boxes[rightNode.count] = leftNode.boxes[i]
rightNode.count++
} else {
// move to equals, at the end of the left array
equals = append(equals, leftNode.boxes[i])
}
leftNode.boxes[i] = leftNode.boxes[leftNode.count-1]
leftNode.boxes[leftNode.count-1].data = nil
leftNode.count--
i--
}
}
for _, b := range equals {
if leftNode.count < rightNode.count {
leftNode.boxes[leftNode.count] = b
leftNode.count++
} else {
rightNode.boxes[rightNode.count] = b
rightNode.count++
}
}
left.recalc()
right.recalc()
}
func (r *box) insert(item *box, height int) (grown bool) {
n := r.data.(*node)
if height == 0 {
n.boxes[n.count] = *item
n.count++
grown = !r.contains(item)
return grown
}
// choose subtree
index := r.chooseLeastEnlargement(item)
child := &n.boxes[index]
grown = child.insert(item, height-1)
if grown {
child.expand(item)
grown = !r.contains(item)
}
if child.data.(*node).count == maxEntries+1 {
child.splitLargestAxisEdgeSnap(&n.boxes[n.count])
n.count++
}
return grown
}
// fit an external item into a box type
func fit(min, max []float64, value interface{}, target *box) {
if max == nil {
max = min
}
if len(min) != len(max) {
panic("min/max dimension mismatch")
}
if len(min) != dims {
panic("invalid number of dimensions")
}
for i := 0; i < dims; i++ {
target.min[i] = min[i]
target.max[i] = max[i]
}
target.data = value
}
type overlapsResult int
const (
not overlapsResult = iota
intersects
contains
)
// overlaps detects if r insersects or contains b.
// return not, intersects, contains
func (r *box) overlaps(b *box) overlapsResult {
for i := 0; i < dims; i++ {
if b.min[i] > r.max[i] || b.max[i] < r.min[i] {
return not
}
if r.min[i] > b.min[i] || b.max[i] > r.max[i] {
i++
for ; i < dims; i++ {
if b.min[i] > r.max[i] || b.max[i] < r.min[i] {
return not
}
}
return intersects
}
}
return contains
}
// contains return struct when b is fully contained inside of n
func (r *box) intersects(b *box) bool {
for i := 0; i < dims; i++ {
if b.min[i] > r.max[i] || b.max[i] < r.min[i] {
return false
}
}
return true
}
func (r *box) search(
target *box, height int,
iter func(min, max []float64, value interface{}) bool,
) bool {
n := r.data.(*node)
if height == 0 {
for i := 0; i < n.count; i++ {
if target.intersects(&n.boxes[i]) {
if !iter(n.boxes[i].min[:], n.boxes[i].max[:],
n.boxes[i].data) {
return false
}
}
}
} else {
for i := 0; i < n.count; i++ {
switch target.overlaps(&n.boxes[i]) {
case intersects:
if !n.boxes[i].search(target, height-1, iter) {
return false
}
case contains:
if !n.boxes[i].scan(target, height-1, iter) {
return false
}
}
}
}
return true
}
func (tr *BoxTree) search(
target *box,
iter func(min, max []float64, value interface{}) bool,
) {
if tr.root.data == nil {
return
}
res := target.overlaps(&tr.root)
if res == intersects {
tr.root.search(target, tr.height, iter)
} else if res == contains {
tr.root.scan(target, tr.height, iter)
}
}
// Search ...
func (tr *BoxTree) Search(min, max []float64,
iter func(min, max []float64, value interface{}) bool,
) {
var target box
fit(min, max, nil, &target)
tr.search(&target, iter)
}
const (
// Continue to first child box and/or next sibling.
Continue = iota
// Ignore child boxes but continue to next sibling.
Ignore
// Stop iterating
Stop
)
// Traverse iterates through all items and container boxes in tree.
func (tr *BoxTree) Traverse(
iter func(min, max []float64, height, level int, value interface{}) int,
) {
if tr.root.data == nil {
return
}
if iter(tr.root.min[:], tr.root.max[:], tr.height+1, 0, nil) == Continue {
tr.root.traverse(tr.height, 1, iter)
}
}
func (r *box) traverse(
height, level int,
iter func(min, max []float64, height, level int, value interface{}) int,
) int {
n := r.data.(*node)
if height == 0 {
for i := 0; i < n.count; i++ {
action := iter(n.boxes[i].min[:], n.boxes[i].max[:], height, level,
n.boxes[i].data)
if action == Stop {
return Stop
}
}
} else {
for i := 0; i < n.count; i++ {
switch iter(n.boxes[i].min[:], n.boxes[i].max[:], height, level,
n.boxes[i].data) {
case Ignore:
case Continue:
if n.boxes[i].traverse(height-1, level+1, iter) == Stop {
return Stop
}
case Stop:
return Stop
}
}
}
return Continue
}
func (r *box) scan(
target *box, height int,
iter func(min, max []float64, value interface{}) bool,
) bool {
n := r.data.(*node)
if height == 0 {
for i := 0; i < n.count; i++ {
if !iter(n.boxes[i].min[:], n.boxes[i].max[:], n.boxes[i].data) {
return false
}
}
} else {
for i := 0; i < n.count; i++ {
if !n.boxes[i].scan(target, height-1, iter) {
return false
}
}
}
return true
}
// Scan iterates through all items in tree.
func (tr *BoxTree) Scan(iter func(min, max []float64, value interface{}) bool) {
if tr.root.data == nil {
return
}
tr.root.scan(nil, tr.height, iter)
}
// Delete ...
func (tr *BoxTree) Delete(min, max []float64, value interface{}) {
var item box
fit(min, max, value, &item)
if tr.root.data == nil || !tr.root.contains(&item) {
return
}
var removed, recalced bool
removed, recalced, tr.reinsert =
tr.root.delete(&item, tr.height, tr.reinsert[:0])
if !removed {
return
}
tr.count -= len(tr.reinsert) + 1
if tr.count == 0 {
tr.root = box{}
recalced = false
} else {
for tr.height > 0 && tr.root.data.(*node).count == 1 {
tr.root = tr.root.data.(*node).boxes[0]
tr.height--
tr.root.recalc()
}
}
if recalced {
tr.root.recalc()
}
for i := range tr.reinsert {
tr.insert(&tr.reinsert[i])
tr.reinsert[i].data = nil
}
}
func (r *box) delete(item *box, height int, reinsert []box) (
removed, recalced bool, reinsertOut []box,
) {
n := r.data.(*node)
if height == 0 {
for i := 0; i < n.count; i++ {
if n.boxes[i].data == item.data {
// found the target item to delete
recalced = r.onEdge(&n.boxes[i])
n.boxes[i] = n.boxes[n.count-1]
n.boxes[n.count-1].data = nil
n.count--
if recalced {
r.recalc()
}
return true, recalced, reinsert
}
}
} else {
for i := 0; i < n.count; i++ {
if !n.boxes[i].contains(item) {
continue
}
removed, recalced, reinsert =
n.boxes[i].delete(item, height-1, reinsert)
if !removed {
continue
}
if n.boxes[i].data.(*node).count < minEntries {
// underflow
if !recalced {
recalced = r.onEdge(&n.boxes[i])
}
reinsert = n.boxes[i].flatten(reinsert, height-1)
n.boxes[i] = n.boxes[n.count-1]
n.boxes[n.count-1].data = nil
n.count--
}
if recalced {
r.recalc()
}
return removed, recalced, reinsert
}
}
return false, false, reinsert
}
// flatten flattens all leaf boxes into a single list
func (r *box) flatten(all []box, height int) []box {
n := r.data.(*node)
if height == 0 {
all = append(all, n.boxes[:n.count]...)
} else {
for i := 0; i < n.count; i++ {
all = n.boxes[i].flatten(all, height-1)
}
}
return all
}
// onedge returns true when b is on the edge of r
func (r *box) onEdge(b *box) bool {
for i := 0; i < dims; i++ {
if r.min[i] == b.min[i] || r.max[i] == b.max[i] {
return true
}
}
return false
}
// Count ...
func (tr *BoxTree) Count() int {
return tr.count
}
func (r *box) totalOverlapArea(height int) float64 {
var area float64
n := r.data.(*node)
for i := 0; i < n.count; i++ {
for j := i + 1; j < n.count; j++ {
area += n.boxes[i].overlapArea(&n.boxes[j])
}
}
if height > 0 {
for i := 0; i < n.count; i++ {
area += n.boxes[i].totalOverlapArea(height - 1)
}
}
return area
}
// TotalOverlapArea ...
func (tr *BoxTree) TotalOverlapArea() float64 {
if tr.root.data == nil {
return 0
}
return tr.root.totalOverlapArea(tr.height)
}
type qnode struct {
dist float64
box box
}
type queue struct {
nodes []qnode
len int
size int
}
func (q *queue) push(dist float64, box box) {
if q.nodes == nil {
q.nodes = make([]qnode, 2)
} else {
q.nodes = append(q.nodes, qnode{})
}
i := q.len + 1
j := i / 2
for i > 1 && q.nodes[j].dist > dist {
q.nodes[i] = q.nodes[j]
i = j
j = j / 2
}
q.nodes[i].dist = dist
q.nodes[i].box = box
q.len++
}
func (q *queue) peek() qnode {
if q.len == 0 {
return qnode{}
}
return q.nodes[1]
}
func (q *queue) pop() qnode {
if q.len == 0 {
return qnode{}
}
n := q.nodes[1]
q.nodes[1] = q.nodes[q.len]
q.len--
var j, k int
i := 1
for i != q.len+1 {
k = q.len + 1
j = 2 * i
if j <= q.len && q.nodes[j].dist < q.nodes[k].dist {
k = j
}
if j+1 <= q.len && q.nodes[j+1].dist < q.nodes[k].dist {
k = j + 1
}
q.nodes[i] = q.nodes[k]
i = k
}
return n
}
// Nearby returns items nearest to farthest.
// The dist param is the "box distance".
func (tr *BoxTree) Nearby(min, max []float64,
iter func(min, max []float64, item interface{}) bool) {
if tr.root.data == nil {
return
}
var bbox box
fit(min, max, nil, &bbox)
box := tr.root
var q queue
for {
n := box.data.(*node)
for i := 0; i < n.count; i++ {
dist := boxDist(&bbox, &n.boxes[i])
q.push(dist, n.boxes[i])
}
for q.len > 0 {
if _, ok := q.peek().box.data.(*node); ok {
break
}
item := q.pop()
if !iter(item.box.min[:], item.box.max[:], item.box.data) {
return
}
}
if q.len == 0 {
break
} else {
box = q.pop().box
}
}
return
}
func boxDist(a, b *box) float64 {
var dist float64
for i := 0; i < len(a.min); i++ {
var min, max float64
if a.min[i] > b.min[i] {
min = a.min[i]
} else {
min = b.min[i]
}
if a.max[i] < b.max[i] {
max = a.max[i]
} else {
max = b.max[i]
}
squared := min - max
if squared > 0 {
dist += squared * squared
}
}
return dist
}
// Bounds returns the minimum bounding box
func (tr *BoxTree) Bounds() (min, max []float64) {
if tr.root.data == nil {
return
}
return tr.root.min[:], tr.root.max[:]
}

379
vendor/github.com/tidwall/boxtree/d2/boxtree_test.go generated vendored Normal file
View File

@ -0,0 +1,379 @@
package d2
import (
"fmt"
"math/rand"
"sort"
"strconv"
"strings"
"testing"
"time"
)
type tBox struct {
min [dims]float64
max [dims]float64
}
var boxes []tBox
var points []tBox
func init() {
seed := time.Now().UnixNano()
// seed = 1532132365683340889
println("seed:", seed)
rand.Seed(seed)
}
func randPoints(N int) []tBox {
boxes := make([]tBox, N)
for i := 0; i < N; i++ {
boxes[i].min[0] = rand.Float64()*360 - 180
boxes[i].min[1] = rand.Float64()*180 - 90
for j := 2; j < dims; j++ {
boxes[i].min[j] = rand.Float64()
}
boxes[i].max = boxes[i].min
}
return boxes
}
func randBoxes(N int) []tBox {
boxes := make([]tBox, N)
for i := 0; i < N; i++ {
boxes[i].min[0] = rand.Float64()*360 - 180
boxes[i].min[1] = rand.Float64()*180 - 90
for j := 2; j < dims; j++ {
boxes[i].min[j] = rand.Float64() * 100
}
boxes[i].max[0] = boxes[i].min[0] + rand.Float64()
boxes[i].max[1] = boxes[i].min[1] + rand.Float64()
for j := 2; j < dims; j++ {
boxes[i].max[j] = boxes[i].min[j] + rand.Float64()
}
if boxes[i].max[0] > 180 || boxes[i].max[1] > 90 {
i--
}
}
return boxes
}
func sortBoxes(boxes []tBox) {
sort.Slice(boxes, func(i, j int) bool {
for k := 0; k < len(boxes[i].min); k++ {
if boxes[i].min[k] < boxes[j].min[k] {
return true
}
if boxes[i].min[k] > boxes[j].min[k] {
return false
}
if boxes[i].max[k] < boxes[j].max[k] {
return true
}
if boxes[i].max[k] > boxes[j].max[k] {
return false
}
}
return i < j
})
}
func sortBoxesNearby(boxes []tBox, min, max []float64) {
sort.Slice(boxes, func(i, j int) bool {
return testBoxDist(boxes[i].min[:], boxes[i].max[:], min, max) <
testBoxDist(boxes[j].min[:], boxes[j].max[:], min, max)
})
}
func testBoxDist(amin, amax, bmin, bmax []float64) float64 {
var dist float64
for i := 0; i < len(amin); i++ {
var min, max float64
if amin[i] > bmin[i] {
min = amin[i]
} else {
min = bmin[i]
}
if amax[i] < bmax[i] {
max = amax[i]
} else {
max = bmax[i]
}
squared := min - max
if squared > 0 {
dist += squared * squared
}
}
return dist
}
func testBoxesVarious(t *testing.T, boxes []tBox, label string) {
N := len(boxes)
var tr BoxTree
// N := 10000
// boxes := randPoints(N)
/////////////////////////////////////////
// insert
/////////////////////////////////////////
for i := 0; i < N; i++ {
tr.Insert(boxes[i].min[:], boxes[i].max[:], boxes[i])
}
if tr.Count() != N {
t.Fatalf("expected %d, got %d", N, tr.Count())
}
// area := tr.TotalOverlapArea()
// fmt.Printf("overlap: %.0f, %.1f/item\n", area, area/float64(N))
// ioutil.WriteFile(label+".svg", []byte(rtreetools.SVG(&tr)), 0600)
/////////////////////////////////////////
// scan all items and count one-by-one
/////////////////////////////////////////
var count int
tr.Scan(func(min, max []float64, value interface{}) bool {
count++
return true
})
if count != N {
t.Fatalf("expected %d, got %d", N, count)
}
/////////////////////////////////////////
// check every point for correctness
/////////////////////////////////////////
var tboxes1 []tBox
tr.Scan(func(min, max []float64, value interface{}) bool {
tboxes1 = append(tboxes1, value.(tBox))
return true
})
tboxes2 := make([]tBox, len(boxes))
copy(tboxes2, boxes)
sortBoxes(tboxes1)
sortBoxes(tboxes2)
for i := 0; i < len(tboxes1); i++ {
if tboxes1[i] != tboxes2[i] {
t.Fatalf("expected '%v', got '%v'", tboxes2[i], tboxes1[i])
}
}
/////////////////////////////////////////
// search for each item one-by-one
/////////////////////////////////////////
for i := 0; i < N; i++ {
var found bool
tr.Search(boxes[i].min[:], boxes[i].max[:],
func(min, max []float64, value interface{}) bool {
if value == boxes[i] {
found = true
return false
}
return true
})
if !found {
t.Fatalf("did not find item %d", i)
}
}
centerMin, centerMax := []float64{-18, -9}, []float64{18, 9}
for j := 2; j < dims; j++ {
centerMin = append(centerMin, -10)
centerMax = append(centerMax, 10)
}
/////////////////////////////////////////
// search for 10% of the items
/////////////////////////////////////////
for i := 0; i < N/5; i++ {
var count int
tr.Search(centerMin, centerMax,
func(min, max []float64, value interface{}) bool {
count++
return true
},
)
}
/////////////////////////////////////////
// delete every other item
/////////////////////////////////////////
for i := 0; i < N/2; i++ {
j := i * 2
tr.Delete(boxes[j].min[:], boxes[j].max[:], boxes[j])
}
/////////////////////////////////////////
// count all items. should be half of N
/////////////////////////////////////////
count = 0
tr.Scan(func(min, max []float64, value interface{}) bool {
count++
return true
})
if count != N/2 {
t.Fatalf("expected %d, got %d", N/2, count)
}
///////////////////////////////////////////////////
// reinsert every other item, but in random order
///////////////////////////////////////////////////
var ij []int
for i := 0; i < N/2; i++ {
j := i * 2
ij = append(ij, j)
}
rand.Shuffle(len(ij), func(i, j int) {
ij[i], ij[j] = ij[j], ij[i]
})
for i := 0; i < N/2; i++ {
j := ij[i]
tr.Insert(boxes[j].min[:], boxes[j].max[:], boxes[j])
}
//////////////////////////////////////////////////////
// replace each item with an item that is very close
//////////////////////////////////////////////////////
var nboxes = make([]tBox, N)
for i := 0; i < N; i++ {
for j := 0; j < len(boxes[i].min); j++ {
nboxes[i].min[j] = boxes[i].min[j] + (rand.Float64() - 0.5)
if boxes[i].min == boxes[i].max {
nboxes[i].max[j] = nboxes[i].min[j]
} else {
nboxes[i].max[j] = boxes[i].max[j] + (rand.Float64() - 0.5)
}
}
}
for i := 0; i < N; i++ {
tr.Insert(nboxes[i].min[:], nboxes[i].max[:], nboxes[i])
tr.Delete(boxes[i].min[:], boxes[i].max[:], boxes[i])
}
if tr.Count() != N {
t.Fatalf("expected %d, got %d", N, tr.Count())
}
// area = tr.TotalOverlapArea()
// fmt.Fprintf(wr, "overlap: %.0f, %.1f/item\n", area, area/float64(N))
/////////////////////////////////////////
// check every point for correctness
/////////////////////////////////////////
tboxes1 = nil
tr.Scan(func(min, max []float64, value interface{}) bool {
tboxes1 = append(tboxes1, value.(tBox))
return true
})
tboxes2 = make([]tBox, len(nboxes))
copy(tboxes2, nboxes)
sortBoxes(tboxes1)
sortBoxes(tboxes2)
for i := 0; i < len(tboxes1); i++ {
if tboxes1[i] != tboxes2[i] {
t.Fatalf("expected '%v', got '%v'", tboxes2[i], tboxes1[i])
}
}
/////////////////////////////////////////
// search for 10% of the items
/////////////////////////////////////////
for i := 0; i < N/5; i++ {
var count int
tr.Search(centerMin, centerMax,
func(min, max []float64, value interface{}) bool {
count++
return true
},
)
}
var boxes3 []tBox
tr.Nearby(centerMin, centerMax,
func(min, max []float64, value interface{}) bool {
boxes3 = append(boxes3, value.(tBox))
return true
},
)
if len(boxes3) != len(nboxes) {
t.Fatalf("expected %d, got %d", len(nboxes), len(boxes3))
}
if len(boxes3) != tr.Count() {
t.Fatalf("expected %d, got %d", tr.Count(), len(boxes3))
}
var ldist float64
for i, box := range boxes3 {
dist := testBoxDist(box.min[:], box.max[:], centerMin, centerMax)
if i > 0 && dist < ldist {
t.Fatalf("out of order")
}
ldist = dist
}
}
func TestRandomBoxes(t *testing.T) {
testBoxesVarious(t, randBoxes(10000), "boxes")
}
func TestRandomPoints(t *testing.T) {
testBoxesVarious(t, randPoints(10000), "points")
}
func (r *box) boxstr() string {
var b []byte
b = append(b, '[', '[')
for i := 0; i < len(r.min); i++ {
if i != 0 {
b = append(b, ' ')
}
b = strconv.AppendFloat(b, r.min[i], 'f', -1, 64)
}
b = append(b, ']', '[')
for i := 0; i < len(r.max); i++ {
if i != 0 {
b = append(b, ' ')
}
b = strconv.AppendFloat(b, r.max[i], 'f', -1, 64)
}
b = append(b, ']', ']')
return string(b)
}
func (r *box) print(height, indent int) {
fmt.Printf("%s%s", strings.Repeat(" ", indent), r.boxstr())
if height == 0 {
fmt.Printf("\t'%v'\n", r.data)
} else {
fmt.Printf("\n")
for i := 0; i < r.data.(*node).count; i++ {
r.data.(*node).boxes[i].print(height-1, indent+1)
}
}
}
func (tr BoxTree) print() {
if tr.root.data == nil {
println("EMPTY TREE")
return
}
tr.root.print(tr.height+1, 0)
}
func TestZeroPoints(t *testing.T) {
N := 10000
var tr BoxTree
pt := make([]float64, dims)
for i := 0; i < N; i++ {
tr.Insert(pt, nil, i)
}
}
func BenchmarkRandomInsert(b *testing.B) {
var tr BoxTree
boxes := randBoxes(b.N)
b.ResetTimer()
for i := 0; i < b.N; i++ {
tr.Insert(boxes[i].min[:], boxes[i].max[:], i)
}
}

707
vendor/github.com/tidwall/boxtree/d3/boxtree.go generated vendored Normal file
View File

@ -0,0 +1,707 @@
package d3
const dims = 3
const (
maxEntries = 16
minEntries = maxEntries * 40 / 100
)
type box struct {
data interface{}
min, max [dims]float64
}
type node struct {
count int
boxes [maxEntries + 1]box
}
// BoxTree ...
type BoxTree struct {
height int
root box
count int
reinsert []box
}
func (r *box) expand(b *box) {
for i := 0; i < dims; i++ {
if b.min[i] < r.min[i] {
r.min[i] = b.min[i]
}
if b.max[i] > r.max[i] {
r.max[i] = b.max[i]
}
}
}
func (r *box) area() float64 {
area := r.max[0] - r.min[0]
for i := 1; i < dims; i++ {
area *= r.max[i] - r.min[i]
}
return area
}
func (r *box) overlapArea(b *box) float64 {
area := 1.0
for i := 0; i < dims; i++ {
var max, min float64
if r.max[i] < b.max[i] {
max = r.max[i]
} else {
max = b.max[i]
}
if r.min[i] > b.min[i] {
min = r.min[i]
} else {
min = b.min[i]
}
if max > min {
area *= max - min
} else {
return 0
}
}
return area
}
func (r *box) enlargedArea(b *box) float64 {
area := 1.0
for i := 0; i < len(r.min); i++ {
if b.max[i] > r.max[i] {
if b.min[i] < r.min[i] {
area *= b.max[i] - b.min[i]
} else {
area *= b.max[i] - r.min[i]
}
} else {
if b.min[i] < r.min[i] {
area *= r.max[i] - b.min[i]
} else {
area *= r.max[i] - r.min[i]
}
}
}
return area
}
// Insert inserts an item into the RTree
func (tr *BoxTree) Insert(min, max []float64, value interface{}) {
var item box
fit(min, max, value, &item)
tr.insert(&item)
}
func (tr *BoxTree) insert(item *box) {
if tr.root.data == nil {
fit(item.min[:], item.max[:], new(node), &tr.root)
}
grown := tr.root.insert(item, tr.height)
if grown {
tr.root.expand(item)
}
if tr.root.data.(*node).count == maxEntries+1 {
newRoot := new(node)
tr.root.splitLargestAxisEdgeSnap(&newRoot.boxes[1])
newRoot.boxes[0] = tr.root
newRoot.count = 2
tr.root.data = newRoot
tr.root.recalc()
tr.height++
}
tr.count++
}
func (r *box) chooseLeastEnlargement(b *box) int {
j, jenlargement, jarea := -1, 0.0, 0.0
n := r.data.(*node)
for i := 0; i < n.count; i++ {
var area float64
if false {
area = n.boxes[i].area()
} else {
// force inline
area = n.boxes[i].max[0] - n.boxes[i].min[0]
for j := 1; j < dims; j++ {
area *= n.boxes[i].max[j] - n.boxes[i].min[j]
}
}
var enlargement float64
if false {
enlargement = n.boxes[i].enlargedArea(b) - area
} else {
// force inline
enlargedArea := 1.0
for j := 0; j < len(n.boxes[i].min); j++ {
if b.max[j] > n.boxes[i].max[j] {
if b.min[j] < n.boxes[i].min[j] {
enlargedArea *= b.max[j] - b.min[j]
} else {
enlargedArea *= b.max[j] - n.boxes[i].min[j]
}
} else {
if b.min[j] < n.boxes[i].min[j] {
enlargedArea *= n.boxes[i].max[j] - b.min[j]
} else {
enlargedArea *= n.boxes[i].max[j] - n.boxes[i].min[j]
}
}
}
enlargement = enlargedArea - area
}
if j == -1 || enlargement < jenlargement {
j, jenlargement, jarea = i, enlargement, area
} else if enlargement == jenlargement {
if area < jarea {
j, jenlargement, jarea = i, enlargement, area
}
}
}
return j
}
func (r *box) recalc() {
n := r.data.(*node)
r.min = n.boxes[0].min
r.max = n.boxes[0].max
for i := 1; i < n.count; i++ {
r.expand(&n.boxes[i])
}
}
// contains return struct when b is fully contained inside of n
func (r *box) contains(b *box) bool {
for i := 0; i < dims; i++ {
if b.min[i] < r.min[i] || b.max[i] > r.max[i] {
return false
}
}
return true
}
func (r *box) largestAxis() (axis int, size float64) {
j, jsz := 0, 0.0
for i := 0; i < dims; i++ {
sz := r.max[i] - r.min[i]
if i == 0 || sz > jsz {
j, jsz = i, sz
}
}
return j, jsz
}
func (r *box) splitLargestAxisEdgeSnap(right *box) {
axis, _ := r.largestAxis()
left := r
leftNode := left.data.(*node)
rightNode := new(node)
right.data = rightNode
var equals []box
for i := 0; i < leftNode.count; i++ {
minDist := leftNode.boxes[i].min[axis] - left.min[axis]
maxDist := left.max[axis] - leftNode.boxes[i].max[axis]
if minDist < maxDist {
// stay left
} else {
if minDist > maxDist {
// move to right
rightNode.boxes[rightNode.count] = leftNode.boxes[i]
rightNode.count++
} else {
// move to equals, at the end of the left array
equals = append(equals, leftNode.boxes[i])
}
leftNode.boxes[i] = leftNode.boxes[leftNode.count-1]
leftNode.boxes[leftNode.count-1].data = nil
leftNode.count--
i--
}
}
for _, b := range equals {
if leftNode.count < rightNode.count {
leftNode.boxes[leftNode.count] = b
leftNode.count++
} else {
rightNode.boxes[rightNode.count] = b
rightNode.count++
}
}
left.recalc()
right.recalc()
}
func (r *box) insert(item *box, height int) (grown bool) {
n := r.data.(*node)
if height == 0 {
n.boxes[n.count] = *item
n.count++
grown = !r.contains(item)
return grown
}
// choose subtree
index := r.chooseLeastEnlargement(item)
child := &n.boxes[index]
grown = child.insert(item, height-1)
if grown {
child.expand(item)
grown = !r.contains(item)
}
if child.data.(*node).count == maxEntries+1 {
child.splitLargestAxisEdgeSnap(&n.boxes[n.count])
n.count++
}
return grown
}
// fit an external item into a box type
func fit(min, max []float64, value interface{}, target *box) {
if max == nil {
max = min
}
if len(min) != len(max) {
panic("min/max dimension mismatch")
}
if len(min) != dims {
panic("invalid number of dimensions")
}
for i := 0; i < dims; i++ {
target.min[i] = min[i]
target.max[i] = max[i]
}
target.data = value
}
type overlapsResult int
const (
not overlapsResult = iota
intersects
contains
)
// overlaps detects if r insersects or contains b.
// return not, intersects, contains
func (r *box) overlaps(b *box) overlapsResult {
for i := 0; i < dims; i++ {
if b.min[i] > r.max[i] || b.max[i] < r.min[i] {
return not
}
if r.min[i] > b.min[i] || b.max[i] > r.max[i] {
i++
for ; i < dims; i++ {
if b.min[i] > r.max[i] || b.max[i] < r.min[i] {
return not
}
}
return intersects
}
}
return contains
}
// contains return struct when b is fully contained inside of n
func (r *box) intersects(b *box) bool {
for i := 0; i < dims; i++ {
if b.min[i] > r.max[i] || b.max[i] < r.min[i] {
return false
}
}
return true
}
func (r *box) search(
target *box, height int,
iter func(min, max []float64, value interface{}) bool,
) bool {
n := r.data.(*node)
if height == 0 {
for i := 0; i < n.count; i++ {
if target.intersects(&n.boxes[i]) {
if !iter(n.boxes[i].min[:], n.boxes[i].max[:],
n.boxes[i].data) {
return false
}
}
}
} else {
for i := 0; i < n.count; i++ {
switch target.overlaps(&n.boxes[i]) {
case intersects:
if !n.boxes[i].search(target, height-1, iter) {
return false
}
case contains:
if !n.boxes[i].scan(target, height-1, iter) {
return false
}
}
}
}
return true
}
func (tr *BoxTree) search(
target *box,
iter func(min, max []float64, value interface{}) bool,
) {
if tr.root.data == nil {
return
}
res := target.overlaps(&tr.root)
if res == intersects {
tr.root.search(target, tr.height, iter)
} else if res == contains {
tr.root.scan(target, tr.height, iter)
}
}
// Search ...
func (tr *BoxTree) Search(min, max []float64,
iter func(min, max []float64, value interface{}) bool,
) {
var target box
fit(min, max, nil, &target)
tr.search(&target, iter)
}
const (
// Continue to first child box and/or next sibling.
Continue = iota
// Ignore child boxes but continue to next sibling.
Ignore
// Stop iterating
Stop
)
// Traverse iterates through all items and container boxes in tree.
func (tr *BoxTree) Traverse(
iter func(min, max []float64, height, level int, value interface{}) int,
) {
if tr.root.data == nil {
return
}
if iter(tr.root.min[:], tr.root.max[:], tr.height+1, 0, nil) == Continue {
tr.root.traverse(tr.height, 1, iter)
}
}
func (r *box) traverse(
height, level int,
iter func(min, max []float64, height, level int, value interface{}) int,
) int {
n := r.data.(*node)
if height == 0 {
for i := 0; i < n.count; i++ {
action := iter(n.boxes[i].min[:], n.boxes[i].max[:], height, level,
n.boxes[i].data)
if action == Stop {
return Stop
}
}
} else {
for i := 0; i < n.count; i++ {
switch iter(n.boxes[i].min[:], n.boxes[i].max[:], height, level,
n.boxes[i].data) {
case Ignore:
case Continue:
if n.boxes[i].traverse(height-1, level+1, iter) == Stop {
return Stop
}
case Stop:
return Stop
}
}
}
return Continue
}
func (r *box) scan(
target *box, height int,
iter func(min, max []float64, value interface{}) bool,
) bool {
n := r.data.(*node)
if height == 0 {
for i := 0; i < n.count; i++ {
if !iter(n.boxes[i].min[:], n.boxes[i].max[:], n.boxes[i].data) {
return false
}
}
} else {
for i := 0; i < n.count; i++ {
if !n.boxes[i].scan(target, height-1, iter) {
return false
}
}
}
return true
}
// Scan iterates through all items in tree.
func (tr *BoxTree) Scan(iter func(min, max []float64, value interface{}) bool) {
if tr.root.data == nil {
return
}
tr.root.scan(nil, tr.height, iter)
}
// Delete ...
func (tr *BoxTree) Delete(min, max []float64, value interface{}) {
var item box
fit(min, max, value, &item)
if tr.root.data == nil || !tr.root.contains(&item) {
return
}
var removed, recalced bool
removed, recalced, tr.reinsert =
tr.root.delete(&item, tr.height, tr.reinsert[:0])
if !removed {
return
}
tr.count -= len(tr.reinsert) + 1
if tr.count == 0 {
tr.root = box{}
recalced = false
} else {
for tr.height > 0 && tr.root.data.(*node).count == 1 {
tr.root = tr.root.data.(*node).boxes[0]
tr.height--
tr.root.recalc()
}
}
if recalced {
tr.root.recalc()
}
for i := range tr.reinsert {
tr.insert(&tr.reinsert[i])
tr.reinsert[i].data = nil
}
}
func (r *box) delete(item *box, height int, reinsert []box) (
removed, recalced bool, reinsertOut []box,
) {
n := r.data.(*node)
if height == 0 {
for i := 0; i < n.count; i++ {
if n.boxes[i].data == item.data {
// found the target item to delete
recalced = r.onEdge(&n.boxes[i])
n.boxes[i] = n.boxes[n.count-1]
n.boxes[n.count-1].data = nil
n.count--
if recalced {
r.recalc()
}
return true, recalced, reinsert
}
}
} else {
for i := 0; i < n.count; i++ {
if !n.boxes[i].contains(item) {
continue
}
removed, recalced, reinsert =
n.boxes[i].delete(item, height-1, reinsert)
if !removed {
continue
}
if n.boxes[i].data.(*node).count < minEntries {
// underflow
if !recalced {
recalced = r.onEdge(&n.boxes[i])
}
reinsert = n.boxes[i].flatten(reinsert, height-1)
n.boxes[i] = n.boxes[n.count-1]
n.boxes[n.count-1].data = nil
n.count--
}
if recalced {
r.recalc()
}
return removed, recalced, reinsert
}
}
return false, false, reinsert
}
// flatten flattens all leaf boxes into a single list
func (r *box) flatten(all []box, height int) []box {
n := r.data.(*node)
if height == 0 {
all = append(all, n.boxes[:n.count]...)
} else {
for i := 0; i < n.count; i++ {
all = n.boxes[i].flatten(all, height-1)
}
}
return all
}
// onedge returns true when b is on the edge of r
func (r *box) onEdge(b *box) bool {
for i := 0; i < dims; i++ {
if r.min[i] == b.min[i] || r.max[i] == b.max[i] {
return true
}
}
return false
}
// Count ...
func (tr *BoxTree) Count() int {
return tr.count
}
func (r *box) totalOverlapArea(height int) float64 {
var area float64
n := r.data.(*node)
for i := 0; i < n.count; i++ {
for j := i + 1; j < n.count; j++ {
area += n.boxes[i].overlapArea(&n.boxes[j])
}
}
if height > 0 {
for i := 0; i < n.count; i++ {
area += n.boxes[i].totalOverlapArea(height - 1)
}
}
return area
}
// TotalOverlapArea ...
func (tr *BoxTree) TotalOverlapArea() float64 {
if tr.root.data == nil {
return 0
}
return tr.root.totalOverlapArea(tr.height)
}
type qnode struct {
dist float64
box box
}
type queue struct {
nodes []qnode
len int
size int
}
func (q *queue) push(dist float64, box box) {
if q.nodes == nil {
q.nodes = make([]qnode, 2)
} else {
q.nodes = append(q.nodes, qnode{})
}
i := q.len + 1
j := i / 2
for i > 1 && q.nodes[j].dist > dist {
q.nodes[i] = q.nodes[j]
i = j
j = j / 2
}
q.nodes[i].dist = dist
q.nodes[i].box = box
q.len++
}
func (q *queue) peek() qnode {
if q.len == 0 {
return qnode{}
}
return q.nodes[1]
}
func (q *queue) pop() qnode {
if q.len == 0 {
return qnode{}
}
n := q.nodes[1]
q.nodes[1] = q.nodes[q.len]
q.len--
var j, k int
i := 1
for i != q.len+1 {
k = q.len + 1
j = 2 * i
if j <= q.len && q.nodes[j].dist < q.nodes[k].dist {
k = j
}
if j+1 <= q.len && q.nodes[j+1].dist < q.nodes[k].dist {
k = j + 1
}
q.nodes[i] = q.nodes[k]
i = k
}
return n
}
// Nearby returns items nearest to farthest.
// The dist param is the "box distance".
func (tr *BoxTree) Nearby(min, max []float64,
iter func(min, max []float64, item interface{}) bool) {
if tr.root.data == nil {
return
}
var bbox box
fit(min, max, nil, &bbox)
box := tr.root
var q queue
for {
n := box.data.(*node)
for i := 0; i < n.count; i++ {
dist := boxDist(&bbox, &n.boxes[i])
q.push(dist, n.boxes[i])
}
for q.len > 0 {
if _, ok := q.peek().box.data.(*node); ok {
break
}
item := q.pop()
if !iter(item.box.min[:], item.box.max[:], item.box.data) {
return
}
}
if q.len == 0 {
break
} else {
box = q.pop().box
}
}
return
}
func boxDist(a, b *box) float64 {
var dist float64
for i := 0; i < len(a.min); i++ {
var min, max float64
if a.min[i] > b.min[i] {
min = a.min[i]
} else {
min = b.min[i]
}
if a.max[i] < b.max[i] {
max = a.max[i]
} else {
max = b.max[i]
}
squared := min - max
if squared > 0 {
dist += squared * squared
}
}
return dist
}
// Bounds returns the minimum bounding box
func (tr *BoxTree) Bounds() (min, max []float64) {
if tr.root.data == nil {
return
}
return tr.root.min[:], tr.root.max[:]
}

379
vendor/github.com/tidwall/boxtree/d3/boxtree_test.go generated vendored Normal file
View File

@ -0,0 +1,379 @@
package d3
import (
"fmt"
"math/rand"
"sort"
"strconv"
"strings"
"testing"
"time"
)
type tBox struct {
min [dims]float64
max [dims]float64
}
var boxes []tBox
var points []tBox
func init() {
seed := time.Now().UnixNano()
// seed = 1532132365683340889
println("seed:", seed)
rand.Seed(seed)
}
func randPoints(N int) []tBox {
boxes := make([]tBox, N)
for i := 0; i < N; i++ {
boxes[i].min[0] = rand.Float64()*360 - 180
boxes[i].min[1] = rand.Float64()*180 - 90
for j := 2; j < dims; j++ {
boxes[i].min[j] = rand.Float64()
}
boxes[i].max = boxes[i].min
}
return boxes
}
func randBoxes(N int) []tBox {
boxes := make([]tBox, N)
for i := 0; i < N; i++ {
boxes[i].min[0] = rand.Float64()*360 - 180
boxes[i].min[1] = rand.Float64()*180 - 90
for j := 2; j < dims; j++ {
boxes[i].min[j] = rand.Float64() * 100
}
boxes[i].max[0] = boxes[i].min[0] + rand.Float64()
boxes[i].max[1] = boxes[i].min[1] + rand.Float64()
for j := 2; j < dims; j++ {
boxes[i].max[j] = boxes[i].min[j] + rand.Float64()
}
if boxes[i].max[0] > 180 || boxes[i].max[1] > 90 {
i--
}
}
return boxes
}
func sortBoxes(boxes []tBox) {
sort.Slice(boxes, func(i, j int) bool {
for k := 0; k < len(boxes[i].min); k++ {
if boxes[i].min[k] < boxes[j].min[k] {
return true
}
if boxes[i].min[k] > boxes[j].min[k] {
return false
}
if boxes[i].max[k] < boxes[j].max[k] {
return true
}
if boxes[i].max[k] > boxes[j].max[k] {
return false
}
}
return i < j
})
}
func sortBoxesNearby(boxes []tBox, min, max []float64) {
sort.Slice(boxes, func(i, j int) bool {
return testBoxDist(boxes[i].min[:], boxes[i].max[:], min, max) <
testBoxDist(boxes[j].min[:], boxes[j].max[:], min, max)
})
}
func testBoxDist(amin, amax, bmin, bmax []float64) float64 {
var dist float64
for i := 0; i < len(amin); i++ {
var min, max float64
if amin[i] > bmin[i] {
min = amin[i]
} else {
min = bmin[i]
}
if amax[i] < bmax[i] {
max = amax[i]
} else {
max = bmax[i]
}
squared := min - max
if squared > 0 {
dist += squared * squared
}
}
return dist
}
func testBoxesVarious(t *testing.T, boxes []tBox, label string) {
N := len(boxes)
var tr BoxTree
// N := 10000
// boxes := randPoints(N)
/////////////////////////////////////////
// insert
/////////////////////////////////////////
for i := 0; i < N; i++ {
tr.Insert(boxes[i].min[:], boxes[i].max[:], boxes[i])
}
if tr.Count() != N {
t.Fatalf("expected %d, got %d", N, tr.Count())
}
// area := tr.TotalOverlapArea()
// fmt.Printf("overlap: %.0f, %.1f/item\n", area, area/float64(N))
// ioutil.WriteFile(label+".svg", []byte(rtreetools.SVG(&tr)), 0600)
/////////////////////////////////////////
// scan all items and count one-by-one
/////////////////////////////////////////
var count int
tr.Scan(func(min, max []float64, value interface{}) bool {
count++
return true
})
if count != N {
t.Fatalf("expected %d, got %d", N, count)
}
/////////////////////////////////////////
// check every point for correctness
/////////////////////////////////////////
var tboxes1 []tBox
tr.Scan(func(min, max []float64, value interface{}) bool {
tboxes1 = append(tboxes1, value.(tBox))
return true
})
tboxes2 := make([]tBox, len(boxes))
copy(tboxes2, boxes)
sortBoxes(tboxes1)
sortBoxes(tboxes2)
for i := 0; i < len(tboxes1); i++ {
if tboxes1[i] != tboxes2[i] {
t.Fatalf("expected '%v', got '%v'", tboxes2[i], tboxes1[i])
}
}
/////////////////////////////////////////
// search for each item one-by-one
/////////////////////////////////////////
for i := 0; i < N; i++ {
var found bool
tr.Search(boxes[i].min[:], boxes[i].max[:],
func(min, max []float64, value interface{}) bool {
if value == boxes[i] {
found = true
return false
}
return true
})
if !found {
t.Fatalf("did not find item %d", i)
}
}
centerMin, centerMax := []float64{-18, -9}, []float64{18, 9}
for j := 2; j < dims; j++ {
centerMin = append(centerMin, -10)
centerMax = append(centerMax, 10)
}
/////////////////////////////////////////
// search for 10% of the items
/////////////////////////////////////////
for i := 0; i < N/5; i++ {
var count int
tr.Search(centerMin, centerMax,
func(min, max []float64, value interface{}) bool {
count++
return true
},
)
}
/////////////////////////////////////////
// delete every other item
/////////////////////////////////////////
for i := 0; i < N/2; i++ {
j := i * 2
tr.Delete(boxes[j].min[:], boxes[j].max[:], boxes[j])
}
/////////////////////////////////////////
// count all items. should be half of N
/////////////////////////////////////////
count = 0
tr.Scan(func(min, max []float64, value interface{}) bool {
count++
return true
})
if count != N/2 {
t.Fatalf("expected %d, got %d", N/2, count)
}
///////////////////////////////////////////////////
// reinsert every other item, but in random order
///////////////////////////////////////////////////
var ij []int
for i := 0; i < N/2; i++ {
j := i * 2
ij = append(ij, j)
}
rand.Shuffle(len(ij), func(i, j int) {
ij[i], ij[j] = ij[j], ij[i]
})
for i := 0; i < N/2; i++ {
j := ij[i]
tr.Insert(boxes[j].min[:], boxes[j].max[:], boxes[j])
}
//////////////////////////////////////////////////////
// replace each item with an item that is very close
//////////////////////////////////////////////////////
var nboxes = make([]tBox, N)
for i := 0; i < N; i++ {
for j := 0; j < len(boxes[i].min); j++ {
nboxes[i].min[j] = boxes[i].min[j] + (rand.Float64() - 0.5)
if boxes[i].min == boxes[i].max {
nboxes[i].max[j] = nboxes[i].min[j]
} else {
nboxes[i].max[j] = boxes[i].max[j] + (rand.Float64() - 0.5)
}
}
}
for i := 0; i < N; i++ {
tr.Insert(nboxes[i].min[:], nboxes[i].max[:], nboxes[i])
tr.Delete(boxes[i].min[:], boxes[i].max[:], boxes[i])
}
if tr.Count() != N {
t.Fatalf("expected %d, got %d", N, tr.Count())
}
// area = tr.TotalOverlapArea()
// fmt.Fprintf(wr, "overlap: %.0f, %.1f/item\n", area, area/float64(N))
/////////////////////////////////////////
// check every point for correctness
/////////////////////////////////////////
tboxes1 = nil
tr.Scan(func(min, max []float64, value interface{}) bool {
tboxes1 = append(tboxes1, value.(tBox))
return true
})
tboxes2 = make([]tBox, len(nboxes))
copy(tboxes2, nboxes)
sortBoxes(tboxes1)
sortBoxes(tboxes2)
for i := 0; i < len(tboxes1); i++ {
if tboxes1[i] != tboxes2[i] {
t.Fatalf("expected '%v', got '%v'", tboxes2[i], tboxes1[i])
}
}
/////////////////////////////////////////
// search for 10% of the items
/////////////////////////////////////////
for i := 0; i < N/5; i++ {
var count int
tr.Search(centerMin, centerMax,
func(min, max []float64, value interface{}) bool {
count++
return true
},
)
}
var boxes3 []tBox
tr.Nearby(centerMin, centerMax,
func(min, max []float64, value interface{}) bool {
boxes3 = append(boxes3, value.(tBox))
return true
},
)
if len(boxes3) != len(nboxes) {
t.Fatalf("expected %d, got %d", len(nboxes), len(boxes3))
}
if len(boxes3) != tr.Count() {
t.Fatalf("expected %d, got %d", tr.Count(), len(boxes3))
}
var ldist float64
for i, box := range boxes3 {
dist := testBoxDist(box.min[:], box.max[:], centerMin, centerMax)
if i > 0 && dist < ldist {
t.Fatalf("out of order")
}
ldist = dist
}
}
func TestRandomBoxes(t *testing.T) {
testBoxesVarious(t, randBoxes(10000), "boxes")
}
func TestRandomPoints(t *testing.T) {
testBoxesVarious(t, randPoints(10000), "points")
}
func (r *box) boxstr() string {
var b []byte
b = append(b, '[', '[')
for i := 0; i < len(r.min); i++ {
if i != 0 {
b = append(b, ' ')
}
b = strconv.AppendFloat(b, r.min[i], 'f', -1, 64)
}
b = append(b, ']', '[')
for i := 0; i < len(r.max); i++ {
if i != 0 {
b = append(b, ' ')
}
b = strconv.AppendFloat(b, r.max[i], 'f', -1, 64)
}
b = append(b, ']', ']')
return string(b)
}
func (r *box) print(height, indent int) {
fmt.Printf("%s%s", strings.Repeat(" ", indent), r.boxstr())
if height == 0 {
fmt.Printf("\t'%v'\n", r.data)
} else {
fmt.Printf("\n")
for i := 0; i < r.data.(*node).count; i++ {
r.data.(*node).boxes[i].print(height-1, indent+1)
}
}
}
func (tr BoxTree) print() {
if tr.root.data == nil {
println("EMPTY TREE")
return
}
tr.root.print(tr.height+1, 0)
}
func TestZeroPoints(t *testing.T) {
N := 10000
var tr BoxTree
pt := make([]float64, dims)
for i := 0; i < N; i++ {
tr.Insert(pt, nil, i)
}
}
func BenchmarkRandomInsert(b *testing.B) {
var tr BoxTree
boxes := randBoxes(b.N)
b.ResetTimer()
for i := 0; i < b.N; i++ {
tr.Insert(boxes[i].min[:], boxes[i].max[:], i)
}
}

BIN
vendor/github.com/tidwall/boxtree/res/cities.png generated vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 336 KiB

17
vendor/github.com/tidwall/boxtree/res/cities/main.go generated vendored Normal file
View File

@ -0,0 +1,17 @@
package main
import (
"io/ioutil"
"github.com/tidwall/boxtree"
"github.com/tidwall/boxtree/res/tools"
"github.com/tidwall/cities"
)
func main() {
tr := boxtree.New(2)
for _, city := range cities.Cities {
tr.Insert([]float64{city.Longitude, city.Latitude}, nil, &city)
}
ioutil.WriteFile("cities.svg", []byte(tools.SVG(tr)), 0600)
}

33
vendor/github.com/tidwall/boxtree/res/gen/main.go generated vendored Normal file
View File

@ -0,0 +1,33 @@
package main
import (
"io/ioutil"
"log"
"os"
"strings"
)
func main() {
b, err := ioutil.ReadFile("d2/boxtree.go")
if err != nil {
log.Fatal(err)
}
s := string(b)
s = strings.Replace(s, "package d2", "package d3", 1)
s = strings.Replace(s, "const dims = 2", "const dims = 3", 1)
if err := os.MkdirAll("d3", 0777); err != nil {
log.Fatal(err)
}
if err := ioutil.WriteFile("d3/boxtree.go", []byte(s), 0666); err != nil {
log.Fatal(err)
}
b, err = ioutil.ReadFile("d2/boxtree_test.go")
if err != nil {
log.Fatal(err)
}
b = []byte(strings.Replace(string(b), "package d2", "package d3", 1))
if err := ioutil.WriteFile("d3/boxtree_test.go", b, 0666); err != nil {
log.Fatal(err)
}
}

112
vendor/github.com/tidwall/boxtree/res/tools/tools.go generated vendored Normal file
View File

@ -0,0 +1,112 @@
package tools
import (
"fmt"
"strconv"
"strings"
)
// RTree interface
type RTree interface {
Insert(min, max []float64, value interface{})
Scan(iter func(min, max []float64, value interface{}) bool)
Search(min, max []float64, iter func(min, max []float64,
value interface{}) bool)
Delete(min, max []float64, value interface{})
Traverse(iter func(min, max []float64, height, level int,
value interface{}) int)
Count() int
TotalOverlapArea() float64
Nearby(min, max []float64, iter func(min, max []float64,
item interface{}) bool)
}
func svg(min, max []float64, height int) string {
var out string
point := true
for i := 0; i < 2; i++ {
if min[i] != max[i] {
point = false
break
}
}
if point { // is point
out += fmt.Sprintf(
"<rect x=\"%.0f\" y=\"%0.f\" width=\"%0.f\" height=\"%0.f\" "+
"stroke=\"%s\" fill=\"purple\" "+
"fill-opacity=\"0\" stroke-opacity=\"1\" "+
"rx=\"15\" ry=\"15\"/>\n",
(min[0])*svgScale,
(min[1])*svgScale,
(max[0]-min[0]+1/svgScale)*svgScale,
(max[1]-min[1]+1/svgScale)*svgScale,
strokes[height%len(strokes)])
} else { // is rect
out += fmt.Sprintf(
"<rect x=\"%.0f\" y=\"%0.f\" width=\"%0.f\" height=\"%0.f\" "+
"stroke=\"%s\" fill=\"purple\" "+
"fill-opacity=\"0\" stroke-opacity=\"1\"/>\n",
(min[0])*svgScale,
(min[1])*svgScale,
(max[0]-min[0]+1/svgScale)*svgScale,
(max[1]-min[1]+1/svgScale)*svgScale,
strokes[height%len(strokes)])
}
return out
}
const (
// Continue to first child rectangle and/or next sibling.
Continue = iota
// Ignore child rectangles but continue to next sibling.
Ignore
// Stop iterating
Stop
)
const svgScale = 4.0
var strokes = [...]string{"black", "#cccc00", "green", "red", "purple"}
// SVG prints 2D rtree in wgs84 coordinate space
func SVG(tr RTree) string {
var out string
out += fmt.Sprintf("<svg viewBox=\"%.0f %.0f %.0f %.0f\" "+
"xmlns =\"http://www.w3.org/2000/svg\">\n",
-190.0*svgScale, -100.0*svgScale,
380.0*svgScale, 190.0*svgScale)
out += fmt.Sprintf("<g transform=\"scale(1,-1)\">\n")
var outb []byte
tr.Traverse(func(min, max []float64, height, level int, _ interface{}) int {
outb = append(outb, svg(min, max, height)...)
return Continue
})
out += string(outb)
out += fmt.Sprintf("</g>\n")
out += fmt.Sprintf("</svg>\n")
return out
}
// Cities returns big list of cities base on json from
// https://github.com/lutangar/cities.json
func Cities(bigJSON string) [][2]float64 {
var out [][2]float64
s := bigJSON
for i := 0; ; i++ {
idx := strings.Index(s, `"lat": "`)
if idx == -1 {
break
}
s = s[idx+8:]
idx = strings.IndexByte(s, '"')
lat, _ := strconv.ParseFloat(s[:idx], 64)
idx = strings.Index(s, `"lng": "`)
s = s[idx+8:]
idx = strings.IndexByte(s, '"')
lng, _ := strconv.ParseFloat(s[:idx], 64)
s = s[idx+1:]
out = append(out, [2]float64{lng, lat})
}
return out
}

View File

@ -1,15 +0,0 @@
ISC License
Copyright (c) 2017, Vladimir Agafonkin
Permission to use, copy, modify, and/or distribute this software for any purpose
with or without fee is hereby granted, provided that the above copyright notice
and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
THIS SOFTWARE.

View File

@ -1,7 +0,0 @@
# tinyqueue
<a href="https://godoc.org/github.com/tidwall/tinyqueue"><img src="https://img.shields.io/badge/api-reference-blue.svg?style=flat-square" alt="GoDoc"></a>
tinyqueue is a Go package for binary heap priority queues.
Ported from the [tinyqueue](https://github.com/mourner/tinyqueue) Javascript library.

View File

@ -1,86 +0,0 @@
package tinyqueue
type Queue struct {
length int
data []Item
}
type Item interface {
Less(Item) bool
}
func New(data []Item) *Queue {
q := &Queue{}
q.data = data
q.length = len(data)
if q.length > 0 {
i := q.length >> 1
for ; i >= 0; i-- {
q.down(i)
}
}
return q
}
func (q *Queue) Push(item Item) {
q.data = append(q.data, item)
q.length++
q.up(q.length - 1)
}
func (q *Queue) Pop() Item {
if q.length == 0 {
return nil
}
top := q.data[0]
q.length--
if q.length > 0 {
q.data[0] = q.data[q.length]
q.down(0)
}
q.data = q.data[:len(q.data)-1]
return top
}
func (q *Queue) Peek() Item {
if q.length == 0 {
return nil
}
return q.data[0]
}
func (q *Queue) Len() int {
return q.length
}
func (q *Queue) down(pos int) {
data := q.data
halfLength := q.length >> 1
item := data[pos]
for pos < halfLength {
left := (pos << 1) + 1
right := left + 1
best := data[left]
if right < q.length && data[right].Less(best) {
left = right
best = data[right]
}
if !best.Less(item) {
break
}
data[pos] = best
pos = left
}
data[pos] = item
}
func (q *Queue) up(pos int) {
data := q.data
item := data[pos]
for pos > 0 {
parent := (pos - 1) >> 1
current := data[parent]
if !item.Less(current) {
break
}
data[pos] = current
pos = parent
}
data[pos] = item
}

View File

@ -1,65 +0,0 @@
package tinyqueue
import (
"math/rand"
"sort"
"testing"
"time"
"github.com/json-iterator/go/assert"
)
type floatValue float64
func (a floatValue) Less(b Item) bool {
return a < b.(floatValue)
}
var data, sorted = func() ([]Item, []Item) {
rand.Seed(time.Now().UnixNano())
var data []Item
for i := 0; i < 100; i++ {
data = append(data, floatValue(rand.Float64()*100))
}
sorted := make([]Item, len(data))
copy(sorted, data)
sort.Slice(sorted, func(i, j int) bool {
return sorted[i].Less(sorted[j])
})
return data, sorted
}()
func TestMaintainsPriorityQueue(t *testing.T) {
q := New(nil)
for i := 0; i < len(data); i++ {
q.Push(data[i])
}
assert.Equal(t, q.Peek(), sorted[0])
var result []Item
for q.length > 0 {
result = append(result, q.Pop())
}
assert.Equal(t, result, sorted)
}
func TestAcceptsDataInConstructor(t *testing.T) {
q := New(data)
var result []Item
for q.length > 0 {
result = append(result, q.Pop())
}
assert.Equal(t, result, sorted)
}
func TestHandlesEdgeCasesWithFewElements(t *testing.T) {
q := New(nil)
q.Push(floatValue(2))
q.Push(floatValue(1))
q.Pop()
q.Pop()
q.Pop()
q.Push(floatValue(2))
q.Push(floatValue(1))
assert.Equal(t, float64(q.Pop().(floatValue)), 1.0)
assert.Equal(t, float64(q.Pop().(floatValue)), 2.0)
assert.Equal(t, q.Pop(), nil)
}