[BACK]Return to autofix_test.go CVS log [TXT][DIR] Up to [cvs.NetBSD.org] / pkgsrc / pkgtools / pkglint / files

Annotation of pkgsrc/pkgtools/pkglint/files/autofix_test.go, Revision 1.23

1.14      rillig      1: package pkglint
1.1       rillig      2:
1.5       rillig      3: import (
                      4:        "gopkg.in/check.v1"
1.10      rillig      5:        "os"
                      6:        "runtime"
1.5       rillig      7:        "strings"
                      8: )
1.1       rillig      9:
1.12      rillig     10: func (s *Suite) Test_Autofix_Warnf__duplicate(c *check.C) {
                     11:        t := s.Init(c)
                     12:
                     13:        line := t.NewLine("DESCR", 1, "Description of the package")
                     14:
                     15:        fix := line.Autofix()
                     16:        fix.Warnf("Warning 1.")
                     17:        t.ExpectPanic(
                     18:                func() { fix.Warnf("Warning 2.") },
                     19:                "Pkglint internal error: Autofix can only have a single diagnostic.")
                     20: }
                     21:
                     22: func (s *Suite) Test_Autofix__default_leaves_line_unchanged(c *check.C) {
                     23:        t := s.Init(c)
                     24:
1.16      rillig     25:        t.SetUpCommandLine("--source")
                     26:        mklines := t.SetUpFileMkLines("Makefile",
1.12      rillig     27:                "# row 1 \\",
                     28:                "continuation of row 1")
                     29:        line := mklines.lines.Lines[0]
                     30:
                     31:        fix := line.Autofix()
                     32:        fix.Warnf("Row should be replaced with line.")
                     33:        fix.Replace("row", "line")
                     34:        fix.ReplaceRegex(`row \d+`, "the above line", -1)
                     35:        fix.InsertBefore("above")
                     36:        fix.InsertAfter("below")
                     37:        fix.Delete()
                     38:        fix.Apply()
                     39:
                     40:        c.Check(fix.RawText(), equals, ""+
                     41:                "# row 1 \\\n"+
                     42:                "continuation of row 1\n")
                     43:        t.CheckOutputLines(
                     44:                ">\t# row 1 \\",
                     45:                ">\tcontinuation of row 1",
                     46:                "WARN: ~/Makefile:1--2: Row should be replaced with line.")
                     47:        c.Check(fix.modified, equals, true)
                     48: }
                     49:
                     50: func (s *Suite) Test_Autofix__show_autofix_modifies_line(c *check.C) {
                     51:        t := s.Init(c)
                     52:
1.16      rillig     53:        t.SetUpCommandLine("--source", "--show-autofix")
                     54:        mklines := t.SetUpFileMkLines("Makefile",
1.12      rillig     55:                "# row 1 \\",
                     56:                "continuation of row 1")
                     57:        line := mklines.lines.Lines[0]
                     58:
                     59:        fix := line.Autofix()
                     60:        fix.Warnf("Row should be replaced with line.")
                     61:        fix.ReplaceAfter("", "row", "line")
                     62:        fix.ReplaceRegex(`row \d+`, "the above line", -1)
                     63:        fix.InsertBefore("above")
                     64:        fix.InsertAfter("below")
                     65:        fix.Delete()
                     66:        fix.Apply()
                     67:
                     68:        c.Check(fix.RawText(), equals, ""+
                     69:                "above\n"+
                     70:                "below\n")
                     71:        t.CheckOutputLines(
                     72:                "WARN: ~/Makefile:1--2: Row should be replaced with line.",
                     73:                "AUTOFIX: ~/Makefile:1: Replacing \"row\" with \"line\".",
                     74:                "AUTOFIX: ~/Makefile:2: Replacing \"row 1\" with \"the above line\".",
                     75:                "AUTOFIX: ~/Makefile:1: Inserting a line \"above\" before this line.",
                     76:                "AUTOFIX: ~/Makefile:2: Inserting a line \"below\" after this line.",
                     77:                "AUTOFIX: ~/Makefile:1: Deleting this line.",
                     78:                "AUTOFIX: ~/Makefile:2: Deleting this line.",
                     79:                "+\tabove",
                     80:                "-\t# row 1 \\",
                     81:                "-\tcontinuation of row 1",
                     82:                "+\tbelow")
                     83:        c.Check(fix.modified, equals, true)
                     84: }
                     85:
                     86: func (s *Suite) Test_Autofix_ReplaceAfter__autofix(c *check.C) {
                     87:        t := s.Init(c)
                     88:
1.16      rillig     89:        t.SetUpCommandLine("--autofix", "--source")
                     90:        mklines := t.SetUpFileMkLines("Makefile",
1.12      rillig     91:                "# line 1 \\",
                     92:                "continuation 1 \\",
                     93:                "continuation 2")
                     94:
                     95:        fix := mklines.lines.Lines[0].Autofix()
                     96:        fix.Warnf("N should be replaced with V.")
                     97:        fix.ReplaceAfter("", "n", "v")
                     98:        fix.Apply()
                     99:
                    100:        t.CheckOutputLines(
                    101:                "AUTOFIX: ~/Makefile:1: Replacing \"n\" with \"v\".",
                    102:                "-\t# line 1 \\",
                    103:                "+\t# live 1 \\",
1.13      rillig    104:                "\tcontinuation 1 \\",
                    105:                "\tcontinuation 2")
1.12      rillig    106: }
                    107:
1.11      rillig    108: func (s *Suite) Test_Autofix_ReplaceRegex__show_autofix(c *check.C) {
1.2       rillig    109:        t := s.Init(c)
                    110:
1.16      rillig    111:        t.SetUpCommandLine("--show-autofix")
                    112:        lines := t.SetUpFileLines("Makefile",
1.2       rillig    113:                "line1",
                    114:                "line2",
                    115:                "line3")
1.1       rillig    116:
1.12      rillig    117:        fix := lines.Lines[1].Autofix()
1.1       rillig    118:        fix.Warnf("Something's wrong here.")
1.3       rillig    119:        fix.ReplaceRegex(`.`, "X", -1)
1.1       rillig    120:        fix.Apply()
                    121:        SaveAutofixChanges(lines)
                    122:
1.12      rillig    123:        c.Check(lines.Lines[1].raw[0].textnl, equals, "XXXXX\n")
1.2       rillig    124:        t.CheckFileLines("Makefile",
                    125:                "line1",
                    126:                "line2",
                    127:                "line3")
                    128:        t.CheckOutputLines(
1.1       rillig    129:                "WARN: ~/Makefile:2: Something's wrong here.",
1.3       rillig    130:                "AUTOFIX: ~/Makefile:2: Replacing \"l\" with \"X\".",
                    131:                "AUTOFIX: ~/Makefile:2: Replacing \"i\" with \"X\".",
                    132:                "AUTOFIX: ~/Makefile:2: Replacing \"n\" with \"X\".",
                    133:                "AUTOFIX: ~/Makefile:2: Replacing \"e\" with \"X\".",
                    134:                "AUTOFIX: ~/Makefile:2: Replacing \"2\" with \"X\".")
1.1       rillig    135: }
                    136:
