aboutsummaryrefslogtreecommitdiff
path: root/internal/config/dir_test.go
diff options
context:
space:
mode:
Diffstat (limited to 'internal/config/dir_test.go')
-rw-r--r--internal/config/dir_test.go122
1 files changed, 122 insertions, 0 deletions
diff --git a/internal/config/dir_test.go b/internal/config/dir_test.go
new file mode 100644
index 0000000..7ddfa2a
--- /dev/null
+++ b/internal/config/dir_test.go
@@ -0,0 +1,122 @@
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+package config
+
+import (
+ "reflect"
+ "testing"
+)
+
+const fullDir = `;; -*- mode: lisp -*-
+(path "~/downloads")
+(recursive yes)
+(ignore "*.part" "*.aria2")
+(ignore ".*")
+
+(rule "acme"
+ (when (type document)
+ (or (content "acme ltd") (name "\bacme\b")))
+ (move "Work/Acme/{mtime:%Y}")
+ (stop))
+
+(rule "photos"
+ (case strict)
+ (when (type image))
+ (rename "{mtime:%Y-%m-%d}_{stem}{ext}")
+ (move "Photos"))
+
+(rule "old" (when (type package) (age > 30d)) (delete permanent))
+(rule "keep" (when (name "^important")) (stop))
+(rule "all" (copy "~/backup"))
+`
+
+func kinds(r *Rule) []ActionKind {
+ var k []ActionKind
+ for _, a := range r.Actions {
+ k = append(k, a.Kind)
+ }
+ return k
+}
+
+func TestParseDir(t *testing.T) {
+ t.Setenv("HOME", "/home/u")
+ dir, errs := ParseDir("downloads", "downloads.conf", []byte(fullDir))
+ if len(errs) > 0 {
+ t.Fatal(errs)
+ }
+ if dir.Name != "downloads" || dir.Path != "/home/u/downloads" || dir.PathText != "~/downloads" {
+ t.Errorf("dir = %+v", dir)
+ }
+ if !reflect.DeepEqual(dir.Ignore, []string{"*.part", "*.aria2", ".*"}) {
+ t.Errorf("Ignore = %q", dir.Ignore)
+ }
+ if !dir.Settings.Over(Builtin()).Recursive {
+ t.Error("recursive not set")
+ }
+ if len(dir.Rules) != 5 {
+ t.Fatalf("got %d rules", len(dir.Rules))
+ }
+ acme := dir.Rules[0]
+ if acme.Name != "acme" || !acme.Stop || !acme.HasWhen || len(acme.When) != 2 || acme.When[1].Head() != "or" {
+ t.Errorf("acme = %+v", acme)
+ }
+ if len(acme.Actions) != 1 || acme.Actions[0].Kind != Move || acme.Actions[0].Arg != "Work/Acme/{mtime:%Y}" {
+ t.Errorf("acme actions = %+v", acme.Actions)
+ }
+ photos := dir.Rules[1]
+ if photos.Settings.Over(Builtin()).Case != CaseStrict {
+ t.Error("photos: case strict not set")
+ }
+ if got := kinds(photos); !reflect.DeepEqual(got, []ActionKind{Rename, Move}) {
+ t.Errorf("photos actions = %v", got)
+ }
+ if got := kinds(dir.Rules[2]); !reflect.DeepEqual(got, []ActionKind{DeletePermanent}) {
+ t.Errorf("old actions = %v", got)
+ }
+ if keep := dir.Rules[3]; !keep.Stop || len(keep.Actions) != 0 {
+ t.Errorf("keep = %+v", keep)
+ }
+ if all := dir.Rules[4]; all.HasWhen || all.When != nil || all.Actions[0].Arg != "~/backup" {
+ t.Errorf("all = %+v", all)
+ }
+}
+
+func TestActionKindString(t *testing.T) {
+ want := map[ActionKind]string{Copy: "copy", Move: "move", Rename: "rename", Delete: "delete", DeletePermanent: "delete permanent"}
+ for k, s := range want {
+ if k.String() != s {
+ t.Errorf("%d.String() = %q, want %q", k, k.String(), s)
+ }
+ }
+}
+
+func TestParseDirErrors(t *testing.T) {
+ tests := []struct{ src, want string }{
+ {``, `d.conf: no (path ...): say which directory this file sorts`},
+ {`(path ~/x)`, `d.conf:1:7: path must be a string: write (path "~/x")`},
+ {`(path "rel")`, `d.conf:1:7: path must be absolute or start with ~, not "rel"`},
+ {`(path "/a") (path "/b")`, `d.conf:1:13: path given twice (first at line 1)`},
+ {`(path "/a") (ignore part)`, `d.conf:1:21: ignore takes patterns in quotes, like "*.part"; got part`},
+ {`(path "/a") (rule x (stop))`, `d.conf:1:13: rule needs a name in quotes first, like (rule "invoices" ...)`},
+ {`(path "/a") (rule "x" (stop)) (rule "x" (stop))`, `d.conf:1:31: rule "x" defined twice (first at line 1)`},
+ {`(path "/a") (rule "x" (when (type pdf)))`, `d.conf:1:13: rule "x" does nothing: give it an action like (move "Somewhere") or (stop)`},
+ {`(path "/a") (rule "x" (when) (stop))`, `d.conf:1:23: rule "x": (when) needs a condition; leave it out to match every file`},
+ {`(path "/a") (rule "x" (when pdf) (stop))`, `d.conf:1:29: rule "x": a condition is a form like (type pdf), not pdf`},
+ {`(path "/a") (rule "x" (when (a)) (when (b)) (stop))`, `d.conf:1:34: rule "x": when given twice (first at line 1)`},
+ {`(path "/a") (rule "x" (recursive yes) (stop))`, `d.conf:1:23: rule "x": recursive cannot be set in a rule, only case, fold and on-conflict`},
+ {`(path "/a") (rule "x" (move Work))`, `d.conf:1:29: rule "x": move takes a string: write (move "Work")`},
+ {`(path "/a") (rule "x" (move))`, `d.conf:1:23: rule "x": move takes one directory in quotes`},
+ {`(path "/a") (rule "x" (rename "a/b"))`, `d.conf:1:31: rule "x": rename gives a new name, not a path; use move to change directory`},
+ {`(path "/a") (rule "x" (delete forever))`, `d.conf:1:23: rule "x": write (delete) for the Trash or (delete permanent)`},
+ {`(path "/a") (rule "x" (delete) (move "y"))`, `d.conf:1:32: rule "x": (move "y") after delete would never run`},
+ {`(path "/a") (rule "x" (stop now))`, `d.conf:1:23: rule "x": stop takes nothing: write (stop)`},
+ {`(path "/a") (rule "x" (fly "y"))`, `d.conf:1:23: rule "x": unknown form (fly ...); a rule has when, copy, move, rename, delete, stop, case, fold and on-conflict`},
+ {`(path "/a") (sort "x")`, `d.conf:1:13: unknown form (sort ...); a directory file has path, ignore, rule and settings like (recursive yes)`},
+ }
+ for _, tt := range tests {
+ _, errs := ParseDir("d", "d.conf", []byte(tt.src))
+ if len(errs) != 1 || errs[0].Error() != tt.want {
+ t.Errorf("%s:\n got %v\n want %s", tt.src, errs, tt.want)
+ }
+ }
+}