aboutsummaryrefslogtreecommitdiff
path: root/gui/internal/model/settings_test.go
diff options
context:
space:
mode:
Diffstat (limited to 'gui/internal/model/settings_test.go')
-rw-r--r--gui/internal/model/settings_test.go154
1 files changed, 154 insertions, 0 deletions
diff --git a/gui/internal/model/settings_test.go b/gui/internal/model/settings_test.go
new file mode 100644
index 0000000..46f8b75
--- /dev/null
+++ b/gui/internal/model/settings_test.go
@@ -0,0 +1,154 @@
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+package model
+
+import (
+ "strings"
+ "testing"
+)
+
+// settingsFile has a header with some settings set and others left out, and
+// comments around them that must survive every edit.
+const settingsFile = `;; the directory krino sorts
+(path "~/dl")
+(min-age 2m) ; leave fresh files alone
+(ignore "*.part" ".*")
+
+(rule "all"
+ (move "Out"))
+`
+
+// TestSettingReads: a directory setting is read as it is written, and one
+// that is not in the file reads as absent.
+func TestSettingReads(t *testing.T) {
+ r := openForms(t, settingsFile)
+ for _, c := range []struct {
+ head, want string
+ set bool
+ }{
+ {"path", `"~/dl"`, true},
+ {"min-age", "2m", true},
+ {"ignore", `"*.part" ".*"`, true},
+ {"recursive", "", false},
+ {"on-conflict", "", false},
+ } {
+ got, ok, err := r.Setting(c.head)
+ if err != nil {
+ t.Fatal(err)
+ }
+ if ok != c.set || got != c.want {
+ t.Errorf("Setting(%q) = %q, %v; want %q, %v", c.head, got, ok, c.want, c.set)
+ }
+ }
+}
+
+// TestSettingWrites: changing a setting rewrites that form and nothing
+// else; the comment on its line stays.
+func TestSettingWrites(t *testing.T) {
+ r := openForms(t, settingsFile)
+ if err := r.SetSetting("min-age", "1d"); err != nil {
+ t.Fatal(err)
+ }
+ if !strings.Contains(r.Text, "(min-age 1d)") {
+ t.Errorf("the new value is not in the file:\n%s", r.Text)
+ }
+ if strings.Contains(r.Text, "(min-age 2m)") {
+ t.Errorf("the old value is still there:\n%s", r.Text)
+ }
+ for _, keep := range []string{";; the directory krino sorts",
+ "; leave fresh files alone", `(ignore "*.part" ".*")`, `(rule "all"`} {
+ if !strings.Contains(r.Text, keep) {
+ t.Errorf("writing one setting lost %q:\n%s", keep, r.Text)
+ }
+ }
+ if diags := r.Check(); len(diags) > 0 {
+ t.Errorf("the file no longer loads: %v", diags)
+ }
+}
+
+// TestSettingAdds: a setting the file does not have is written into the
+// header, above the rules, and the file still loads.
+func TestSettingAdds(t *testing.T) {
+ r := openForms(t, settingsFile)
+ if err := r.SetSetting("on-conflict", "skip"); err != nil {
+ t.Fatal(err)
+ }
+ if !strings.Contains(r.Text, "(on-conflict skip)") {
+ t.Errorf("the setting was not written:\n%s", r.Text)
+ }
+ if strings.Index(r.Text, "(on-conflict skip)") > strings.Index(r.Text, `(rule "all"`) {
+ t.Errorf("the setting landed below the rules:\n%s", r.Text)
+ }
+ if diags := r.Check(); len(diags) > 0 {
+ t.Errorf("the file no longer loads: %v", diags)
+ }
+ got, ok, err := r.Setting("on-conflict")
+ if err != nil || !ok || got != "skip" {
+ t.Errorf("reading it back = %q, %v, %v", got, ok, err)
+ }
+}
+
+// TestSettingAddsAboveTheRules: a setting written below the rules - legal,
+// since order does not matter to krino - does not drag a new setting down
+// with it. The header is where settings go.
+func TestSettingAddsAboveTheRules(t *testing.T) {
+ r := openForms(t, "(path \"~/dl\")\n\n(rule \"all\"\n (move \"Out\"))\n\n(min-age 2m)\n")
+ if err := r.SetSetting("on-conflict", "skip"); err != nil {
+ t.Fatal(err)
+ }
+ if strings.Index(r.Text, "(on-conflict skip)") > strings.Index(r.Text, `(rule "all"`) {
+ t.Errorf("the new setting landed below the rules:\n%s", r.Text)
+ }
+ if diags := r.Check(); len(diags) > 0 {
+ t.Errorf("the file no longer loads: %v", diags)
+ }
+}
+
+// TestSettingRemoves: clearing a setting takes its whole line, leaving the
+// rest of the header as it was.
+func TestSettingRemoves(t *testing.T) {
+ r := openForms(t, settingsFile)
+ if err := r.SetSetting("min-age", ""); err != nil {
+ t.Fatal(err)
+ }
+ if strings.Contains(r.Text, "min-age") {
+ t.Errorf("the setting is still there:\n%s", r.Text)
+ }
+ if strings.Contains(r.Text, "leave fresh files alone") {
+ t.Errorf("the comment on its line was left stranded:\n%s", r.Text)
+ }
+ if !strings.Contains(r.Text, `(path "~/dl")`) || !strings.Contains(r.Text, ";; the directory krino sorts") {
+ t.Errorf("removing one setting took more than its line:\n%s", r.Text)
+ }
+ if diags := r.Check(); len(diags) > 0 {
+ t.Errorf("the file no longer loads: %v", diags)
+ }
+ if _, ok, _ := r.Setting("min-age"); ok {
+ t.Error("the removed setting still reads as set")
+ }
+}
+
+// TestSettingRefusesThePathAway: a directory file without (path ...) does
+// not load, so clearing it is refused rather than written.
+func TestSettingRefusesThePathAway(t *testing.T) {
+ r := openForms(t, settingsFile)
+ before := r.Text
+ if err := r.SetSetting("path", ""); err == nil {
+ t.Error("clearing the path was accepted")
+ }
+ if r.Text != before {
+ t.Error("the refused edit changed the file")
+ }
+}
+
+// TestSettingRefusesAnUnknownHead: only the forms krino.conf(5) documents
+// as directory settings can be written this way.
+func TestSettingRefusesAnUnknownHead(t *testing.T) {
+ r := openForms(t, settingsFile)
+ if err := r.SetSetting("nonsense", "1"); err == nil {
+ t.Error("an unknown setting was accepted")
+ }
+ if _, _, err := r.Setting("nonsense"); err == nil {
+ t.Error("an unknown setting was read")
+ }
+}