1.11      rillig    137: func (s *Suite) Test_Autofix_ReplaceRegex__autofix(c *check.C) {
1.2       rillig    138:        t := s.Init(c)
                    139:
1.16      rillig    140:        t.SetUpCommandLine("--autofix", "--source")
                    141:        lines := t.SetUpFileLines("Makefile",
1.2       rillig    142:                "line1",
                    143:                "line2",
                    144:                "line3")
1.1       rillig    145:
1.12      rillig    146:        fix := lines.Lines[1].Autofix()
1.1       rillig    147:        fix.Warnf("Something's wrong here.")
1.3       rillig    148:        fix.ReplaceRegex(`.`, "X", 3)
1.1       rillig    149:        fix.Apply()
                    150:
1.3       rillig    151:        t.CheckOutputLines(
                    152:                "AUTOFIX: ~/Makefile:2: Replacing \"l\" with \"X\".",
                    153:                "AUTOFIX: ~/Makefile:2: Replacing \"i\" with \"X\".",
                    154:                "AUTOFIX: ~/Makefile:2: Replacing \"n\" with \"X\".",
                    155:                "-\tline2",
                    156:                "+\tXXXe2")
                    157:
1.12      rillig    158:        // After calling fix.Apply above, the autofix is ready to be used again.
1.1       rillig    159:        fix.Warnf("Use Y instead of X.")
                    160:        fix.Replace("X", "Y")
                    161:        fix.Apply()
                    162:
1.3       rillig    163:        t.CheckOutputLines(
                    164:                "AUTOFIX: ~/Makefile:2: Replacing \"X\" with \"Y\".",
                    165:                "-\tline2",
                    166:                "+\tYXXe2")
                    167:
1.1       rillig    168:        SaveAutofixChanges(lines)
                    169:
1.2       rillig    170:        t.CheckFileLines("Makefile",
                    171:                "line1",
1.3       rillig    172:                "YXXe2",
1.2       rillig    173:                "line3")
1.1       rillig    174: }
                    175:
1.11      rillig    176: func (s *Suite) Test_Autofix_ReplaceRegex__show_autofix_and_source(c *check.C) {
1.2       rillig    177:        t := s.Init(c)
                    178:
1.16      rillig    179:        t.SetUpCommandLine("--show-autofix", "--source")
                    180:        lines := t.SetUpFileLines("Makefile",
1.2       rillig    181:                "line1",
                    182:                "line2",
                    183:                "line3")
1.1       rillig    184:
1.12      rillig    185:        fix := lines.Lines[1].Autofix()
1.1       rillig    186:        fix.Warnf("Something's wrong here.")
1.3       rillig    187:        fix.ReplaceRegex(`.`, "X", -1)
1.1       rillig    188:        fix.Apply()
                    189:
                    190:        fix.Warnf("Use Y instead of X.")
                    191:        fix.Replace("X", "Y")
                    192:        fix.Apply()
                    193:
                    194:        SaveAutofixChanges(lines)
                    195:
1.2       rillig    196:        t.CheckOutputLines(
1.1       rillig    197:                "WARN: ~/Makefile:2: Something's wrong here.",
1.3       rillig    198:                "AUTOFIX: ~/Makefile:2: Replacing \"l\" with \"X\".",
                    199:                "AUTOFIX: ~/Makefile:2: Replacing \"i\" with \"X\".",
                    200:                "AUTOFIX: ~/Makefile:2: Replacing \"n\" with \"X\".",
                    201:                "AUTOFIX: ~/Makefile:2: Replacing \"e\" with \"X\".",
                    202:                "AUTOFIX: ~/Makefile:2: Replacing \"2\" with \"X\".",
1.2       rillig    203:                "-\tline2",
                    204:                "+\tXXXXX",
1.1       rillig    205:                "",
                    206:                "WARN: ~/Makefile:2: Use Y instead of X.",
                    207:                "AUTOFIX: ~/Makefile:2: Replacing \"X\" with \"Y\".",
1.2       rillig    208:                "-\tline2",
                    209:                "+\tYXXXX")
1.1       rillig    210: }
                    211:
1.18      rillig    212: // When an autofix replaces text, it does not touch those
                    213: // lines that have been inserted before since these are
                    214: // usually already correct.
                    215: func (s *Suite) Test_Autofix_ReplaceAfter__after_inserting_a_line(c *check.C) {
                    216:        t := s.Init(c)
                    217:
                    218:        t.SetUpCommandLine("--show-autofix")
                    219:        line := t.NewLine("filename", 5, "initial text")
                    220:
                    221:        fix := line.Autofix()
                    222:        fix.Notef("Inserting a line.")
                    223:        fix.InsertBefore("line before")
                    224:        fix.InsertAfter("line after")
                    225:        fix.Apply()
                    226:
                    227:        fix.Notef("Replacing text.")
                    228:        fix.Replace("l", "L")
                    229:        fix.ReplaceRegex(`i`, "I", 1)
                    230:        fix.Apply()
                    231:
                    232:        t.CheckOutputLines(
                    233:                "NOTE: filename:5: Inserting a line.",
                    234:                "AUTOFIX: filename:5: Inserting a line \"line before\" before this line.",
                    235:                "AUTOFIX: filename:5: Inserting a line \"line after\" after this line.",
                    236:                "NOTE: filename:5: Replacing text.",
                    237:                "AUTOFIX: filename:5: Replacing \"l\" with \"L\".",
                    238:                "AUTOFIX: filename:5: Replacing \"i\" with \"I\".")
                    239: }
                    240:
1.11      rillig    241: func (s *Suite) Test_SaveAutofixChanges(c *check.C) {
1.2       rillig    242:        t := s.Init(c)
                    243:
1.16      rillig    244:        t.SetUpCommandLine("--autofix")
                    245:        lines := t.SetUpFileLines("example.txt",
1.2       rillig    246:                "line1 := value1",
                    247:                "line2 := value2",
                    248:                "line3 := value3")
1.1       rillig    249:
1.12      rillig    250:        fix := lines.Lines[1].Autofix()
1.1       rillig    251:        fix.Warnf("Something's wrong here.")
1.12      rillig    252:        fix.ReplaceRegex(`...`, "XXX", 2)
1.3       rillig    253:        fix.Apply()
                    254:
1.12      rillig    255:        SaveAutofixChanges(lines)
1.1       rillig    256:
1.3       rillig    257:        t.CheckOutputLines(
1.12      rillig    258:                "AUTOFIX: ~/example.txt:2: Replacing \"lin\" with \"XXX\".",
                    259:                "AUTOFIX: ~/example.txt:2: Replacing \"e2 \" with \"XXX\".")
                    260:        t.CheckFileLines("example.txt",
1.2       rillig    261:                "line1 := value1",
1.12      rillig    262:                "XXXXXX:= value2",
                    263:                "line3 := value3")
1.1       rillig    264: }
                    265:
1.11      rillig    266: func (s *Suite) Test_SaveAutofixChanges__no_changes_necessary(c *check.C) {
                    267:        t := s.Init(c)
                    268:
1.16      rillig    269:        t.SetUpCommandLine("--autofix")
                    270:        lines := t.SetUpFileLines("DESCR",
1.11      rillig    271:                "Line 1",
                    272:                "Line 2")
                    273:
1.12      rillig    274:        fix := lines.Lines[0].Autofix()
1.11      rillig    275:        fix.Warnf("Dummy warning.")
                    276:        fix.Replace("X", "Y")
                    277:        fix.Apply()
                    278:
                    279:        // Since nothing has been effectively changed,
                    280:        // nothing needs to be saved.
                    281:        SaveAutofixChanges(lines)
                    282:
                    283:        // And therefore, no AUTOFIX action must appear in the log.
                    284:        t.CheckOutputEmpty()
                    285: }
                    286:
