Annotation of pkgsrc/pkgtools/pkglint/files/util_test.go, Revision 1.50
1.19 rillig 1: package pkglint
1.1 rillig 2:
3: import (
1.30 rillig 4: "errors"
1.40 rillig 5: "fmt"
1.9 rillig 6: "gopkg.in/check.v1"
1.13 rillig 7: "os"
1.40 rillig 8: "reflect"
9: "sort"
1.6 rillig 10: "testing"
1.13 rillig 11: "time"
1.1 rillig 12: )
13:
1.35 rillig 14: func (s *Suite) Test_YesNoUnknown_String(c *check.C) {
15: t := s.Init(c)
16:
17: t.CheckEquals(yes.String(), "yes")
18: t.CheckEquals(no.String(), "no")
19: t.CheckEquals(unknown.String(), "unknown")
20: }
21:
22: func (s *Suite) Test_trimHspace(c *check.C) {
23: t := s.Init(c)
24:
25: t.CheckEquals(trimHspace("a b"), "a b")
26: t.CheckEquals(trimHspace(" a b "), "a b")
27: t.CheckEquals(trimHspace("\ta b\t"), "a b")
28: t.CheckEquals(trimHspace(" \t a b\t \t"), "a b")
29: }
30:
31: func (s *Suite) Test_trimCommon(c *check.C) {
32: t := s.Init(c)
33:
34: test := func(a, b, trimmedA, trimmedB string) {
35: ta, tb := trimCommon(a, b)
36: t.CheckEquals(ta, trimmedA)
37: t.CheckEquals(tb, trimmedB)
38: }
39:
40: test("", "",
41: "", "")
42:
43: test("equal", "equal",
44: "", "")
45:
46: test("prefixA", "prefixB",
47: "A", "B")
48:
49: test("ASuffix", "BSuffix",
50: "A", "B")
51:
52: test("PreMiddlePost", "PreCenterPost",
53: "Middle", "Center")
54:
55: test("", "b",
56: "", "b")
57:
58: test("a", "",
59: "a", "")
60: }
61:
1.45 rillig 62: func (s *Suite) Test_replaceOnce(c *check.C) {
63: t := s.Init(c)
64:
65: test := func(s, from, to, result string) {
66: ok, actualResult := replaceOnce(s, from, to)
67:
68: t.CheckEquals(actualResult, result)
69: t.CheckEquals(ok, result != s)
70: }
71:
72: // The text does not occur at all.
73: test("something else", "from", "to", "something else")
74:
75: // The text occurs exactly once.
76: test("from", "from", "to", "to")
77:
78: // The text occurs at two places, non-overlapping.
79: test("from from", "from", "to", "from from")
80:
81: // The text occurs at three places, non-overlapping.
82: test("aaa", "a", "b", "aaa")
83:
84: // The text occurs at two places, the occurrences overlap.
85: test("aaa", "aa", "b", "aaa")
86: }
87:
1.47 rillig 88: func (s *Suite) Test_condStr(c *check.C) {
89: t := s.Init(c)
90:
91: t.CheckEquals(condStr(true, "T", "F"), "T")
92: t.CheckEquals(condStr(false, "T", "F"), "F")
93: }
94:
95: func (s *Suite) Test_condInt(c *check.C) {
96: t := s.Init(c)
97:
98: t.CheckEquals(condInt(true, 123, 456), 123)
99: t.CheckEquals(condInt(false, 123, 456), 456)
100: }
101:
102: func (s *Suite) Test_imax(c *check.C) {
103: t := s.Init(c)
104:
105: t.CheckEquals(imax(2, 5), 5)
106: t.CheckEquals(imax(5, 2), 5)
107: }
108:
109: func (s *Suite) Test_imin(c *check.C) {
110: t := s.Init(c)
111:
112: t.CheckEquals(imin(2, 5), 2)
113: t.CheckEquals(imin(5, 2), 2)
114: }
115:
1.30 rillig 116: func (s *Suite) Test_assertNil(c *check.C) {
117: t := s.Init(c)
118:
119: assertNil(nil, "this is not an error")
120:
121: t.ExpectPanic(
122: func() { assertNil(errors.New("unexpected error"), "Oops") },
123: "Pkglint internal error: Oops: unexpected error")
124: }
125:
1.31 rillig 126: func (s *Suite) Test_assertNotNil(c *check.C) {
127: t := s.Init(c)
128:
129: assertNotNil("this string is not nil")
130:
131: t.ExpectPanic(
132: func() { assertNotNil(nil) },
133: "Pkglint internal error: unexpected nil pointer")
134: t.ExpectPanic(
135: func() { var ptr *string; assertNotNil(ptr) },
136: "Pkglint internal error: unexpected nil pointer")
137: }
138:
1.47 rillig 139: func (s *Suite) Test_assert(c *check.C) {
140: t := s.Init(c)
141:
142: assert(true)
143: t.ExpectAssert(func() { assert(false) })
144: }
145:
1.35 rillig 146: func (s *Suite) Test_isEmptyDir(c *check.C) {
147: t := s.Init(c)
148:
149: t.CreateFileLines("CVS/Entries",
150: "dummy")
151: t.CreateFileLines("subdir/CVS/Entries",
152: "dummy")
153:
154: t.CheckEquals(isEmptyDir(t.File(".")), true)
155: t.CheckEquals(isEmptyDir(t.File("CVS")), true)
1.37 rillig 156:
157: t.Chdir(".")
158:
159: t.CheckEquals(isEmptyDir("."), true)
160: t.CheckEquals(isEmptyDir("CVS"), true)
1.35 rillig 161: }
162:
163: func (s *Suite) Test_isEmptyDir__and_getSubdirs(c *check.C) {
164: t := s.Init(c)
165:
166: t.CreateFileLines("CVS/Entries",
167: "dummy")
168:
169: if dir := t.File("."); true {
170: t.CheckEquals(isEmptyDir(dir), true)
1.40 rillig 171: t.CheckDeepEquals(getSubdirs(dir), []RelPath(nil))
1.35 rillig 172:
173: t.CreateFileLines("somedir/file")
174:
175: t.CheckEquals(isEmptyDir(dir), false)
1.40 rillig 176: t.CheckDeepEquals(getSubdirs(dir), []RelPath{"somedir"})
1.35 rillig 177: }
178:
179: if absent := t.File("nonexistent"); true {
180: t.CheckEquals(isEmptyDir(absent), true) // Counts as empty.
181:
182: // The last group from the error message is localized, therefore the matching.
183: t.ExpectFatalMatches(
184: func() { getSubdirs(absent) },
185: `FATAL: ~/nonexistent: Cannot be read: open ~/nonexistent: (.+)\n`)
186: }
187: }
188:
189: func (s *Suite) Test_getSubdirs(c *check.C) {
190: t := s.Init(c)
191:
192: t.CreateFileLines("subdir/file")
193: t.CreateFileLines("empty/file")
1.36 rillig 194: c.Check(os.Remove(t.File("empty/file").String()), check.IsNil)
1.35 rillig 195:
1.40 rillig 196: t.CheckDeepEquals(getSubdirs(t.File(".")), []RelPath{"subdir"})
197: }
198:
199: func (s *Suite) Test_isIgnoredFilename(c *check.C) {
200: t := s.Init(c)
201:
202: test := func(filename string, isIgnored bool) {
203: t.CheckEquals(isIgnoredFilename(filename), isIgnored)
204: }
205:
206: test("filename.mk", false)
207: test(".gitignore", false)
208: test(".git", true)
209: test(".gitattributes", false)
210: test("CVS", true)
211: test(".svn", true)
212: test(".hg", true)
213:
214: // There is actually an IDEA plugin for pkgsrc.
215: // See https://github.com/rillig/intellij-pkgsrc.
216: test(".idea", true)
1.35 rillig 217: }
218:
219: func (s *Suite) Test_isLocallyModified(c *check.C) {
1.32 rillig 220: t := s.Init(c)
221:
1.35 rillig 222: unmodified := t.CreateFileLines("unmodified")
223: modTime := time.Unix(1136239445, 0).UTC()
224:
1.36 rillig 225: err := os.Chtimes(unmodified.String(), modTime, modTime)
1.35 rillig 226: c.Check(err, check.IsNil)
227:
1.36 rillig 228: st, err := os.Lstat(unmodified.String())
1.35 rillig 229: c.Check(err, check.IsNil)
230:
231: // Make sure that the file system has second precision and accuracy.
232: t.CheckDeepEquals(st.ModTime().UTC(), modTime)
233:
234: modified := t.CreateFileLines("modified")
235:
236: t.CreateFileLines("CVS/Entries",
237: "/unmodified//"+modTime.Format(time.ANSIC)+"//",
238: "/modified//"+modTime.Format(time.ANSIC)+"//",
239: "/enoent//"+modTime.Format(time.ANSIC)+"//")
240:
241: t.CheckEquals(isLocallyModified(unmodified), false)
242: t.CheckEquals(isLocallyModified(modified), true)
243: t.CheckEquals(isLocallyModified(t.File("enoent")), true)
244: t.CheckEquals(isLocallyModified(t.File("not_mentioned")), false)
245: t.CheckEquals(isLocallyModified(t.File("subdir/file")), false)
246:
247: t.DisableTracing()
248:
249: t.CheckEquals(isLocallyModified(t.File("unmodified")), false)
1.11 rillig 250: }
251:
1.35 rillig 252: func (s *Suite) Test_tabWidth(c *check.C) {
1.32 rillig 253: t := s.Init(c)
254:
1.35 rillig 255: t.CheckEquals(tabWidth("12345"), 5)
256: t.CheckEquals(tabWidth("\t"), 8)
257: t.CheckEquals(tabWidth("123\t"), 8)
258: t.CheckEquals(tabWidth("1234567\t"), 8)
259: t.CheckEquals(tabWidth("12345678\t"), 16)
1.1 rillig 260: }
261:
1.35 rillig 262: // Since tabWidthAppend is used with logical lines (Line.Text) as well as with
263: // raw lines (RawLine.textnl or RawLine.orignl), and since the width only
264: // makes sense for a single line, better panic.
265: func (s *Suite) Test_tabWidthAppend__panic(c *check.C) {
1.32 rillig 266: t := s.Init(c)
267:
1.35 rillig 268: t.ExpectAssert(func() { tabWidthAppend(0, "\n") })
1.1 rillig 269: }
270:
1.35 rillig 271: func (s *Suite) Test_detab(c *check.C) {
1.32 rillig 272: t := s.Init(c)
273:
1.35 rillig 274: t.CheckEquals(detab(""), "")
275: t.CheckEquals(detab("\t"), " ")
276: t.CheckEquals(detab("1234\t9"), "1234 9")
277: t.CheckEquals(detab("1234567\t"), "1234567 ")
278: t.CheckEquals(detab("12345678\t"), "12345678 ")
1.1 rillig 279: }
280:
1.35 rillig 281: func (s *Suite) Test_alignWith(c *check.C) {
1.32 rillig 282: t := s.Init(c)
283:
1.35 rillig 284: test := func(str, other, expected string) {
1.43 rillig 285: aligned := alignWith(str, other)
286: t.CheckEquals(aligned, expected)
287: t.CheckEquals(hasPrefix(aligned, str), true)
288:
289: // It would be unusual to call this function with a string
290: // that itself ends with space.
291: t.CheckEquals(rtrimHspace(aligned), str)
1.35 rillig 292: }
293:
1.43 rillig 294: // The needed alignment may be empty.
295: // In some contexts like the value of a variable assignment, this
296: // should not happen. In other contexts it's ok.
297: test("", "", "")
1.35 rillig 298:
1.42 rillig 299: test("VAR=", "1234567", "VAR= ")
1.35 rillig 300: test("VAR=", "12345678", "VAR=\t")
1.42 rillig 301: test("VAR=", "123456789", "VAR=\t ")
1.35 rillig 302:
1.43 rillig 303: // If the other string is shorter, no extra tab is added.
304: test("1234567890=", "V=", "1234567890=")
1.1 rillig 305: }
306:
1.50 ! rillig 307: func (s *Suite) Test_alignmentToWidths(c *check.C) {
! 308: t := s.Init(c)
! 309:
! 310: t.CheckEquals(alignmentToWidths(8, 72), "\t\t\t\t\t\t\t\t")
! 311: }
! 312:
1.35 rillig 313: func (s *Suite) Test_indent(c *check.C) {
1.32 rillig 314: t := s.Init(c)
315:
1.35 rillig 316: test := func(width int, ind string) {
317: actual := indent(width)
318:
319: t.CheckEquals(actual, ind)
320: }
321:
322: test(0, "")
323: test(1, " ")
324: test(7, " ")
325: test(8, "\t")
326: test(15, "\t ")
327: test(16, "\t\t")
328: test(72, "\t\t\t\t\t\t\t\t\t")
1.43 rillig 329: test(79, "\t\t\t\t\t\t\t\t\t ")
330: test(80, "\t\t\t\t\t\t\t\t\t\t")
331: test(87, "\t\t\t\t\t\t\t\t\t\t ")
1.1 rillig 332: }
333:
1.35 rillig 334: func (s *Suite) Test_alignmentAfter(c *check.C) {
1.32 rillig 335: t := s.Init(c)
336:
1.35 rillig 337: test := func(prefix string, width int, ind string) {
338: actual := alignmentAfter(prefix, width)
339:
340: t.CheckEquals(actual, ind)
341: }
342:
343: test("", 0, "")
344: test("", 15, "\t ")
345:
346: test(" ", 5, " ")
347: test(" ", 10, "\t ")
348:
349: test("\t", 15, " ")
350: test(" \t", 15, " ")
351: test(" \t", 15, " ")
352: test("\t ", 15, " ")
353:
354: test(" ", 16, "\t\t")
1.1 rillig 355:
1.35 rillig 356: // The desired width must be at least the width of the prefix.
357: t.ExpectAssert(func() { test("\t", 7, "") })
1.1 rillig 358: }
359:
1.13 rillig 360: func (s *Suite) Test_shorten(c *check.C) {
1.32 rillig 361: t := s.Init(c)
362:
363: t.CheckEquals(shorten("aaaaa", 3), "aaa...")
364: t.CheckEquals(shorten("aaaaa", 5), "aaaaa")
365: t.CheckEquals(shorten("aaa", 5), "aaa")
1.13 rillig 366: }
367:
1.35 rillig 368: func (s *Suite) Test_varnameBase(c *check.C) {
369: t := s.Init(c)
370:
371: t.CheckEquals(varnameBase("VAR"), "VAR")
372: t.CheckEquals(varnameBase("VAR.param"), "VAR")
373: t.CheckEquals(varnameBase(".CURDIR"), ".CURDIR")
374: }
375:
376: func (s *Suite) Test_varnameCanon(c *check.C) {
377: t := s.Init(c)
378:
379: t.CheckEquals(varnameCanon("VAR"), "VAR")
380: t.CheckEquals(varnameCanon("VAR.param"), "VAR.*")
381: t.CheckEquals(varnameCanon(".CURDIR"), ".CURDIR")
382: }
383:
384: func (s *Suite) Test_varnameParam(c *check.C) {
385: t := s.Init(c)
386:
387: t.CheckEquals(varnameParam("VAR"), "")
388: t.CheckEquals(varnameParam("VAR.param"), "param")
389: t.CheckEquals(varnameParam(".CURDIR"), "")
390: }
391:
392: func (s *Suite) Test__regex_ReplaceFirst(c *check.C) {
393: t := s.Init(c)
1.21 rillig 394:
1.35 rillig 395: m, rest := G.res.ReplaceFirst("a+b+c+d", `(\w)(.)(\w)`, "X")
1.24 rillig 396:
1.35 rillig 397: c.Assert(m, check.NotNil)
398: t.CheckDeepEquals(m, []string{"a+b", "a", "+", "b"})
399: t.CheckEquals(rest, "X+c+d")
1.24 rillig 400: }
401:
1.17 rillig 402: const reMkIncludeBenchmark = `^\.([\t ]*)(s?include)[\t ]+\"([^\"]+)\"[\t ]*(?:#.*)?$`
403: const reMkIncludeBenchmarkPositive = `^\.([\t ]*)(s?include)[\t ]+\"(.+)\"[\t ]*(?:#.*)?$`
1.6 rillig 404:
405: func Benchmark_match3_buildlink3(b *testing.B) {
406: for i := 0; i < b.N; i++ {
407: match3(".include \"../../category/package/buildlink3.mk\"", reMkIncludeBenchmark)
408: }
409: }
410:
411: func Benchmark_match3_bsd_pkg_mk(b *testing.B) {
412: for i := 0; i < b.N; i++ {
413: match3(".include \"../../mk/bsd.pkg.mk\"", reMkIncludeBenchmark)
414: }
415: }
416:
1.14 rillig 417: func Benchmark_match3_same_dir(b *testing.B) {
1.6 rillig 418: for i := 0; i < b.N; i++ {
419: match3(".include \"options.mk\"", reMkIncludeBenchmark)
420: }
421: }
422:
423: func Benchmark_match3_bsd_pkg_mk_comment(b *testing.B) {
424: for i := 0; i < b.N; i++ {
425: match3(".include \"../../mk/bsd.pkg.mk\" # infrastructure ", reMkIncludeBenchmark)
426: }
427: }
428:
429: func Benchmark_match3_buildlink3_positive(b *testing.B) {
430: for i := 0; i < b.N; i++ {
431: match3(".include \"../../category/package/buildlink3.mk\"", reMkIncludeBenchmarkPositive)
432: }
433: }
434:
435: func Benchmark_match3_bsd_pkg_mk_positive(b *testing.B) {
436: for i := 0; i < b.N; i++ {
437: match3(".include \"../../mk/bsd.pkg.mk\"", reMkIncludeBenchmarkPositive)
438: }
439: }
440:
1.14 rillig 441: func Benchmark_match3_same_dir_positive(b *testing.B) {
1.6 rillig 442: for i := 0; i < b.N; i++ {
443: match3(".include \"options.mk\"", reMkIncludeBenchmarkPositive)
444: }
445: }
446:
447: func Benchmark_match3_bsd_pkg_mk_comment_positive(b *testing.B) {
448: for i := 0; i < b.N; i++ {
449: match3(".include \"../../mk/bsd.pkg.mk\" # infrastructure ", reMkIncludeBenchmarkPositive)
450: }
451: }
452:
453: func Benchmark_match3_explicit(b *testing.B) {
454: for i := 0; i < b.N; i++ {
455: MatchMkInclude(".include \"../../mk/bsd.pkg.mk\" # infrastructure ")
456: }
457: }
1.9 rillig 458:
459: func emptyToNil(slice []string) []string {
460: if len(slice) == 0 {
461: return nil
462: }
463: return slice
464: }
1.10 rillig 465:
1.46 rillig 466: func (s *Suite) Test_containsVarUse(c *check.C) {
1.40 rillig 467: t := s.Init(c)
468:
469: test := func(str string, containsVar bool) {
1.46 rillig 470: t.CheckEquals(containsVarUse(str), containsVar)
1.40 rillig 471: }
472:
473: test("", false)
474: test("$", false) // A syntax error.
475:
476: // See the bmake manual page.
1.41 rillig 477: test("$>", true) // .ALLSRC
478: test("$!", true) // .ARCHIVE
479: test("$<", true) // .IMPSRC
480: test("$%", true) // .MEMBER
481: test("$?", true) // .OODATE
482: test("$*", true) // .PREFIX
483: test("$@", true) // .TARGET
1.40 rillig 484:
1.41 rillig 485: test("$V", true)
486: test("$v", true)
1.40 rillig 487: test("${Var}", true)
488: test("${VAR.${param}}", true)
489: test("$(VAR)", true)
490:
1.41 rillig 491: test("$$", false) // An escaped dollar character.
492: test("$$(VAR)", false) // An escaped dollar character; probably a subshell.
493: test("$${VAR}", false) // An escaped dollar character; probably a shell variable.
494: test("$$VAR", false) // An escaped dollar character.
1.40 rillig 495: }
496:
1.35 rillig 497: func (s *Suite) Test_Once(c *check.C) {
1.32 rillig 498: t := s.Init(c)
499:
1.35 rillig 500: var once Once
1.32 rillig 501:
1.35 rillig 502: t.CheckEquals(once.FirstTime("str"), true)
503: t.CheckEquals(once.FirstTime("str"), false)
504: t.CheckEquals(once.FirstTimeSlice("str"), false)
505: t.CheckEquals(once.FirstTimeSlice("str", "str2"), true)
506: t.CheckEquals(once.FirstTimeSlice("str", "str2"), false)
507: }
1.32 rillig 508:
1.35 rillig 509: func (s *Suite) Test_Once__trace(c *check.C) {
510: t := s.Init(c)
1.32 rillig 511:
1.35 rillig 512: var once Once
513: once.Trace = true
1.32 rillig 514:
1.35 rillig 515: t.CheckEquals(once.FirstTime("str"), true)
516: t.CheckEquals(once.FirstTime("str"), false)
517: t.CheckEquals(once.FirstTimeSlice("str"), false)
518: t.CheckEquals(once.FirstTimeSlice("str", "str2"), true)
519: t.CheckEquals(once.FirstTimeSlice("str", "str2"), false)
1.32 rillig 520:
1.35 rillig 521: t.CheckOutputLines(
522: "FirstTime: str",
523: "FirstTime: str, str2")
1.32 rillig 524: }
525:
1.35 rillig 526: func (s *Suite) Test_Scope__no_tracing(c *check.C) {
1.13 rillig 527: t := s.Init(c)
528:
1.35 rillig 529: scope := NewScope()
530: scope.Define("VAR.param", t.NewMkLine("fname.mk", 3, "VAR.param=\tvalue"))
531: t.DisableTracing()
1.13 rillig 532:
1.35 rillig 533: t.CheckEquals(scope.IsDefinedSimilar("VAR.param"), true)
534: t.CheckEquals(scope.IsDefinedSimilar("VAR.other"), true)
535: t.CheckEquals(scope.IsDefinedSimilar("OTHER"), false)
536: }
1.13 rillig 537:
1.35 rillig 538: func (s *Suite) Test_Scope__commented_varassign(c *check.C) {
539: t := s.Init(c)
1.13 rillig 540:
1.35 rillig 541: mkline := t.NewMkLine("mk/defaults/mk.conf", 3, "#VAR=default")
542: scope := NewScope()
543: scope.Define("VAR", mkline)
1.13 rillig 544:
1.35 rillig 545: t.CheckEquals(scope.IsDefined("VAR"), false)
546: t.Check(scope.FirstDefinition("VAR"), check.IsNil)
547: t.Check(scope.LastDefinition("VAR"), check.IsNil)
1.13 rillig 548:
1.35 rillig 549: t.CheckEquals(scope.Mentioned("VAR"), mkline)
550: t.CheckEquals(scope.Commented("VAR"), mkline)
1.13 rillig 551:
1.49 rillig 552: value, found, indeterminate := scope.LastValueFound("VAR")
1.35 rillig 553: t.CheckEquals(value, "")
554: t.CheckEquals(found, false)
1.49 rillig 555: t.CheckEquals(indeterminate, false)
1.13 rillig 556: }
557:
1.23 rillig 558: func (s *Suite) Test_Scope_Define(c *check.C) {
559: t := s.Init(c)
560:
561: scope := NewScope()
562:
1.49 rillig 563: test := func(line string, found, indeterminate bool, value string) {
1.48 rillig 564: mkline := t.NewMkLine("file.mk", 123, line)
565: scope.Define("BUILD_DIRS", mkline)
1.43 rillig 566:
1.49 rillig 567: actualValue, actualFound, actualIndeterminate := scope.LastValueFound("BUILD_DIRS")
1.43 rillig 568:
1.49 rillig 569: t.CheckDeepEquals(
570: []interface{}{actualFound, actualIndeterminate, actualValue},
571: []interface{}{found, indeterminate, value})
572: t.CheckEquals(scope.vs["BUILD_DIRS"].value, value)
1.43 rillig 573: }
574:
575: test("BUILD_DIRS?=\tdefault",
1.49 rillig 576: true, false, "default")
1.23 rillig 577:
1.43 rillig 578: test(
579: "BUILD_DIRS=\tone two three",
1.49 rillig 580: true, false, "one two three")
1.23 rillig 581:
1.43 rillig 582: test(
583: "BUILD_DIRS+=\tfour",
1.49 rillig 584: true, false, "one two three four")
1.23 rillig 585:
586: // Later default assignments do not have an effect.
1.43 rillig 587: test("BUILD_DIRS?=\tdefault",
1.49 rillig 588: true, false, "one two three four")
1.23 rillig 589:
1.43 rillig 590: test("BUILD_DIRS!=\techo dynamic",
1.49 rillig 591: true, true, "")
1.48 rillig 592:
1.49 rillig 593: // The shell assignment above sets the variable to an indeterminate
594: // value, after which all further default assignments are ignored.
1.48 rillig 595: test("BUILD_DIRS?=\tdefault after shell assignment",
1.49 rillig 596: true, true, "")
1.23 rillig 597: }
598:
1.35 rillig 599: func (s *Suite) Test_Scope_Mentioned(c *check.C) {
1.13 rillig 600: t := s.Init(c)
601:
1.35 rillig 602: assigned := t.NewMkLine("filename.mk", 3, "VAR=\tvalue")
603: commented := t.NewMkLine("filename.mk", 4, "#COMMENTED=\tvalue")
604: documented := t.NewMkLine("filename.mk", 5, "# DOCUMENTED is a variable.")
605:
1.13 rillig 606: scope := NewScope()
1.35 rillig 607: scope.Define("VAR", assigned)
608: scope.Define("COMMENTED", commented)
609: scope.Define("DOCUMENTED", documented)
1.13 rillig 610:
1.35 rillig 611: t.CheckEquals(scope.Mentioned("VAR"), assigned)
612: t.CheckEquals(scope.Mentioned("COMMENTED"), commented)
613: t.CheckEquals(scope.Mentioned("DOCUMENTED"), documented)
614: t.Check(scope.Mentioned("UNKNOWN"), check.IsNil)
1.13 rillig 615: }
616:
1.35 rillig 617: func (s *Suite) Test_Scope_IsDefined(c *check.C) {
1.13 rillig 618: t := s.Init(c)
619:
620: scope := NewScope()
1.35 rillig 621: scope.Define("VAR.param", t.NewMkLine("file.mk", 1, "VAR.param=value"))
622:
623: t.CheckEquals(scope.IsDefined("VAR.param"), true)
624: t.CheckEquals(scope.IsDefined("VAR.other"), false)
625: t.CheckEquals(scope.IsDefined("VARIABLE.*"), false)
1.13 rillig 626:
1.35 rillig 627: t.CheckEquals(scope.IsDefinedSimilar("VAR.param"), true)
628: t.CheckEquals(scope.IsDefinedSimilar("VAR.other"), true)
629: t.CheckEquals(scope.IsDefinedSimilar("VARIABLE.*"), false)
1.13 rillig 630: }
631:
1.35 rillig 632: func (s *Suite) Test_Scope_IsUsed(c *check.C) {
1.13 rillig 633: t := s.Init(c)
634:
1.35 rillig 635: scope := NewScope()
636: mkline := t.NewMkLine("file.mk", 1, "\techo ${VAR.param}")
637: scope.Use("VAR.param", mkline, VucRunTime)
1.13 rillig 638:
1.35 rillig 639: t.CheckEquals(scope.IsUsed("VAR.param"), true)
640: t.CheckEquals(scope.IsUsed("VAR.other"), false)
641: t.CheckEquals(scope.IsUsed("VARIABLE.*"), false)
642:
643: t.CheckEquals(scope.IsUsedSimilar("VAR.param"), true)
644: t.CheckEquals(scope.IsUsedSimilar("VAR.other"), true)
645: t.CheckEquals(scope.IsUsedSimilar("VARIABLE.*"), false)
1.13 rillig 646: }
647:
1.21 rillig 648: func (s *Suite) Test_Scope_FirstDefinition(c *check.C) {
649: t := s.Init(c)
650:
651: mkline1 := t.NewMkLine("fname.mk", 3, "VAR=\tvalue")
1.23 rillig 652: mkline2 := t.NewMkLine("fname.mk", 3, ".if ${SNEAKY::=value}")
1.21 rillig 653:
654: scope := NewScope()
655: scope.Define("VAR", mkline1)
656: scope.Define("SNEAKY", mkline2)
657:
1.32 rillig 658: t.CheckEquals(scope.FirstDefinition("VAR"), mkline1)
1.21 rillig 659:
660: // This call returns nil because it's not a variable assignment
661: // and the calling code typically assumes a variable definition.
662: // These sneaky variables with implicit definition are an edge
663: // case that only few people actually know. It's better that way.
664: t.Check(scope.FirstDefinition("SNEAKY"), check.IsNil)
1.40 rillig 665:
666: t.CheckOutputLines(
667: "ERROR: fname.mk:3: Assignment modifiers like \":=\" " +
668: "must not be used at all.")
1.21 rillig 669: }
670:
1.35 rillig 671: func (s *Suite) Test_Scope_Commented(c *check.C) {
672: t := s.Init(c)
673:
674: assigned := t.NewMkLine("filename.mk", 3, "VAR=\tvalue")
675: commented := t.NewMkLine("filename.mk", 4, "#COMMENTED=\tvalue")
676: documented := t.NewMkLine("filename.mk", 5, "# DOCUMENTED is a variable.")
677:
678: scope := NewScope()
679: scope.Define("VAR", assigned)
680: scope.Define("COMMENTED", commented)
681: scope.Define("DOCUMENTED", documented)
682:
683: t.Check(scope.Commented("VAR"), check.IsNil)
684: t.CheckEquals(scope.Commented("COMMENTED"), commented)
685: t.Check(scope.Commented("DOCUMENTED"), check.IsNil)
686: t.Check(scope.Commented("UNKNOWN"), check.IsNil)
687: }
688:
1.23 rillig 689: func (s *Suite) Test_Scope_LastValue(c *check.C) {
690: t := s.Init(c)
691:
692: mklines := t.NewMkLines("file.mk",
1.31 rillig 693: MkCvsID,
1.23 rillig 694: "VAR=\tfirst",
695: "VAR=\tsecond",
696: ".if 1",
697: "VAR=\tthird (conditional)",
698: ".endif")
699:
700: mklines.Check()
701:
1.41 rillig 702: // TODO: At load time, use loadVars instead of allVars.
703: t.CheckEquals(mklines.allVars.LastValue("VAR"), "third (conditional)")
1.23 rillig 704:
705: t.CheckOutputLines(
706: "WARN: file.mk:2: VAR is defined but not used.")
707: }
708:
1.47 rillig 709: // Up to 2020-01-06, pkglint wrongly returned "one" as the variable value,
710: // even though Makefile.common is included before appending "two".
711: func (s *Suite) Test_Scope_LastValue__append_in_multiple_files(c *check.C) {
712: t := s.Init(c)
713:
714: t.SetUpPackage("category/package",
715: ".include \"Makefile.common\"",
716: "PLIST_VARS+=\ttwo",
717: "PLIST.two=\tyes")
718: t.CreateFileLines("category/package/Makefile.common",
719: MkCvsID,
720: "PLIST_VARS=\tone",
721: "PLIST.one=\tyes")
722: pkg := NewPackage(t.File("category/package"))
723: t.FinishSetUp()
724:
725: pkg.Check()
726:
727: t.CheckEquals(pkg.vars.LastValue("PLIST_VARS"), "one two")
728: }
729:
1.35 rillig 730: func (s *Suite) Test_Scope_DefineAll(c *check.C) {
1.21 rillig 731: t := s.Init(c)
732:
1.35 rillig 733: src := NewScope()
1.21 rillig 734:
1.35 rillig 735: dst := NewScope()
736: dst.DefineAll(src)
1.21 rillig 737:
1.49 rillig 738: c.Check(dst.vs, check.HasLen, 0)
1.28 rillig 739:
1.35 rillig 740: src.Define("VAR", t.NewMkLine("file.mk", 1, "VAR=value"))
741: dst.DefineAll(src)
1.28 rillig 742:
1.35 rillig 743: t.CheckEquals(dst.IsDefined("VAR"), true)
1.28 rillig 744: }
745:
1.13 rillig 746: func (s *Suite) Test_naturalLess(c *check.C) {
1.32 rillig 747: t := s.Init(c)
1.21 rillig 748:
1.32 rillig 749: var elements = []string{
750: "",
751: // Numbers are always considered smaller than other characters.
752: "0", "000", "0000", "5", "7", "00011", "12", "00012", "000111",
753: "!", "a", "a0", "a ", "aa", "ab", "b"}
1.21 rillig 754:
1.32 rillig 755: test := func(i int, ie string, j int, je string) {
756: actual := naturalLess(ie, je)
757: expected := i < j
758: if actual != expected {
759: t.CheckDeepEquals(
760: []interface{}{i, ie, j, je, actual},
761: []interface{}{i, ie, j, je, expected})
762: }
763: }
1.21 rillig 764:
1.32 rillig 765: for i, ie := range elements {
766: for j, je := range elements {
767: test(i, ie, j, je)
768: }
769: }
1.14 rillig 770: }
771:
772: func (s *Suite) Test_FileCache(c *check.C) {
773: t := s.Init(c)
774:
1.18 rillig 775: t.EnableTracingToLog()
776:
1.14 rillig 777: cache := NewFileCache(3)
778:
779: lines := t.NewLines("Makefile",
1.31 rillig 780: MkCvsID,
1.14 rillig 781: "# line 2")
782:
783: c.Check(cache.Get("Makefile", 0), check.IsNil)
1.32 rillig 784: t.CheckEquals(cache.hits, 0)
785: t.CheckEquals(cache.misses, 1)
1.14 rillig 786:
787: cache.Put("Makefile", 0, lines)
788: c.Check(cache.Get("Makefile", MustSucceed|LogErrors), check.IsNil) // Wrong LoadOptions.
789:
790: linesFromCache := cache.Get("Makefile", 0)
1.39 rillig 791: t.CheckEquals(linesFromCache.Filename, NewCurrPath("Makefile"))
1.17 rillig 792: c.Check(linesFromCache.Lines, check.HasLen, 2)
1.48 rillig 793: t.CheckEquals(linesFromCache.Lines[0].Filename(), NewCurrPath("Makefile"))
1.14 rillig 794:
795: // Cache keys are normalized using path.Clean.
796: linesFromCache2 := cache.Get("./Makefile", 0)
1.39 rillig 797: t.CheckEquals(linesFromCache2.Filename, NewCurrPath("./Makefile"))
1.17 rillig 798: c.Check(linesFromCache2.Lines, check.HasLen, 2)
1.48 rillig 799: t.CheckEquals(linesFromCache2.Lines[0].Filename(), NewCurrPath("./Makefile"))
1.14 rillig 800:
801: cache.Put("file1.mk", 0, lines)
802: cache.Put("file2.mk", 0, lines)
803:
804: // Now the cache is full. All three entries can be retrieved.
805: c.Check(cache.Get("Makefile", 0), check.NotNil)
806: c.Check(cache.Get("file1.mk", 0), check.NotNil)
807: c.Check(cache.Get("file2.mk", 0), check.NotNil)
808:
809: // Adding another entry removes all entries with minimum count,
810: // which currently are file1.mk and file2.mk.
811: // Makefile is still in the cache because it was accessed once.
812: cache.Put("file3.mk", 0, lines)
813:
814: c.Check(cache.Get("Makefile", 0), check.NotNil)
815: c.Check(cache.Get("file1.mk", 0), check.IsNil)
816: c.Check(cache.Get("file2.mk", 0), check.IsNil)
817: c.Check(cache.Get("file3.mk", 0), check.NotNil)
818:
819: cache.Evict("Makefile")
820:
821: c.Check(cache.Get("Makefile", 0), check.IsNil)
822: c.Check(cache.table, check.HasLen, 1)
823: c.Check(cache.mapping, check.HasLen, 1)
1.32 rillig 824: t.CheckEquals(cache.hits, 7)
825: t.CheckEquals(cache.misses, 5)
1.17 rillig 826:
827: t.CheckOutputLines(
1.18 rillig 828: "TRACE: FileCache \"Makefile\" with count 4.",
829: "TRACE: FileCache \"file1.mk\" with count 2.",
830: "TRACE: FileCache \"file2.mk\" with count 2.",
831: "TRACE: FileCache.Evict \"file2.mk\" with count 2.",
832: "TRACE: FileCache.Evict \"file1.mk\" with count 2.",
833: "TRACE: FileCache.Halve \"Makefile\" with count 4.")
1.17 rillig 834: }
835:
1.29 rillig 836: func (s *Suite) Test_FileCache_removeOldEntries__branch_coverage(c *check.C) {
837: t := s.Init(c)
838:
839: t.EnableTracingToLog()
840: G.Testing = false
841:
842: lines := t.NewLines("filename.mk",
1.31 rillig 843: MkCvsID)
1.29 rillig 844: cache := NewFileCache(3)
845: cache.Put("filename1.mk", 0, lines)
846: cache.Put("filename2.mk", 0, lines)
847: cache.Get("filename2.mk", 0)
848: cache.Get("filename2.mk", 0)
849: cache.Put("filename3.mk", 0, lines)
850: cache.Put("filename4.mk", 0, lines)
851:
852: t.CheckOutputLines(
853: "TRACE: FileCache.Evict \"filename3.mk\" with count 1.",
854: "TRACE: FileCache.Evict \"filename1.mk\" with count 1.",
855: "TRACE: FileCache.Halve \"filename2.mk\" with count 3.")
856: }
857:
1.30 rillig 858: func (s *Suite) Test_FileCache_removeOldEntries__no_tracing(c *check.C) {
859: t := s.Init(c)
860:
861: t.DisableTracing()
862:
863: lines := t.NewLines("filename.mk",
1.31 rillig 864: MkCvsID)
1.30 rillig 865: cache := NewFileCache(3)
866: cache.Put("filename1.mk", 0, lines)
867: cache.Put("filename2.mk", 0, lines)
868: cache.Get("filename2.mk", 0)
869: cache.Get("filename2.mk", 0)
870: cache.Put("filename3.mk", 0, lines)
871: cache.Put("filename4.mk", 0, lines)
872:
873: t.CheckOutputEmpty()
874: }
875:
876: // Covers the newLen > 0 condition.
877: func (s *Suite) Test_FileCache_removeOldEntries__zero_capacity(c *check.C) {
878: t := s.Init(c)
879:
880: lines := t.NewLines("filename.mk",
1.31 rillig 881: MkCvsID)
1.30 rillig 882: cache := NewFileCache(1)
883: cache.Put("filename1.mk", 0, lines)
884:
885: // This call removes all existing entries from the cache,
886: // as the cache's capacity is only 1.
887: cache.Put("filename2.mk", 0, lines)
888: }
889:
890: func (s *Suite) Test_FileCache_Evict__sort(c *check.C) {
891: t := s.Init(c)
892:
893: lines := t.NewLines("filename.mk",
1.31 rillig 894: MkCvsID)
1.30 rillig 895: cache := NewFileCache(10)
896: cache.Put("filename0.mk", 0, lines)
897: cache.Put("filename1.mk", 0, lines)
898: cache.Put("filename2.mk", 0, lines)
899: cache.Put("filename3.mk", 0, lines)
900: cache.Put("filename4.mk", 0, lines)
901: cache.Put("filename5.mk", 0, lines)
902: cache.Put("filename6.mk", 0, lines)
903: cache.Put("filename7.mk", 0, lines)
904: cache.Put("filename8.mk", 0, lines)
905: cache.Put("filename9.mk", 0, lines)
906:
907: cache.Evict("filename5.mk")
908:
909: t.Check(cache.table, check.HasLen, 9)
910: t.Check(cache.Get("filename5.mk", 0), check.IsNil)
911: t.Check(cache.Get("filename6.mk", 0), check.NotNil)
912: }
913:
1.34 rillig 914: func (s *Suite) Test_bmakeHelp(c *check.C) {
1.32 rillig 915: t := s.Init(c)
916:
1.34 rillig 917: t.CheckEquals(bmakeHelp("subst"), confMake+" help topic=subst")
1.13 rillig 918: }
1.18 rillig 919:
920: func (s *Suite) Test_wrap(c *check.C) {
1.32 rillig 921: t := s.Init(c)
1.18 rillig 922:
923: wrapped := wrap(20,
924: "See the pkgsrc guide, section \"Package components, Makefile\":",
925: "https://www.NetBSD.org/doc/pkgsrc/pkgsrc.html#components.Makefile.",
926: "",
927: "For more information, ask on the tech-pkg@NetBSD.org mailing list.",
928: "",
929: "\tpreformatted line 1",
930: "\tpreformatted line 2",
931: "",
932: " intentionally indented",
933: "* itemization",
934: "",
935: "Normal",
936: "text",
937: "continues",
938: "here",
939: "with",
940: "linebreaks.",
941: "",
1.22 rillig 942: "Sentence one. Sentence two.",
943: "",
944: "A\tB\tC\tD",
945: "E\tveryVeryVeryVeryVeryVeryVeryVeryLong")
1.18 rillig 946:
947: expected := []string{
948: "See the pkgsrc",
949: "guide, section",
950: "\"Package components,",
951: "Makefile\":",
952: "https://www.NetBSD.org/doc/pkgsrc/pkgsrc.html#components.Makefile.",
953: "",
954: "For more",
955: "information, ask on",
956: "the",
957: "tech-pkg@NetBSD.org",
958: "mailing list.",
959: "",
960: "\tpreformatted line 1",
961: "\tpreformatted line 2",
962: "",
963: " intentionally indented",
964: "* itemization",
965: "",
966: "Normal text",
967: "continues here with",
968: "linebreaks.",
969: "",
970: "Sentence one.",
1.22 rillig 971: "Sentence two.",
972: "",
973: "A\tB\tC\tD E",
974: "veryVeryVeryVeryVeryVeryVeryVeryLong"}
1.18 rillig 975:
1.32 rillig 976: t.CheckDeepEquals(wrapped, expected)
1.18 rillig 977: }
978:
979: func (s *Suite) Test_escapePrintable(c *check.C) {
1.32 rillig 980: t := s.Init(c)
981:
982: t.CheckEquals(escapePrintable(""), "")
983: t.CheckEquals(escapePrintable("ASCII only~\n\t"), "ASCII only~\n\t")
984: t.CheckEquals(escapePrintable("Beep \u0007 control \u001F"), "Beep <U+0007> control <U+001F>")
985: t.CheckEquals(escapePrintable("Bad \xFF character"), "Bad <0xFF> character")
986: t.CheckEquals(escapePrintable("Unicode \uFFFD replacement"), "Unicode <U+FFFD> replacement")
1.18 rillig 987: }
1.22 rillig 988:
989: func (s *Suite) Test_stringSliceLess(c *check.C) {
1.32 rillig 990: t := s.Init(c)
991:
1.22 rillig 992: var elements = [][][]string{
993: {nil, {}},
994: {{"a"}},
995: {{"a", "a"}},
996: {{"a", "b"}},
997: {{"b"}},
998: {{"b", "a"}}}
999:
1000: test := func(i int, iElement []string, j int, jElement []string) {
1001: actual := stringSliceLess(iElement, jElement)
1002: expected := i < j
1003: if actual != expected {
1.32 rillig 1004: t.CheckDeepEquals(
1.22 rillig 1005: []interface{}{i, iElement, j, jElement, actual},
1006: []interface{}{i, iElement, j, jElement, expected})
1007: }
1008: }
1009:
1010: for i, iElements := range elements {
1011: for j, jElements := range elements {
1012: for _, iElement := range iElements {
1013: for _, jElement := range jElements {
1014: test(i, iElement, j, jElement)
1015: }
1016: }
1017: }
1018: }
1019: }
1020:
1021: func (s *Suite) Test_joinSkipEmpty(c *check.C) {
1022: t := s.Init(c)
1023:
1.32 rillig 1024: t.CheckDeepEquals(
1.22 rillig 1025: joinSkipEmpty(", ", "", "one", "", "", "two", "", "three"),
1026: "one, two, three")
1027: }
1028:
1.49 rillig 1029: func (s *Suite) Test_joinCambridge(c *check.C) {
1.22 rillig 1030: t := s.Init(c)
1031:
1.32 rillig 1032: t.CheckDeepEquals(
1.49 rillig 1033: joinCambridge("and", "", "one", "", "", "two", "", "three"),
1.22 rillig 1034: "one, two and three")
1035:
1.32 rillig 1036: t.CheckDeepEquals(
1.49 rillig 1037: joinCambridge("and", "", "one", "", ""),
1.22 rillig 1038: "one")
1039: }
1040:
1.49 rillig 1041: func (s *Suite) Test_joinOxford(c *check.C) {
1.22 rillig 1042: t := s.Init(c)
1043:
1.32 rillig 1044: t.CheckDeepEquals(
1.49 rillig 1045: joinOxford("and", "", "one", "", "", "two", "", "three"),
1.22 rillig 1046: "one, two, and three")
1047: }
1048:
1.31 rillig 1049: func (s *Suite) Test_newPathMatcher(c *check.C) {
1050: t := s.Init(c)
1051:
1052: test := func(pattern string, matchType pathMatchType, matchPattern string) {
1.32 rillig 1053: t.CheckEquals(*newPathMatcher(pattern), pathMatcher{matchType, matchPattern, pattern})
1.31 rillig 1054: }
1055:
1056: testPanic := func(pattern string) {
1057: t.ExpectPanic(
1058: func() { _ = newPathMatcher(pattern) },
1059: "Pkglint internal error")
1060: }
1061:
1062: testPanic("*.[0123456]")
1063: testPanic("file.???")
1064: testPanic("*.???")
1065: test("", pmExact, "")
1066: test("exact", pmExact, "exact")
1067: test("*.mk", pmSuffix, ".mk")
1068: test("Makefile.*", pmPrefix, "Makefile.")
1069: testPanic("*.*")
1070: testPanic("**")
1071: testPanic("a*b")
1072: testPanic("[")
1073: testPanic("malformed[")
1074: }
1075:
1076: func (s *Suite) Test_pathMatcher_matches(c *check.C) {
1.32 rillig 1077: t := s.Init(c)
1.31 rillig 1078:
1079: test := func(pattern string, subject string, expected bool) {
1080: matcher := newPathMatcher(pattern)
1.32 rillig 1081: t.CheckEquals(matcher.matches(subject), expected)
1.31 rillig 1082: }
1083:
1084: test("", "", true)
1085: test("", "any", false)
1086: test("exact", "exact", true)
1087: test("exact", "different", false)
1088:
1089: test("*.mk", "filename.mk", true)
1090: test("*.mk", "filename.txt", false)
1091: test("*.mk", "filename.mkx", false)
1092: test("*.mk", ".mk", true)
1093:
1094: test("Makefile.*", "Makefile", false)
1095: test("Makefile.*", "Makefile.", true)
1096: test("Makefile.*", "Makefile.txt", true)
1097: test("Makefile.*", "makefile.txt", false)
1098: }
1099:
1.22 rillig 1100: func (s *Suite) Test_StringInterner(c *check.C) {
1101: t := s.Init(c)
1102:
1103: si := NewStringInterner()
1104:
1.32 rillig 1105: t.CheckEquals(si.Intern(""), "")
1106: t.CheckEquals(si.Intern("Hello"), "Hello")
1107: t.CheckEquals(si.Intern("Hello, world"), "Hello, world")
1108: t.CheckEquals(si.Intern("Hello, world"[0:5]), "Hello")
1.22 rillig 1109: }
1.34 rillig 1110:
1111: func (s *Suite) Test_shquote(c *check.C) {
1112: t := s.Init(c)
1113:
1114: test := func(in, out string) {
1115: t.CheckEquals(shquote(in), out)
1116: }
1117:
1118: test("", "''")
1119: test("'", "''\\'''")
1120: test("simple", "simple")
1121: test("~", "'~'")
1122: }
1.38 rillig 1123:
1124: func (s *Suite) Test_LazyStringBuilder_WriteByte__exact_match(c *check.C) {
1125: t := s.Init(c)
1126:
1127: sb := NewLazyStringBuilder("word")
1128:
1129: sb.WriteByte('w')
1130: sb.WriteByte('o')
1131: sb.WriteByte('r')
1132: sb.WriteByte('d')
1133:
1134: t.CheckEquals(sb.String(), "word")
1135: c.Check(sb.buf, check.IsNil)
1136: }
1137:
1138: func (s *Suite) Test_LazyStringBuilder_WriteByte__longer_than_expected(c *check.C) {
1139: t := s.Init(c)
1140:
1141: sb := NewLazyStringBuilder("word")
1142: sb.WriteByte('w')
1143: sb.WriteByte('o')
1144: sb.WriteByte('r')
1145: sb.WriteByte('d')
1146: sb.WriteByte('s')
1147:
1148: t.CheckEquals(sb.String(), "words")
1149: t.CheckDeepEquals(sb.buf, []byte{'w', 'o', 'r', 'd', 's'})
1150: }
1151:
1152: func (s *Suite) Test_LazyStringBuilder_WriteByte__shorter_than_expected(c *check.C) {
1153: t := s.Init(c)
1154:
1155: sb := NewLazyStringBuilder("word")
1156: sb.WriteByte('w')
1157: sb.WriteByte('o')
1158:
1159: t.CheckEquals(sb.String(), "wo")
1160: c.Check(sb.buf, check.IsNil)
1161:
1162: sb.WriteByte('r')
1163: sb.WriteByte('d')
1164:
1165: t.CheckEquals(sb.String(), "word")
1166: c.Check(sb.buf, check.IsNil)
1167: }
1168:
1169: func (s *Suite) Test_LazyStringBuilder_WriteByte__other_than_expected(c *check.C) {
1170: t := s.Init(c)
1171:
1172: sb := NewLazyStringBuilder("word")
1173: sb.WriteByte('w')
1174: sb.WriteByte('o')
1175: sb.WriteByte('l')
1176: sb.WriteByte('f')
1177:
1178: t.CheckEquals(sb.String(), "wolf")
1179: t.CheckDeepEquals(sb.buf, []byte{'w', 'o', 'l', 'f'})
1180: }
1181:
1.39 rillig 1182: func (s *Suite) Test_LazyStringBuilder_writeToBuf__assertion(c *check.C) {
1183: t := s.Init(c)
1184:
1185: sb := NewLazyStringBuilder("0123456789abcdef0123456789abcdef")
1186: sb.WriteString("0123456789abcdef0123456789abcdeX")
1187:
1188: t.CheckEquals(cap(sb.buf), 32)
1189:
1190: sb.Reset("0123456789abcdef")
1191: sb.WriteString("01234567")
1192:
1193: // Intentionally violate the invariant of the LazyStringBuilder that
1194: // as long as sb.usingBuf is false, sb.len is at most len(sb.expected).
1195: sb.expected = ""
1196: t.ExpectAssert(func() { sb.writeToBuf('x') })
1197: }
1198:
1.38 rillig 1199: func (s *Suite) Test_LazyStringBuilder_Reset(c *check.C) {
1200: t := s.Init(c)
1201:
1202: sb := NewLazyStringBuilder("word")
1203: sb.WriteByte('w')
1204:
1205: sb.Reset("other")
1206:
1207: t.CheckEquals(sb.String(), "")
1208:
1209: sb.WriteString("word")
1210:
1211: t.CheckEquals(sb.String(), "word")
1212: t.CheckEquals(sb.usingBuf, true)
1213: t.CheckDeepEquals(sb.buf, []byte("word"))
1214:
1215: sb.Reset("")
1216:
1217: t.CheckEquals(sb.String(), "")
1218: t.CheckEquals(sb.usingBuf, false)
1219: t.CheckDeepEquals(sb.buf, []byte("word"))
1220:
1221: sb.WriteByte('x')
1222:
1223: // Ensure that the buffer is reset properly.
1224: t.CheckEquals(sb.String(), "x")
1225: t.CheckEquals(sb.usingBuf, true)
1226: t.CheckDeepEquals(sb.buf, []byte("x"))
1227: }
1.40 rillig 1228:
1229: // sortedKeys takes the keys from an arbitrary map,
1230: // converts them to strings if necessary,
1231: // and then returns them sorted.
1232: //
1233: // It is only available during tests since it uses reflection.
1234: func keys(m interface{}) []string {
1235: var keys []string
1236: for _, key := range reflect.ValueOf(m).MapKeys() {
1237: switch key := key.Interface().(type) {
1238: case fmt.Stringer:
1239: keys = append(keys, key.String())
1240: default:
1241: keys = append(keys, key.(string))
1242: }
1243: }
1244: sort.Strings(keys)
1245: return keys
1246: }
1.44 rillig 1247:
1248: func (s *Suite) Test_interval(c *check.C) {
1249: t := s.Init(c)
1250:
1251: i := newInterval()
1252:
1253: t.CheckEquals(i.min > i.max, true)
1254:
1255: i.add(3)
1256:
1257: t.CheckEquals(i.min, 3)
1258: t.CheckEquals(i.max, 3)
1259:
1260: i.add(7)
1261:
1262: t.CheckEquals(i.min, 3)
1263: t.CheckEquals(i.max, 7)
1264:
1265: i.add(-5)
1266:
1267: t.CheckEquals(i.min, -5)
1268: t.CheckEquals(i.max, 7)
1269: }
CVSweb <webmaster@jp.NetBSD.org>