1 package systemdconfig
2
3 import (
4 "errors"
5 "testing"
6
7 "github.com/stretchr/testify/assert"
8 )
9
10 var (
11 defaultSectionLines = "# This file is part of systemd.\n#\n# systemd is free software; you can redistribute it and/or modify it\n# under the terms of the GNU Lesser General Public License as published by\n# the Free Software Foundation; either version 2.1 of the License, or\n# (at your option) any later version.\n#\n# Entries in this file show the compile time defaults.\n# You can change settings by editing this file.\n# Defaults can be restored by simply deleting this file.\n#\n# See systemd.syntax(7) for details."
12 defaultSection = &Section{
13 map[string]*Field{
14 "!0": {"", "", "# This file is part of systemd."},
15 "!1": {"", "", "#"},
16 "!2": {"", "", "# systemd is free software; you can redistribute it and/or modify it"},
17 "!3": {"", "", "# under the terms of the GNU Lesser General Public License as published by"},
18 "!4": {"", "", "# the Free Software Foundation; either version 2.1 of the License, or"},
19 "!5": {"", "", "# (at your option) any later version."},
20 "!6": {"", "", "#"},
21 "!7": {"", "", "# Entries in this file show the compile time defaults."},
22 "!8": {"", "", "# You can change settings by editing this file."},
23 "!9": {"", "", "# Defaults can be restored by simply deleting this file."},
24 "!10": {"", "", "#"},
25 "!11": {"", "", "# See systemd.syntax(7) for details."},
26 },
27 []string{"!0", "!1", "!2", "!3", "!4", "!5", "!6", "!7", "!8", "!9", "!10", "!11"},
28 }
29
30 testSectionAHeader = "[Section A]"
31 testSectionALines = "KeyOne=value 1\nKeyTwo=value 2\nKeyThree=value 3\n#KeyFour=value 4 ; comment\n;KeyFive=value 5\n\nKeySix=value 6"
32 testSectionA = &Section{
33 map[string]*Field{
34 "KeyOne": {"KeyOne", "value 1", ""},
35 "KeyTwo": {"KeyTwo", "value 2", ""},
36 "KeyThree": {"KeyThree", "value 3", ""},
37 "KeyFour": {"#KeyFour", "value 4", "; comment"},
38 "KeyFive": {";KeyFive", "value 5", ""},
39 "!5": {"", "", ""},
40 "KeySix": {"KeySix", "value 6", ""},
41 },
42 []string{"KeyOne", "KeyTwo", "KeyThree", "KeyFour", "KeyFive", "!5", "KeySix"},
43 }
44 testSectionAUpdated = &Section{
45 map[string]*Field{
46 "KeyOne": {"KeyOne", "value 1", ""},
47 "KeyTwo": {"KeyTwo", "value updated", ""},
48 "KeyThree": {"KeyThree", "value 3", ""},
49 "KeyFour": {"#KeyFour", "value 4", "; comment"},
50 "KeyFive": {";KeyFive", "value 5", ""},
51 "!5": {"", "", ""},
52 "KeySix": {"KeySix", "value 6", ""},
53 },
54 []string{"KeyOne", "KeyTwo", "KeyThree", "KeyFour", "KeyFive", "!5", "KeySix"},
55 }
56 testSectionAKeyRemoved = &Section{
57 map[string]*Field{
58 "KeyOne": {"KeyOne", "value 1", ""},
59 "KeyTwo": {"KeyTwo", "value 2", ""},
60 "KeyFour": {"#KeyFour", "value 4", "; comment"},
61 "KeyFive": {";KeyFive", "value 5", ""},
62 "!5": {"", "", ""},
63 "KeySix": {"KeySix", "value 6", ""},
64 },
65 []string{"KeyOne", "KeyTwo", "KeyFour", "KeyFive", "!5", "KeySix"},
66 }
67 testSectionBHeader = "[Section B]"
68 testSectionBLines = "#KeyOne=value 1\n\nKeyTwo=value 2 # comment\n;KeyThree=value 3\n#KeyFour=value 4"
69 testSectionB = &Section{
70 map[string]*Field{
71 "KeyOne": {"#KeyOne", "value 1", ""},
72 "!1": {"", "", ""},
73 "KeyTwo": {"KeyTwo", "value 2", "# comment"},
74 "KeyThree": {";KeyThree", "value 3", ""},
75 "KeyFour": {"#KeyFour", "value 4", ""},
76 },
77 []string{"KeyOne", "!1", "KeyTwo", "KeyThree", "KeyFour"},
78 }
79 testSectionBUpdated = &Section{
80 map[string]*Field{
81 "KeyOne": {"#KeyOne", "value 1", ""},
82 "!1": {"", "", ""},
83 "KeyTwo": {"KeyTwo", "value 2", "# comment"},
84 "KeyThree": {";KeyThree", "value 3", ""},
85 "KeyFour": {"KeyFour", "value updated", ""},
86 },
87 []string{"KeyOne", "!1", "KeyTwo", "KeyThree", "KeyFour"},
88 }
89 testSectionCHeader = "[Section C]"
90 testSectionCLines = "#KeyOne=value 1\n#KeyTwo=\n#KeyThree=value 3\n#KeyFour=value 4\n# comment\n;KeyFive=value 5"
91 testSectionC = &Section{
92 map[string]*Field{
93 "KeyOne": {"#KeyOne", "value 1", ""},
94 "KeyTwo": {"#KeyTwo", "", ""},
95 "KeyThree": {"#KeyThree", "value 3", ""},
96 "KeyFour": {"#KeyFour", "value 4", ""},
97 "!4": {"", "", "# comment"},
98 "KeyFive": {";KeyFive", "value 5", ""},
99 },
100 []string{"KeyOne", "KeyTwo", "KeyThree", "KeyFour", "!4", "KeyFive"},
101 }
102 testSectionCKeyAdded = &Section{
103 map[string]*Field{
104 "KeyOne": {"#KeyOne", "value 1", ""},
105 "KeyTwo": {"#KeyTwo", "", ""},
106 "KeyThree": {"#KeyThree", "value 3", ""},
107 "KeyFour": {"#KeyFour", "value 4", ""},
108 "!4": {"", "", "# comment"},
109 "KeyFive": {";KeyFive", "value 5", ""},
110 "NewKey": {"NewKey", "value added", ""},
111 },
112 []string{"KeyOne", "KeyTwo", "KeyThree", "KeyFour", "!4", "KeyFive", "NewKey"},
113 }
114 testSectionD = &Section{map[string]*Field{}, []string{}}
115
116 testSystemdConfig = &SystemdConfig{
117 sections: map[string]*Section{defaultSectionHeader: defaultSection, testSectionAHeader: testSectionA, testSectionBHeader: testSectionB, testSectionCHeader: testSectionC},
118 orderedSectionHeaders: []string{defaultSectionHeader, testSectionAHeader, testSectionBHeader, testSectionCHeader},
119 }
120 testSystemdConfigKeyValueUpdated = &SystemdConfig{
121 sections: map[string]*Section{defaultSectionHeader: defaultSection, testSectionAHeader: testSectionAUpdated, testSectionBHeader: testSectionB, testSectionCHeader: testSectionC},
122 orderedSectionHeaders: []string{defaultSectionHeader, testSectionAHeader, testSectionBHeader, testSectionCHeader},
123 }
124 testSystemdConfigCommentedKeyValueUpdated = &SystemdConfig{
125 sections: map[string]*Section{defaultSectionHeader: defaultSection, testSectionAHeader: testSectionA, testSectionBHeader: testSectionBUpdated, testSectionCHeader: testSectionC},
126 orderedSectionHeaders: []string{defaultSectionHeader, testSectionAHeader, testSectionBHeader, testSectionCHeader},
127 }
128 testSystemdConfigKeyAdded = &SystemdConfig{
129 sections: map[string]*Section{defaultSectionHeader: defaultSection, testSectionAHeader: testSectionA, testSectionBHeader: testSectionB, testSectionCHeader: testSectionCKeyAdded},
130 orderedSectionHeaders: []string{defaultSectionHeader, testSectionAHeader, testSectionBHeader, testSectionCHeader},
131 }
132 testSystemdConfigKeyRemoved = &SystemdConfig{
133 sections: map[string]*Section{defaultSectionHeader: defaultSection, testSectionAHeader: testSectionAKeyRemoved, testSectionBHeader: testSectionB, testSectionCHeader: testSectionC},
134 orderedSectionHeaders: []string{defaultSectionHeader, testSectionAHeader, testSectionBHeader, testSectionCHeader},
135 }
136 testSystemdConfigSectionRemoved = &SystemdConfig{
137 sections: map[string]*Section{defaultSectionHeader: defaultSection, testSectionAHeader: testSectionA, testSectionCHeader: testSectionC},
138 orderedSectionHeaders: []string{defaultSectionHeader, testSectionAHeader, testSectionCHeader},
139 }
140
141 testData = []byte(defaultSectionLines + "\n\n" + testSectionAHeader + "\n" + testSectionALines + "\n\n" + testSectionBHeader + "\n" + testSectionBLines + "\n\n" + testSectionCHeader + "\n" + testSectionCLines)
142 testInvalidKey = []byte(defaultSectionLines + "\n\n" + testSectionAHeader + "\n" + "KeyOne=value 1\nKey!!!=value 2\nKeyThree=value 3")
143 testEmptyKey = []byte(defaultSectionLines + "\n\n" + testSectionAHeader + "\n" + "KeyOne=value 1\n#=value 2\nKeyThree=value 3")
144 testValueWithoutKey = []byte(defaultSectionLines + "\n\n" + testSectionAHeader + "\n" + "KeyOne=value 1\n=value 2\nKeyThree=value 3")
145 testMultiLineValueNotSupported = []byte(defaultSectionLines + "\n\n" + testSectionAHeader + "\n" + "KeyOne=value 1\nKeyTwo=value 2\nKeyThree=value 3\\\ncontinued")
146 testInvalidLine = []byte(defaultSectionLines + "\n\n" + testSectionAHeader + "\n" + "KeyOne=value 1\ninvalid line\nKeyThree=value 3")
147 )
148
149
150 func TestSyntaxError(t *testing.T) {
151 expectedErr := errors.New("context: error message")
152 err := &SyntaxError{"context", errors.New("error message")}
153 assert.Equal(t, expectedErr.Error(), err.Error())
154 }
155
156
157 func TestUnsupportedError(t *testing.T) {
158 expectedErr := errors.New("context: error message")
159 err := &UnsupportedError{"context", errors.New("error message")}
160 assert.Equal(t, expectedErr.Error(), err.Error())
161 }
162
163
164 func TestLoadFromBytes(t *testing.T) {
165 tests := []struct {
166 inputBytes []byte
167 expectedSystemConfig *SystemdConfig
168 expectedErr error
169 }{
170 {testData, testSystemdConfig, nil},
171 {testInvalidKey, nil, &SyntaxError{}},
172 {testEmptyKey, nil, &SyntaxError{}},
173 {testValueWithoutKey, nil, &SyntaxError{}},
174 {testInvalidLine, nil, &SyntaxError{}},
175 {testMultiLineValueNotSupported, nil, &SyntaxError{}},
176 }
177 for i, test := range tests {
178 systemdConfig, err := LoadFromBytes(test.inputBytes)
179 assert.Equal(t, test.expectedSystemConfig, systemdConfig)
180 if test.expectedErr != nil {
181 assert.ErrorAs(t, err, &tests[i].expectedErr)
182 } else {
183 assert.NoError(t, err)
184 }
185 }
186 }
187
188
189 func TestSectionHeaderIsValid(t *testing.T) {
190 tests := []struct {
191 inputLine string
192 expectedResult bool
193 }{
194 {"[Time]", true},
195 {"[section]", true},
196 {"not-section", false},
197 {"not[section]", false},
198 {"[[not] section]", false},
199 {"[test section]", true},
200 }
201 for _, test := range tests {
202 result := sectionHeaderIsValid(test.inputLine)
203 assert.Equal(t, test.expectedResult, result)
204 }
205 }
206
207
208 func TestBytes(t *testing.T) {
209 bytes := testSystemdConfig.Bytes()
210 assert.Equal(t, testData, bytes)
211 }
212
213
214 func TestDeepCopySystemdConfig(t *testing.T) {
215 systemdConfig := testSystemdConfig
216 systemdConfigCopied := systemdConfig.DeepCopy()
217 _ = systemdConfigCopied.Section("[Section A]").SetField("KeyOne", "value updated")
218 assert.NotEqual(t, systemdConfig, systemdConfigCopied)
219 assert.Equal(t, testSystemdConfig, systemdConfig)
220 }
221
222
223 func TestListSectionsFromSystemdConfig(t *testing.T) {
224 expectedSections := []string{testSectionAHeader, testSectionBHeader, testSectionCHeader}
225 sections := testSystemdConfig.ListSections()
226 assert.Equal(t, expectedSections, sections)
227 }
228
229
230 func TestAddSectionToSystemdConfig(t *testing.T) {
231
232 systemdConfig := testSystemdConfig.DeepCopy()
233 assert.NoError(t, systemdConfig.AddSection("[Section D]"))
234 assert.Equal(t, testSectionD, systemdConfig.Section("[Section D]"))
235
236
237 systemdConfig = testSystemdConfig.DeepCopy()
238 expectedErr := &SyntaxError{}
239 err := systemdConfig.AddSection("Section D")
240 assert.ErrorAs(t, err, &expectedErr)
241 }
242
243
244 func TestRemoveSectionFromSystemdConfig(t *testing.T) {
245
246 systemdConfig := testSystemdConfig.DeepCopy()
247 assert.NoError(t, systemdConfig.RemoveSection("[Section B]"))
248 assert.Equal(t, testSystemdConfigSectionRemoved, systemdConfig)
249
250
251 systemdConfig = testSystemdConfig.DeepCopy()
252 expectedErr := &SyntaxError{}
253 err := systemdConfig.RemoveSection("Section A")
254 assert.ErrorAs(t, err, &expectedErr)
255
256
257 systemdConfig = testSystemdConfig.DeepCopy()
258 assert.NoError(t, systemdConfig.RemoveSection("[Section D]"))
259 assert.Equal(t, testSystemdConfig, systemdConfig)
260 }
261
262
263 func TestSystemdConfigHasSection(t *testing.T) {
264 assert.True(t, testSystemdConfig.HasSection("[Section A]"))
265 assert.False(t, testSystemdConfig.HasSection("[Section D]"))
266 assert.False(t, testSystemdConfig.HasSection("Section A"))
267 assert.False(t, testSystemdConfig.HasSection(""))
268 }
269
270
271 func TestListFieldsForSection(t *testing.T) {
272 expectedKeys := []string{"KeyOne", "KeyTwo", "KeyThree", "KeyFour", "KeyFive", "KeySix"}
273 keys := testSystemdConfig.Section("[Section A]").ListFields()
274 assert.Equal(t, expectedKeys, keys)
275 }
276
277
278 func TestSetFieldInSection(t *testing.T) {
279
280 systemdConfig := testSystemdConfig.DeepCopy()
281 assert.NoError(t, systemdConfig.Section("[Section A]").SetField("KeyTwo", "value updated"))
282 assert.Equal(t, testSystemdConfigKeyValueUpdated, systemdConfig)
283
284
285 systemdConfig = testSystemdConfig.DeepCopy()
286 assert.NoError(t, systemdConfig.Section("[Section B]").SetField("KeyFour", "value updated"))
287 assert.Equal(t, testSystemdConfigCommentedKeyValueUpdated, systemdConfig)
288
289
290 systemdConfig = testSystemdConfig.DeepCopy()
291 assert.NoError(t, systemdConfig.Section("[Section C]").SetField("NewKey", "value added"))
292 assert.Equal(t, testSystemdConfigKeyAdded, systemdConfig)
293
294
295 systemdConfig = testSystemdConfig.DeepCopy()
296 expectedErr := &SyntaxError{}
297 err := systemdConfig.Section("[Section C]").SetField("NewKey!", "value added")
298 assert.ErrorAs(t, err, &expectedErr)
299 assert.False(t, systemdConfig.Section("[Section C]").HasField("NewKey!"))
300
301
302 systemdConfig = testSystemdConfig.DeepCopy()
303 expectedErr = &SyntaxError{}
304 err = systemdConfig.Section("[Section C]").SetField("NewKey", "value #added")
305 assert.ErrorAs(t, err, &expectedErr)
306 assert.False(t, systemdConfig.Section("[Section C]").HasField("NewKey"))
307
308
309 systemdConfig = testSystemdConfig.DeepCopy()
310 expectedUnsupporedErr := &UnsupportedError{}
311 err = systemdConfig.Section("[Section C]").SetField("NewKey", "value added \\")
312 assert.ErrorAs(t, err, &expectedUnsupporedErr)
313 assert.False(t, systemdConfig.Section("[Section C]").HasField("NewKey"))
314 }
315
316
317 func TestRemoveKeyFromSection(t *testing.T) {
318
319 systemdConfig := testSystemdConfig.DeepCopy()
320 assert.NoError(t, systemdConfig.Section("[Section A]").RemoveField("KeyThree"))
321 assert.Equal(t, testSystemdConfigKeyRemoved.Section("[Section A]"), systemdConfig.Section("[Section A]"))
322
323
324 systemdConfig = testSystemdConfig.DeepCopy()
325 assert.NoError(t, systemdConfig.Section("[Section A]").RemoveField("KeyTen"))
326 assert.Equal(t, testSystemdConfig, systemdConfig)
327
328
329 systemdConfig = testSystemdConfig.DeepCopy()
330 expectedErr := &SyntaxError{}
331 err := systemdConfig.Section("[Section C]").RemoveField("NewKey!")
332 assert.ErrorAs(t, err, &expectedErr)
333 }
334
335
336 func TestSectionHasKey(t *testing.T) {
337 assert.True(t, testSystemdConfig.Section("[Section A]").HasField("KeyOne"))
338 assert.False(t, testSystemdConfig.Section("[Section A]").HasField("KeyTen"))
339 assert.False(t, testSystemdConfig.Section("[Section A]").HasField(""))
340 }
341
342
343 func TestFieldHasKey(t *testing.T) {
344 assert.True(t, testSystemdConfig.Section("[Section A]").Field("KeyOne").HasKey())
345 assert.True(t, testSystemdConfig.Section("[Section A]").Field("KeyFive").HasKey())
346 assert.False(t, testSystemdConfig.Section("[Section C]").Field("!4").HasKey())
347 assert.False(t, testSystemdConfig.Section("[Section A]").Field("!5").HasKey())
348 }
349
350
351 func TestFieldKeyIsCommented(t *testing.T) {
352
353 expectedKey := "#KeyOne"
354 systemdConfig := testSystemdConfig.DeepCopy()
355 systemdConfig.Section("[Section A]").Field("KeyOne").CommentKey()
356 assert.Equal(t, expectedKey, systemdConfig.Section("[Section A]").Field("KeyOne").GetKey())
357
358
359 expectedKey = ";KeyFive"
360 systemdConfig = testSystemdConfig.DeepCopy()
361 systemdConfig.Section("[Section A]").Field("KeyFive").CommentKey()
362 assert.Equal(t, expectedKey, systemdConfig.Section("[Section A]").Field("KeyFive").GetKey())
363 }
364
365
366 func TestFieldKeyIsUncommented(t *testing.T) {
367
368 expectedKey := "KeyFive"
369 systemdConfig := testSystemdConfig.DeepCopy()
370 systemdConfig.Section("[Section A]").Field("KeyFive").UncommentKey()
371 assert.Equal(t, expectedKey, systemdConfig.Section("[Section A]").Field("KeyFive").GetKey())
372
373
374 expectedKey = "KeyOne"
375 systemdConfig = testSystemdConfig.DeepCopy()
376 systemdConfig.Section("[Section A]").Field("KeyOne").UncommentKey()
377 assert.Equal(t, expectedKey, systemdConfig.Section("[Section A]").Field("KeyOne").GetKey())
378 }
379
380
381 func TestSetValueInField(t *testing.T) {
382
383 expectedValue := "new value"
384 systemdConfig := testSystemdConfig.DeepCopy()
385 err := systemdConfig.Section("[Section A]").Field("KeyOne").SetValue(expectedValue)
386 assert.NoError(t, err)
387 assert.Equal(t, expectedValue, systemdConfig.Section("[Section A]").Field("KeyOne").GetValue())
388
389
390 expectedValue = ""
391 systemdConfig = testSystemdConfig.DeepCopy()
392 err = systemdConfig.Section("[Section A]").Field("KeyOne").SetValue(expectedValue)
393 assert.NoError(t, err)
394 assert.Equal(t, expectedValue, systemdConfig.Section("[Section A]").Field("KeyOne").GetValue())
395
396
397 invalidValue := "new #value"
398 expectedErr := &SyntaxError{}
399 systemdConfig = testSystemdConfig.DeepCopy()
400 err = systemdConfig.Section("[Section A]").Field("KeyOne").SetValue(invalidValue)
401 assert.ErrorAs(t, err, &expectedErr)
402 assert.Equal(t, testSystemdConfig.Section("[Section A]").Field("KeyOne").GetValue(), systemdConfig.Section("[Section A]").Field("KeyOne").GetValue())
403
404
405 multiLineValue := "new value \\"
406 expectedUnsupporedErr := &UnsupportedError{}
407 systemdConfig = testSystemdConfig.DeepCopy()
408 err = systemdConfig.Section("[Section A]").Field("KeyOne").SetValue(multiLineValue)
409 assert.ErrorAs(t, err, &expectedUnsupporedErr)
410 assert.Equal(t, testSystemdConfig.Section("[Section A]").Field("KeyOne").GetValue(), systemdConfig.Section("[Section A]").Field("KeyOne").GetValue())
411 }
412
413
414 func TestRemoveValueFromField(t *testing.T) {
415
416 systemdConfig := testSystemdConfig.DeepCopy()
417 systemdConfig.Section("[Section A]").Field("KeyOne").RemoveValue()
418 assert.Equal(t, "", systemdConfig.Section("[Section A]").Field("KeyOne").GetValue())
419
420
421 systemdConfig = testSystemdConfig.DeepCopy()
422 systemdConfig.Section("[Section C]").Field("KeyTwo").RemoveValue()
423 assert.Equal(t, testSystemdConfig.Section("[Section C]").Field("KeyTwo").GetValue(), systemdConfig.Section("[Section C]").Field("KeyTwo").GetValue())
424 }
425
426
427 func TestFieldHasValue(t *testing.T) {
428 assert.True(t, testSystemdConfig.Section("[Section A]").Field("KeyOne").HasValue())
429 assert.False(t, testSystemdConfig.Section("[Section C]").Field("KeyTwo").HasValue())
430 }
431
432
433 func TestSetCommentInField(t *testing.T) {
434 expectedComment := "# new comment"
435 systemdConfig := testSystemdConfig.DeepCopy()
436 systemdConfig.Section("[Section A]").Field("KeyOne").SetComment("new comment")
437 assert.Equal(t, expectedComment, systemdConfig.Section("[Section A]").Field("KeyOne").GetComment())
438 }
439
440
441 func TestRemoveCommentFromField(t *testing.T) {
442
443 systemdConfig := testSystemdConfig.DeepCopy()
444 systemdConfig.Section("[Section A]").Field("KeyFour").RemoveComment()
445 assert.Equal(t, "", systemdConfig.Section("[Section A]").Field("KeyFour").GetComment())
446
447
448 systemdConfig = testSystemdConfig.DeepCopy()
449 systemdConfig.Section("[Section A]").Field("!5").RemoveComment()
450 assert.True(t, systemdConfig.Section("[Section A]").Field("!5").IsEmptyLine())
451
452
453 systemdConfig = testSystemdConfig.DeepCopy()
454 systemdConfig.Section("[Section A]").Field("KeyOne").RemoveComment()
455 assert.Equal(t, testSystemdConfig.Section("[Section A]").Field("KeyOne").GetComment(), systemdConfig.Section("[Section A]").Field("KeyOne").GetComment())
456 }
457
458
459 func TestFieldHasComment(t *testing.T) {
460 assert.False(t, testSystemdConfig.Section("[Section A]").Field("KeyOne").HasComment())
461 assert.False(t, testSystemdConfig.Section("[Section A]").Field("!5").HasComment())
462 assert.True(t, testSystemdConfig.Section("[Section A]").Field("KeyFour").HasComment())
463 assert.True(t, testSystemdConfig.Section("[Section C]").Field("!4").HasComment())
464 }
465
View as plain text