1.12      rillig    287: func (s *Suite) Test_Autofix__multiple_fixes(c *check.C) {
1.2       rillig    288:        t := s.Init(c)
                    289:
1.16      rillig    290:        t.SetUpCommandLine("--show-autofix", "--explain")
1.1       rillig    291:
1.13      rillig    292:        line := t.NewLine("filename", 1, "original")
1.1       rillig    293:
                    294:        c.Check(line.autofix, check.IsNil)
1.2       rillig    295:        c.Check(line.raw, check.DeepEquals, t.NewRawLines(1, "original\n"))
1.1       rillig    296:
                    297:        {
                    298:                fix := line.Autofix()
1.12      rillig    299:                fix.Warnf(SilentAutofixFormat)
1.3       rillig    300:                fix.ReplaceRegex(`(.)(.*)(.)`, "lriginao", 1) // XXX: the replacement should be "$3$2$1"
1.1       rillig    301:                fix.Apply()
                    302:        }
                    303:
                    304:        c.Check(line.autofix, check.NotNil)
1.2       rillig    305:        c.Check(line.raw, check.DeepEquals, t.NewRawLines(1, "original\n", "lriginao\n"))
                    306:        t.CheckOutputLines(
1.13      rillig    307:                "AUTOFIX: filename:1: Replacing \"original\" with \"lriginao\".")
1.1       rillig    308:
                    309:        {
                    310:                fix := line.Autofix()
1.12      rillig    311:                fix.Warnf(SilentAutofixFormat)
1.1       rillig    312:                fix.Replace("i", "u")
                    313:                fix.Apply()
                    314:        }
                    315:
                    316:        c.Check(line.autofix, check.NotNil)
1.2       rillig    317:        c.Check(line.raw, check.DeepEquals, t.NewRawLines(1, "original\n", "lruginao\n"))
1.1       rillig    318:        c.Check(line.raw[0].textnl, equals, "lruginao\n")
1.2       rillig    319:        t.CheckOutputLines(
1.13      rillig    320:                "AUTOFIX: filename:1: Replacing \"i\" with \"u\".")
1.1       rillig    321:
                    322:        {
                    323:                fix := line.Autofix()
1.12      rillig    324:                fix.Warnf(SilentAutofixFormat)
1.1       rillig    325:                fix.Replace("lruginao", "middle")
                    326:                fix.Apply()
                    327:        }
                    328:
                    329:        c.Check(line.autofix, check.NotNil)
1.2       rillig    330:        c.Check(line.raw, check.DeepEquals, t.NewRawLines(1, "original\n", "middle\n"))
1.1       rillig    331:        c.Check(line.raw[0].textnl, equals, "middle\n")
1.2       rillig    332:        t.CheckOutputLines(
1.13      rillig    333:                "AUTOFIX: filename:1: Replacing \"lruginao\" with \"middle\".")
1.12      rillig    334:
                    335:        c.Check(line.raw[0].textnl, equals, "middle\n")
                    336:        t.CheckOutputEmpty()
1.1       rillig    337:
                    338:        {
                    339:                fix := line.Autofix()
1.12      rillig    340:                fix.Warnf(SilentAutofixFormat)
                    341:                fix.Delete()
1.1       rillig    342:                fix.Apply()
1.12      rillig    343:        }
                    344:
                    345:        c.Check(line.Autofix().RawText(), equals, "")
                    346:        t.CheckOutputLines(
1.13      rillig    347:                "AUTOFIX: filename:1: Deleting this line.")
1.12      rillig    348: }
                    349:
                    350: func (s *Suite) Test_Autofix_Explain__without_explain_option(c *check.C) {
                    351:        t := s.Init(c)
                    352:
                    353:        line := t.NewLine("Makefile", 74, "line1")
1.1       rillig    354:
1.12      rillig    355:        fix := line.Autofix()
                    356:        fix.Warnf("Please write row instead of line.")
                    357:        fix.Replace("line", "row")
                    358:        fix.Explain("Explanation")
                    359:        fix.Apply()
                    360:
                    361:        t.CheckOutputLines(
                    362:                "WARN: Makefile:74: Please write row instead of line.")
1.23    ! rillig    363:        c.Check(G.Logger.explanationsAvailable, equals, true)
1.12      rillig    364: }
                    365:
                    366: func (s *Suite) Test_Autofix_Explain__default(c *check.C) {
                    367:        t := s.Init(c)
                    368:
1.16      rillig    369:        t.SetUpCommandLine("--explain")
1.12      rillig    370:        line := t.NewLine("Makefile", 74, "line1")
                    371:
                    372:        fix := line.Autofix()
                    373:        fix.Warnf("Please write row instead of line.")
                    374:        fix.Replace("line", "row")
                    375:        fix.Explain("Explanation")
                    376:        fix.Apply()
                    377:
                    378:        t.CheckOutputLines(
                    379:                "WARN: Makefile:74: Please write row instead of line.",
                    380:                "",
                    381:                "\tExplanation",
                    382:                "")
1.23    ! rillig    383:        c.Check(G.Logger.explanationsAvailable, equals, true)
1.12      rillig    384: }
                    385:
                    386: func (s *Suite) Test_Autofix_Explain__show_autofix(c *check.C) {
                    387:        t := s.Init(c)
1.1       rillig    388:
1.16      rillig    389:        t.SetUpCommandLine("--show-autofix", "--explain")
1.12      rillig    390:        line := t.NewLine("Makefile", 74, "line1")
1.1       rillig    391:
1.12      rillig    392:        fix := line.Autofix()
                    393:        fix.Warnf("Please write row instead of line.")
                    394:        fix.Replace("line", "row")
                    395:        fix.Explain("Explanation")
                    396:        fix.Apply()
1.1       rillig    397:
1.12      rillig    398:        t.CheckOutputLines(
                    399:                "WARN: Makefile:74: Please write row instead of line.",
                    400:                "AUTOFIX: Makefile:74: Replacing \"line\" with \"row\".",
1.1       rillig    401:                "",
1.12      rillig    402:                "\tExplanation",
1.1       rillig    403:                "")
1.23    ! rillig    404:        c.Check(G.Logger.explanationsAvailable, equals, true)
1.12      rillig    405: }
                    406:
                    407: func (s *Suite) Test_Autofix_Explain__autofix(c *check.C) {
                    408:        t := s.Init(c)
                    409:
1.16      rillig    410:        t.SetUpCommandLine("--autofix", "--explain")
1.12      rillig    411:        line := t.NewLine("Makefile", 74, "line1")
                    412:
                    413:        fix := line.Autofix()
                    414:        fix.Warnf("Please write row instead of line.")
                    415:        fix.Replace("line", "row")
                    416:        fix.Explain("Explanation")
                    417:        fix.Apply()
1.1       rillig    418:
1.12      rillig    419:        t.CheckOutputLines(
                    420:                "AUTOFIX: Makefile:74: Replacing \"line\" with \"row\".")
1.23    ! rillig    421:        c.Check(G.Logger.explanationsAvailable, equals, false) // Not necessary.
1.12      rillig    422: }
                    423:
                    424: func (s *Suite) Test_Autofix_Explain__SilentAutofixFormat(c *check.C) {
                    425:        t := s.Init(c)
                    426:
1.16      rillig    427:        t.SetUpCommandLine("--explain")
1.12      rillig    428:        line := t.NewLine("example.txt", 1, "Text")
                    429:
                    430:        fix := line.Autofix()
                    431:        fix.Warnf(SilentAutofixFormat)
                    432:        t.ExpectPanic(
                    433:                func() { fix.Explain("Explanation for inserting a line before.") },
                    434:                "Pkglint internal error: Autofix: Silent fixes cannot have an explanation.")
                    435: }
                    436:
                    437: // To combine a silent diagnostic with an explanation, two separate autofixes
                    438: // are necessary.
                    439: func (s *Suite) Test_Autofix_Explain__silent_with_diagnostic(c *check.C) {
                    440:        t := s.Init(c)
                    441:
1.16      rillig    442:        t.SetUpCommandLine("--explain")
1.12      rillig    443:        line := t.NewLine("example.txt", 1, "Text")
                    444:
                    445:        fix := line.Autofix()
                    446:        fix.Warnf(SilentAutofixFormat)
                    447:        fix.InsertBefore("before")
                    448:        fix.Apply()
                    449:
                    450:        fix.Notef("This diagnostic is necessary for the following explanation.")
                    451:        fix.Explain(
                    452:                "When inserting multiple lines, Apply must be called in-between.",
                    453:                "Otherwise the changes are not described to the human reader.")
                    454:        fix.InsertAfter("after")
                    455:        fix.Apply()
1.1       rillig    456:
1.2       rillig    457:        t.CheckOutputLines(
1.12      rillig    458:                "NOTE: example.txt:1: This diagnostic is necessary for the following explanation.",
                    459:                "",
                    460:                "\tWhen inserting multiple lines, Apply must be called in-between.",
                    461:                "\tOtherwise the changes are not described to the human reader.",
                    462:                "")
                    463:        c.Check(fix.RawText(), equals, "Text\n")
1.1       rillig    464: }
                    465:
