From aec3b8d204dd8b4f9308f536e9b5eefcf966f86e Mon Sep 17 00:00:00 2001 From: Tulir Asokan <tulir@maunium.net> Date: Wed, 2 May 2018 22:30:43 +0300 Subject: Add tests for PushRule.Match and fork glob to make it compatible with the spec --- Gopkg.lock | 10 +- Gopkg.toml | 4 - lib/glob/LICENSE | 22 ++++ lib/glob/README.md | 28 +++++ lib/glob/glob.go | 108 +++++++++++++++++ matrix/pushrules/condition.go | 7 +- matrix/pushrules/condition_eventmatch_test.go | 6 + matrix/pushrules/rule.go | 2 +- matrix/pushrules/rule_test.go | 166 ++++++++++++++++++++++++++ vendor/github.com/zyedidia/glob/LICENSE | 22 ---- vendor/github.com/zyedidia/glob/README.md | 28 ----- vendor/github.com/zyedidia/glob/glob.go | 94 --------------- 12 files changed, 338 insertions(+), 159 deletions(-) create mode 100644 lib/glob/LICENSE create mode 100644 lib/glob/README.md create mode 100644 lib/glob/glob.go delete mode 100644 vendor/github.com/zyedidia/glob/LICENSE delete mode 100644 vendor/github.com/zyedidia/glob/README.md delete mode 100644 vendor/github.com/zyedidia/glob/glob.go diff --git a/Gopkg.lock b/Gopkg.lock index b78c6ef..44e4ee4 100644 --- a/Gopkg.lock +++ b/Gopkg.lock @@ -61,12 +61,6 @@ packages = ["."] revision = "4611e809d8b1a3051c11d11f4b610c44df73fa38" -[[projects]] - branch = "master" - name = "github.com/zyedidia/glob" - packages = ["."] - revision = "dd4023a66dc351ae26e592d21cd133b5b143f3d8" - [[projects]] branch = "master" name = "golang.org/x/image" @@ -88,7 +82,7 @@ "html", "html/atom" ] - revision = "5f9ae10d9af5b1c89ae6904293b14b064d4ada23" + revision = "640f4622ab692b87c2f3a94265e6f579fe38263d" [[projects]] name = "golang.org/x/text" @@ -144,6 +138,6 @@ [solve-meta] analyzer-name = "dep" analyzer-version = 1 - inputs-digest = "ea0f742be221116a10b3fb332aee126fd8f4b3392b65a8e38a7c26e8c45faf8f" + inputs-digest = "375d42f271992a59ae0bcf25e2401aae2f4d8adb7b63605c4ffef577c5154025" solver-name = "gps-cdcl" solver-version = 1 diff --git a/Gopkg.toml b/Gopkg.toml index 312c8a1..ab8782e 100644 --- a/Gopkg.toml +++ b/Gopkg.toml @@ -41,10 +41,6 @@ branch = "master" name = "github.com/zyedidia/clipboard" -[[constraint]] - branch = "master" - name = "github.com/zyedidia/glob" - [[constraint]] branch = "master" name = "golang.org/x/image" diff --git a/lib/glob/LICENSE b/lib/glob/LICENSE new file mode 100644 index 0000000..cb00d95 --- /dev/null +++ b/lib/glob/LICENSE @@ -0,0 +1,22 @@ +Glob is licensed under the MIT "Expat" License: + +Copyright (c) 2016: Zachary Yedidia. + +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. diff --git a/lib/glob/README.md b/lib/glob/README.md new file mode 100644 index 0000000..e2e6c64 --- /dev/null +++ b/lib/glob/README.md @@ -0,0 +1,28 @@ +# String globbing in Go + +[](http://godoc.org/github.com/zyedidia/glob) + +This package adds support for globs in Go. + +It simply converts glob expressions to regexps. I try to follow the standard defined [here](http://pubs.opengroup.org/onlinepubs/009695399/utilities/xcu_chap02.html#tag_02_13). + +# Example + +```go +package main + +import "github.com/zyedidia/glob" + +func main() { + glob, err := glob.Compile("{*.go,*.c}") + if err != nil { + // Error + } + + glob.Match([]byte("test.c")) // true + glob.Match([]byte("hello.go")) // true + glob.Match([]byte("test.d")) // false +} +``` + +You can call all the same functions on a glob that you can call on a regexp. diff --git a/lib/glob/glob.go b/lib/glob/glob.go new file mode 100644 index 0000000..c270dbc --- /dev/null +++ b/lib/glob/glob.go @@ -0,0 +1,108 @@ +// Package glob provides objects for matching strings with globs +package glob + +import "regexp" + +// Glob is a wrapper of *regexp.Regexp. +// It should contain a glob expression compiled into a regular expression. +type Glob struct { + *regexp.Regexp +} + +// Compile a takes a glob expression as a string and transforms it +// into a *Glob object (which is really just a regular expression) +// Compile also returns a possible error. +func Compile(pattern string) (*Glob, error) { + r, err := globToRegex(pattern) + return &Glob{r}, err +} + +func globToRegex(glob string) (*regexp.Regexp, error) { + regex := "" + inGroup := 0 + inClass := 0 + firstIndexInClass := -1 + arr := []byte(glob) + + hasGlobCharacters := false + + for i := 0; i < len(arr); i++ { + ch := arr[i] + + switch ch { + case '\\': + i++ + if i >= len(arr) { + regex += "\\" + } else { + next := arr[i] + switch next { + case ',': + // Nothing + case 'Q', 'E': + regex += "\\\\" + default: + regex += "\\" + } + regex += string(next) + } + case '*': + if inClass == 0 { + regex += ".*" + } else { + regex += "*" + } + hasGlobCharacters = true + case '?': + if inClass == 0 { + regex += "." + } else { + regex += "?" + } + hasGlobCharacters = true + case '[': + inClass++ + firstIndexInClass = i + 1 + regex += "[" + hasGlobCharacters = true + case ']': + inClass-- + regex += "]" + case '.', '(', ')', '+', '|', '^', '$', '@', '%': + if inClass == 0 || (firstIndexInClass == i && ch == '^') { + regex += "\\" + } + regex += string(ch) + hasGlobCharacters = true + case '!': + if firstIndexInClass == i { + regex += "^" + } else { + regex += "!" + } + hasGlobCharacters = true + case '{': + inGroup++ + regex += "(" + hasGlobCharacters = true + case '}': + inGroup-- + regex += ")" + case ',': + if inGroup > 0 { + regex += "|" + hasGlobCharacters = true + } else { + regex += "," + } + default: + regex += string(ch) + } + } + + if hasGlobCharacters { + return regexp.Compile("^" + regex + "$") + } else { + return regexp.Compile(regex) + } +} diff --git a/matrix/pushrules/condition.go b/matrix/pushrules/condition.go index 4d17695..6607323 100644 --- a/matrix/pushrules/condition.go +++ b/matrix/pushrules/condition.go @@ -21,7 +21,7 @@ import ( "strconv" "strings" - "github.com/zyedidia/glob" + "maunium.net/go/gomuks/lib/glob" "maunium.net/go/gomatrix" "maunium.net/go/gomuks/matrix/rooms" ) @@ -82,7 +82,10 @@ func (cond *PushCondition) matchValue(room Room, event *gomatrix.Event) bool { key = key[0:index] } - pattern, _ := glob.Compile(cond.Pattern) + pattern, err := glob.Compile(cond.Pattern) + if err != nil { + return false + } switch key { case "type": diff --git a/matrix/pushrules/condition_eventmatch_test.go b/matrix/pushrules/condition_eventmatch_test.go index 2fcd054..160edd5 100644 --- a/matrix/pushrules/condition_eventmatch_test.go +++ b/matrix/pushrules/condition_eventmatch_test.go @@ -47,6 +47,12 @@ func TestPushCondition_Match_KindEvent_EventType(t *testing.T) { assert.True(t, condition.Match(blankTestRoom, event)) } +func TestPushCondition_Match_KindEvent_EventType_IllegalGlob(t *testing.T) { + condition := newMatchPushCondition("type", "m.room.invalid_glo[b") + event := newFakeEvent("m.room.invalid_glob", map[string]interface{}{}) + assert.False(t, condition.Match(blankTestRoom, event)) +} + func TestPushCondition_Match_KindEvent_Sender_Fail(t *testing.T) { condition := newMatchPushCondition("sender", "@foo:maunium.net") event := newFakeEvent("m.room.foo", map[string]interface{}{}) diff --git a/matrix/pushrules/rule.go b/matrix/pushrules/rule.go index 0caa13d..5c32a05 100644 --- a/matrix/pushrules/rule.go +++ b/matrix/pushrules/rule.go @@ -17,7 +17,7 @@ package pushrules import ( - "github.com/zyedidia/glob" + "maunium.net/go/gomuks/lib/glob" "maunium.net/go/gomatrix" ) diff --git a/matrix/pushrules/rule_test.go b/matrix/pushrules/rule_test.go index e8b56f4..3d3f03c 100644 --- a/matrix/pushrules/rule_test.go +++ b/matrix/pushrules/rule_test.go @@ -15,3 +15,169 @@ // along with this program. If not, see <http://www.gnu.org/licenses/>. package pushrules_test + +import ( + "testing" + "github.com/stretchr/testify/assert" + "maunium.net/go/gomuks/matrix/pushrules" +) + +func TestPushRule_Match_Conditions(t *testing.T) { + cond1 := newMatchPushCondition("content.msgtype", "m.emote") + cond2 := newMatchPushCondition("content.body", "*pushrules") + rule := &pushrules.PushRule{ + Type: pushrules.OverrideRule, + Enabled: true, + Conditions: []*pushrules.PushCondition{cond1, cond2}, + } + + event := newFakeEvent("m.room.message", map[string]interface{}{ + "msgtype": "m.emote", + "body": "is testing pushrules", + }) + assert.True(t, rule.Match(blankTestRoom, event)) +} + +func TestPushRule_Match_Conditions_Disabled(t *testing.T) { + cond1 := newMatchPushCondition("content.msgtype", "m.emote") + cond2 := newMatchPushCondition("content.body", "*pushrules") + rule := &pushrules.PushRule{ + Type: pushrules.OverrideRule, + Enabled: false, + Conditions: []*pushrules.PushCondition{cond1, cond2}, + } + + event := newFakeEvent("m.room.message", map[string]interface{}{ + "msgtype": "m.emote", + "body": "is testing pushrules", + }) + assert.False(t, rule.Match(blankTestRoom, event)) +} + +func TestPushRule_Match_Conditions_FailIfOneFails(t *testing.T) { + cond1 := newMatchPushCondition("content.msgtype", "m.emote") + cond2 := newMatchPushCondition("content.body", "*pushrules") + rule := &pushrules.PushRule{ + Type: pushrules.OverrideRule, + Enabled: true, + Conditions: []*pushrules.PushCondition{cond1, cond2}, + } + + event := newFakeEvent("m.room.message", map[string]interface{}{ + "msgtype": "m.text", + "body": "I'm testing pushrules", + }) + assert.False(t, rule.Match(blankTestRoom, event)) +} + +func TestPushRule_Match_Content(t *testing.T) { + rule := &pushrules.PushRule{ + Type: pushrules.ContentRule, + Enabled: true, + Pattern: "is testing*", + } + + event := newFakeEvent("m.room.message", map[string]interface{}{ + "msgtype": "m.emote", + "body": "is testing pushrules", + }) + assert.True(t, rule.Match(blankTestRoom, event)) +} + +func TestPushRule_Match_Content_Fail(t *testing.T) { + rule := &pushrules.PushRule{ + Type: pushrules.ContentRule, + Enabled: true, + Pattern: "is testing*", + } + + event := newFakeEvent("m.room.message", map[string]interface{}{ + "msgtype": "m.emote", + "body": "is not testing pushrules", + }) + assert.False(t, rule.Match(blankTestRoom, event)) +} + +func TestPushRule_Match_Content_ImplicitGlob(t *testing.T) { + rule := &pushrules.PushRule{ + Type: pushrules.ContentRule, + Enabled: true, + Pattern: "testing", + } + + event := newFakeEvent("m.room.message", map[string]interface{}{ + "msgtype": "m.emote", + "body": "is not testing pushrules", + }) + assert.True(t, rule.Match(blankTestRoom, event)) +} + +func TestPushRule_Match_Content_IllegalGlob(t *testing.T) { + rule := &pushrules.PushRule{ + Type: pushrules.ContentRule, + Enabled: true, + Pattern: "this is not a valid glo[b", + } + + event := newFakeEvent("m.room.message", map[string]interface{}{ + "msgtype": "m.emote", + "body": "this is not a valid glob", + }) + assert.False(t, rule.Match(blankTestRoom, event)) +} + +func TestPushRule_Match_Room(t *testing.T) { + rule := &pushrules.PushRule{ + Type: pushrules.RoomRule, + Enabled: true, + RuleID: "!fakeroom:maunium.net", + } + + event := newFakeEvent("m.room.message", map[string]interface{}{}) + assert.True(t, rule.Match(blankTestRoom, event)) +} + +func TestPushRule_Match_Room_Fail(t *testing.T) { + rule := &pushrules.PushRule{ + Type: pushrules.RoomRule, + Enabled: true, + RuleID: "!otherroom:maunium.net", + } + + event := newFakeEvent("m.room.message", map[string]interface{}{}) + assert.False(t, rule.Match(blankTestRoom, event)) +} + + +func TestPushRule_Match_Sender(t *testing.T) { + rule := &pushrules.PushRule{ + Type: pushrules.SenderRule, + Enabled: true, + RuleID: "@tulir:maunium.net", + } + + event := newFakeEvent("m.room.message", map[string]interface{}{}) + assert.True(t, rule.Match(blankTestRoom, event)) +} + +func TestPushRule_Match_Sender_Fail(t *testing.T) { + rule := &pushrules.PushRule{ + Type: pushrules.RoomRule, + Enabled: true, + RuleID: "@someone:matrix.org", + } + + event := newFakeEvent("m.room.message", map[string]interface{}{}) + assert.False(t, rule.Match(blankTestRoom, event)) +} + +func TestPushRule_Match_UnknownTypeAlwaysFail(t *testing.T) { + rule := &pushrules.PushRule{ + Type: pushrules.PushRuleType("foobar"), + Enabled: true, + RuleID: "@someone:matrix.org", + } + + event := newFakeEvent("m.room.message", map[string]interface{}{}) + assert.False(t, rule.Match(blankTestRoom, event)) +} diff --git a/vendor/github.com/zyedidia/glob/LICENSE b/vendor/github.com/zyedidia/glob/LICENSE deleted file mode 100644 index cb00d95..0000000 --- a/vendor/github.com/zyedidia/glob/LICENSE +++ /dev/null @@ -1,22 +0,0 @@ -Glob is licensed under the MIT "Expat" License: - -Copyright (c) 2016: Zachary Yedidia. - -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. diff --git a/vendor/github.com/zyedidia/glob/README.md b/vendor/github.com/zyedidia/glob/README.md deleted file mode 100644 index e2e6c64..0000000 --- a/vendor/github.com/zyedidia/glob/README.md +++ /dev/null @@ -1,28 +0,0 @@ -# String globbing in Go - -[](http://godoc.org/github.com/zyedidia/glob) - -This package adds support for globs in Go. - -It simply converts glob expressions to regexps. I try to follow the standard defined [here](http://pubs.opengroup.org/onlinepubs/009695399/utilities/xcu_chap02.html#tag_02_13). - -# Example - -```go -package main - -import "github.com/zyedidia/glob" - -func main() { - glob, err := glob.Compile("{*.go,*.c}") - if err != nil { - // Error - } - - glob.Match([]byte("test.c")) // true - glob.Match([]byte("hello.go")) // true - glob.Match([]byte("test.d")) // false -} -``` - -You can call all the same functions on a glob that you can call on a regexp. diff --git a/vendor/github.com/zyedidia/glob/glob.go b/vendor/github.com/zyedidia/glob/glob.go deleted file mode 100644 index 10c9b5d..0000000 --- a/vendor/github.com/zyedidia/glob/glob.go +++ /dev/null @@ -1,94 +0,0 @@ -// Package glob provides objects for matching strings with globs -package glob - -import "regexp" - -// Glob is a wrapper of *regexp.Regexp. -// It should contain a glob expression compiled into a regular expression. -type Glob struct { - *regexp.Regexp -} - -// Compile a takes a glob expression as a string and transforms it -// into a *Glob object (which is really just a regular expression) -// Compile also returns a possible error. -func Compile(pattern string) (*Glob, error) { - r, err := globToRegex(pattern) - return &Glob{r}, err -} - -func globToRegex(glob string) (*regexp.Regexp, error) { - regex := "" - inGroup := 0 - inClass := 0 - firstIndexInClass := -1 - arr := []byte(glob) - - for i := 0; i < len(arr); i++ { - ch := arr[i] - - switch ch { - case '\\': - i++ - if i >= len(arr) { - regex += "\\" - } else { - next := arr[i] - switch next { - case ',': - // Nothing - case 'Q', 'E': - regex += "\\\\" - default: - regex += "\\" - } - regex += string(next) - } - case '*': - if inClass == 0 { - regex += ".*" - } else { - regex += "*" - } - case '?': - if inClass == 0 { - regex += "." - } else { - regex += "?" - } - case '[': - inClass++ - firstIndexInClass = i + 1 - regex += "[" - case ']': - inClass-- - regex += "]" - case '.', '(', ')', '+', '|', '^', '$', '@', '%': - if inClass == 0 || (firstIndexInClass == i && ch == '^') { - regex += "\\" - } - regex += string(ch) - case '!': - if firstIndexInClass == i { - regex += "^" - } else { - regex += "!" - } - case '{': - inGroup++ - regex += "(" - case '}': - inGroup-- - regex += ")" - case ',': - if inGroup > 0 { - regex += "|" - } else { - regex += "," - } - default: - regex += string(ch) - } - } - return regexp.Compile("^" + regex + "$") -} -- cgit v1.2.3-70-g09d2