Can cause issues on Windows consider filepath.Join instead
ignoreFileName := filepath.Join(tmpDir, ".gitignore")
1package main23import (4 "fmt"5 "os"6 "os/exec"7 "path/filepath"8 "regexp"9 "runtime"10 "slices"11 "strconv"12 "strings"13 "testing"14)1516const sccTestFlag string = "-test.main"1718var sccBinPath = os.Args[0]1920func TestMain(m *testing.M) {21 idx := slices.Index(os.Args, sccTestFlag)22 if idx != -1 {23 os.Args = slices.Delete(os.Args, idx, idx+1)24 main()25 return26 }2728 os.Exit(m.Run())29}3031func runSCC(args ...string) (string, error) {32 args = slices.Insert(args, 0, sccTestFlag)33 cmd := exec.Command(sccBinPath, args...)34 res, err := cmd.CombinedOutput()35 return string(res), err36}3738func TestNoGitIgnore(t *testing.T) {39 tmpDir := t.TempDir()40 ignoreFileName := filepath.Join(tmpDir, ".gitignore")41 err := os.WriteFile(ignoreFileName, []byte("ignored.xml\n"), 0644)42 if err != nil {43 t.Fatal(err)44 }45 xmlFileName := filepath.Join(tmpDir, "ignored.xml")46 err = os.WriteFile(xmlFileName, []byte(`<?xml version="1.0" encoding="UTF-8"?>`), 0644)47 if err != nil {48 t.Fatal(err)49 }5051 output, err := runSCC(tmpDir)52 if err != nil {53 t.Fatal(err)54 }55 if strings.Contains(output, "XML") {56 t.Fatalf("test --no-gitignore failed, output:\n%s", output)57 }5859 output, err = runSCC("--no-gitignore", tmpDir)60 if err != nil {61 t.Fatal(err)62 }63 if !strings.Contains(output, "XML") {64 t.Fatalf("test --no-gitignore failed, output:\n%s", output)65 }66}6768func TestIssue82(t *testing.T) {69 t.Parallel()70 // Regression issue https://github.com/boyter/scc/issues/8271 output1, err := runSCC(".")72 if err != nil {73 t.Fatal(err)74 }7576 pwd, err := os.Getwd()77 if err != nil {78 t.Fatal(err)79 }80 output2, err := runSCC(pwd)81 if err != nil {82 t.Fatal(err)83 }8485 if output1 != output2 {86 t.Fatalf("`./scc .` not equal to `./scc ${PWD}`")87 }88}8990func TestIncludeExt(t *testing.T) {91 t.Parallel()92 // Regression issue https://github.com/boyter/scc/issues/10893 output, err := runSCC("--include-ext", "go", "examples/language")94 if err != nil {95 t.Fatal(err)96 }97 if !strings.Contains(output, "Go") || strings.Contains(output, "Java") {98 t.Fatalf("include-ext check failed, output:\n%s", output)99 }100}101102func TestIssue115(t *testing.T) {103 t.Parallel()104 // Regression issue https://github.com/boyter/scc/issues/115105 output, err := runSCC("examples/issue115/.test/file")106 if err != nil {107 t.Fatal(err)108 }109 if strings.Contains(output, "Perl") {110 t.Fatalf("Should not print Perl, output:\n%s", output)111 }112}113114func TestIssue120(t *testing.T) {115 t.Parallel()116 // Regression issue https://github.com/boyter/scc/issues/120117 output, err := runSCC("-i", "java", "./examples/issue120")118 if err != nil {119 t.Fatal(err)120 }121 if strings.Contains(output, "Perl") {122 t.Fatal("extension param should ignore Shebang")123 }124}125126func TestIssue152(t *testing.T) {127 t.Parallel()128 // Regression issue https://github.com/boyter/scc/issues/152129 output, err := runSCC("-i", "css", "./examples/issue152/")130 if err != nil {131 t.Fatal(err)132 }133 if !strings.Contains(output, "CSS") {134 t.Fatalf("`-i css` extension check failed, output:\n%s", output)135 }136}137138func TestIssue250(t *testing.T) {139 // Regression issue https://github.com/boyter/scc/issues/250140 output1, err := runSCC("--exclude-dir", "examples/")141 if err != nil {142 t.Fatal(err)143 }144 output2, err := runSCC("--exclude-dir", "examples")145 if err != nil {146 t.Fatal(err)147 }148149 if output1 != output2 {150 t.Fatalf("examples exclude-dir check failed, output1:\n%s, output2:\n%s", output1, output2)151 }152}153154func TestIssue259(t *testing.T) {155 // Regression issue https://github.com/boyter/scc/issues/259156 output, err := runSCC("-f", "csv", "--exclude-ext", "go")157 if err != nil {158 t.Fatal(err)159 }160161 if strings.Contains(output, "Go,") {162 t.Fatalf("exclude-ext check failed, output:\n%s", output)163 }164}165166func TestIssue260(t *testing.T) {167 t.Parallel()168 // Regression issue https://github.com/boyter/scc/issues/260169 _, err := runSCC("-d", "examples/issue260/")170 if err != nil {171 t.Fatalf("duplicate empty crash: %v", err)172 }173}174175func TestIssue345(t *testing.T) {176 t.Parallel()177 // Regression issue https://github.com/boyter/scc/issues/345178 const expectedOutput = "C++,4,3,1,0,0,76,1,0"179 output, err := runSCC("-f", "csv", "--no-scc-ignore", "examples/issue345/")180 if err != nil {181 t.Fatal(err)182 }183 lines := strings.Split(output, "\n")184 if len(lines) < 2 {185 t.Fatalf("wrong output: %s", output)186 }187 if lines[1] != expectedOutput {188 t.Fatalf("got: %s, want: %s", lines[1], expectedOutput)189 }190}191192func TestIssue379(t *testing.T) {193 t.Parallel()194 // Regression issue https://github.com/boyter/scc/issues/379195 const expectedOutput = "Python,7,4,2,1,1,83,1,0"196 output, err := runSCC("-f", "csv", "--no-scc-ignore", "examples/issue379/")197 if err != nil {198 t.Fatal(err)199 }200 lines := strings.Split(output, "\n")201 if len(lines) < 2 {202 t.Fatalf("wrong output: %s", output)203 }204 if lines[1] != expectedOutput {205 t.Fatalf("got: %s, want: %s", lines[1], expectedOutput)206 }207}208209func TestIssue457(t *testing.T) {210 t.Parallel()211 // Regression issue https://github.com/boyter/scc/issues/457212 output, err := runSCC("-M", ".*")213 if err != nil {214 t.Fatal(err)215 }216 if !strings.Contains(output, "0.000 megabytes") {217 t.Fatalf("Issue 457 test failed, output:\n%s", output)218 }219}220221func TestIssue564(t *testing.T) {222 t.Parallel()223 // Regression issue https://github.com/boyter/scc/issues/564224 const expectedPythonOutput = "Python,3,3,0,0,0,84,3,0"225 const expectedGoOutput = "Go,6,4,0,2,0,58,2,0"226 output, err := runSCC("-f", "csv", "--no-scc-ignore", "examples/issue564/")227 if err != nil {228 t.Fatal(err)229 }230 lines := strings.Split(output, "\n")231 if len(lines) < 3 {232 t.Fatalf("wrong output: %s", output)233 }234 if lines[1] != expectedPythonOutput {235 t.Fatalf("got: %s, want: %s", lines[1], expectedPythonOutput)236 }237 if lines[2] != expectedGoOutput {238 t.Fatalf("got: %s, want: %s", lines[2], expectedGoOutput)239 }240}241242func TestIssue610(t *testing.T) {243 t.Parallel()244 // Regression issue https://github.com/boyter/scc/issues/610245 const expectedOutput = "TypeScript,11,7,2,2,1,214,1,0"246 output, err := runSCC("-f", "csv", "--no-scc-ignore", "examples/issue610/")247 if err != nil {248 t.Fatal(err)249 }250 lines := strings.Split(output, "\n")251 if len(lines) < 2 {252 t.Fatalf("wrong output: %s", output)253 }254 if lines[1] != expectedOutput {255 t.Fatalf("got: %s, want: %s", lines[1], expectedOutput)256 }257}258259func TestIssue339(t *testing.T) {260 t.Parallel()261 // Regression issue https://github.com/boyter/scc/issues/339262 output, err := runSCC("-f", "csv", "--no-scc-ignore", "examples/issue339/")263 if err != nil {264 t.Fatal(err)265 }266 if !strings.Contains(output, "MATLAB") {267 t.Errorf("can not find MATLAB, output: %s", output)268 }269 if !strings.Contains(output, "Objective C") {270 t.Errorf("can not find Objective C, output:\n%s", output)271 }272}273274func TestInvalidOption(t *testing.T) {275 t.Parallel()276 output, err := runSCC("--not-a-real-option")277 if err == nil {278 t.Fatal("scc should exit with error code")279 }280 if !strings.Contains(output, "Error: unknown flag: --not-a-real-option") {281 t.Fatalf("scc should report invalid options, output:\n%s", output)282 }283}284285func TestFileFlagSyntax(t *testing.T) {286 tmpDir := t.TempDir()287 flagsFileName := filepath.Join(tmpDir, "flags.txt")288 // include \n, \r\n and no line terminators289 testCases := []string{290 "go.mod\ngo.sum\nLICENSE\n",291 "go.mod\r\ngo.sum\r\nLICENSE\r\n",292 "go.mod\ngo.sum\nLICENSE",293 "go.mod\r\ngo.sum\r\nLICENSE",294 "go.mod\ngo.sum\r\nLICENSE",295 }296297 for _, tc := range testCases {298 err := os.WriteFile(flagsFileName, []byte(tc), 0644)299 if err != nil {300 t.Fatal(err)301 }302 _, err = runSCC("@" + flagsFileName)303 if err != nil {304 t.Errorf("flag syntax faild: %q, %v", tc, err)305 }306 }307}308309func TestLineLength(t *testing.T) {310 t.Parallel()311 output, err := runSCC("-m")312 if err != nil {313 t.Fatal(err)314 }315 if strings.Count(output, "MaxLine / MeanLine") < 2 {316 t.Fatalf("line length test failed, output:\n%s", output)317 }318}319320func TestFormatHTML(t *testing.T) {321 t.Parallel()322 output, err := runSCC("--format", "html")323 if err != nil {324 t.Fatal(err)325 }326 if !strings.Contains(output, "<title>scc html output</title>") {327 t.Fatalf("html format test failed, output:\n%s", output)328 }329}330331func TestFormatHTMLTable(t *testing.T) {332 t.Parallel()333 output, err := runSCC("--format", "html-table")334 if err != nil {335 t.Fatal(err)336 }337 if !strings.Contains(output, `<table id="scc-table">`) {338 t.Fatalf("html-table format test failed, output:\n%s", output)339 }340}341342func TestFormatSQL(t *testing.T) {343 t.Parallel()344 output, err := runSCC("--format", "sql")345 if err != nil {346 t.Fatal(err)347 }348 if !strings.Contains(output, "create table metadata ( -- github.com/boyter/scc") {349 t.Fatalf("sql format test failed, output:\n%s", output)350 }351}352353func TestFormatSQLInsert(t *testing.T) {354 t.Parallel()355 output, err := runSCC("--format", "sql-insert")356 if err != nil {357 t.Fatal(err)358 }359 if !strings.Contains(output, "begin transaction;\ninsert into t values(") {360 t.Fatalf("sql-insert format test failed, output:\n%s", output)361 }362}363364func TestMultipleFormatStdout(t *testing.T) {365 output, err := runSCC("--format-multi", "tabular:stdout,html:stdout,csv:stdout,sql:stdout")366 if err != nil {367 t.Fatal(err)368 }369370 tabularPattern := regexp.MustCompile(`Processed .+? bytes, .+? megabytes \(SI\)`)371 if !tabularPattern.MatchString(output) {372 t.Errorf("multi-format tabular failed, output:\n%s", output)373 }374375 if !strings.Contains(output, `<html lang="en"><head><meta charset="utf-8" /><title>scc html output</title>`) {376 t.Errorf("multi-format html failed, output:\n%s", output)377 }378379 if !strings.Contains(output, "Language,Lines,Code,Comments,Blanks,Complexity,Bytes,Files,ULOC") {380 t.Errorf("multi-format csv failed, output:\n%s", output)381 }382383 sqlPattern := regexp.MustCompile(`insert into t values\(.+?\);`)384 if !sqlPattern.MatchString(output) {385 t.Errorf("multi-format sql failed, output:\n%s", output)386 }387}388389func TestMultipleFormatWriteFile(t *testing.T) {390 tmpDir := t.TempDir()391 outputTabular := filepath.Join(tmpDir, "output.tab")392 outputWide := filepath.Join(tmpDir, "output.wide")393 outputJSON1 := filepath.Join(tmpDir, "output.json")394 outputJSON2 := filepath.Join(tmpDir, "output2.json")395 outputCSV := filepath.Join(tmpDir, "output.csv")396 outputYAML := filepath.Join(tmpDir, "output.yaml")397 outputHTML := filepath.Join(tmpDir, "output.html")398 outputHTMLTable := filepath.Join(tmpDir, "output_table.html")399 outputSQL := filepath.Join(tmpDir, "output.sql")400401 multiFormatArgs := fmt.Sprintf(402 "tabular:%s,wide:%s,json:%s,json2:%s,csv:%s,cloc-yaml:%s,html:%s,html-table:%s,sql:%s",403 outputTabular,404 outputWide,405 outputJSON1,406 outputJSON2,407 outputCSV,408 outputYAML,409 outputHTML,410 outputHTMLTable,411 outputSQL,412 )413414 _, err := runSCC("--format-multi", multiFormatArgs)415 if err != nil {416 t.Fatal(err)417 }418419 if info, err := os.Stat(outputTabular); err != nil || info.Size() <= 0 {420 t.Fatal("tabular write file test failed")421 }422 if info, err := os.Stat(outputWide); err != nil || info.Size() <= 0 {423 t.Fatal("wide write file test failed")424 }425 if info, err := os.Stat(outputJSON1); err != nil || info.Size() <= 0 {426 t.Fatal("json write file test failed")427 }428 if info, err := os.Stat(outputJSON2); err != nil || info.Size() <= 0 {429 t.Fatal("json2 write file test failed")430 }431 if info, err := os.Stat(outputCSV); err != nil || info.Size() <= 0 {432 t.Fatal("csv write file test failed")433 }434 if info, err := os.Stat(outputYAML); err != nil || info.Size() <= 0 {435 t.Fatal("cloc-yaml write file test failed")436 }437 if info, err := os.Stat(outputHTML); err != nil || info.Size() <= 0 {438 t.Fatal("html write file test failed")439 }440 if info, err := os.Stat(outputHTMLTable); err != nil || info.Size() <= 0 {441 t.Fatal("html-table write file test failed")442 }443 if info, err := os.Stat(outputSQL); err != nil || info.Size() <= 0 {444 t.Fatal("sql write file test failed")445 }446}447448func TestRecursivelyIgnore(t *testing.T) {449 tmpDir := t.TempDir()450 err := os.WriteFile(filepath.Join(tmpDir, ".gitignore"), []byte("ignore-git.txt\n"), 0644)451 if err != nil {452 t.Fatal(err)453 }454 err = os.WriteFile(filepath.Join(tmpDir, ".ignore"), []byte("vendor/\nignore.txt\n"), 0644)455 if err != nil {456 t.Fatal(err)457 }458 err = os.Mkdir(filepath.Join(tmpDir, "ignore"), 0755)459 if err != nil {460 t.Fatal(err)461 }462 err = os.WriteFile(filepath.Join(tmpDir, "ignore", "README.md"), []byte("Files in here are to ensure that .ignore and .gitignore work recursively\n"), 0644)463 if err != nil {464 t.Fatal(err)465 }466 err = os.WriteFile(filepath.Join(tmpDir, "ignore", "ignore.txt"), []byte("testing\n"), 0644)467 if err != nil {468 t.Fatal(err)469 }470 err = os.WriteFile(filepath.Join(tmpDir, "ignore", "ignore-git.txt"), []byte("git\ntesting\n"), 0644)471 if err != nil {472 t.Fatal(err)473 }474475 output, err := runSCC("--by-file", "--no-scc-ignore", tmpDir)476 if err != nil {477 t.Fatal(err)478 }479 if strings.Contains(output, "ignore.txt") || strings.Contains(output, "ignore-git.txt") {480 t.Errorf("ignore recursive filter failed, output:\n%s", output)481 }482483 output, err = runSCC("--by-file", "--no-scc-ignore", "--no-ignore", tmpDir)484 if err != nil {485 t.Fatal(err)486 }487 if !strings.Contains(output, "ignore.txt") || strings.Contains(output, "ignore-git.txt") {488 t.Errorf("ignore recursive filter failed, output:\n%s", output)489 }490491 output, err = runSCC("--by-file", "--no-scc-ignore", "--no-gitignore", tmpDir)492 if err != nil {493 t.Fatal(err)494 }495 if strings.Contains(output, "ignore.txt") || !strings.Contains(output, "ignore-git.txt") {496 t.Errorf("ignore recursive filter failed, output:\n%s", output)497 }498499 output, err = runSCC("--by-file", "--no-scc-ignore", "--no-ignore", "--no-gitignore", tmpDir)500 if err != nil {501 t.Fatal(err)502 }503 if !strings.Contains(output, "ignore.txt") || !strings.Contains(output, "ignore-git.txt") {504 t.Errorf("ignore recursive filter failed, output:\n%s", output)505 }506}507508func TestMultipleGitIgnore(t *testing.T) {509 tmpDir := t.TempDir()510 err := os.WriteFile(filepath.Join(tmpDir, ".gitignore"), []byte("ignore.txt\n"), 0644)511 if err != nil {512 t.Fatal(err)513 }514 err = os.Mkdir(filepath.Join(tmpDir, "ignore"), 0755)515 if err != nil {516 t.Fatal(err)517 }518 err = os.WriteFile(filepath.Join(tmpDir, "ignore", ".gitignore"), []byte("ignore.java\n"), 0644)519 if err != nil {520 t.Fatal(err)521 }522 err = os.WriteFile(filepath.Join(tmpDir, "ignore", "ignore.java"), []byte("//test\n"), 0644)523 if err != nil {524 t.Fatal(err)525 }526527 output, err := runSCC(tmpDir)528 if err != nil {529 t.Fatal(err)530 }531 if strings.Contains(output, "Java") {532 t.Fatalf("multiple gitignore failed, output:\n%s", output)533 }534}535536func TestFlagSuggestion(t *testing.T) {537 t.Parallel()538 testCases := []struct {539 args []string540 expectedOutput string541 }{542 {543 args: []string{"--farmat"},544 expectedOutput: "The most similar flag of --farmat is:\n\t--format\n",545 },546 {547 args: []string{"--no-gignore"},548 expectedOutput: "The most similar flags of --no-gignore are:\n\t--no-ignore\n\t--no-gitignore\n",549 },550 }551552 for _, tc := range testCases {553 output, err := runSCC(tc.args...)554 if err == nil {555 t.Fatal("scc should exit with error code")556 }557 if !strings.Contains(output, tc.expectedOutput) {558 t.Errorf("wrong suggestion for %v, want: %s, got: %s", tc.args, tc.expectedOutput, output)559 }560 }561}562563func TestDeterministicOutput(t *testing.T) {564 output, err := runSCC(".")565 if err != nil {566 t.Fatal(err)567 }568 for range 10 {569 output2, err := runSCC(".")570 if err != nil {571 t.Fatal(err)572 }573 if output != output2 {574 t.Fatalf("want:\n%s, got:\n%s", output, output2)575 }576 }577}578579func TestDuplicates(t *testing.T) {580 for range 10 {581 output, err := runSCC("-f", "json", "-d", "./examples/duplicates/")582 if err != nil {583 t.Fatal(err)584 }585 if !strings.Contains(output, `"Count":1`) {586 t.Fatalf("duplicates check failed, output:\n%s", output)587 }588 }589}590591func TestCountAs(t *testing.T) {592 testCases := []struct {593 countAs string594 expected []string595 }{596 {597 countAs: "jsp:html",598 expected: []string{"HTML"},599 },600 {601 countAs: "JsP:html",602 expected: []string{"HTML"},603 },604 {605 countAs: "jsp:j2",606 expected: []string{"Jinja"},607 },608 {609 countAs: "jsp:html,new:java",610 expected: []string{"HTML", "Java"},611 },612 {613 countAs: "jsp:html,new:C Header",614 expected: []string{"HTML", "C Header"},615 },616 }617618 for _, tc := range testCases {619 output, err := runSCC("-f", "csv", "--count-as", tc.countAs, "./examples/countas/")620 if err != nil {621 t.Fatal(err)622 }623 for _, expectedLang := range tc.expected {624 if !strings.Contains(output, expectedLang+",") {625 t.Errorf("count as failed, count as: %s, output:\n%s", tc.countAs, output)626 }627 }628 }629}630631func TestCountAsPattern(t *testing.T) {632 // foo_spec.rb is relabelled to the new Ruby Spec category, app.rb stays Ruby633 output, err := runSCC("-f", "csv", "--count-as-pattern", "glob:*_spec.rb:Ruby Spec:Ruby", "./examples/countaspattern/")634 if err != nil {635 t.Fatal(err)636 }637638 // The minted category row, with non-zero comment and complexity counts which639 // proves the counting rules were cloned from the Ruby base language640 if !strings.Contains(output, "Ruby Spec,9,7,1,1,1,") {641 t.Errorf("count-as-pattern failed to produce Ruby Spec row, output:\n%s", output)642 }643 // The non matching file is still counted as plain Ruby644 if !strings.Contains(output, "Ruby,7,5,1,1,1,") {645 t.Errorf("count-as-pattern should leave app.rb as Ruby, output:\n%s", output)646 }647648 // The regex engine should produce the same relabelling649 output, err = runSCC("-f", "csv", "--count-as-pattern", `re:_spec\.rb$:Ruby Spec:Ruby`, "./examples/countaspattern/")650 if err != nil {651 t.Fatal(err)652 }653 if !strings.Contains(output, "Ruby Spec,") {654 t.Errorf("regex count-as-pattern failed, output:\n%s", output)655 }656}657658func TestCountAsPatternInvalidSkipped(t *testing.T) {659 // An unresolvable base language is reported and skipped, the run still works660 output, err := runSCC("-f", "csv", "--count-as-pattern", "glob:*_spec.rb:Ruby Spec:Nonexistent", "./examples/countaspattern/")661 if err != nil {662 t.Fatal(err)663 }664 if !strings.Contains(output, "is not a known language or extension") {665 t.Errorf("expected error message for unknown base language, output:\n%s", output)666 }667 if strings.Contains(output, "Ruby Spec,") {668 t.Errorf("Ruby Spec should not appear when the rule was skipped, output:\n%s", output)669 }670}671672func TestRemapUnknown(t *testing.T) {673 t.Parallel()674 output, err := runSCC("-f", "csv", "--remap-unknown", "-*- C++ -*-:C Header", "./examples/remap/unknown")675 if err != nil {676 t.Fatal(err)677 }678 if !strings.Contains(output, "C Header,") {679 t.Fatalf("remap unknown failed, output:\n%s", output)680 }681}682683func TestRemapAll(t *testing.T) {684 t.Parallel()685 output, err := runSCC("-f", "csv", "--remap-all", "-*- C++ -*-:C Header", "./examples/remap/java.java")686 if err != nil {687 t.Fatal(err)688 }689 if !strings.Contains(output, "C Header,") {690 t.Fatalf("remap all failed, output:\n%s", output)691 }692}693694func TestCocomoProjectType(t *testing.T) {695 projectTypes := []string{"organic", "semi-detached", "embedded", "custom,1,1,1,1"}696 for _, typ := range projectTypes {697 output, err := runSCC("--cocomo-project-type", typ)698 if err != nil {699 t.Fatal(err)700 }701 if !strings.Contains(output, fmt.Sprintf("Estimated Cost to Develop (%s)", typ)) ||702 !strings.Contains(output, fmt.Sprintf("Estimated Schedule Effort (%s)", typ)) ||703 !strings.Contains(output, fmt.Sprintf("Estimated People Required (%s)", typ)) {704 t.Errorf("check cocomo project type failed: %s", typ)705 }706 }707}708709func TestCocomoProjectTypeFallback(t *testing.T) {710 unknownTypes := []string{"doesnotexist", "custom,1,1,1"}711 for _, typ := range unknownTypes {712 output, err := runSCC("--cocomo-project-type", typ)713 if err != nil {714 t.Fatal(err)715 }716 if !strings.Contains(output, "Estimated Cost to Develop (organic)") ||717 !strings.Contains(output, "Estimated Schedule Effort (organic)") ||718 !strings.Contains(output, "Estimated People Required (organic)") {719 t.Errorf("check cocomo project type fallback failed: %s", typ)720 }721 }722}723724func TestOutputBytes(t *testing.T) {725 jsonOutput, err := runSCC("-f", "json")726 if err != nil {727 t.Fatal(err)728 }729 if !strings.Contains(jsonOutput, `"Bytes":`) {730 t.Errorf("json output does not contain `Bytes` field, output:\n%s", jsonOutput)731 }732733 output, err := runSCC()734 if err != nil {735 t.Fatal(err)736 }737 if !strings.Contains(output, "megabytes") {738 t.Errorf("output does not contain `megabytes`, output:\n%s", output)739 }740}741742func TestFileGCCount(t *testing.T) {743 const target = "./examples/duplicates"744 files, err := os.ReadDir(target)745 if err != nil {746 t.Fatal(err)747 }748749 output, err := runSCC("--file-gc-count", strconv.Itoa(len(files)-1), "-v", target)750 if err != nil {751 t.Fatal(err)752 }753 if !strings.Contains(output, "read file limit exceeded GC re-enabled") {754 t.Errorf("test file GC count failed, file count: %d, limit: %d", len(files), len(files)-1)755 }756757 output, err = runSCC("--file-gc-count", strconv.Itoa(len(files)+1), "-v", target)758 if err != nil {759 t.Fatal(err)760 }761 if strings.Contains(output, "read file limit exceeded GC re-enabled") {762 t.Errorf("test file GC count failed, file count: %d, limit: %d", len(files), len(files)+1)763 }764}765766func TestIncludeSymlinks(t *testing.T) {767 if runtime.GOOS == "windows" {768 t.Skip("skipping symlink test on Windows due to privilege requirements")769 }770771 tmpDir, err := filepath.EvalSymlinks(t.TempDir())772 if err != nil {773 t.Fatal(err)774 }775776 dirA := filepath.Join(tmpDir, "a")777 dirB := filepath.Join(tmpDir, "b")778 if err := os.Mkdir(dirA, 0755); err != nil {779 t.Fatal(err)780 }781 if err := os.Mkdir(dirB, 0755); err != nil {782 t.Fatal(err)783 }784785 const fileName = "source.go"786 if err := os.WriteFile(filepath.Join(dirA, fileName), []byte("package main\n"), 0644); err != nil {787 t.Fatal(err)788 }789 if err := os.WriteFile(filepath.Join(dirB, fileName), []byte("package main\n"), 0644); err != nil {790 t.Fatal(err)791 }792 // link to another dir, should be counted under --include-symlinks793 if err := os.Symlink(filepath.Join(dirB, fileName), filepath.Join(dirA, "link1.go")); err != nil {794 t.Fatal(err)795 }796 // link to the same dir, this should be ignored in all times797 if err := os.Symlink(filepath.Join(dirA, fileName), filepath.Join(dirA, "link2.go")); err != nil {798 t.Fatal(err)799 }800801 output, err := runSCC("-f", "json", "--no-scc-ignore", dirA)802 if err != nil {803 t.Fatal(err)804 }805 if !strings.Contains(output, `"Count":1`) {806 t.Errorf("count without symlink failed, output:\n%s", output)807 }808809 output, err = runSCC("-f", "json", "--no-scc-ignore", "--include-symlinks", dirA)810 if err != nil {811 t.Fatal(err)812 }813 if !strings.Contains(output, `"Count":2`) {814 t.Errorf("count includes symlink failed, output:\n%s", output)815 }816}817818func TestLanguageNameTruncate(t *testing.T) {819 output, err := runSCC("examples/language")820 if err != nil {821 t.Fatal(err)822 }823 if strings.Count(output, "Bitbucket Pipe…") != 1 {824 t.Errorf("`Bitbucket Pipeline` truncate test failed")825 }826 if strings.Count(output, "CloudFormation…") != 2 {827 t.Errorf("`CloudFormation (JSON)` and `CloudFormation (YAML)` truncate test failed")828 }829}830831func TestSpecificLanguages(t *testing.T) {832 languages := [...]string{833 "ABNF",834 "Alchemist",835 "Algol 68",836 "Alloy",837 "Amber",838 "Apex",839 "ArkTs",840 "Arturo",841 "Astro",842 "AWK",843 "BASH",844 "Bean",845 "Bicep",846 "Bitbucket Pipeline",847 "Blueprint",848 "Boo",849 "Bosque",850 "Bru",851 "C",852 "C3",853 "C Header",854 "C Shell",855 "C#",856 "C++",857 "C++ Header",858 "Cairo",859 "Cangjie",860 "Chapel",861 "Circom",862 "Clipper",863 "Clojure",864 "CMake",865 "Cuda",866 "Cypher",867 "D2",868 "DAML",869 "DM",870 "Docker ignore",871 "Dockerfile",872 "DOT",873 "Elixir Template",874 "Elm",875 "EmiT",876 "F#",877 "Factor",878 "Flow9",879 "FSL",880 "Futhark",881 "FXML",882 "Gemfile",883 "Gleam",884 "Go",885 "Go+",886 "Godot Scene",887 "GraphQL",888 "Gremlin",889 "Gwion",890 "HAML",891 "Hare",892 "Haskell",893 "HCL",894 "IEC61131-3",895 "ignore",896 "INI",897 "Java",898 "JavaScript",899 "JCL",900 "JSON5",901 "JSONC",902 "jq",903 "Korn Shell",904 "Koto",905 "LALRPOP",906 "License",907 "LiveScript",908 "LLVM IR",909 "Lua",910 "Luau",911 "Luna",912 "MLIR",913 "Makefile",914 "Metal",915 "Mojo",916 "Monkey C",917 "Moonbit",918 "Move",919 "Nature",920 "Nushell",921 "OpenQASM",922 "OpenTofu",923 "Patch",924 "Perl",925 "Pkl",926 "Plain Text",927 "POML",928 "PostScript",929 "Proto",930 "Python",931 "Q#",932 "R",933 "Racket",934 "Rakefile",935 "RAML",936 "Rebol",937 "Redscript",938 "Rich Text Format",939 "Scallop",940 "Seed7",941 "Shell",942 "Sieve",943 "Slang",944 "Slint",945 "Smalltalk",946 "Snakemake",947 "Stan",948 "Systemd",949 "Tact",950 "Teal",951 "Tera",952 "Templ",953 "Terraform",954 "TOML",955 "TOON",956 "TTCN-3",957 "TypeScript",958 "TypeSpec",959 "Typst",960 "Up",961 "Vala",962 "Vim Script",963 "Web Services Description Language",964 "WebGPU Enhanced Shading Language",965 "wenyan",966 "Wren",967 "XHTML",968 "XMake",969 "XML Schema",970 "YAML",971 "Yarn",972 "Zen C",973 "Zig",974 "ZoKrates",975 "Zsh",976 }977978 output, err := runSCC("-f", "csv", "examples/language")979 if err != nil {980 t.Fatal(err)981 }982983 for _, language := range languages {984 if !strings.Contains(output, language+",") {985 t.Errorf("language not found in output: %v", language)986 }987 }988}
Same data, no extra tab — call code_get_file + code_get_findings over MCP from Claude/Cursor/Copilot.