1.12      rillig    466: func (s *Suite) Test_Autofix__show_autofix_and_source_continuation_line(c *check.C) {
1.2       rillig    467:        t := s.Init(c)
                    468:
1.16      rillig    469:        t.SetUpCommandLine("--show-autofix", "--source")
                    470:        mklines := t.SetUpFileMkLines("Makefile",
1.4       rillig    471:                MkRcsID,
1.9       rillig    472:                "# before \\",
1.2       rillig    473:                "The old song \\",
                    474:                "after")
1.12      rillig    475:        line := mklines.lines.Lines[1]
1.1       rillig    476:
1.13      rillig    477:        fix := line.Autofix()
                    478:        fix.Warnf("Using \"old\" is deprecated.")
                    479:        fix.Replace("old", "new")
                    480:        fix.Apply()
1.1       rillig    481:
1.13      rillig    482:        // Using a tab for indentation preserves the exact layout in the output
                    483:        // since in pkgsrc Makefiles, tabs are also used in the middle of the line
                    484:        // to align the variable values. Using a single space for indentation would
                    485:        // make some of the lines appear misaligned in the pkglint output although
                    486:        // they are correct in the Makefiles.
1.2       rillig    487:        t.CheckOutputLines(
                    488:                "WARN: ~/Makefile:2--4: Using \"old\" is deprecated.",
                    489:                "AUTOFIX: ~/Makefile:3: Replacing \"old\" with \"new\".",
1.13      rillig    490:                "\t# before \\",
1.2       rillig    491:                "-\tThe old song \\",
                    492:                "+\tThe new song \\",
1.13      rillig    493:                "\tafter")
1.1       rillig    494: }
                    495:
                    496: func (s *Suite) Test_Autofix_InsertBefore(c *check.C) {
1.2       rillig    497:        t := s.Init(c)
                    498:
1.16      rillig    499:        t.SetUpCommandLine("--show-autofix", "--source")
1.2       rillig    500:        line := t.NewLine("Makefile", 30, "original")
1.1       rillig    501:
                    502:        fix := line.Autofix()
1.6       rillig    503:        fix.Warnf("Dummy.")
1.1       rillig    504:        fix.InsertBefore("inserted")
                    505:        fix.Apply()
                    506:
1.2       rillig    507:        t.CheckOutputLines(
1.6       rillig    508:                "WARN: Makefile:30: Dummy.",
1.1       rillig    509:                "AUTOFIX: Makefile:30: Inserting a line \"inserted\" before this line.",
1.2       rillig    510:                "+\tinserted",
                    511:                ">\toriginal")
1.1       rillig    512: }
                    513:
                    514: func (s *Suite) Test_Autofix_Delete(c *check.C) {
1.2       rillig    515:        t := s.Init(c)
                    516:
1.16      rillig    517:        t.SetUpCommandLine("--show-autofix", "--source")
1.2       rillig    518:        line := t.NewLine("Makefile", 30, "to be deleted")
1.1       rillig    519:
                    520:        fix := line.Autofix()
1.6       rillig    521:        fix.Warnf("Dummy.")
1.1       rillig    522:        fix.Delete()
                    523:        fix.Apply()
                    524:
1.2       rillig    525:        t.CheckOutputLines(
1.6       rillig    526:                "WARN: Makefile:30: Dummy.",
1.1       rillig    527:                "AUTOFIX: Makefile:30: Deleting this line.",
1.2       rillig    528:                "-\tto be deleted")
1.1       rillig    529: }
                    530:
1.12      rillig    531: // Deleting a line from a Makefile also deletes its continuation lines.
                    532: func (s *Suite) Test_Autofix_Delete__continuation_line(c *check.C) {
                    533:        t := s.Init(c)
                    534:
1.16      rillig    535:        t.SetUpCommandLine("--show-autofix", "--source")
                    536:        mklines := t.SetUpFileMkLines("Makefile",
1.12      rillig    537:                MkRcsID,
                    538:                "# line 1 \\",
                    539:                "continued")
                    540:        line := mklines.lines.Lines[1]
                    541:
                    542:        fix := line.Autofix()
                    543:        fix.Warnf("Dummy warning.")
                    544:        fix.Delete()
                    545:        fix.Apply()
                    546:
                    547:        t.CheckOutputLines(
                    548:                "WARN: ~/Makefile:2--3: Dummy warning.",
                    549:                "AUTOFIX: ~/Makefile:2: Deleting this line.",
                    550:                "AUTOFIX: ~/Makefile:3: Deleting this line.",
                    551:                "-\t# line 1 \\",
                    552:                "-\tcontinued")
                    553: }
                    554:
                    555: func (s *Suite) Test_Autofix_Delete__combined_with_insert(c *check.C) {
                    556:        t := s.Init(c)
                    557:
1.16      rillig    558:        t.SetUpCommandLine("--show-autofix", "--source")
1.12      rillig    559:        line := t.NewLine("Makefile", 30, "to be deleted")
                    560:
                    561:        fix := line.Autofix()
                    562:        fix.Warnf("This line should be replaced completely.")
                    563:        fix.Delete()
                    564:        fix.InsertAfter("below")
                    565:        fix.InsertBefore("above")
                    566:        fix.Apply()
                    567:
                    568:        t.CheckOutputLines(
                    569:                "WARN: Makefile:30: This line should be replaced completely.",
                    570:                "AUTOFIX: Makefile:30: Deleting this line.",
                    571:                "AUTOFIX: Makefile:30: Inserting a line \"below\" after this line.",
                    572:                "AUTOFIX: Makefile:30: Inserting a line \"above\" before this line.",
                    573:                "+\tabove",
                    574:                "-\tto be deleted",
                    575:                "+\tbelow")
                    576: }
                    577:
1.17      rillig    578: // Demonstrates that without the --show-autofix option, diagnostics are
                    579: // shown even when they cannot be autofixed.
                    580: //
                    581: // This is typical when an autofix is provided for simple scenarios,
                    582: // but the code actually found is a little more complicated.
                    583: func (s *Suite) Test_Autofix__show_unfixable_diagnostics_in_default_mode(c *check.C) {
                    584:        t := s.Init(c)
                    585:
                    586:        t.SetUpCommandLine("--source")
                    587:        lines := t.NewLines("Makefile",
                    588:                "line1",
                    589:                "line2",
                    590:                "line3")
                    591:
                    592:        lines.Lines[0].Warnf("This warning is shown since the --show-autofix option is not given.")
                    593:
                    594:        fix := lines.Lines[1].Autofix()
                    595:        fix.Warnf("This warning cannot be fixed and is therefore not shown.")
                    596:        fix.Replace("XXX", "TODO")
                    597:        fix.Apply()
                    598:
                    599:        fix.Warnf("This warning cannot be fixed automatically but should be shown anyway.")
                    600:        fix.Replace("XXX", "TODO")
                    601:        fix.Anyway()
                    602:        fix.Apply()
                    603:
                    604:        // If this warning should ever appear it is probably because fix.anyway is not reset properly.
                    605:        fix.Warnf("This warning cannot be fixed and is therefore not shown.")
                    606:        fix.Replace("XXX", "TODO")
                    607:        fix.Apply()
                    608:
                    609:        lines.Lines[2].Warnf("This warning is also shown.")
                    610:
                    611:        t.CheckOutputLines(
                    612:                ">\tline1",
                    613:                "WARN: Makefile:1: This warning is shown since the --show-autofix option is not given.",
                    614:                "",
                    615:                ">\tline2",
                    616:                "WARN: Makefile:2: This warning cannot be fixed automatically but should be shown anyway.",
                    617:                "",
                    618:                ">\tline3",
                    619:                "WARN: Makefile:3: This warning is also shown.")
                    620: }
                    621:
