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
+
+[![GoDoc](https://godoc.org/github.com/zyedidia/glob?status.svg)](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
-
-[![GoDoc](https://godoc.org/github.com/zyedidia/glob?status.svg)](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