aboutsummaryrefslogtreecommitdiff
path: root/gui/internal/model/settings_test.go
blob: 46f8b7568b6cad68af3da61a2394647832857b98 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
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")
	}
}