1.1       rillig    622: // Demonstrates that the --show-autofix option only shows those diagnostics
                    623: // that would be fixed.
1.17      rillig    624: func (s *Suite) Test_Autofix__suppress_unfixable_warnings_with_show_autofix(c *check.C) {
1.2       rillig    625:        t := s.Init(c)
                    626:
1.16      rillig    627:        t.SetUpCommandLine("--show-autofix", "--source")
1.2       rillig    628:        lines := t.NewLines("Makefile",
1.1       rillig    629:                "line1",
                    630:                "line2",
                    631:                "line3")
                    632:
1.12      rillig    633:        lines.Lines[0].Warnf("This warning is not shown since it is not part of a fix.")
1.1       rillig    634:
1.12      rillig    635:        fix := lines.Lines[1].Autofix()
1.1       rillig    636:        fix.Warnf("Something's wrong here.")
1.12      rillig    637:        fix.ReplaceRegex(`.....`, "XXX", 1)
1.1       rillig    638:        fix.Apply()
                    639:
1.11      rillig    640:        fix.Warnf("Since XXX marks are usually not fixed, use TODO instead to draw attention.")
1.1       rillig    641:        fix.Replace("XXX", "TODO")
                    642:        fix.Apply()
                    643:
1.12      rillig    644:        lines.Lines[2].Warnf("Neither is this warning shown.")
1.1       rillig    645:
1.2       rillig    646:        t.CheckOutputLines(
1.1       rillig    647:                "WARN: Makefile:2: Something's wrong here.",
1.12      rillig    648:                "AUTOFIX: Makefile:2: Replacing \"line2\" with \"XXX\".",
1.2       rillig    649:                "-\tline2",
1.12      rillig    650:                "+\tXXX",
1.1       rillig    651:                "",
1.11      rillig    652:                "WARN: Makefile:2: Since XXX marks are usually not fixed, use TODO instead to draw attention.",
1.1       rillig    653:                "AUTOFIX: Makefile:2: Replacing \"XXX\" with \"TODO\".",
1.2       rillig    654:                "-\tline2",
1.12      rillig    655:                "+\tTODO")
1.2       rillig    656: }
                    657:
1.18      rillig    658: // With the default command line options, this warning is printed.
                    659: // With the --show-autofix option this warning is NOT printed since it
                    660: // cannot be fixed automatically.
                    661: func (s *Suite) Test_Autofix_Anyway__options(c *check.C) {
                    662:        t := s.Init(c)
                    663:
                    664:        t.SetUpCommandLine("--show-autofix")
                    665:
                    666:        line := t.NewLine("filename", 3, "")
                    667:        fix := line.Autofix()
                    668:
                    669:        fix.Warnf("This autofix doesn't match.")
                    670:        fix.Replace("doesn't match", "")
                    671:        fix.Anyway()
                    672:        fix.Apply()
                    673:
                    674:        t.CheckOutputEmpty()
                    675:
                    676:        t.SetUpCommandLine()
                    677:
                    678:        fix.Warnf("This autofix doesn't match.")
                    679:        fix.Replace("doesn't match", "")
                    680:        fix.Anyway()
                    681:        fix.Apply()
                    682:
                    683:        t.CheckOutputLines(
                    684:                "WARN: filename:3: This autofix doesn't match.")
                    685: }
                    686:
1.12      rillig    687: // If an Autofix doesn't do anything, it must not log any diagnostics.
1.11      rillig    688: func (s *Suite) Test_Autofix__noop_replace(c *check.C) {
1.2       rillig    689:        t := s.Init(c)
                    690:
                    691:        line := t.NewLine("Makefile", 14, "Original text")
                    692:
                    693:        fix := line.Autofix()
                    694:        fix.Warnf("All-uppercase words should not be used at all.")
1.3       rillig    695:        fix.ReplaceRegex(`\b[A-Z]{3,}\b`, "---censored---", -1)
1.2       rillig    696:        fix.Apply()
                    697:
                    698:        // No output since there was no all-uppercase word in the text.
                    699:        t.CheckOutputEmpty()
1.1       rillig    700: }
                    701:
1.12      rillig    702: // When using Autofix.Custom, it is tricky to get all the details right.
1.11      rillig    703: // For best results, see the existing examples and the documentation.
1.12      rillig    704: //
                    705: // Since this custom fix only operates on the text of the current line,
                    706: // it can handle both the --show-autofix and the --autofix cases using
                    707: // the same code.
                    708: func (s *Suite) Test_Autofix_Custom__in_memory(c *check.C) {
1.5       rillig    709:        t := s.Init(c)
                    710:
                    711:        lines := t.NewLines("Makefile",
                    712:                "line1",
                    713:                "line2",
                    714:                "line3")
                    715:
                    716:        doFix := func(line Line) {
                    717:                fix := line.Autofix()
1.6       rillig    718:                fix.Warnf("Please write in ALL-UPPERCASE.")
1.12      rillig    719:                fix.Custom(func(showAutofix, autofix bool) {
1.5       rillig    720:                        fix.Describef(int(line.firstLine), "Converting to uppercase")
1.12      rillig    721:                        if showAutofix || autofix {
1.5       rillig    722:                                line.Text = strings.ToUpper(line.Text)
                    723:                        }
                    724:                })
                    725:                fix.Apply()
                    726:        }
                    727:
1.12      rillig    728:        doFix(lines.Lines[0])
1.5       rillig    729:
                    730:        t.CheckOutputLines(
1.6       rillig    731:                "WARN: Makefile:1: Please write in ALL-UPPERCASE.")
1.5       rillig    732:
1.16      rillig    733:        t.SetUpCommandLine("--show-autofix")
1.5       rillig    734:
1.12      rillig    735:        doFix(lines.Lines[1])
1.5       rillig    736:
                    737:        t.CheckOutputLines(
1.6       rillig    738:                "WARN: Makefile:2: Please write in ALL-UPPERCASE.",
1.5       rillig    739:                "AUTOFIX: Makefile:2: Converting to uppercase")
1.12      rillig    740:        c.Check(lines.Lines[1].Text, equals, "LINE2")
1.5       rillig    741:
1.16      rillig    742:        t.SetUpCommandLine("--autofix")
1.5       rillig    743:
1.12      rillig    744:        doFix(lines.Lines[2])
1.5       rillig    745:
                    746:        t.CheckOutputLines(
                    747:                "AUTOFIX: Makefile:3: Converting to uppercase")
1.12      rillig    748:        c.Check(lines.Lines[2].Text, equals, "LINE3")
1.6       rillig    749: }
1.8       rillig    750:
                    751: // Since the diagnostic doesn't contain the string "few", nothing happens.
