
The big change is that the GeoJSON package has been completely rewritten to fix a few of geometry calculation bugs, increase performance, and to better follow the GeoJSON spec RFC 7946. GeoJSON updates - A LineString now requires at least two points. - All json members, even foreign, now persist with the object. - The bbox member persists too but is no longer used for geometry calculations. This is change in behavior. Previously Tile38 would treat the bbox as the object's physical rectangle. - Corrections to geometry intersects and within calculations. Faster spatial queries - The performance of Point-in-polygon and object intersect operations are greatly improved for complex polygons and line strings. It went from O(n) to roughly O(log n). - The same for all collection types with many children, including FeatureCollection, GeometryCollection, MultiPoint, MultiLineString, and MultiPolygon. Codebase changes - The pkg directory has been renamed to internal - The GeoJSON internal package has been moved to a seperate repo at https://github.com/tidwall/geojson. It's now vendored. Please look out for higher memory usage for datasets using complex shapes. A complex shape is one that has 64 or more points. For these shapes it's expected that there will be increase of least 54 bytes per point.
427 lines
10 KiB
Go
427 lines
10 KiB
Go
package glob
|
|
|
|
import (
|
|
"math/rand"
|
|
"testing"
|
|
"time"
|
|
)
|
|
|
|
func test(t *testing.T, pattern string, desc bool, limitsExpect []string, isGlobExpect bool) {
|
|
g := Parse(pattern, desc)
|
|
if g.IsGlob != isGlobExpect {
|
|
t.Fatalf("pattern[%v] desc[%v] (isGlob=%v, expected=%v)", pattern, desc, g.IsGlob, isGlobExpect)
|
|
}
|
|
if g.Limits[0] != limitsExpect[0] || g.Limits[1] != limitsExpect[1] {
|
|
t.Fatalf("pattern[%v] desc[%v] (limits=%v, expected=%v)", pattern, desc, g.Limits, limitsExpect)
|
|
}
|
|
if g.Pattern != pattern {
|
|
t.Fatalf("pattern[%v] desc[%v] (pattern=%v, expected=%v)", pattern, desc, g.Pattern, pattern)
|
|
}
|
|
if g.Desc != desc {
|
|
t.Fatalf("pattern[%v] desc[%v] (desc=%v, expected=%v)", pattern, desc, g.Desc, desc)
|
|
}
|
|
}
|
|
|
|
func TestGlob(t *testing.T) {
|
|
test(t, "*", false, []string{"", ""}, true)
|
|
test(t, "", false, []string{"", ""}, false)
|
|
test(t, "hello*", false, []string{"hello", "hellp"}, true)
|
|
test(t, "hello", false, []string{"hello", "hellp"}, false)
|
|
test(t, "\xff*", false, []string{"\xff", "\xff\x00"}, true)
|
|
test(t, "\x00*", false, []string{"\x00", "\x01"}, true)
|
|
test(t, "\xff", false, []string{"\xff", "\xff\x00"}, false)
|
|
|
|
test(t, "*", true, []string{"", ""}, true)
|
|
test(t, "", true, []string{"", ""}, false)
|
|
test(t, "hello*", true, []string{"hellp", "helln"}, true)
|
|
test(t, "hello", true, []string{"hellp", "helln"}, false)
|
|
test(t, "a\xff*", true, []string{"a\xff\x00", "a\xfe"}, true)
|
|
test(t, "\x00*", true, []string{"\x01", ""}, true)
|
|
test(t, "\x01*", true, []string{"\x02", "\x00"}, true)
|
|
test(t, "b\x00*", true, []string{"b\x01", "a\xff"}, true)
|
|
test(t, "\x00\x00*", true, []string{"\x00\x01", ""}, true)
|
|
test(t, "\x00\x01\x00*", true, []string{"\x00\x01\x01", "\x00\x00\xff"}, true)
|
|
}
|
|
|
|
func testMatch(s, pattern string) bool {
|
|
ok, _ := Match(pattern, s)
|
|
return ok
|
|
}
|
|
|
|
func TestMatch(t *testing.T) {
|
|
if !testMatch("hello world", "hello world") {
|
|
t.Fatal("fail")
|
|
}
|
|
if testMatch("hello world", "jello world") {
|
|
t.Fatal("fail")
|
|
}
|
|
if !testMatch("hello world", "hello*") {
|
|
t.Fatal("fail")
|
|
}
|
|
if testMatch("hello world", "jello*") {
|
|
t.Fatal("fail")
|
|
}
|
|
if !testMatch("hello world", "hello?world") {
|
|
t.Fatal("fail")
|
|
}
|
|
if testMatch("hello world", "jello?world") {
|
|
t.Fatal("fail")
|
|
}
|
|
if !testMatch("hello world", "he*o?world") {
|
|
t.Fatal("fail")
|
|
}
|
|
if !testMatch("hello world", "he*o?wor*") {
|
|
t.Fatal("fail")
|
|
}
|
|
if !testMatch("hello world", "he*o?*r*") {
|
|
t.Fatal("fail")
|
|
}
|
|
if !testMatch("的情况下解析一个", "*") {
|
|
t.Fatal("fail")
|
|
}
|
|
if !testMatch("的情况下解析一个", "*况下*") {
|
|
t.Fatal("fail")
|
|
}
|
|
if !testMatch("的情况下解析一个", "*况?*") {
|
|
t.Fatal("fail")
|
|
}
|
|
if !testMatch("的情况下解析一个", "的情况?解析一个") {
|
|
t.Fatal("fail")
|
|
}
|
|
}
|
|
|
|
// TestWildcardMatch - Tests validate the logic of wild card matching.
|
|
// `WildcardMatch` supports '*' and '?' wildcards.
|
|
// Sample usage: In resource matching for folder policy validation.
|
|
func TestWildcardMatch(t *testing.T) {
|
|
testCases := []struct {
|
|
pattern string
|
|
text string
|
|
matched bool
|
|
}{
|
|
// Test case - 1.
|
|
// Test case with pattern containing key name with a prefix. Should accept the same text without a "*".
|
|
{
|
|
pattern: "my-folder/oo*",
|
|
text: "my-folder/oo",
|
|
matched: true,
|
|
},
|
|
// Test case - 2.
|
|
// Test case with "*" at the end of the pattern.
|
|
{
|
|
pattern: "my-folder/In*",
|
|
text: "my-folder/India/Karnataka/",
|
|
matched: true,
|
|
},
|
|
// Test case - 3.
|
|
// Test case with prefixes shuffled.
|
|
// This should fail.
|
|
{
|
|
pattern: "my-folder/In*",
|
|
text: "my-folder/Karnataka/India/",
|
|
matched: false,
|
|
},
|
|
// Test case - 4.
|
|
// Test case with text expanded to the wildcards in the pattern.
|
|
{
|
|
pattern: "my-folder/In*/Ka*/Ban",
|
|
text: "my-folder/India/Karnataka/Ban",
|
|
matched: true,
|
|
},
|
|
// Test case - 5.
|
|
// Test case with the keyname part is repeated as prefix several times.
|
|
// This is valid.
|
|
{
|
|
pattern: "my-folder/In*/Ka*/Ban",
|
|
text: "my-folder/India/Karnataka/Ban/Ban/Ban/Ban/Ban",
|
|
matched: true,
|
|
},
|
|
// Test case - 6.
|
|
// Test case to validate that `*` can be expanded into multiple prefixes.
|
|
{
|
|
pattern: "my-folder/In*/Ka*/Ban",
|
|
text: "my-folder/India/Karnataka/Area1/Area2/Area3/Ban",
|
|
matched: true,
|
|
},
|
|
// Test case - 7.
|
|
// Test case to validate that `*` can be expanded into multiple prefixes.
|
|
{
|
|
pattern: "my-folder/In*/Ka*/Ban",
|
|
text: "my-folder/India/State1/State2/Karnataka/Area1/Area2/Area3/Ban",
|
|
matched: true,
|
|
},
|
|
// Test case - 8.
|
|
// Test case where the keyname part of the pattern is expanded in the text.
|
|
{
|
|
pattern: "my-folder/In*/Ka*/Ban",
|
|
text: "my-folder/India/Karnataka/Bangalore",
|
|
matched: false,
|
|
},
|
|
// Test case - 9.
|
|
// Test case with prefixes and wildcard expanded for all "*".
|
|
{
|
|
pattern: "my-folder/In*/Ka*/Ban*",
|
|
text: "my-folder/India/Karnataka/Bangalore",
|
|
matched: true,
|
|
},
|
|
// Test case - 10.
|
|
// Test case with keyname part being a wildcard in the pattern.
|
|
{pattern: "my-folder/*",
|
|
text: "my-folder/India",
|
|
matched: true,
|
|
},
|
|
// Test case - 11.
|
|
{
|
|
pattern: "my-folder/oo*",
|
|
text: "my-folder/odo",
|
|
matched: false,
|
|
},
|
|
|
|
// Test case with pattern containing wildcard '?'.
|
|
// Test case - 12.
|
|
// "my-folder?/" matches "my-folder1/", "my-folder2/", "my-folder3" etc...
|
|
// doesn't match "myfolder/".
|
|
{
|
|
pattern: "my-folder?/abc*",
|
|
text: "myfolder/abc",
|
|
matched: false,
|
|
},
|
|
// Test case - 13.
|
|
{
|
|
pattern: "my-folder?/abc*",
|
|
text: "my-folder1/abc",
|
|
matched: true,
|
|
},
|
|
// Test case - 14.
|
|
{
|
|
pattern: "my-?-folder/abc*",
|
|
text: "my--folder/abc",
|
|
matched: false,
|
|
},
|
|
// Test case - 15.
|
|
{
|
|
pattern: "my-?-folder/abc*",
|
|
text: "my-1-folder/abc",
|
|
matched: true,
|
|
},
|
|
// Test case - 16.
|
|
{
|
|
pattern: "my-?-folder/abc*",
|
|
text: "my-k-folder/abc",
|
|
matched: true,
|
|
},
|
|
// Test case - 17.
|
|
{
|
|
pattern: "my??folder/abc*",
|
|
text: "myfolder/abc",
|
|
matched: false,
|
|
},
|
|
// Test case - 18.
|
|
{
|
|
pattern: "my??folder/abc*",
|
|
text: "my4afolder/abc",
|
|
matched: true,
|
|
},
|
|
// Test case - 19.
|
|
{
|
|
pattern: "my-folder?abc*",
|
|
text: "my-folder/abc",
|
|
matched: true,
|
|
},
|
|
// Test case 20-21.
|
|
// '?' matches '/' too. (works with s3).
|
|
// This is because the namespace is considered flat.
|
|
// "abc?efg" matches both "abcdefg" and "abc/efg".
|
|
{
|
|
pattern: "my-folder/abc?efg",
|
|
text: "my-folder/abcdefg",
|
|
matched: true,
|
|
},
|
|
{
|
|
pattern: "my-folder/abc?efg",
|
|
text: "my-folder/abc/efg",
|
|
matched: true,
|
|
},
|
|
// Test case - 22.
|
|
{
|
|
pattern: "my-folder/abc????",
|
|
text: "my-folder/abc",
|
|
matched: false,
|
|
},
|
|
// Test case - 23.
|
|
{
|
|
pattern: "my-folder/abc????",
|
|
text: "my-folder/abcde",
|
|
matched: false,
|
|
},
|
|
// Test case - 24.
|
|
{
|
|
pattern: "my-folder/abc????",
|
|
text: "my-folder/abcdefg",
|
|
matched: true,
|
|
},
|
|
// Test case 25-26.
|
|
// test case with no '*'.
|
|
{
|
|
pattern: "my-folder/abc?",
|
|
text: "my-folder/abc",
|
|
matched: false,
|
|
},
|
|
{
|
|
pattern: "my-folder/abc?",
|
|
text: "my-folder/abcd",
|
|
matched: true,
|
|
},
|
|
{
|
|
pattern: "my-folder/abc?",
|
|
text: "my-folder/abcde",
|
|
matched: false,
|
|
},
|
|
// Test case 27.
|
|
{
|
|
pattern: "my-folder/mnop*?",
|
|
text: "my-folder/mnop",
|
|
matched: false,
|
|
},
|
|
// Test case 28.
|
|
{
|
|
pattern: "my-folder/mnop*?",
|
|
text: "my-folder/mnopqrst/mnopqr",
|
|
matched: true,
|
|
},
|
|
// Test case 29.
|
|
{
|
|
pattern: "my-folder/mnop*?",
|
|
text: "my-folder/mnopqrst/mnopqrs",
|
|
matched: true,
|
|
},
|
|
// Test case 30.
|
|
{
|
|
pattern: "my-folder/mnop*?",
|
|
text: "my-folder/mnop",
|
|
matched: false,
|
|
},
|
|
// Test case 31.
|
|
{
|
|
pattern: "my-folder/mnop*?",
|
|
text: "my-folder/mnopq",
|
|
matched: true,
|
|
},
|
|
// Test case 32.
|
|
{
|
|
pattern: "my-folder/mnop*?",
|
|
text: "my-folder/mnopqr",
|
|
matched: true,
|
|
},
|
|
// Test case 33.
|
|
{
|
|
pattern: "my-folder/mnop*?and",
|
|
text: "my-folder/mnopqand",
|
|
matched: true,
|
|
},
|
|
// Test case 34.
|
|
{
|
|
pattern: "my-folder/mnop*?and",
|
|
text: "my-folder/mnopand",
|
|
matched: false,
|
|
},
|
|
// Test case 35.
|
|
{
|
|
pattern: "my-folder/mnop*?and",
|
|
text: "my-folder/mnopqand",
|
|
matched: true,
|
|
},
|
|
// Test case 36.
|
|
{
|
|
pattern: "my-folder/mnop*?",
|
|
text: "my-folder/mn",
|
|
matched: false,
|
|
},
|
|
// Test case 37.
|
|
{
|
|
pattern: "my-folder/mnop*?",
|
|
text: "my-folder/mnopqrst/mnopqrs",
|
|
matched: true,
|
|
},
|
|
// Test case 38.
|
|
{
|
|
pattern: "my-folder/mnop*??",
|
|
text: "my-folder/mnopqrst",
|
|
matched: true,
|
|
},
|
|
// Test case 39.
|
|
{
|
|
pattern: "my-folder/mnop*qrst",
|
|
text: "my-folder/mnopabcdegqrst",
|
|
matched: true,
|
|
},
|
|
// Test case 40.
|
|
{
|
|
pattern: "my-folder/mnop*?and",
|
|
text: "my-folder/mnopqand",
|
|
matched: true,
|
|
},
|
|
// Test case 41.
|
|
{
|
|
pattern: "my-folder/mnop*?and",
|
|
text: "my-folder/mnopand",
|
|
matched: false,
|
|
},
|
|
// Test case 42.
|
|
{
|
|
pattern: "my-folder/mnop*?and?",
|
|
text: "my-folder/mnopqanda",
|
|
matched: true,
|
|
},
|
|
// Test case 43.
|
|
{
|
|
pattern: "my-folder/mnop*?and",
|
|
text: "my-folder/mnopqanda",
|
|
matched: false,
|
|
},
|
|
// Test case 44.
|
|
|
|
{
|
|
pattern: "my-?-folder/abc*",
|
|
text: "my-folder/mnopqanda",
|
|
matched: false,
|
|
},
|
|
}
|
|
// Iterating over the test cases, call the function under test and asert the output.
|
|
for i, testCase := range testCases {
|
|
actualResult := testMatch(testCase.text, testCase.pattern)
|
|
if testCase.matched != actualResult {
|
|
t.Errorf("Test %d: Expected the result to be `%v`, but instead found it to be `%v`", i+1, testCase.matched, actualResult)
|
|
}
|
|
}
|
|
}
|
|
func TestRandomInput(t *testing.T) {
|
|
rand.Seed(time.Now().UnixNano())
|
|
b1 := make([]byte, 100)
|
|
b2 := make([]byte, 100)
|
|
for i := 0; i < 1000000; i++ {
|
|
if _, err := rand.Read(b1); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if _, err := rand.Read(b2); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
testMatch(string(b1), string(b2))
|
|
}
|
|
}
|
|
func BenchmarkAscii(t *testing.B) {
|
|
for i := 0; i < t.N; i++ {
|
|
if !testMatch("hello", "hello") {
|
|
t.Fatal("fail")
|
|
}
|
|
}
|
|
}
|
|
|
|
func BenchmarkUnicode(t *testing.B) {
|
|
for i := 0; i < t.N; i++ {
|
|
if !testMatch("h情llo", "h情llo") {
|
|
t.Fatal("fail")
|
|
}
|
|
}
|
|
}
|