main_test.go GO 946 lines View on github.com → Search inside
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 TestRemapUnknown(t *testing.T) {632	t.Parallel()633	output, err := runSCC("-f", "csv", "--remap-unknown", "-*- C++ -*-:C Header", "./examples/remap/unknown")634	if err != nil {635		t.Fatal(err)636	}637	if !strings.Contains(output, "C Header,") {638		t.Fatalf("remap unknown failed, output:\n%s", output)639	}640}641642func TestRemapAll(t *testing.T) {643	t.Parallel()644	output, err := runSCC("-f", "csv", "--remap-all", "-*- C++ -*-:C Header", "./examples/remap/java.java")645	if err != nil {646		t.Fatal(err)647	}648	if !strings.Contains(output, "C Header,") {649		t.Fatalf("remap all failed, output:\n%s", output)650	}651}652653func TestCocomoProjectType(t *testing.T) {654	projectTypes := []string{"organic", "semi-detached", "embedded", "custom,1,1,1,1"}655	for _, typ := range projectTypes {656		output, err := runSCC("--cocomo-project-type", typ)657		if err != nil {658			t.Fatal(err)659		}660		if !strings.Contains(output, fmt.Sprintf("Estimated Cost to Develop (%s)", typ)) ||661			!strings.Contains(output, fmt.Sprintf("Estimated Schedule Effort (%s)", typ)) ||662			!strings.Contains(output, fmt.Sprintf("Estimated People Required (%s)", typ)) {663			t.Errorf("check cocomo project type failed: %s", typ)664		}665	}666}667668func TestCocomoProjectTypeFallback(t *testing.T) {669	unknownTypes := []string{"doesnotexist", "custom,1,1,1"}670	for _, typ := range unknownTypes {671		output, err := runSCC("--cocomo-project-type", typ)672		if err != nil {673			t.Fatal(err)674		}675		if !strings.Contains(output, "Estimated Cost to Develop (organic)") ||676			!strings.Contains(output, "Estimated Schedule Effort (organic)") ||677			!strings.Contains(output, "Estimated People Required (organic)") {678			t.Errorf("check cocomo project type fallback failed: %s", typ)679		}680	}681}682683func TestOutputBytes(t *testing.T) {684	jsonOutput, err := runSCC("-f", "json")685	if err != nil {686		t.Fatal(err)687	}688	if !strings.Contains(jsonOutput, `"Bytes":`) {689		t.Errorf("json output does not contain `Bytes` field, output:\n%s", jsonOutput)690	}691692	output, err := runSCC()693	if err != nil {694		t.Fatal(err)695	}696	if !strings.Contains(output, "megabytes") {697		t.Errorf("output does not contain `megabytes`, output:\n%s", output)698	}699}700701func TestFileGCCount(t *testing.T) {702	const target = "./examples/duplicates"703	files, err := os.ReadDir(target)704	if err != nil {705		t.Fatal(err)706	}707708	output, err := runSCC("--file-gc-count", strconv.Itoa(len(files)-1), "-v", target)709	if err != nil {710		t.Fatal(err)711	}712	if !strings.Contains(output, "read file limit exceeded GC re-enabled") {713		t.Errorf("test file GC count failed, file count: %d, limit: %d", len(files), len(files)-1)714	}715716	output, err = runSCC("--file-gc-count", strconv.Itoa(len(files)+1), "-v", target)717	if err != nil {718		t.Fatal(err)719	}720	if strings.Contains(output, "read file limit exceeded GC re-enabled") {721		t.Errorf("test file GC count failed, file count: %d, limit: %d", len(files), len(files)+1)722	}723}724725func TestIncludeSymlinks(t *testing.T) {726	if runtime.GOOS == "windows" {727		t.Skip("skipping symlink test on Windows due to privilege requirements")728	}729730	tmpDir, err := filepath.EvalSymlinks(t.TempDir())731	if err != nil {732		t.Fatal(err)733	}734735	dirA := filepath.Join(tmpDir, "a")736	dirB := filepath.Join(tmpDir, "b")737	if err := os.Mkdir(dirA, 0755); err != nil {738		t.Fatal(err)739	}740	if err := os.Mkdir(dirB, 0755); err != nil {741		t.Fatal(err)742	}743744	const fileName = "source.go"745	if err := os.WriteFile(filepath.Join(dirA, fileName), []byte("package main\n"), 0644); err != nil {746		t.Fatal(err)747	}748	if err := os.WriteFile(filepath.Join(dirB, fileName), []byte("package main\n"), 0644); err != nil {749		t.Fatal(err)750	}751	// link to another dir, should be counted under --include-symlinks752	if err := os.Symlink(filepath.Join(dirB, fileName), filepath.Join(dirA, "link1.go")); err != nil {753		t.Fatal(err)754	}755	// link to the same dir, this should be ignored in all times756	if err := os.Symlink(filepath.Join(dirA, fileName), filepath.Join(dirA, "link2.go")); err != nil {757		t.Fatal(err)758	}759760	output, err := runSCC("-f", "json", "--no-scc-ignore", dirA)761	if err != nil {762		t.Fatal(err)763	}764	if !strings.Contains(output, `"Count":1`) {765		t.Errorf("count without symlink failed, output:\n%s", output)766	}767768	output, err = runSCC("-f", "json", "--no-scc-ignore", "--include-symlinks", dirA)769	if err != nil {770		t.Fatal(err)771	}772	if !strings.Contains(output, `"Count":2`) {773		t.Errorf("count includes symlink failed, output:\n%s", output)774	}775}776777func TestLanguageNameTruncate(t *testing.T) {778	output, err := runSCC("examples/language")779	if err != nil {780		t.Fatal(err)781	}782	if strings.Count(output, "Bitbucket Pipe…") != 1 {783		t.Errorf("`Bitbucket Pipeline` truncate test failed")784	}785	if strings.Count(output, "CloudFormation…") != 2 {786		t.Errorf("`CloudFormation (JSON)` and `CloudFormation (YAML)` truncate test failed")787	}788}789790func TestSpecificLanguages(t *testing.T) {791	languages := [...]string{792		"ABNF",793		"Alchemist",794		"Algol 68",795		"Alloy",796		"Amber",797		"Apex",798		"ArkTs",799		"Arturo",800		"Astro",801		"AWK",802		"BASH",803		"Bean",804		"Bicep",805		"Bitbucket Pipeline",806		"Blueprint",807		"Boo",808		"Bosque",809		"Bru",810		"C",811		"C3",812		"C Header",813		"C Shell",814		"C#",815		"C++",816		"C++ Header",817		"Cairo",818		"Cangjie",819		"Chapel",820		"Circom",821		"Clipper",822		"Clojure",823		"CMake",824		"Cuda",825		"Cypher",826		"D2",827		"DAML",828		"DM",829		"Docker ignore",830		"Dockerfile",831		"DOT",832		"Elixir Template",833		"Elm",834		"EmiT",835		"F#",836		"Factor",837		"Flow9",838		"FSL",839		"Futhark",840		"FXML",841		"Gemfile",842		"Gleam",843		"Go",844		"Go+",845		"Godot Scene",846		"GraphQL",847		"Gremlin",848		"Gwion",849		"HAML",850		"Hare",851		"Haskell",852		"HCL",853		"IEC61131-3",854		"ignore",855		"INI",856		"Java",857		"JavaScript",858		"JCL",859		"JSON5",860		"JSONC",861		"jq",862		"Korn Shell",863		"Koto",864		"LALRPOP",865		"License",866		"LiveScript",867		"LLVM IR",868		"Lua",869		"Luau",870		"Luna",871		"MLIR",872		"Makefile",873		"Metal",874		"Mojo",875		"Monkey C",876		"Moonbit",877		"Move",878		"Nature",879		"Nushell",880		"OpenQASM",881		"OpenTofu",882		"Perl",883		"Pkl",884		"Plain Text",885		"POML",886		"PostScript",887		"Proto",888		"Python",889		"Q#",890		"R",891		"Racket",892		"Rakefile",893		"RAML",894		"Rebol",895		"Redscript",896		"Rich Text Format",897		"Scallop",898		"Seed7",899		"Shell",900		"Sieve",901		"Slang",902		"Slint",903		"Smalltalk",904		"Snakemake",905		"Stan",906		"Systemd",907		"Tact",908		"Teal",909		"Tera",910		"Templ",911		"Terraform",912		"TOML",913		"TOON",914		"TTCN-3",915		"TypeScript",916		"TypeSpec",917		"Typst",918		"Up",919		"Vala",920		"Vim Script",921		"Web Services Description Language",922		"WebGPU Enhanced Shading Language",923		"wenyan",924		"Wren",925		"XHTML",926		"XMake",927		"XML Schema",928		"YAML",929		"Yarn",930		"Zig",931		"ZoKrates",932		"Zsh",933	}934935	output, err := runSCC("-f", "csv", "examples/language")936	if err != nil {937		t.Fatal(err)938	}939940	for _, language := range languages {941		if !strings.Contains(output, language+",") {942			t.Errorf("language not found in output: %v", language)943		}944	}945}

Code quality findings 31

Can cause issues on Windows consider filepath.Join instead
info correctness path-join-windows
ignoreFileName := filepath.Join(tmpDir, ".gitignore")
Can cause issues on Windows consider filepath.Join instead
info correctness path-join-windows
xmlFileName := filepath.Join(tmpDir, "ignored.xml")
Can cause issues on Windows consider filepath.Join instead
info correctness path-join-windows
flagsFileName := filepath.Join(tmpDir, "flags.txt")
String to byte slice conversion inside loop allocates a new slice each iteration; convert once before the loop
info correctness string-to-byte-in-loop
err := os.WriteFile(flagsFileName, []byte(tc), 0644)
Regexp compiled inside function; compile once at package level to avoid recompilation on each call
info performance regexp-compile-in-func
tabularPattern := regexp.MustCompile(`Processed .+? bytes, .+? megabytes \(SI\)`)
Regexp compiled inside function; compile once at package level to avoid recompilation on each call
info performance regexp-compile-in-func
sqlPattern := regexp.MustCompile(`insert into t values\(.+?\);`)
Can cause issues on Windows consider filepath.Join instead
info correctness path-join-windows
outputTabular := filepath.Join(tmpDir, "output.tab")
Can cause issues on Windows consider filepath.Join instead
info correctness path-join-windows
outputWide := filepath.Join(tmpDir, "output.wide")
Can cause issues on Windows consider filepath.Join instead
info correctness path-join-windows
outputJSON1 := filepath.Join(tmpDir, "output.json")
Can cause issues on Windows consider filepath.Join instead
info correctness path-join-windows
outputJSON2 := filepath.Join(tmpDir, "output2.json")
Can cause issues on Windows consider filepath.Join instead
info correctness path-join-windows
outputCSV := filepath.Join(tmpDir, "output.csv")
Can cause issues on Windows consider filepath.Join instead
info correctness path-join-windows
outputYAML := filepath.Join(tmpDir, "output.yaml")
Can cause issues on Windows consider filepath.Join instead
info correctness path-join-windows
outputHTML := filepath.Join(tmpDir, "output.html")
Can cause issues on Windows consider filepath.Join instead
info correctness path-join-windows
outputHTMLTable := filepath.Join(tmpDir, "output_table.html")
Can cause issues on Windows consider filepath.Join instead
info correctness path-join-windows
outputSQL := filepath.Join(tmpDir, "output.sql")
Can cause issues on Windows consider filepath.Join instead
info correctness path-join-windows
err := os.WriteFile(filepath.Join(tmpDir, ".gitignore"), []byte("ignore-git.txt\n"), 0644)
Can cause issues on Windows consider filepath.Join instead
info correctness path-join-windows
err = os.WriteFile(filepath.Join(tmpDir, ".ignore"), []byte("vendor/\nignore.txt\n"), 0644)
Can cause issues on Windows consider filepath.Join instead
info correctness path-join-windows
err = os.Mkdir(filepath.Join(tmpDir, "ignore"), 0755)
Can cause issues on Windows consider filepath.Join instead
info correctness path-join-windows
err = os.WriteFile(filepath.Join(tmpDir, "ignore", "README.md"), []byte("Files in here are to ensure that .ignore and .gitignore work recursively\n"), 0644)
Can cause issues on Windows consider filepath.Join instead
info correctness path-join-windows
err = os.WriteFile(filepath.Join(tmpDir, "ignore", "ignore.txt"), []byte("testing\n"), 0644)
Can cause issues on Windows consider filepath.Join instead
info correctness path-join-windows
err = os.WriteFile(filepath.Join(tmpDir, "ignore", "ignore-git.txt"), []byte("git\ntesting\n"), 0644)
Can cause issues on Windows consider filepath.Join instead
info correctness path-join-windows
err := os.WriteFile(filepath.Join(tmpDir, ".gitignore"), []byte("ignore.txt\n"), 0644)
Can cause issues on Windows consider filepath.Join instead
info correctness path-join-windows
err = os.Mkdir(filepath.Join(tmpDir, "ignore"), 0755)
Can cause issues on Windows consider filepath.Join instead
info correctness path-join-windows
err = os.WriteFile(filepath.Join(tmpDir, "ignore", ".gitignore"), []byte("ignore.java\n"), 0644)
Can cause issues on Windows consider filepath.Join instead
info correctness path-join-windows
err = os.WriteFile(filepath.Join(tmpDir, "ignore", "ignore.java"), []byte("//test\n"), 0644)
Can cause issues on Windows consider filepath.Join instead
info correctness path-join-windows
dirA := filepath.Join(tmpDir, "a")
Can cause issues on Windows consider filepath.Join instead
info correctness path-join-windows
dirB := filepath.Join(tmpDir, "b")
Can cause issues on Windows consider filepath.Join instead
info correctness path-join-windows
if err := os.WriteFile(filepath.Join(dirA, fileName), []byte("package main\n"), 0644); err != nil {
Can cause issues on Windows consider filepath.Join instead
info correctness path-join-windows
if err := os.WriteFile(filepath.Join(dirB, fileName), []byte("package main\n"), 0644); err != nil {
Can cause issues on Windows consider filepath.Join instead
info correctness path-join-windows
if err := os.Symlink(filepath.Join(dirB, fileName), filepath.Join(dirA, "link1.go")); err != nil {
Can cause issues on Windows consider filepath.Join instead
info correctness path-join-windows
if err := os.Symlink(filepath.Join(dirA, fileName), filepath.Join(dirA, "link2.go")); err != nil {

Get this view in your editor

Same data, no extra tab — call code_get_file + code_get_findings over MCP from Claude/Cursor/Copilot.