1.12      rillig    752: func (s *Suite) Test_Autofix_skip(c *check.C) {
1.8       rillig    753:        t := s.Init(c)
                    754:
1.16      rillig    755:        t.SetUpCommandLine("--only", "few", "--autofix")
1.8       rillig    756:
1.16      rillig    757:        mklines := t.SetUpFileMkLines("filename",
1.12      rillig    758:                "VAR=\t111 222 333 444 555 \\",
                    759:                "666")
                    760:        lines := mklines.lines
1.8       rillig    761:
1.12      rillig    762:        fix := lines.Lines[0].Autofix()
1.8       rillig    763:        fix.Warnf("Many.")
                    764:        fix.Explain(
                    765:                "Explanation.")
1.12      rillig    766:
                    767:        // None of the following actions has any effect because of the --only option above.
1.8       rillig    768:        fix.Replace("111", "___")
                    769:        fix.ReplaceAfter(" ", "222", "___")
                    770:        fix.ReplaceRegex(`\d+`, "___", 1)
                    771:        fix.InsertBefore("before")
                    772:        fix.InsertAfter("after")
                    773:        fix.Delete()
1.12      rillig    774:        fix.Custom(func(showAutofix, autofix bool) {})
                    775:        fix.Realign(mklines.mklines[0], 32)
                    776:
1.8       rillig    777:        fix.Apply()
                    778:
                    779:        SaveAutofixChanges(lines)
                    780:
                    781:        t.CheckOutputEmpty()
1.13      rillig    782:        t.CheckFileLines("filename",
1.12      rillig    783:                "VAR=\t111 222 333 444 555 \\",
                    784:                "666")
                    785:        c.Check(fix.RawText(), equals, ""+
                    786:                "VAR=\t111 222 333 444 555 \\\n"+
                    787:                "666\n")
                    788: }
                    789:
                    790: // Demonstrates how to filter log messages.
                    791: // The --autofix option can restrict the fixes to exactly one group or topic.
                    792: func (s *Suite) Test_Autofix_Apply__only(c *check.C) {
                    793:        t := s.Init(c)
                    794:
1.16      rillig    795:        t.SetUpCommandLine("--autofix", "--source", "--only", "interesting")
1.12      rillig    796:        line := t.NewLine("Makefile", 27, "The old song")
                    797:
                    798:        // Is completely ignored, including any autofixes.
                    799:        fix := line.Autofix()
                    800:        fix.Warnf("Using \"old\" is deprecated.")
                    801:        fix.Replace("old", "new1")
                    802:        fix.Apply()
                    803:
                    804:        fix.Warnf("Using \"old\" is interesting.")
                    805:        fix.Replace("old", "new2")
                    806:        fix.Apply()
                    807:
                    808:        t.CheckOutputLines(
                    809:                "AUTOFIX: Makefile:27: Replacing \"old\" with \"new2\".",
                    810:                "-\tThe old song",
                    811:                "+\tThe new2 song")
1.8       rillig    812: }
1.10      rillig    813:
                    814: func (s *Suite) Test_Autofix_Apply__panic(c *check.C) {
                    815:        t := s.Init(c)
                    816:
1.13      rillig    817:        line := t.NewLine("filename", 123, "text")
1.12      rillig    818:
                    819:        t.ExpectPanic(
                    820:                func() {
                    821:                        fix := line.Autofix()
                    822:                        fix.Apply()
                    823:                },
                    824:                "Pkglint internal error: Each autofix must have a log level and a diagnostic.")
                    825:
                    826:        t.ExpectPanic(
                    827:                func() {
                    828:                        fix := line.Autofix()
                    829:                        fix.Replace("from", "to")
                    830:                        fix.Apply()
                    831:                },
                    832:                "Pkglint internal error: Autofix: The diagnostic must be given before the action.")
                    833:
                    834:        t.ExpectPanic(
                    835:                func() {
                    836:                        fix := line.Autofix()
                    837:                        fix.Warnf("Warning without period")
                    838:                        fix.Apply()
                    839:                },
                    840:                "Pkglint internal error: Autofix: format \"Warning without period\" must end with a period.")
                    841: }
1.10      rillig    842:
1.12      rillig    843: // Ensures that empty lines are logged between the diagnostics,
                    844: // even when combining normal warnings and autofix warnings.
                    845: //
                    846: // Up to 2018-10-27, pkglint didn't insert the required empty line in this case.
                    847: func (s *Suite) Test_Autofix_Apply__explanation_followed_by_note(c *check.C) {
                    848:        t := s.Init(c)
                    849:
1.16      rillig    850:        t.SetUpCommandLine("--source")
1.12      rillig    851:        line := t.NewLine("README.txt", 123, "text")
                    852:
                    853:        fix := line.Autofix()
                    854:        fix.Warnf("A warning with autofix.")
                    855:        fix.Explain("Explanation.")
                    856:        fix.Replace("text", "Text")
                    857:        fix.Apply()
1.10      rillig    858:
1.12      rillig    859:        line.Notef("A note without fix.")
1.10      rillig    860:
1.12      rillig    861:        t.CheckOutputLines(
                    862:                ">\ttext",
                    863:                "WARN: README.txt:123: A warning with autofix.",
                    864:                "",
                    865:                ">\ttext",
                    866:                "NOTE: README.txt:123: A note without fix.")
1.10      rillig    867: }
                    868:
