diff options
Diffstat (limited to 'internal/config/dir_test.go')
| -rw-r--r-- | internal/config/dir_test.go | 122 |
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) + } + } +} |
