From 36bd464e8c503b9171ed824ddee99b88dbc2370c Mon Sep 17 00:00:00 2001
From: Lorenz Bausch <info@lorenzbausch.de>
Date: Sat, 7 May 2022 20:55:15 +0200
Subject: [PATCH] Add tests for validating exclude patterns

---
 cmd/restic/integration_filter_pattern_test.go | 69 +++++++++++++++++++
 internal/filter/filter_patterns_test.go       | 57 +++++++++++++++
 2 files changed, 126 insertions(+)
 create mode 100644 cmd/restic/integration_filter_pattern_test.go
 create mode 100644 internal/filter/filter_patterns_test.go

diff --git a/cmd/restic/integration_filter_pattern_test.go b/cmd/restic/integration_filter_pattern_test.go
new file mode 100644
index 000000000..e1534d841
--- /dev/null
+++ b/cmd/restic/integration_filter_pattern_test.go
@@ -0,0 +1,69 @@
+//go:build go1.16
+// +build go1.16
+
+// Before Go 1.16 filepath.Match returned early on a failed match,
+// and thus did not report any later syntax error in the pattern.
+// https://go.dev/doc/go1.16#path/filepath
+
+package main
+
+import (
+	"io/ioutil"
+	"path/filepath"
+	"testing"
+
+	rtest "github.com/restic/restic/internal/test"
+)
+
+func TestBackupFailsWhenUsingInvalidPatterns(t *testing.T) {
+	env, cleanup := withTestEnvironment(t)
+	defer cleanup()
+
+	testRunInit(t, env.gopts)
+
+	var err error
+
+	// Test --exclude
+	err = testRunBackupAssumeFailure(t, filepath.Dir(env.testdata), []string{"testdata"}, BackupOptions{Excludes: []string{"*[._]log[.-][0-9]", "!*[._]log[.-][0-9]"}}, env.gopts)
+
+	rtest.Equals(t, `Fatal: --exclude: invalid pattern(s) provided:
+*[._]log[.-][0-9]
+!*[._]log[.-][0-9]`, err.Error())
+
+	// Test --iexclude
+	err = testRunBackupAssumeFailure(t, filepath.Dir(env.testdata), []string{"testdata"}, BackupOptions{InsensitiveExcludes: []string{"*[._]log[.-][0-9]", "!*[._]log[.-][0-9]"}}, env.gopts)
+
+	rtest.Equals(t, `Fatal: --iexclude: invalid pattern(s) provided:
+*[._]log[.-][0-9]
+!*[._]log[.-][0-9]`, err.Error())
+}
+
+func TestBackupFailsWhenUsingInvalidPatternsFromFile(t *testing.T) {
+	env, cleanup := withTestEnvironment(t)
+	defer cleanup()
+
+	testRunInit(t, env.gopts)
+
+	// Create an exclude file with some invalid patterns
+	excludeFile := env.base + "/excludefile"
+	fileErr := ioutil.WriteFile(excludeFile, []byte("*.go\n*[._]log[.-][0-9]\n!*[._]log[.-][0-9]"), 0644)
+	if fileErr != nil {
+		t.Fatalf("Could not write exclude file: %v", fileErr)
+	}
+
+	var err error
+
+	// Test --exclude-file:
+	err = testRunBackupAssumeFailure(t, filepath.Dir(env.testdata), []string{"testdata"}, BackupOptions{ExcludeFiles: []string{excludeFile}}, env.gopts)
+
+	rtest.Equals(t, `Fatal: --exclude-file: invalid pattern(s) provided:
+*[._]log[.-][0-9]
+!*[._]log[.-][0-9]`, err.Error())
+
+	// Test --iexclude-file
+	err = testRunBackupAssumeFailure(t, filepath.Dir(env.testdata), []string{"testdata"}, BackupOptions{InsensitiveExcludeFiles: []string{excludeFile}}, env.gopts)
+
+	rtest.Equals(t, `Fatal: --iexclude-file: invalid pattern(s) provided:
+*[._]log[.-][0-9]
+!*[._]log[.-][0-9]`, err.Error())
+}
diff --git a/internal/filter/filter_patterns_test.go b/internal/filter/filter_patterns_test.go
new file mode 100644
index 000000000..215471500
--- /dev/null
+++ b/internal/filter/filter_patterns_test.go
@@ -0,0 +1,57 @@
+//go:build go1.16
+// +build go1.16
+
+// Before Go 1.16 filepath.Match returned early on a failed match,
+// and thus did not report any later syntax error in the pattern.
+// https://go.dev/doc/go1.16#path/filepath
+
+package filter_test
+
+import (
+	"strings"
+	"testing"
+
+	"github.com/restic/restic/internal/filter"
+	rtest "github.com/restic/restic/internal/test"
+)
+
+func TestValidPatterns(t *testing.T) {
+	// Test invalid patterns are detected and returned
+	t.Run("detect-invalid-patterns", func(t *testing.T) {
+		allValid, invalidPatterns := filter.ValidatePatterns([]string{"*.foo", "*[._]log[.-][0-9]", "!*[._]log[.-][0-9]"})
+
+		rtest.Assert(t, allValid == false, "Expected invalid patterns to be detected")
+
+		rtest.Equals(t, invalidPatterns, []string{"*[._]log[.-][0-9]", "!*[._]log[.-][0-9]"})
+	})
+
+	// Test all patterns defined in matchTests are valid
+	patterns := make([]string, 0)
+
+	for _, data := range matchTests {
+		patterns = append(patterns, data.pattern)
+	}
+
+	t.Run("validate-patterns", func(t *testing.T) {
+		allValid, invalidPatterns := filter.ValidatePatterns(patterns)
+
+		if !allValid {
+			t.Errorf("Found invalid pattern(s):\n%s", strings.Join(invalidPatterns, "\n"))
+		}
+	})
+
+	// Test all patterns defined in childMatchTests are valid
+	childPatterns := make([]string, 0)
+
+	for _, data := range childMatchTests {
+		childPatterns = append(childPatterns, data.pattern)
+	}
+
+	t.Run("validate-child-patterns", func(t *testing.T) {
+		allValid, invalidPatterns := filter.ValidatePatterns(childPatterns)
+
+		if !allValid {
+			t.Errorf("Found invalid child pattern(s):\n%s", strings.Join(invalidPatterns, "\n"))
+		}
+	})
+}