1.18      rillig    869: func (s *Suite) Test_Autofix_Anyway__autofix_option(c *check.C) {
                    870:        t := s.Init(c)
                    871:
                    872:        t.SetUpCommandLine("--autofix")
                    873:        line := t.NewLine("filename", 5, "text")
                    874:
                    875:        fix := line.Autofix()
                    876:        fix.Notef("This line is quite short.")
                    877:        fix.Replace("not found", "needle")
                    878:        fix.Anyway()
                    879:        fix.Apply()
                    880:
                    881:        // The replacement text is not found, therefore the note should not be logged.
                    882:        // Because of fix.Anyway, the note should be logged anyway.
                    883:        // But because of the --autofix option, the note should not be logged.
                    884:        // Therefore, in the end nothing is shown in this case.
                    885:        t.CheckOutputEmpty()
                    886: }
                    887:
                    888: func (s *Suite) Test_Autofix_Anyway__autofix_and_show_autofix_options(c *check.C) {
                    889:        t := s.Init(c)
                    890:
                    891:        t.SetUpCommandLine("--autofix", "--show-autofix")
                    892:        line := t.NewLine("filename", 5, "text")
                    893:
                    894:        fix := line.Autofix()
                    895:        fix.Notef("This line is quite short.")
                    896:        fix.Replace("not found", "needle")
                    897:        fix.Anyway()
                    898:        fix.Apply()
                    899:
1.21      rillig    900:        // The text to be replaced is not found. Because nothing is fixed here,
                    901:        // there's no need to log anything.
                    902:        //
                    903:        // But fix.Anyway is set, therefore the diagnostic should be logged even
                    904:        // though it cannot be fixed automatically. This comes handy in situations
                    905:        // where simple cases can be fixed automatically and more complex cases
                    906:        // (often involving special characters that need to be escaped properly)
                    907:        // should nevertheless result in a diagnostics.
                    908:        //
                    909:        // The --autofix option is set, which means that the diagnostics don't
                    910:        // get logged, only the actual fixes do.
                    911:        //
                    912:        // But then there's also the --show-autofix option, which logs the
                    913:        // corresponding diagnostic for each autofix that actually changes
                    914:        // something. But this autofix doesn't change anything, therefore even
                    915:        // the --show-autofix doesn't have an effect.
                    916:        //
                    917:        // Therefore, in the end nothing is logged in this case.
1.18      rillig    918:        t.CheckOutputEmpty()
                    919: }
                    920:
                    921: // The --autofix option normally suppresses the diagnostics and just logs
                    922: // the actual fixes. Adding the --show-autofix option logs both.
                    923: func (s *Suite) Test_Autofix_Apply__autofix_and_show_autofix_options(c *check.C) {
                    924:        t := s.Init(c)
                    925:
                    926:        t.SetUpCommandLine("--autofix", "--show-autofix")
                    927:        line := t.NewLine("filename", 5, "text")
                    928:
                    929:        fix := line.Autofix()
                    930:        fix.Notef("This line is quite short.")
                    931:        fix.Replace("text", "replacement")
                    932:        fix.Apply()
                    933:
                    934:        t.CheckOutputLines(
                    935:                "NOTE: filename:5: This line is quite short.",
                    936:                "AUTOFIX: filename:5: Replacing \"text\" with \"replacement\".")
                    937: }
                    938:
                    939: // Ensures that without explanations, the separator between the individual
                    940: // diagnostics are generated.
                    941: func (s *Suite) Test_Autofix_Apply__source_without_explain(c *check.C) {
                    942:        t := s.Init(c)
                    943:
                    944:        t.SetUpCommandLine("--source", "--explain", "--show-autofix")
                    945:        line := t.NewLine("filename", 5, "text")
                    946:
                    947:        fix := line.Autofix()
                    948:        fix.Notef("This line is quite short.")
                    949:        fix.Replace("text", "replacement")
                    950:        fix.Apply()
                    951:
                    952:        fix.Warnf("Follow-up warning, separated.")
                    953:        fix.Replace("replacement", "text again")
                    954:        fix.Apply()
                    955:
                    956:        t.CheckOutputLines(
                    957:                "NOTE: filename:5: This line is quite short.",
                    958:                "AUTOFIX: filename:5: Replacing \"text\" with \"replacement\".",
                    959:                "-\ttext",
                    960:                "+\treplacement",
                    961:                "",
                    962:                "WARN: filename:5: Follow-up warning, separated.",
                    963:                "AUTOFIX: filename:5: Replacing \"replacement\" with \"text again\".",
                    964:                "-\ttext",
                    965:                "+\ttext again")
                    966: }
                    967:
                    968: func (s *Suite) Test_Autofix_Realign__wrong_line_type(c *check.C) {
                    969:        t := s.Init(c)
                    970:
                    971:        mklines := t.NewMkLines("file.mk",
                    972:                MkRcsID,
                    973:                ".if \\",
                    974:                "${PKGSRC_RUN_TESTS}")
                    975:
                    976:        mkline := mklines.mklines[1]
                    977:        fix := mkline.Autofix()
                    978:
                    979:        t.ExpectPanic(
                    980:                func() { fix.Realign(mkline, 16) },
                    981:                "Pkglint internal error: Line must be a variable assignment.")
                    982: }
                    983:
                    984: func (s *Suite) Test_Autofix_Realign__short_continuation_line(c *check.C) {
                    985:        t := s.Init(c)
                    986:
                    987:        t.SetUpCommandLine("--autofix")
                    988:        mklines := t.SetUpFileMkLines("file.mk",
                    989:                MkRcsID,
                    990:                "BUILD_DIRS= \\",
                    991:                "\tdir \\",
                    992:                "")
                    993:        mkline := mklines.mklines[1]
                    994:        fix := mkline.Autofix()
                    995:        fix.Warnf("Line should be realigned.")
                    996:
                    997:        // In this case realigning has no effect since the oldWidth == 8,
                    998:        // which counts as "sufficiently intentional not to be modified".
                    999:        fix.Realign(mkline, 16)
                   1000:
                   1001:        mklines.SaveAutofixChanges()
                   1002:
                   1003:        t.CheckOutputEmpty()
                   1004:        t.CheckFileLines("file.mk",
                   1005:                MkRcsID,
                   1006:                "BUILD_DIRS= \\",
                   1007:                "\tdir \\",
                   1008:                "")
                   1009: }
                   1010:
                   1011: func (s *Suite) Test_Autofix_Realign__multiline_indented_with_spaces(c *check.C) {
                   1012:        t := s.Init(c)
                   1013:
                   1014:        t.SetUpCommandLine("--autofix")
                   1015:        mklines := t.SetUpFileMkLines("file.mk",
                   1016:                MkRcsID,
                   1017:                "BUILD_DIRS= \\",
                   1018:                "\t        dir1 \\",
                   1019:                "\t\tdir2 \\",
                   1020:                "  ") // Trailing whitespace is not fixed by Autofix.Realign.
                   1021:
                   1022:        mkline := mklines.mklines[1]
                   1023:
                   1024:        fix := mkline.Autofix()
                   1025:        fix.Warnf("Warning.")
                   1026:        fix.Realign(mkline, 16)
                   1027:        fix.Apply()
                   1028:
                   1029:        mklines.SaveAutofixChanges()
                   1030:
                   1031:        t.CheckOutputLines(
                   1032:                "AUTOFIX: ~/file.mk:3: Replacing indentation \"\\t        \" with \"\\t\\t\".")
                   1033:        t.CheckFileLines("file.mk",
                   1034:                MkRcsID,
                   1035:                "BUILD_DIRS= \\",
                   1036:                "\t\tdir1 \\",
                   1037:                "\t\tdir2 \\",
                   1038:                "  ")
                   1039: }
                   1040:
                   1041: // Just for branch coverage.
                   1042: func (s *Suite) Test_Autofix_setDiag__no_testing_mode(c *check.C) {
                   1043:        t := s.Init(c)
                   1044:
                   1045:        line := t.NewLine("file.mk", 123, "text")
                   1046:
                   1047:        G.Testing = false
                   1048:
                   1049:        fix := line.Autofix()
                   1050:        fix.Notef("Note.")
                   1051:        fix.Replace("from", "to")
                   1052:        fix.Apply()
                   1053:
                   1054:        t.CheckOutputEmpty()
                   1055: }
                   1056:
                   1057: func (s *Suite) Test_Autofix_setDiag__bad_call_sequence(c *check.C) {
                   1058:        t := s.Init(c)
                   1059:
                   1060:        line := t.NewLine("file.mk", 123, "text")
                   1061:        fix := line.Autofix()
                   1062:        fix.Notef("Note.")
                   1063:
                   1064:        t.ExpectPanic(
                   1065:                func() { fix.Notef("Note 2.") },
                   1066:                "Pkglint internal error: Autofix can only have a single diagnostic.")
                   1067:
                   1068:        fix.level = nil // To cover the second assertion.
                   1069:        t.ExpectPanic(
                   1070:                func() { fix.Notef("Note 2.") },
                   1071:                "Pkglint internal error: Autofix can only have a single diagnostic.")
                   1072: }
                   1073:
                   1074: func (s *Suite) Test_Autofix_assertRealLine(c *check.C) {
                   1075:        t := s.Init(c)
                   1076:
                   1077:        line := NewLineEOF("filename.mk")
                   1078:        fix := line.Autofix()
                   1079:        fix.Warnf("Warning.")
                   1080:
                   1081:        t.ExpectPanic(
                   1082:                func() { fix.Replace("from", "to") },
                   1083:                "Pkglint internal error: Cannot autofix this line since it is not a real line.")
                   1084: }
                   1085:
1.12      rillig   1086: func (s *Suite) Test_SaveAutofixChanges__file_removed(c *check.C) {
1.10      rillig   1087:        t := s.Init(c)
                   1088:
1.16      rillig   1089:        t.SetUpCommandLine("--autofix")
                   1090:        lines := t.SetUpFileLines("subdir/file.txt",
1.10      rillig   1091:                "line 1")
1.12      rillig   1092:        _ = os.RemoveAll(t.File("subdir"))
1.10      rillig   1093:
1.12      rillig   1094:        fix := lines.Lines[0].Autofix()
1.10      rillig   1095:        fix.Warnf("Should start with an uppercase letter.")
                   1096:        fix.Replace("line", "Line")
                   1097:        fix.Apply()
                   1098:
                   1099:        SaveAutofixChanges(lines)
                   1100:
1.22      rillig   1101:        t.CheckOutputMatches(
                   1102:                "AUTOFIX: ~/subdir/file.txt:1: Replacing \"line\" with \"Line\".",
                   1103:                `ERROR: ~/subdir/file.txt.pkglint.tmp: Cannot write: .*`)
1.10      rillig   1104: }
                   1105:
1.12      rillig   1106: func (s *Suite) Test_SaveAutofixChanges__file_busy_Windows(c *check.C) {
1.10      rillig   1107:        t := s.Init(c)
                   1108:
                   1109:        if runtime.GOOS != "windows" {
                   1110:                return
                   1111:        }
                   1112:
1.16      rillig   1113:        t.SetUpCommandLine("--autofix")
                   1114:        lines := t.SetUpFileLines("subdir/file.txt",
1.10      rillig   1115:                "line 1")
                   1116:
                   1117:        // As long as the file is kept open, it cannot be overwritten or deleted.
                   1118:        openFile, err := os.OpenFile(t.File("subdir/file.txt"), 0, 0666)
                   1119:        defer openFile.Close()
                   1120:        c.Check(err, check.IsNil)
                   1121:
1.12      rillig   1122:        fix := lines.Lines[0].Autofix()
1.10      rillig   1123:        fix.Warnf("Should start with an uppercase letter.")
                   1124:        fix.Replace("line", "Line")
                   1125:        fix.Apply()
                   1126:
                   1127:        SaveAutofixChanges(lines)
                   1128:
1.22      rillig   1129:        t.CheckOutputMatches(
                   1130:                "AUTOFIX: ~/subdir/file.txt:1: Replacing \"line\" with \"Line\".",
                   1131:                `ERROR: ~/subdir/file.txt.pkglint.tmp: Cannot overwrite with autofixed content: .*`)
1.10      rillig   1132: }
                   1133:
