1// Copyright 2018 Frank Schroeder. All rights reserved.
2// Use of this source code is governed by a BSD-style
3// license that can be found in the LICENSE file.
4
5package properties
6
7import (
8	"bytes"
9	"flag"
10	"fmt"
11	"math"
12	"os"
13	"reflect"
14	"strings"
15	"testing"
16	"time"
17
18	"github.com/magiconair/properties/assert"
19)
20
21var verbose = flag.Bool("verbose", false, "Verbose output")
22
23func init() {
24	ErrorHandler = PanicHandler
25}
26
27// ----------------------------------------------------------------------------
28
29// define test cases in the form of
30// {"input", "key1", "value1", "key2", "value2", ...}
31var complexTests = [][]string{
32	// whitespace prefix
33	{" key=value", "key", "value"},     // SPACE prefix
34	{"\fkey=value", "key", "value"},    // FF prefix
35	{"\tkey=value", "key", "value"},    // TAB prefix
36	{" \f\tkey=value", "key", "value"}, // mix prefix
37
38	// multiple keys
39	{"key1=value1\nkey2=value2\n", "key1", "value1", "key2", "value2"},
40	{"key1=value1\rkey2=value2\r", "key1", "value1", "key2", "value2"},
41	{"key1=value1\r\nkey2=value2\r\n", "key1", "value1", "key2", "value2"},
42
43	// blank lines
44	{"\nkey=value\n", "key", "value"},
45	{"\rkey=value\r", "key", "value"},
46	{"\r\nkey=value\r\n", "key", "value"},
47	{"\nkey=value\n \nkey2=value2", "key", "value", "key2", "value2"},
48	{"\nkey=value\n\t\nkey2=value2", "key", "value", "key2", "value2"},
49
50	// escaped chars in key
51	{"k\\ ey = value", "k ey", "value"},
52	{"k\\:ey = value", "k:ey", "value"},
53	{"k\\=ey = value", "k=ey", "value"},
54	{"k\\fey = value", "k\fey", "value"},
55	{"k\\ney = value", "k\ney", "value"},
56	{"k\\rey = value", "k\rey", "value"},
57	{"k\\tey = value", "k\tey", "value"},
58
59	// escaped chars in value
60	{"key = v\\ alue", "key", "v alue"},
61	{"key = v\\:alue", "key", "v:alue"},
62	{"key = v\\=alue", "key", "v=alue"},
63	{"key = v\\falue", "key", "v\falue"},
64	{"key = v\\nalue", "key", "v\nalue"},
65	{"key = v\\ralue", "key", "v\ralue"},
66	{"key = v\\talue", "key", "v\talue"},
67
68	// silently dropped escape character
69	{"k\\zey = value", "kzey", "value"},
70	{"key = v\\zalue", "key", "vzalue"},
71
72	// unicode literals
73	{"key\\u2318 = value", "key⌘", "value"},
74	{"k\\u2318ey = value", "k⌘ey", "value"},
75	{"key = value\\u2318", "key", "value⌘"},
76	{"key = valu\\u2318e", "key", "valu⌘e"},
77
78	// multiline values
79	{"key = valueA,\\\n    valueB", "key", "valueA,valueB"},   // SPACE indent
80	{"key = valueA,\\\n\f\f\fvalueB", "key", "valueA,valueB"}, // FF indent
81	{"key = valueA,\\\n\t\t\tvalueB", "key", "valueA,valueB"}, // TAB indent
82	{"key = valueA,\\\n \f\tvalueB", "key", "valueA,valueB"},  // mix indent
83
84	// comments
85	{"# this is a comment\n! and so is this\nkey1=value1\nkey#2=value#2\n\nkey!3=value!3\n# and another one\n! and the final one", "key1", "value1", "key#2", "value#2", "key!3", "value!3"},
86
87	// expansion tests
88	{"key=value\nkey2=${key}", "key", "value", "key2", "value"},
89	{"key=value\nkey2=aa${key}", "key", "value", "key2", "aavalue"},
90	{"key=value\nkey2=${key}bb", "key", "value", "key2", "valuebb"},
91	{"key=value\nkey2=aa${key}bb", "key", "value", "key2", "aavaluebb"},
92	{"key=value\nkey2=${key}\nkey3=${key2}", "key", "value", "key2", "value", "key3", "value"},
93	{"key=value\nkey2=${key}${key}", "key", "value", "key2", "valuevalue"},
94	{"key=value\nkey2=${key}${key}${key}${key}", "key", "value", "key2", "valuevaluevaluevalue"},
95	{"key=value\nkey2=${key}${key3}\nkey3=${key}", "key", "value", "key2", "valuevalue", "key3", "value"},
96	{"key=value\nkey2=${key3}${key}${key4}\nkey3=${key}\nkey4=${key}", "key", "value", "key2", "valuevaluevalue", "key3", "value", "key4", "value"},
97	{"key=${USER}", "key", os.Getenv("USER")},
98	{"key=${USER}\nUSER=value", "key", "value", "USER", "value"},
99}
100
101// ----------------------------------------------------------------------------
102
103var commentTests = []struct {
104	input, key, value string
105	comments          []string
106}{
107	{"key=value", "key", "value", nil},
108	{"#\nkey=value", "key", "value", []string{""}},
109	{"#comment\nkey=value", "key", "value", []string{"comment"}},
110	{"# comment\nkey=value", "key", "value", []string{"comment"}},
111	{"#  comment\nkey=value", "key", "value", []string{"comment"}},
112	{"# comment\n\nkey=value", "key", "value", []string{"comment"}},
113	{"# comment1\n# comment2\nkey=value", "key", "value", []string{"comment1", "comment2"}},
114	{"# comment1\n\n# comment2\n\nkey=value", "key", "value", []string{"comment1", "comment2"}},
115	{"!comment\nkey=value", "key", "value", []string{"comment"}},
116	{"! comment\nkey=value", "key", "value", []string{"comment"}},
117	{"!  comment\nkey=value", "key", "value", []string{"comment"}},
118	{"! comment\n\nkey=value", "key", "value", []string{"comment"}},
119	{"! comment1\n! comment2\nkey=value", "key", "value", []string{"comment1", "comment2"}},
120	{"! comment1\n\n! comment2\n\nkey=value", "key", "value", []string{"comment1", "comment2"}},
121}
122
123// ----------------------------------------------------------------------------
124
125var errorTests = []struct {
126	input, msg string
127}{
128	// unicode literals
129	{"key\\u1 = value", "invalid unicode literal"},
130	{"key\\u12 = value", "invalid unicode literal"},
131	{"key\\u123 = value", "invalid unicode literal"},
132	{"key\\u123g = value", "invalid unicode literal"},
133	{"key\\u123", "invalid unicode literal"},
134
135	// circular references
136	{"key=${key}", "circular reference"},
137	{"key1=${key2}\nkey2=${key1}", "circular reference"},
138
139	// malformed expressions
140	{"key=${ke", "malformed expression"},
141	{"key=valu${ke", "malformed expression"},
142}
143
144// ----------------------------------------------------------------------------
145
146var writeTests = []struct {
147	input, output, encoding string
148}{
149	// ISO-8859-1 tests
150	{"key = value", "key = value\n", "ISO-8859-1"},
151	{"key = value \\\n   continued", "key = value continued\n", "ISO-8859-1"},
152	{"key⌘ = value", "key\\u2318 = value\n", "ISO-8859-1"},
153	{"ke\\ \\:y = value", "ke\\ \\:y = value\n", "ISO-8859-1"},
154
155	// UTF-8 tests
156	{"key = value", "key = value\n", "UTF-8"},
157	{"key = value \\\n   continued", "key = value continued\n", "UTF-8"},
158	{"key⌘ = value⌘", "key⌘ = value⌘\n", "UTF-8"},
159	{"ke\\ \\:y = value", "ke\\ \\:y = value\n", "UTF-8"},
160}
161
162// ----------------------------------------------------------------------------
163
164var writeCommentTests = []struct {
165	input, output, encoding string
166}{
167	// ISO-8859-1 tests
168	{"key = value", "key = value\n", "ISO-8859-1"},
169	{"#\nkey = value", "key = value\n", "ISO-8859-1"},
170	{"#\n#\n#\nkey = value", "key = value\n", "ISO-8859-1"},
171	{"# comment\nkey = value", "# comment\nkey = value\n", "ISO-8859-1"},
172	{"\n# comment\nkey = value", "# comment\nkey = value\n", "ISO-8859-1"},
173	{"# comment\n\nkey = value", "# comment\nkey = value\n", "ISO-8859-1"},
174	{"# comment1\n# comment2\nkey = value", "# comment1\n# comment2\nkey = value\n", "ISO-8859-1"},
175	{"#comment1\nkey1 = value1\n#comment2\nkey2 = value2", "# comment1\nkey1 = value1\n\n# comment2\nkey2 = value2\n", "ISO-8859-1"},
176
177	// UTF-8 tests
178	{"key = value", "key = value\n", "UTF-8"},
179	{"# comment⌘\nkey = value⌘", "# comment⌘\nkey = value⌘\n", "UTF-8"},
180	{"\n# comment⌘\nkey = value⌘", "# comment⌘\nkey = value⌘\n", "UTF-8"},
181	{"# comment⌘\n\nkey = value⌘", "# comment⌘\nkey = value⌘\n", "UTF-8"},
182	{"# comment1⌘\n# comment2⌘\nkey = value⌘", "# comment1⌘\n# comment2⌘\nkey = value⌘\n", "UTF-8"},
183	{"#comment1⌘\nkey1 = value1⌘\n#comment2⌘\nkey2 = value2⌘", "# comment1⌘\nkey1 = value1⌘\n\n# comment2⌘\nkey2 = value2⌘\n", "UTF-8"},
184}
185
186// ----------------------------------------------------------------------------
187
188var boolTests = []struct {
189	input, key string
190	def, value bool
191}{
192	// valid values for TRUE
193	{"key = 1", "key", false, true},
194	{"key = on", "key", false, true},
195	{"key = On", "key", false, true},
196	{"key = ON", "key", false, true},
197	{"key = true", "key", false, true},
198	{"key = True", "key", false, true},
199	{"key = TRUE", "key", false, true},
200	{"key = yes", "key", false, true},
201	{"key = Yes", "key", false, true},
202	{"key = YES", "key", false, true},
203
204	// valid values for FALSE (all other)
205	{"key = 0", "key", true, false},
206	{"key = off", "key", true, false},
207	{"key = false", "key", true, false},
208	{"key = no", "key", true, false},
209
210	// non existent key
211	{"key = true", "key2", false, false},
212}
213
214// ----------------------------------------------------------------------------
215
216var durationTests = []struct {
217	input, key string
218	def, value time.Duration
219}{
220	// valid values
221	{"key = 1", "key", 999, 1},
222	{"key = 0", "key", 999, 0},
223	{"key = -1", "key", 999, -1},
224	{"key = 0123", "key", 999, 123},
225
226	// invalid values
227	{"key = 0xff", "key", 999, 999},
228	{"key = 1.0", "key", 999, 999},
229	{"key = a", "key", 999, 999},
230
231	// non existent key
232	{"key = 1", "key2", 999, 999},
233}
234
235// ----------------------------------------------------------------------------
236
237var parsedDurationTests = []struct {
238	input, key string
239	def, value time.Duration
240}{
241	// valid values
242	{"key = -1ns", "key", 999, -1 * time.Nanosecond},
243	{"key = 300ms", "key", 999, 300 * time.Millisecond},
244	{"key = 5s", "key", 999, 5 * time.Second},
245	{"key = 3h", "key", 999, 3 * time.Hour},
246	{"key = 2h45m", "key", 999, 2*time.Hour + 45*time.Minute},
247
248	// invalid values
249	{"key = 0xff", "key", 999, 999},
250	{"key = 1.0", "key", 999, 999},
251	{"key = a", "key", 999, 999},
252	{"key = 1", "key", 999, 999},
253	{"key = 0", "key", 999, 0},
254
255	// non existent key
256	{"key = 1", "key2", 999, 999},
257}
258
259// ----------------------------------------------------------------------------
260
261var floatTests = []struct {
262	input, key string
263	def, value float64
264}{
265	// valid values
266	{"key = 1.0", "key", 999, 1.0},
267	{"key = 0.0", "key", 999, 0.0},
268	{"key = -1.0", "key", 999, -1.0},
269	{"key = 1", "key", 999, 1},
270	{"key = 0", "key", 999, 0},
271	{"key = -1", "key", 999, -1},
272	{"key = 0123", "key", 999, 123},
273
274	// invalid values
275	{"key = 0xff", "key", 999, 999},
276	{"key = a", "key", 999, 999},
277
278	// non existent key
279	{"key = 1", "key2", 999, 999},
280}
281
282// ----------------------------------------------------------------------------
283
284var int64Tests = []struct {
285	input, key string
286	def, value int64
287}{
288	// valid values
289	{"key = 1", "key", 999, 1},
290	{"key = 0", "key", 999, 0},
291	{"key = -1", "key", 999, -1},
292	{"key = 0123", "key", 999, 123},
293
294	// invalid values
295	{"key = 0xff", "key", 999, 999},
296	{"key = 1.0", "key", 999, 999},
297	{"key = a", "key", 999, 999},
298
299	// non existent key
300	{"key = 1", "key2", 999, 999},
301}
302
303// ----------------------------------------------------------------------------
304
305var uint64Tests = []struct {
306	input, key string
307	def, value uint64
308}{
309	// valid values
310	{"key = 1", "key", 999, 1},
311	{"key = 0", "key", 999, 0},
312	{"key = 0123", "key", 999, 123},
313
314	// invalid values
315	{"key = -1", "key", 999, 999},
316	{"key = 0xff", "key", 999, 999},
317	{"key = 1.0", "key", 999, 999},
318	{"key = a", "key", 999, 999},
319
320	// non existent key
321	{"key = 1", "key2", 999, 999},
322}
323
324// ----------------------------------------------------------------------------
325
326var stringTests = []struct {
327	input, key string
328	def, value string
329}{
330	// valid values
331	{"key = abc", "key", "def", "abc"},
332
333	// non existent key
334	{"key = abc", "key2", "def", "def"},
335}
336
337// ----------------------------------------------------------------------------
338
339var keysTests = []struct {
340	input string
341	keys  []string
342}{
343	{"", []string{}},
344	{"key = abc", []string{"key"}},
345	{"key = abc\nkey2=def", []string{"key", "key2"}},
346	{"key2 = abc\nkey=def", []string{"key2", "key"}},
347	{"key = abc\nkey=def", []string{"key"}},
348}
349
350// ----------------------------------------------------------------------------
351
352var filterTests = []struct {
353	input   string
354	pattern string
355	keys    []string
356	err     string
357}{
358	{"", "", []string{}, ""},
359	{"", "abc", []string{}, ""},
360	{"key=value", "", []string{"key"}, ""},
361	{"key=value", "key=", []string{}, ""},
362	{"key=value\nfoo=bar", "", []string{"foo", "key"}, ""},
363	{"key=value\nfoo=bar", "f", []string{"foo"}, ""},
364	{"key=value\nfoo=bar", "fo", []string{"foo"}, ""},
365	{"key=value\nfoo=bar", "foo", []string{"foo"}, ""},
366	{"key=value\nfoo=bar", "fooo", []string{}, ""},
367	{"key=value\nkey2=value2\nfoo=bar", "ey", []string{"key", "key2"}, ""},
368	{"key=value\nkey2=value2\nfoo=bar", "key", []string{"key", "key2"}, ""},
369	{"key=value\nkey2=value2\nfoo=bar", "^key", []string{"key", "key2"}, ""},
370	{"key=value\nkey2=value2\nfoo=bar", "^(key|foo)", []string{"foo", "key", "key2"}, ""},
371	{"key=value\nkey2=value2\nfoo=bar", "[ abc", nil, "error parsing regexp.*"},
372}
373
374// ----------------------------------------------------------------------------
375
376var filterPrefixTests = []struct {
377	input  string
378	prefix string
379	keys   []string
380}{
381	{"", "", []string{}},
382	{"", "abc", []string{}},
383	{"key=value", "", []string{"key"}},
384	{"key=value", "key=", []string{}},
385	{"key=value\nfoo=bar", "", []string{"foo", "key"}},
386	{"key=value\nfoo=bar", "f", []string{"foo"}},
387	{"key=value\nfoo=bar", "fo", []string{"foo"}},
388	{"key=value\nfoo=bar", "foo", []string{"foo"}},
389	{"key=value\nfoo=bar", "fooo", []string{}},
390	{"key=value\nkey2=value2\nfoo=bar", "key", []string{"key", "key2"}},
391}
392
393// ----------------------------------------------------------------------------
394
395var filterStripPrefixTests = []struct {
396	input  string
397	prefix string
398	keys   []string
399}{
400	{"", "", []string{}},
401	{"", "abc", []string{}},
402	{"key=value", "", []string{"key"}},
403	{"key=value", "key=", []string{}},
404	{"key=value\nfoo=bar", "", []string{"foo", "key"}},
405	{"key=value\nfoo=bar", "f", []string{"foo"}},
406	{"key=value\nfoo=bar", "fo", []string{"foo"}},
407	{"key=value\nfoo=bar", "foo", []string{"foo"}},
408	{"key=value\nfoo=bar", "fooo", []string{}},
409	{"key=value\nkey2=value2\nfoo=bar", "key", []string{"key", "key2"}},
410}
411
412// ----------------------------------------------------------------------------
413
414var setTests = []struct {
415	input      string
416	key, value string
417	prev       string
418	ok         bool
419	err        string
420	keys       []string
421}{
422	{"", "", "", "", false, "", []string{}},
423	{"", "key", "value", "", false, "", []string{"key"}},
424	{"key=value", "key2", "value2", "", false, "", []string{"key", "key2"}},
425	{"key=value", "abc", "value3", "", false, "", []string{"key", "abc"}},
426	{"key=value", "key", "value3", "value", true, "", []string{"key"}},
427}
428
429// ----------------------------------------------------------------------------
430
431// TestBasic tests basic single key/value combinations with all possible
432// whitespace, delimiter and newline permutations.
433func TestBasic(t *testing.T) {
434	testWhitespaceAndDelimiterCombinations(t, "key", "")
435	testWhitespaceAndDelimiterCombinations(t, "key", "value")
436	testWhitespaceAndDelimiterCombinations(t, "key", "value   ")
437}
438
439func TestComplex(t *testing.T) {
440	for _, test := range complexTests {
441		testKeyValue(t, test[0], test[1:]...)
442	}
443}
444
445func TestErrors(t *testing.T) {
446	for _, test := range errorTests {
447		_, err := Load([]byte(test.input), ISO_8859_1)
448		assert.Equal(t, err != nil, true, "want error")
449		assert.Equal(t, strings.Contains(err.Error(), test.msg), true)
450	}
451}
452
453func TestVeryDeep(t *testing.T) {
454	input := "key0=value\n"
455	prefix := "${"
456	postfix := "}"
457	i := 0
458	for i = 0; i < maxExpansionDepth-1; i++ {
459		input += fmt.Sprintf("key%d=%skey%d%s\n", i+1, prefix, i, postfix)
460	}
461
462	p, err := Load([]byte(input), ISO_8859_1)
463	assert.Equal(t, err, nil)
464	p.Prefix = prefix
465	p.Postfix = postfix
466
467	assert.Equal(t, p.MustGet(fmt.Sprintf("key%d", i)), "value")
468
469	// Nudge input over the edge
470	input += fmt.Sprintf("key%d=%skey%d%s\n", i+1, prefix, i, postfix)
471
472	_, err = Load([]byte(input), ISO_8859_1)
473	assert.Equal(t, err != nil, true, "want error")
474	assert.Equal(t, strings.Contains(err.Error(), "expansion too deep"), true)
475}
476
477func TestDisableExpansion(t *testing.T) {
478	input := "key=value\nkey2=${key}"
479	p := mustParse(t, input)
480	p.DisableExpansion = true
481	assert.Equal(t, p.MustGet("key"), "value")
482	assert.Equal(t, p.MustGet("key2"), "${key}")
483
484	// with expansion disabled we can introduce circular references
485	p.MustSet("keyA", "${keyB}")
486	p.MustSet("keyB", "${keyA}")
487	assert.Equal(t, p.MustGet("keyA"), "${keyB}")
488	assert.Equal(t, p.MustGet("keyB"), "${keyA}")
489}
490
491func TestDisableExpansionStillUpdatesKeys(t *testing.T) {
492	p := NewProperties()
493	p.MustSet("p1", "a")
494	assert.Equal(t, p.Keys(), []string{"p1"})
495	assert.Equal(t, p.String(), "p1 = a\n")
496
497	p.DisableExpansion = true
498	p.MustSet("p2", "b")
499
500	assert.Equal(t, p.Keys(), []string{"p1", "p2"})
501	assert.Equal(t, p.String(), "p1 = a\np2 = b\n")
502}
503
504func TestMustGet(t *testing.T) {
505	input := "key = value\nkey2 = ghi"
506	p := mustParse(t, input)
507	assert.Equal(t, p.MustGet("key"), "value")
508	assert.Panic(t, func() { p.MustGet("invalid") }, "unknown property: invalid")
509}
510
511func TestGetBool(t *testing.T) {
512	for _, test := range boolTests {
513		p := mustParse(t, test.input)
514		assert.Equal(t, p.Len(), 1)
515		assert.Equal(t, p.GetBool(test.key, test.def), test.value)
516	}
517}
518
519func TestMustGetBool(t *testing.T) {
520	input := "key = true\nkey2 = ghi"
521	p := mustParse(t, input)
522	assert.Equal(t, p.MustGetBool("key"), true)
523	assert.Panic(t, func() { p.MustGetBool("invalid") }, "unknown property: invalid")
524}
525
526func TestGetDuration(t *testing.T) {
527	for _, test := range durationTests {
528		p := mustParse(t, test.input)
529		assert.Equal(t, p.Len(), 1)
530		assert.Equal(t, p.GetDuration(test.key, test.def), test.value)
531	}
532}
533
534func TestMustGetDuration(t *testing.T) {
535	input := "key = 123\nkey2 = ghi"
536	p := mustParse(t, input)
537	assert.Equal(t, p.MustGetDuration("key"), time.Duration(123))
538	assert.Panic(t, func() { p.MustGetDuration("key2") }, "strconv.ParseInt: parsing.*")
539	assert.Panic(t, func() { p.MustGetDuration("invalid") }, "unknown property: invalid")
540}
541
542func TestGetParsedDuration(t *testing.T) {
543	for _, test := range parsedDurationTests {
544		p := mustParse(t, test.input)
545		assert.Equal(t, p.Len(), 1)
546		assert.Equal(t, p.GetParsedDuration(test.key, test.def), test.value)
547	}
548}
549
550func TestMustGetParsedDuration(t *testing.T) {
551	input := "key = 123ms\nkey2 = ghi"
552	p := mustParse(t, input)
553	assert.Equal(t, p.MustGetParsedDuration("key"), 123*time.Millisecond)
554	assert.Panic(t, func() { p.MustGetParsedDuration("key2") }, "time: invalid duration ghi")
555	assert.Panic(t, func() { p.MustGetParsedDuration("invalid") }, "unknown property: invalid")
556}
557
558func TestGetFloat64(t *testing.T) {
559	for _, test := range floatTests {
560		p := mustParse(t, test.input)
561		assert.Equal(t, p.Len(), 1)
562		assert.Equal(t, p.GetFloat64(test.key, test.def), test.value)
563	}
564}
565
566func TestMustGetFloat64(t *testing.T) {
567	input := "key = 123\nkey2 = ghi"
568	p := mustParse(t, input)
569	assert.Equal(t, p.MustGetFloat64("key"), float64(123))
570	assert.Panic(t, func() { p.MustGetFloat64("key2") }, "strconv.ParseFloat: parsing.*")
571	assert.Panic(t, func() { p.MustGetFloat64("invalid") }, "unknown property: invalid")
572}
573
574func TestGetInt(t *testing.T) {
575	for _, test := range int64Tests {
576		p := mustParse(t, test.input)
577		assert.Equal(t, p.Len(), 1)
578		assert.Equal(t, p.GetInt(test.key, int(test.def)), int(test.value))
579	}
580}
581
582func TestMustGetInt(t *testing.T) {
583	input := "key = 123\nkey2 = ghi"
584	p := mustParse(t, input)
585	assert.Equal(t, p.MustGetInt("key"), int(123))
586	assert.Panic(t, func() { p.MustGetInt("key2") }, "strconv.ParseInt: parsing.*")
587	assert.Panic(t, func() { p.MustGetInt("invalid") }, "unknown property: invalid")
588}
589
590func TestGetInt64(t *testing.T) {
591	for _, test := range int64Tests {
592		p := mustParse(t, test.input)
593		assert.Equal(t, p.Len(), 1)
594		assert.Equal(t, p.GetInt64(test.key, test.def), test.value)
595	}
596}
597
598func TestMustGetInt64(t *testing.T) {
599	input := "key = 123\nkey2 = ghi"
600	p := mustParse(t, input)
601	assert.Equal(t, p.MustGetInt64("key"), int64(123))
602	assert.Panic(t, func() { p.MustGetInt64("key2") }, "strconv.ParseInt: parsing.*")
603	assert.Panic(t, func() { p.MustGetInt64("invalid") }, "unknown property: invalid")
604}
605
606func TestGetUint(t *testing.T) {
607	for _, test := range uint64Tests {
608		p := mustParse(t, test.input)
609		assert.Equal(t, p.Len(), 1)
610		assert.Equal(t, p.GetUint(test.key, uint(test.def)), uint(test.value))
611	}
612}
613
614func TestMustGetUint(t *testing.T) {
615	input := "key = 123\nkey2 = ghi"
616	p := mustParse(t, input)
617	assert.Equal(t, p.MustGetUint("key"), uint(123))
618	assert.Panic(t, func() { p.MustGetUint64("key2") }, "strconv.ParseUint: parsing.*")
619	assert.Panic(t, func() { p.MustGetUint64("invalid") }, "unknown property: invalid")
620}
621
622func TestGetUint64(t *testing.T) {
623	for _, test := range uint64Tests {
624		p := mustParse(t, test.input)
625		assert.Equal(t, p.Len(), 1)
626		assert.Equal(t, p.GetUint64(test.key, test.def), test.value)
627	}
628}
629
630func TestMustGetUint64(t *testing.T) {
631	input := "key = 123\nkey2 = ghi"
632	p := mustParse(t, input)
633	assert.Equal(t, p.MustGetUint64("key"), uint64(123))
634	assert.Panic(t, func() { p.MustGetUint64("key2") }, "strconv.ParseUint: parsing.*")
635	assert.Panic(t, func() { p.MustGetUint64("invalid") }, "unknown property: invalid")
636}
637
638func TestGetString(t *testing.T) {
639	for _, test := range stringTests {
640		p := mustParse(t, test.input)
641		assert.Equal(t, p.Len(), 1)
642		assert.Equal(t, p.GetString(test.key, test.def), test.value)
643	}
644}
645
646func TestMustGetString(t *testing.T) {
647	input := `key = value`
648	p := mustParse(t, input)
649	assert.Equal(t, p.MustGetString("key"), "value")
650	assert.Panic(t, func() { p.MustGetString("invalid") }, "unknown property: invalid")
651}
652
653func TestComment(t *testing.T) {
654	for _, test := range commentTests {
655		p := mustParse(t, test.input)
656		assert.Equal(t, p.MustGetString(test.key), test.value)
657		assert.Equal(t, p.GetComments(test.key), test.comments)
658		if test.comments != nil {
659			assert.Equal(t, p.GetComment(test.key), test.comments[len(test.comments)-1])
660		} else {
661			assert.Equal(t, p.GetComment(test.key), "")
662		}
663
664		// test setting comments
665		if len(test.comments) > 0 {
666			// set single comment
667			p.ClearComments()
668			assert.Equal(t, len(p.c), 0)
669			p.SetComment(test.key, test.comments[0])
670			assert.Equal(t, p.GetComment(test.key), test.comments[0])
671
672			// set multiple comments
673			p.ClearComments()
674			assert.Equal(t, len(p.c), 0)
675			p.SetComments(test.key, test.comments)
676			assert.Equal(t, p.GetComments(test.key), test.comments)
677
678			// clear comments for a key
679			p.SetComments(test.key, nil)
680			assert.Equal(t, p.GetComment(test.key), "")
681			assert.Equal(t, p.GetComments(test.key), ([]string)(nil))
682		}
683	}
684}
685
686func TestFilter(t *testing.T) {
687	for _, test := range filterTests {
688		p := mustParse(t, test.input)
689		pp, err := p.Filter(test.pattern)
690		if err != nil {
691			assert.Matches(t, err.Error(), test.err)
692			continue
693		}
694		assert.Equal(t, pp != nil, true, "want properties")
695		assert.Equal(t, pp.Len(), len(test.keys))
696		for _, key := range test.keys {
697			v1, ok1 := p.Get(key)
698			v2, ok2 := pp.Get(key)
699			assert.Equal(t, ok1, true)
700			assert.Equal(t, ok2, true)
701			assert.Equal(t, v1, v2)
702		}
703	}
704}
705
706func TestFilterPrefix(t *testing.T) {
707	for _, test := range filterPrefixTests {
708		p := mustParse(t, test.input)
709		pp := p.FilterPrefix(test.prefix)
710		assert.Equal(t, pp != nil, true, "want properties")
711		assert.Equal(t, pp.Len(), len(test.keys))
712		for _, key := range test.keys {
713			v1, ok1 := p.Get(key)
714			v2, ok2 := pp.Get(key)
715			assert.Equal(t, ok1, true)
716			assert.Equal(t, ok2, true)
717			assert.Equal(t, v1, v2)
718		}
719	}
720}
721
722func TestFilterStripPrefix(t *testing.T) {
723	for _, test := range filterStripPrefixTests {
724		p := mustParse(t, test.input)
725		pp := p.FilterPrefix(test.prefix)
726		assert.Equal(t, pp != nil, true, "want properties")
727		assert.Equal(t, pp.Len(), len(test.keys))
728		for _, key := range test.keys {
729			v1, ok1 := p.Get(key)
730			v2, ok2 := pp.Get(key)
731			assert.Equal(t, ok1, true)
732			assert.Equal(t, ok2, true)
733			assert.Equal(t, v1, v2)
734		}
735	}
736}
737
738func TestKeys(t *testing.T) {
739	for _, test := range keysTests {
740		p := mustParse(t, test.input)
741		assert.Equal(t, p.Len(), len(test.keys))
742		assert.Equal(t, len(p.Keys()), len(test.keys))
743		assert.Equal(t, p.Keys(), test.keys)
744	}
745}
746
747func TestSet(t *testing.T) {
748	for _, test := range setTests {
749		p := mustParse(t, test.input)
750		prev, ok, err := p.Set(test.key, test.value)
751		if test.err != "" {
752			assert.Matches(t, err.Error(), test.err)
753			continue
754		}
755
756		assert.Equal(t, err, nil)
757		assert.Equal(t, ok, test.ok)
758		if ok {
759			assert.Equal(t, prev, test.prev)
760		}
761		assert.Equal(t, p.Keys(), test.keys)
762	}
763}
764
765func TestSetValue(t *testing.T) {
766	tests := []interface{}{
767		true, false,
768		int8(123), int16(123), int32(123), int64(123), int(123),
769		uint8(123), uint16(123), uint32(123), uint64(123), uint(123),
770		float32(1.23), float64(1.23),
771		"abc",
772	}
773
774	for _, v := range tests {
775		p := NewProperties()
776		err := p.SetValue("x", v)
777		assert.Equal(t, err, nil)
778		assert.Equal(t, p.GetString("x", ""), fmt.Sprintf("%v", v))
779	}
780}
781
782func TestMustSet(t *testing.T) {
783	input := "key=${key}"
784	p := mustParse(t, input)
785	assert.Panic(t, func() { p.MustSet("key", "${key}") }, "circular reference .*")
786}
787
788func TestWrite(t *testing.T) {
789	for _, test := range writeTests {
790		p, err := parse(test.input)
791
792		buf := new(bytes.Buffer)
793		var n int
794		switch test.encoding {
795		case "UTF-8":
796			n, err = p.Write(buf, UTF8)
797		case "ISO-8859-1":
798			n, err = p.Write(buf, ISO_8859_1)
799		}
800		assert.Equal(t, err, nil)
801		s := string(buf.Bytes())
802		assert.Equal(t, n, len(test.output), fmt.Sprintf("input=%q expected=%q obtained=%q", test.input, test.output, s))
803		assert.Equal(t, s, test.output, fmt.Sprintf("input=%q expected=%q obtained=%q", test.input, test.output, s))
804	}
805}
806
807func TestWriteComment(t *testing.T) {
808	for _, test := range writeCommentTests {
809		p, err := parse(test.input)
810
811		buf := new(bytes.Buffer)
812		var n int
813		switch test.encoding {
814		case "UTF-8":
815			n, err = p.WriteComment(buf, "# ", UTF8)
816		case "ISO-8859-1":
817			n, err = p.WriteComment(buf, "# ", ISO_8859_1)
818		}
819		assert.Equal(t, err, nil)
820		s := string(buf.Bytes())
821		assert.Equal(t, n, len(test.output), fmt.Sprintf("input=%q expected=%q obtained=%q", test.input, test.output, s))
822		assert.Equal(t, s, test.output, fmt.Sprintf("input=%q expected=%q obtained=%q", test.input, test.output, s))
823	}
824}
825
826func TestCustomExpansionExpression(t *testing.T) {
827	testKeyValuePrePostfix(t, "*[", "]*", "key=value\nkey2=*[key]*", "key", "value", "key2", "value")
828}
829
830func TestPanicOn32BitIntOverflow(t *testing.T) {
831	is32Bit = true
832	var min, max int64 = math.MinInt32 - 1, math.MaxInt32 + 1
833	input := fmt.Sprintf("min=%d\nmax=%d", min, max)
834	p := mustParse(t, input)
835	assert.Equal(t, p.MustGetInt64("min"), min)
836	assert.Equal(t, p.MustGetInt64("max"), max)
837	assert.Panic(t, func() { p.MustGetInt("min") }, ".* out of range")
838	assert.Panic(t, func() { p.MustGetInt("max") }, ".* out of range")
839}
840
841func TestPanicOn32BitUintOverflow(t *testing.T) {
842	is32Bit = true
843	var max uint64 = math.MaxUint32 + 1
844	input := fmt.Sprintf("max=%d", max)
845	p := mustParse(t, input)
846	assert.Equal(t, p.MustGetUint64("max"), max)
847	assert.Panic(t, func() { p.MustGetUint("max") }, ".* out of range")
848}
849
850func TestDeleteKey(t *testing.T) {
851	input := "#comments should also be gone\nkey=to-be-deleted\nsecond=key"
852	p := mustParse(t, input)
853	assert.Equal(t, len(p.m), 2)
854	assert.Equal(t, len(p.c), 1)
855	assert.Equal(t, len(p.k), 2)
856	p.Delete("key")
857	assert.Equal(t, len(p.m), 1)
858	assert.Equal(t, len(p.c), 0)
859	assert.Equal(t, len(p.k), 1)
860	assert.Equal(t, p.k[0], "second")
861	assert.Equal(t, p.m["second"], "key")
862}
863
864func TestDeleteUnknownKey(t *testing.T) {
865	input := "#comments should also be gone\nkey=to-be-deleted"
866	p := mustParse(t, input)
867	assert.Equal(t, len(p.m), 1)
868	assert.Equal(t, len(p.c), 1)
869	assert.Equal(t, len(p.k), 1)
870	p.Delete("wrong-key")
871	assert.Equal(t, len(p.m), 1)
872	assert.Equal(t, len(p.c), 1)
873	assert.Equal(t, len(p.k), 1)
874}
875
876func TestMerge(t *testing.T) {
877	input1 := "#comment\nkey=value\nkey2=value2"
878	input2 := "#another comment\nkey=another value\nkey3=value3"
879	p1 := mustParse(t, input1)
880	p2 := mustParse(t, input2)
881	p1.Merge(p2)
882	assert.Equal(t, len(p1.m), 3)
883	assert.Equal(t, len(p1.c), 1)
884	assert.Equal(t, len(p1.k), 3)
885	assert.Equal(t, p1.MustGet("key"), "another value")
886	assert.Equal(t, p1.GetComment("key"), "another comment")
887}
888
889func TestMap(t *testing.T) {
890	input := "key=value\nabc=def"
891	p := mustParse(t, input)
892	m := map[string]string{"key": "value", "abc": "def"}
893	assert.Equal(t, p.Map(), m)
894}
895
896func TestFilterFunc(t *testing.T) {
897	input := "key=value\nabc=def"
898	p := mustParse(t, input)
899	pp := p.FilterFunc(func(k, v string) bool {
900		return k != "abc"
901	})
902	m := map[string]string{"key": "value"}
903	assert.Equal(t, pp.Map(), m)
904}
905
906func TestLoad(t *testing.T) {
907	x := "key=${value}\nvalue=${key}"
908	p := NewProperties()
909	p.DisableExpansion = true
910	err := p.Load([]byte(x), UTF8)
911	assert.Equal(t, err, nil)
912}
913
914// ----------------------------------------------------------------------------
915
916// tests all combinations of delimiters, leading and/or trailing whitespace and newlines.
917func testWhitespaceAndDelimiterCombinations(t *testing.T, key, value string) {
918	whitespace := []string{"", " ", "\f", "\t"}
919	delimiters := []string{"", " ", "=", ":"}
920	newlines := []string{"", "\r", "\n", "\r\n"}
921	for _, dl := range delimiters {
922		for _, ws1 := range whitespace {
923			for _, ws2 := range whitespace {
924				for _, nl := range newlines {
925					// skip the one case where there is nothing between a key and a value
926					if ws1 == "" && dl == "" && ws2 == "" && value != "" {
927						continue
928					}
929
930					input := fmt.Sprintf("%s%s%s%s%s%s", key, ws1, dl, ws2, value, nl)
931					testKeyValue(t, input, key, value)
932				}
933			}
934		}
935	}
936}
937
938// tests whether key/value pairs exist for a given input.
939// keyvalues is expected to be an even number of strings of "key", "value", ...
940func testKeyValue(t *testing.T, input string, keyvalues ...string) {
941	testKeyValuePrePostfix(t, "${", "}", input, keyvalues...)
942}
943
944// tests whether key/value pairs exist for a given input.
945// keyvalues is expected to be an even number of strings of "key", "value", ...
946func testKeyValuePrePostfix(t *testing.T, prefix, postfix, input string, keyvalues ...string) {
947	p, err := Load([]byte(input), ISO_8859_1)
948	assert.Equal(t, err, nil)
949	p.Prefix = prefix
950	p.Postfix = postfix
951	assertKeyValues(t, input, p, keyvalues...)
952}
953
954// tests whether key/value pairs exist for a given input.
955// keyvalues is expected to be an even number of strings of "key", "value", ...
956func assertKeyValues(t *testing.T, input string, p *Properties, keyvalues ...string) {
957	assert.Equal(t, p != nil, true, "want properties")
958	assert.Equal(t, 2*p.Len(), len(keyvalues), "Odd number of key/value pairs.")
959
960	for i := 0; i < len(keyvalues); i += 2 {
961		key, value := keyvalues[i], keyvalues[i+1]
962		v, ok := p.Get(key)
963		if !ok {
964			t.Errorf("No key %q found (input=%q)", key, input)
965		}
966		if got, want := v, value; !reflect.DeepEqual(got, want) {
967			t.Errorf("Value %q does not match %q (input=%q)", v, value, input)
968		}
969	}
970}
971
972func mustParse(t *testing.T, s string) *Properties {
973	p, err := parse(s)
974	if err != nil {
975		t.Fatalf("parse failed with %s", err)
976	}
977	return p
978}
979
980// prints to stderr if the -verbose flag was given.
981func printf(format string, args ...interface{}) {
982	if *verbose {
983		fmt.Fprintf(os.Stderr, format, args...)
984	}
985}
986