1.12      rillig   1134: // This test covers the highly unlikely situation in which a file is loaded
1.10      rillig   1135: // by pkglint, and just before writing the autofixed content back, another
                   1136: // process takes the file and replaces it with a directory of the same name.
                   1137: //
                   1138: // 100% code coverage sometimes requires creativity. :)
1.12      rillig   1139: func (s *Suite) Test_SaveAutofixChanges__cannot_overwrite(c *check.C) {
1.10      rillig   1140:        t := s.Init(c)
                   1141:
1.16      rillig   1142:        t.SetUpCommandLine("--autofix")
                   1143:        lines := t.SetUpFileLines("file.txt",
1.10      rillig   1144:                "line 1")
                   1145:
                   1146:        c.Check(os.RemoveAll(t.File("file.txt")), check.IsNil)
                   1147:        c.Check(os.MkdirAll(t.File("file.txt"), 0777), check.IsNil)
                   1148:
1.12      rillig   1149:        fix := lines.Lines[0].Autofix()
1.10      rillig   1150:        fix.Warnf("Should start with an uppercase letter.")
                   1151:        fix.Replace("line", "Line")
                   1152:        fix.Apply()
                   1153:
                   1154:        SaveAutofixChanges(lines)
                   1155:
1.22      rillig   1156:        t.CheckOutputMatches(
                   1157:                "AUTOFIX: ~/file.txt:1: Replacing \"line\" with \"Line\".",
                   1158:                `ERROR: ~/file.txt.pkglint.tmp: Cannot overwrite with autofixed content: .*`)
1.12      rillig   1159: }
                   1160:
1.13      rillig   1161: // Up to 2018-11-25, pkglint in some cases logged only the source without
                   1162: // a corresponding warning.
                   1163: func (s *Suite) Test_Autofix__lonely_source(c *check.C) {
                   1164:        t := s.Init(c)
                   1165:
1.16      rillig   1166:        t.SetUpCommandLine("-Wall", "--source")
1.13      rillig   1167:        G.Logger.Opts.LogVerbose = false // For realistic conditions; otherwise all diagnostics are logged.
                   1168:
1.16      rillig   1169:        t.SetUpPackage("x11/xorg-cf-files",
1.13      rillig   1170:                ".include \"../../x11/xorgproto/buildlink3.mk\"")
1.16      rillig   1171:        t.SetUpPackage("x11/xorgproto",
1.13      rillig   1172:                "DISTNAME=\txorgproto-1.0")
                   1173:        t.CreateFileDummyBuildlink3("x11/xorgproto/buildlink3.mk")
                   1174:        t.CreateFileLines("x11/xorgproto/builtin.mk",
                   1175:                MkRcsID,
                   1176:                "",
                   1177:                "BUILTIN_PKG:=\txorgproto",
                   1178:                "",
                   1179:                "PRE_XORGPROTO_LIST_MISSING =\tapplewmproto",
                   1180:                "",
                   1181:                ".for id in ${PRE_XORGPROTO_LIST_MISSING}",
                   1182:                ".endfor")
                   1183:        t.Chdir(".")
1.20      rillig   1184:        t.FinishSetUp()
1.13      rillig   1185:
1.15      rillig   1186:        G.Check("x11/xorg-cf-files")
                   1187:        G.Check("x11/xorgproto")
1.13      rillig   1188:
                   1189:        t.CheckOutputLines(
                   1190:                ">\tPRE_XORGPROTO_LIST_MISSING =\tapplewmproto",
                   1191:                "NOTE: x11/xorgproto/builtin.mk:5: Unnecessary space after variable name \"PRE_XORGPROTO_LIST_MISSING\".")
                   1192: }
                   1193:
                   1194: // Up to 2018-11-26, pkglint in some cases logged only the source without
                   1195: // a corresponding warning.
                   1196: func (s *Suite) Test_Autofix__lonely_source_2(c *check.C) {
                   1197:        t := s.Init(c)
                   1198:
1.16      rillig   1199:        t.SetUpCommandLine("-Wall", "--source", "--explain")
1.13      rillig   1200:        G.Logger.Opts.LogVerbose = false // For realistic conditions; otherwise all diagnostics are logged.
                   1201:
1.16      rillig   1202:        t.SetUpPackage("print/tex-bibtex8",
1.13      rillig   1203:                "MAKE_FLAGS+=\tCFLAGS=${CFLAGS.${PKGSRC_COMPILER}}")
                   1204:        t.Chdir(".")
1.20      rillig   1205:        t.FinishSetUp()
1.13      rillig   1206:
1.15      rillig   1207:        G.Check("print/tex-bibtex8")
1.13      rillig   1208:
                   1209:        t.CheckOutputLines(
                   1210:                ">\tMAKE_FLAGS+=\tCFLAGS=${CFLAGS.${PKGSRC_COMPILER}}",
                   1211:                "WARN: print/tex-bibtex8/Makefile:20: Please use ${CFLAGS.${PKGSRC_COMPILER}:Q} instead of ${CFLAGS.${PKGSRC_COMPILER}}.",
                   1212:                "",
                   1213:                "\tSee the pkgsrc guide, section \"Echoing a string exactly as-is\":",
                   1214:                "\thttps://www.NetBSD.org/docs/pkgsrc/pkgsrc.html#echo-literal",
                   1215:                "",
                   1216:                ">\tMAKE_FLAGS+=\tCFLAGS=${CFLAGS.${PKGSRC_COMPILER}}",
                   1217:                "WARN: print/tex-bibtex8/Makefile:20: The list variable PKGSRC_COMPILER should not be embedded in a word.",
                   1218:                "",
                   1219:                "\tWhen a list variable has multiple elements, this expression expands",
                   1220:                "\tto something unexpected:",
                   1221:                "",
                   1222:                "\tExample: ${MASTER_SITE_SOURCEFORGE}directory/ expands to",
                   1223:                "",
                   1224:                "\t\thttps://mirror1.sf.net/ https://mirror2.sf.net/directory/",
                   1225:                "",
1.14      rillig   1226:                "\tThe first URL is missing the directory. To fix this, write",
1.13      rillig   1227:                "\t\t${MASTER_SITE_SOURCEFORGE:=directory/}.",
                   1228:                "",
                   1229:                "\tExample: -l${LIBS} expands to",
                   1230:                "",
                   1231:                "\t\t-llib1 lib2",
                   1232:                "",
1.14      rillig   1233:                "\tThe second library is missing the -l. To fix this, write",
                   1234:                "\t${LIBS:S,^,-l,}.",
1.13      rillig   1235:                "")
                   1236: }
                   1237:
1.12      rillig   1238: // RawText returns the raw text of the fixed line, including line ends.
                   1239: // This may differ from the original text when the --show-autofix
                   1240: // or --autofix options are enabled.
                   1241: func (fix *Autofix) RawText() string {
                   1242:        var text strings.Builder
                   1243:        for _, lineBefore := range fix.linesBefore {
                   1244:                text.WriteString(lineBefore)
                   1245:        }
                   1246:        for _, raw := range fix.line.raw {
                   1247:                text.WriteString(raw.textnl)
                   1248:        }
                   1249:        for _, lineAfter := range fix.linesAfter {
                   1250:                text.WriteString(lineAfter)
                   1251:        }
                   1252:        return text.String()
1.10      rillig   1253: }

CVSweb <webmaster@jp.NetBSD.org>