processor/formatters_test.go GO 1,836 lines View on github.com → Search inside
1// SPDX-License-Identifier: MIT23package processor45import (6	"io"7	"os"8	"slices"9	"strings"10	"testing"1112	"github.com/mattn/go-runewidth"13)1415func TestCalculateCocomo(t *testing.T) {16	var str strings.Builder17	calculateCocomo(1, &str)1819	if !strings.Contains(str.String(), "Estimated Schedule Effort (organic) 0.22 months") {20		t.Error("expected to match got", str.String())21	}22}2324func TestCalculateSizeSingleByte(t *testing.T) {25	var str strings.Builder26	calculateSize(1, &str)2728	if !strings.Contains(str.String(), "Processed 1 bytes, 0.000 megabytes (SI)") {29		t.Error("expected to match got", str.String())30	}31}3233func TestCalculateSize(t *testing.T) {34	var str strings.Builder35	calculateSize(1000000, &str)3637	if !strings.Contains(str.String(), "Processed 1000000 bytes, 1.000 megabytes (SI)") {38		t.Error("expected to match got", str.String())39	}40}4142func TestSortSummaryFilesEmpty(t *testing.T) {43	summary := LanguageSummary{}44	sortSummaryFiles(&summary)45}4647func TestSortSummaryFiles(t *testing.T) {48	files := []*FileJob{}49	files = append(files, &FileJob{50		Language:           "Go",51		Filename:           "bbbb.go",52		Extension:          "go",53		Location:           "./bbbb.go",54		Bytes:              1000,55		Lines:              1000,56		Code:               1000,57		Comment:            1000,58		Blank:              1000,59		Complexity:         1000,60		WeightedComplexity: 1000,61		Binary:             false,62	})63	files = append(files, &FileJob{64		Language:           "Go",65		Filename:           "aaaa.go",66		Extension:          "go",67		Location:           "./aaaa.go",68		Bytes:              2000,69		Lines:              2000,70		Code:               2000,71		Comment:            2000,72		Blank:              2000,73		Complexity:         2000,74		WeightedComplexity: 2000,75		Binary:             false,76	})7778	summary := LanguageSummary{79		Name:               "Go",80		Bytes:              1000,81		Lines:              1000,82		Code:               1000,83		Comment:            1000,84		Blank:              1000,85		Complexity:         1000,86		Count:              1000,87		WeightedComplexity: 1000,88		Files:              files,89	}9091	lineSort := []string{"name", "names", "language", "languages", "line", "lines", "RANDOMTHING"}92	for _, val := range lineSort {93		SortBy = val94		sortSummaryFiles(&summary)9596		if summary.Files[0].Filename != "aaaa.go" {97			t.Error("Sorting on lines failed", val)98		}99	}100101	blankSort := []string{"blank", "blanks"}102	for _, val := range blankSort {103		SortBy = val104		sortSummaryFiles(&summary)105106		if summary.Files[0].Filename != "aaaa.go" {107			t.Error("Sorting on blank failed", val)108		}109	}110111	codeSort := []string{"code", "codes"}112	for _, val := range codeSort {113		SortBy = val114		sortSummaryFiles(&summary)115116		if summary.Files[0].Filename != "aaaa.go" {117			t.Error("Sorting on code failed", val)118		}119	}120121	commentSort := []string{"comment", "comments"}122	for _, val := range commentSort {123		SortBy = val124		sortSummaryFiles(&summary)125126		if summary.Files[0].Filename != "aaaa.go" {127			t.Error("Sorting on comment failed", val)128		}129	}130131	complexitySort := []string{"complexity", "complexitys"}132	for _, val := range complexitySort {133		SortBy = val134		sortSummaryFiles(&summary)135136		if summary.Files[0].Filename != "aaaa.go" {137			t.Error("Sorting on complexity failed", val)138		}139	}140}141142func TestSortSummaryFilesName(t *testing.T) {143	goFiles := []*FileJob{}144	goFiles = append(goFiles, &FileJob{145		Language: "Go",146		Location: "bbbb.go",147	})148149	goFiles = append(goFiles, &FileJob{150		Language: "Go",151		Location: "aaaa.go",152	})153154	goFiles = append(goFiles, &FileJob{155		Language: "Go",156		Location: "cccc.go",157	})158159	summary := LanguageSummary{160		Name:  "Go",161		Files: goFiles,162	}163164	lineSort := []string{"name", "names", "language", "languages"}165	for _, val := range lineSort {166		SortBy = val167		sortSummaryFiles(&summary)168169		if summary.Files[0].Location != "aaaa.go" {170			t.Error("Sorting on lines failed", val)171		}172	}173	SortBy = ""174}175176func TestSortLanguageSummaryName(t *testing.T) {177	SortBy = "name"178	ls := []LanguageSummary{179		{180			Name:  "b",181			Lines: 1,182		},183		{184			Name:  "a",185			Lines: 1,186		},187	}188189	ls = sortLanguageSummary(ls)190191	if ls[0].Name != "a" {192		t.Error("Expected a to be first")193	}194}195196func TestSortLanguageSummaryLine(t *testing.T) {197	SortBy = "line"198	ls := []LanguageSummary{199		{200			Name:  "a",201			Lines: 1,202		},203		{204			Name:  "b",205			Lines: 1,206		},207		{208			Name:  "c",209			Lines: 2,210		},211	}212213	ls = sortLanguageSummary(ls)214215	if ls[0].Name != "c" || ls[1].Name != "a" {216		t.Error("Expected c to be first and a second")217	}218}219220func TestSortLanguageSummaryBlank(t *testing.T) {221	SortBy = "blank"222	ls := []LanguageSummary{223		{224			Name:  "a",225			Blank: 1,226		},227		{228			Name:  "b",229			Blank: 1,230		},231		{232			Name:  "c",233			Blank: 2,234		},235	}236237	ls = sortLanguageSummary(ls)238239	if ls[0].Name != "c" || ls[1].Name != "a" {240		t.Error("Expected c to be first and a second")241	}242}243244func TestSortLanguageSummaryCode(t *testing.T) {245	SortBy = "code"246	ls := []LanguageSummary{247		{248			Name: "a",249			Code: 1,250		},251		{252			Name: "b",253			Code: 1,254		},255		{256			Name: "c",257			Code: 2,258		},259	}260261	ls = sortLanguageSummary(ls)262263	if ls[0].Name != "c" || ls[1].Name != "a" {264		t.Error("Expected c to be first and a second")265	}266}267268func TestSortLanguageSummaryComment(t *testing.T) {269	SortBy = "comment"270	ls := []LanguageSummary{271		{272			Name:    "a",273			Comment: 1,274		},275		{276			Name:    "b",277			Comment: 1,278		},279		{280			Name:    "c",281			Comment: 2,282		},283	}284285	ls = sortLanguageSummary(ls)286287	if ls[0].Name != "c" || ls[1].Name != "a" {288		t.Error("Expected c to be first and a second")289	}290}291292func TestSortLanguageSummaryComplexity(t *testing.T) {293	SortBy = "complexity"294	ls := []LanguageSummary{295		{296			Name:       "a",297			Complexity: 1,298		},299		{300			Name:       "b",301			Complexity: 1,302		},303		{304			Name:       "c",305			Complexity: 2,306		},307	}308309	ls = sortLanguageSummary(ls)310311	if ls[0].Name != "c" || ls[1].Name != "a" {312		t.Error("Expected c to be first and a second")313	}314}315316func TestSortLanguageSummaryBytes(t *testing.T) {317	SortBy = "bytes"318	ls := []LanguageSummary{319		{320			Name:  "a",321			Bytes: 1,322		},323		{324			Name:  "b",325			Bytes: 1,326		},327		{328			Name:  "c",329			Bytes: 2,330		},331	}332333	ls = sortLanguageSummary(ls)334335	if ls[0].Name != "c" || ls[1].Name != "a" {336		t.Error("Expected c to be first and a second")337	}338}339340func TestSortLanguageSummaryFiles(t *testing.T) {341	SortBy = "files"342	ls := []LanguageSummary{343		{344			Name:  "a",345			Count: 1,346		},347		{348			Name:  "b",349			Count: 1,350		},351		{352			Name:  "c",353			Count: 2,354		},355	}356357	ls = sortLanguageSummary(ls)358359	if ls[0].Name != "c" || ls[1].Name != "a" {360		t.Error("Expected c to be first and a second")361	}362}363364func TestSortSummaryNames(t *testing.T) {365	SortBy = "name"366	ls := []LanguageSummary{367		{368			Name:       "a",369			Complexity: 1,370		},371		{372			Name:       "b",373			Complexity: 1,374		},375		{376			Name:       "c",377			Complexity: 2,378		},379	}380381	ls = sortLanguageSummary(ls)382383	if ls[0].Name != "a" || ls[1].Name != "b" || ls[2].Name != "c" {384		t.Error("Expected a to be first and b second and c third")385	}386}387388func TestToJSONEmpty(t *testing.T) {389	inputChan := make(chan *FileJob, 1000)390	close(inputChan)391	res := toJSON(inputChan)392393	if res != "[]" {394		t.Error("Expected empty JSON return", res)395	}396}397398func TestToJSONSingle(t *testing.T) {399	inputChan := make(chan *FileJob, 1000)400	inputChan <- &FileJob{401		Language:           "Go",402		Filename:           "bbbb.go",403		Extension:          "go",404		Location:           "./",405		Bytes:              1000,406		Lines:              1000,407		Code:               1000,408		Comment:            1000,409		Blank:              1000,410		Complexity:         1000,411		WeightedComplexity: 1000,412		Binary:             false,413	}414	close(inputChan)415	Debug = true // Increase coverage slightly416	Files = true417	res := toJSON(inputChan)418	Debug = false419420	if !strings.Contains(res, `"Name":"Go"`) || !strings.Contains(res, `"Code":1000`) || !strings.Contains(res, `"Filename":"bbbb.go"`) {421		t.Error("Expected JSON return", res)422	}423	if strings.Contains(res, `"Content":`) {424		t.Error("Expected JSON return", res)425	}426}427428func TestToJSONSingleWithoutFiles(t *testing.T) {429	inputChan := make(chan *FileJob, 1000)430	inputChan <- &FileJob{431		Language:           "Go",432		Filename:           "bbbb.go",433		Extension:          "go",434		Location:           "./",435		Bytes:              1000,436		Lines:              1000,437		Code:               1000,438		Comment:            1000,439		Blank:              1000,440		Complexity:         1000,441		WeightedComplexity: 1000,442		Binary:             false,443	}444	close(inputChan)445	Debug = true // Increase coverage slightly446	Files = false447	res := toJSON(inputChan)448	Debug = false449450	if !strings.Contains(res, `"Name":"Go"`) || !strings.Contains(res, `"Code":1000`) {451		t.Error("Expected JSON return", res)452	}453	if strings.Contains(res, `"Filename":"bbbb.go"`) {454		t.Error("Expected JSON return", res)455	}456}457458func TestToJSONMultiple(t *testing.T) {459	inputChan := make(chan *FileJob, 1000)460	inputChan <- &FileJob{461		Language:           "Go",462		Filename:           "bbbb.go",463		Extension:          "go",464		Location:           "./",465		Bytes:              1000,466		Lines:              1000,467		Code:               1000,468		Comment:            1000,469		Blank:              1000,470		Complexity:         1000,471		WeightedComplexity: 1000,472		Binary:             false,473	}474	inputChan <- &FileJob{475		Language:           "Go",476		Filename:           "aaaa.go",477		Extension:          "go",478		Location:           "./",479		Bytes:              1000,480		Lines:              1000,481		Code:               1000,482		Comment:            1000,483		Blank:              1000,484		Complexity:         1000,485		WeightedComplexity: 1000,486		Binary:             false,487	}488	close(inputChan)489	Debug = true // Increase coverage slightly490	Files = true491	res := toJSON(inputChan)492	Debug = false493494	if !strings.Contains(res, `aaaa.go`) || !strings.Contains(res, `bbbb.go`) {495		t.Error("Expected JSON return", res)496	}497}498499func TestToYAMLEmpty(t *testing.T) {500	inputChan := make(chan *FileJob, 1000)501	close(inputChan)502	res := toClocYAML(inputChan)503504	if !strings.Contains(res, "{}") || !strings.Contains(res, "header:") || !strings.Contains(res, "n_files: 0") {505		t.Error("Expected empty Cloc YAML return", res)506	}507}508509func TestToYAMLSingle(t *testing.T) {510	inputChan := make(chan *FileJob, 1000)511	inputChan <- &FileJob{512		Language:           "Go",513		Filename:           "bbbb.go",514		Extension:          "go",515		Location:           "./",516		Bytes:              1000,517		Lines:              1000,518		Code:               1000,519		Comment:            1000,520		Blank:              1000,521		Complexity:         1000,522		WeightedComplexity: 1000,523		Binary:             false,524	}525	close(inputChan)526	Debug = true // Increase coverage slightly527	res := toClocYAML(inputChan)528	Debug = false529530	if !strings.Contains(res, `n_lines: 1000`) {531		t.Error("Expected Cloc YAML return", res)532	}533}534535func TestToYAMLMultiple(t *testing.T) {536	inputChan := make(chan *FileJob, 1000)537	inputChan <- &FileJob{538		Language:           "Go",539		Filename:           "bbbb.go",540		Extension:          "go",541		Location:           "./",542		Bytes:              1000,543		Lines:              1000,544		Code:               1000,545		Comment:            1000,546		Blank:              1000,547		Complexity:         1000,548		WeightedComplexity: 1000,549		Binary:             false,550	}551	inputChan <- &FileJob{552		Language:           "Go",553		Filename:           "aaaa.go",554		Extension:          "go",555		Location:           "./",556		Bytes:              1000,557		Lines:              1000,558		Code:               1000,559		Comment:            1000,560		Blank:              1000,561		Complexity:         1000,562		WeightedComplexity: 1000,563		Binary:             false,564	}565	close(inputChan)566	Debug = true // Increase coverage slightly567	res := toClocYAML(inputChan)568	Debug = false569570	if !strings.Contains(res, `code: 2000`) || !strings.Contains(res, `n_lines: 2000`) {571		t.Error("Expected Cloc JSON return", res)572	}573}574575func TestToCsvMultiple(t *testing.T) {576	inputChan := make(chan *FileJob, 1000)577	inputChan <- &FileJob{578		Language:           "Go",579		Filename:           "bbbb.go",580		Extension:          "go",581		Location:           "./",582		Bytes:              1000,583		Lines:              1000,584		Code:               1000,585		Comment:            1000,586		Blank:              1000,587		Complexity:         1000,588		WeightedComplexity: 1000,589		Binary:             false,590	}591	inputChan <- &FileJob{592		Language:           "Go",593		Filename:           "aaaa.go",594		Extension:          "go",595		Location:           "./",596		Bytes:              1000,597		Lines:              1000,598		Code:               1000,599		Comment:            1000,600		Blank:              1000,601		Complexity:         1000,602		WeightedComplexity: 1000,603		Binary:             false,604	}605	close(inputChan)606	Debug = true // Increase coverage slightly607	res := toCSV(inputChan)608	Debug = false609610	if !strings.Contains(res, `aaaa.go,`) || !strings.Contains(res, `bbbb.go`) {611		t.Error("Expected CSV return", res)612	}613}614615func TestToCsvStreamMultiple(t *testing.T) {616	inputChan := make(chan *FileJob, 1000)617	inputChan <- &FileJob{618		Language:           "Go",619		Filename:           "bbbb.go",620		Extension:          "go",621		Location:           "./",622		Bytes:              1000,623		Lines:              1000,624		Code:               1000,625		Comment:            1000,626		Blank:              1000,627		Complexity:         1000,628		WeightedComplexity: 1000,629		Binary:             false,630	}631	inputChan <- &FileJob{632		Language:           "Go",633		Filename:           "aaaa.go",634		Extension:          "go",635		Location:           "./",636		Bytes:              1000,637		Lines:              1000,638		Code:               1000,639		Comment:            1000,640		Blank:              1000,641		Complexity:         1000,642		WeightedComplexity: 1000,643		Binary:             false,644	}645	close(inputChan)646	Debug = true // Increase coverage slightly647	res := toCSVStream(inputChan)648	Debug = false649650	if res != "" {651		t.Error("Expected CSV return", res)652	}653}654655func TestToCsvFilesSorted(t *testing.T) {656	fj1 := &FileJob{657		Language:           "Go",658		Filename:           "bbbb.go",659		Extension:          "go",660		Location:           "./",661		Bytes:              90,662		Lines:              90,663		Code:               90,664		Comment:            90,665		Blank:              90,666		Complexity:         90,667		WeightedComplexity: 90,668		Binary:             false,669	}670	fj2 := &FileJob{671		Language:           "Go",672		Filename:           "aaaa.go",673		Extension:          "go",674		Location:           "./",675		Bytes:              1000,676		Lines:              1000,677		Code:               1000,678		Comment:            1000,679		Blank:              1000,680		Complexity:         1000,681		WeightedComplexity: 1000,682		Binary:             false,683	}684685	Files = true686	SortBy = "lines"687688	inputChan1 := make(chan *FileJob, 1000)689	inputChan1 <- fj1690	inputChan1 <- fj2691	close(inputChan1)692	res1 := toCSV(inputChan1)693694	inputChan2 := make(chan *FileJob, 1000)695	inputChan2 <- fj2696	inputChan2 <- fj1697	close(inputChan2)698	res2 := toCSV(inputChan2)699700	Files = false701702	if res1 != res2 {703		t.Error("Should be sorted to be the same")704	}705}706707func TestToOpenMetricsMultiple(t *testing.T) {708	inputChan := make(chan *FileJob, 1000)709	inputChan <- &FileJob{710		Language:           "Go",711		Filename:           "bbbb.go",712		Extension:          "go",713		Location:           "./",714		Bytes:              1000,715		Lines:              1000,716		Code:               1000,717		Comment:            1000,718		Blank:              1000,719		Complexity:         1000,720		WeightedComplexity: 1000,721		Binary:             false,722	}723	inputChan <- &FileJob{724		Language:           "Go",725		Filename:           "aaaa.go",726		Extension:          "go",727		Location:           "./",728		Bytes:              1000,729		Lines:              1000,730		Code:               1000,731		Comment:            1000,732		Blank:              1000,733		Complexity:         1000,734		WeightedComplexity: 1000,735		Binary:             false,736	}737	close(inputChan)738	Files = false739	Debug = true // Increase coverage slightly740	res := toOpenMetrics(inputChan)741	Debug = false742743	var expectedResult = `# TYPE scc_files gauge744# HELP scc_files Number of sourcecode files.745# TYPE scc_lines gauge746# HELP scc_lines Number of lines.747# TYPE scc_code gauge748# HELP scc_code Number of lines of actual code.749# TYPE scc_comments gauge750# HELP scc_comments Number of comments.751# TYPE scc_blanks gauge752# HELP scc_blanks Number of blank lines.753# TYPE scc_complexity gauge754# HELP scc_complexity Code complexity.755# TYPE scc_bytes gauge756# UNIT scc_bytes bytes757# HELP scc_bytes Size in bytes.758scc_files{language="Go"} 2759scc_lines{language="Go"} 2000760scc_code{language="Go"} 2000761scc_comments{language="Go"} 2000762scc_blanks{language="Go"} 2000763scc_complexity{language="Go"} 2000764scc_bytes{language="Go"} 2000765`766767	if res != expectedResult {768		t.Error("Expected OpenMetrics return", res)769	}770}771772func TestToSQLSingle(t *testing.T) {773	inputChan := make(chan *FileJob, 1000)774	inputChan <- &FileJob{775		Language:           "Go",776		Filename:           "bbbb.go",777		Extension:          "go",778		Location:           "./",779		Bytes:              1000,780		Lines:              1000,781		Code:               1000,782		Comment:            1000,783		Blank:              1000,784		Complexity:         1000,785		WeightedComplexity: 1000,786		Binary:             false,787		Uloc:               99,788	}789	close(inputChan)790	Files = false791	Debug = true // Increase coverage slightly792	res := toSql(inputChan)793	Debug = false794795	if !strings.Contains(res, `create table metadata`) {796		t.Error("Expected create table return", res)797	}798799	if !strings.Contains(res, `create table t`) {800		t.Error("Expected create table return", res)801	}802803	if !strings.Contains(res, `begin transaction`) {804		t.Error("Expected begin transaction return", res)805	}806807	if !strings.Contains(res, `insert into t values('', 'Go', './', './', 'bbbb.go', 1000, 1000, 1000, 1000, 1000, 99);`) {808		t.Error("Expected insert return", res)809	}810811	if !strings.Contains(res, `insert into metadata values`) {812		t.Error("Expected insert return", res)813	}814}815816func TestFileSummarizeWide(t *testing.T) {817	inputChan := make(chan *FileJob, 1000)818	inputChan <- &FileJob{819		Language:           "Go",820		Filename:           "bbbb.go",821		Extension:          "go",822		Location:           "./",823		Bytes:              1000,824		Lines:              1000,825		Code:               1000,826		Comment:            1000,827		Blank:              1000,828		Complexity:         1000,829		WeightedComplexity: 1000,830		Binary:             false,831	}832833	close(inputChan)834	Format = "wide"835	More = true836	res := fileSummarize(inputChan)837	More = false838839	if !strings.Contains(res, `Language`) {840		t.Error("Expected CSV return", res)841	}842}843844func TestFileSummarizeJson(t *testing.T) {845	inputChan := make(chan *FileJob, 1000)846	inputChan <- &FileJob{847		Language:           "Go",848		Filename:           "bbbb.go",849		Extension:          "go",850		Location:           "./",851		Bytes:              1000,852		Lines:              1000,853		Code:               1000,854		Comment:            1000,855		Blank:              1000,856		Complexity:         1000,857		WeightedComplexity: 1000,858		Binary:             false,859	}860861	close(inputChan)862	Format = "JSON"863	More = false864	Files = true865	res := fileSummarize(inputChan)866867	if !strings.Contains(res, `bbbb.go`) || !strings.HasPrefix(res, "[") {868		t.Error("Expected JSON return", res)869	}870}871872func TestFileSummarizeCsv(t *testing.T) {873	inputChan := make(chan *FileJob, 1000)874	inputChan <- &FileJob{875		Language:           "Go",876		Filename:           "bbbb.go",877		Extension:          "go",878		Location:           "./",879		Bytes:              1000,880		Lines:              1000,881		Code:               1000,882		Comment:            1000,883		Blank:              1000,884		Complexity:         1000,885		WeightedComplexity: 1000,886		Binary:             false,887	}888889	close(inputChan)890	Format = "CSV"891	More = false892	res := fileSummarize(inputChan)893894	if !strings.Contains(res, `bbbb.go`) {895		t.Error("Expected CSV return", res)896	}897}898899func TestFileSummarizeYaml(t *testing.T) {900	inputChan := make(chan *FileJob, 1000)901	inputChan <- &FileJob{902		Language:           "Go",903		Filename:           "bbbb.go",904		Extension:          "go",905		Location:           "./",906		Bytes:              1000,907		Lines:              1000,908		Code:               1000,909		Comment:            1000,910		Blank:              1000,911		Complexity:         1000,912		WeightedComplexity: 1000,913		Binary:             false,914	}915916	close(inputChan)917	Format = "cloc-yml"918	More = false919	res := fileSummarize(inputChan)920921	if !strings.Contains(res, `code: 1000`) {922		t.Error("Expected YAML return", res)923	}924}925926func TestFileSummarizeYml(t *testing.T) {927	inputChan := make(chan *FileJob, 1000)928	inputChan <- &FileJob{929		Language:           "Go",930		Filename:           "bbbb.go",931		Extension:          "go",932		Location:           "./",933		Bytes:              1000,934		Lines:              1000,935		Code:               1000,936		Comment:            1000,937		Blank:              1000,938		Complexity:         1000,939		WeightedComplexity: 1000,940		Binary:             false,941	}942943	close(inputChan)944	Format = "cloc-YAML"945	More = false946	res := fileSummarize(inputChan)947948	if !strings.Contains(res, `code: 1000`) {949		t.Error("Expected YML return", res)950	}951}952953func TestFileSummarizeOpenMetrics(t *testing.T) {954	inputChan := make(chan *FileJob, 1000)955	inputChan <- &FileJob{956		Language:           "Go",957		Filename:           "bbbb.go",958		Extension:          "go",959		Location:           "./",960		Bytes:              1000,961		Lines:              1000,962		Code:               1000,963		Comment:            1000,964		Blank:              1000,965		Complexity:         1000,966		WeightedComplexity: 1000,967		Binary:             false,968	}969970	close(inputChan)971	Files = false972	Format = "OpenMetrics"973	More = false974	res := fileSummarize(inputChan)975976	var expectedResult = `# TYPE scc_files gauge977# HELP scc_files Number of sourcecode files.978# TYPE scc_lines gauge979# HELP scc_lines Number of lines.980# TYPE scc_code gauge981# HELP scc_code Number of lines of actual code.982# TYPE scc_comments gauge983# HELP scc_comments Number of comments.984# TYPE scc_blanks gauge985# HELP scc_blanks Number of blank lines.986# TYPE scc_complexity gauge987# HELP scc_complexity Code complexity.988# TYPE scc_bytes gauge989# UNIT scc_bytes bytes990# HELP scc_bytes Size in bytes.991scc_files{language="Go"} 1992scc_lines{language="Go"} 1000993scc_code{language="Go"} 1000994scc_comments{language="Go"} 1000995scc_blanks{language="Go"} 1000996scc_complexity{language="Go"} 1000997scc_bytes{language="Go"} 1000998`9991000	if res != expectedResult {1001		t.Error("Expected OpenMetrics return", res)1002	}1003}10041005func TestFileSummarizeOpenMetricsPerFile(t *testing.T) {1006	inputChan := make(chan *FileJob, 1000)1007	inputChan <- &FileJob{1008		Language:           "Go",1009		Filename:           "bbbb.go",1010		Extension:          "go",1011		Location:           "C:\\bbbb.go", // to test escaping of the backslash1012		Bytes:              1000,1013		Lines:              1000,1014		Code:               1000,1015		Comment:            1000,1016		Blank:              1000,1017		Complexity:         1000,1018		WeightedComplexity: 1000,1019		Binary:             false,1020	}10211022	close(inputChan)1023	Format = "OpenMetrics"1024	More = false1025	Files = true1026	res := fileSummarize(inputChan)10271028	var expectedResult = `# TYPE scc_files gauge1029# HELP scc_files Number of sourcecode files.1030# TYPE scc_lines gauge1031# HELP scc_lines Number of lines.1032# TYPE scc_code gauge1033# HELP scc_code Number of lines of actual code.1034# TYPE scc_comments gauge1035# HELP scc_comments Number of comments.1036# TYPE scc_blanks gauge1037# HELP scc_blanks Number of blank lines.1038# TYPE scc_complexity gauge1039# HELP scc_complexity Code complexity.1040# TYPE scc_bytes gauge1041# UNIT scc_bytes bytes1042# HELP scc_bytes Size in bytes.1043scc_lines{language="Go",file="C:\\bbbb.go"} 10001044scc_code{language="Go",file="C:\\bbbb.go"} 10001045scc_comments{language="Go",file="C:\\bbbb.go"} 10001046scc_blanks{language="Go",file="C:\\bbbb.go"} 10001047scc_complexity{language="Go",file="C:\\bbbb.go"} 10001048scc_bytes{language="Go",file="C:\\bbbb.go"} 10001049# EOF1050`10511052	if res != expectedResult {1053		t.Error("Expected OpenMetrics return", res)1054	}1055}10561057func TestFileSummarizeHtml(t *testing.T) {1058	inputChan := make(chan *FileJob, 1000)1059	inputChan <- &FileJob{1060		Language:           "Go",1061		Filename:           "bbbb.go",1062		Extension:          "go",1063		Location:           "./",1064		Bytes:              1000,1065		Lines:              1000,1066		Code:               1000,1067		Comment:            1000,1068		Blank:              1000,1069		Complexity:         1000,1070		WeightedComplexity: 1000,1071		Binary:             false,1072	}10731074	close(inputChan)1075	Format = "html"1076	More = false1077	res := fileSummarize(inputChan)10781079	if !strings.Contains(res, `<th>1000`) {1080		t.Error("Expected HTML return", res)1081	}1082}10831084func TestFileSummarizeHtmlTable(t *testing.T) {1085	inputChan := make(chan *FileJob, 1000)1086	inputChan <- &FileJob{1087		Language:           "Go",1088		Filename:           "bbbb.go",1089		Extension:          "go",1090		Location:           "./",1091		Bytes:              1000,1092		Lines:              1000,1093		Code:               1000,1094		Comment:            1000,1095		Blank:              1000,1096		Complexity:         1000,1097		WeightedComplexity: 1000,1098		Binary:             false,1099	}11001101	close(inputChan)1102	Format = "html-table"1103	More = false1104	res := fileSummarize(inputChan)11051106	if !strings.Contains(res, `<th>1000`) {1107		t.Error("Expected HTML-table return", res)1108	}1109}11101111func TestFileSummarizeDefault(t *testing.T) {1112	inputChan := make(chan *FileJob, 1000)1113	inputChan <- &FileJob{1114		Language:           "Go",1115		Filename:           "bbbb.go",1116		Extension:          "go",1117		Location:           "./",1118		Bytes:              1000,1119		Lines:              1000,1120		Code:               1000,1121		Comment:            1000,1122		Blank:              1000,1123		Complexity:         1000,1124		WeightedComplexity: 1000,1125		Binary:             false,1126	}11271128	close(inputChan)1129	Format = ""1130	More = false1131	res := fileSummarize(inputChan)11321133	if !strings.Contains(res, `Estimated Cost to Develop`) {1134		t.Error("Expected summary return", res)1135	}1136}11371138func TestFileSummarizeLong(t *testing.T) {1139	inputChan := make(chan *FileJob, 1000)1140	inputChan <- &FileJob{1141		Language:           "Go",1142		Filename:           "bbbb.go",1143		Extension:          "go",1144		Location:           "./",1145		Bytes:              1000,1146		Lines:              1000,1147		Code:               1000,1148		Comment:            1000,1149		Blank:              1000,1150		Complexity:         1000,1151		WeightedComplexity: 1000,1152		Binary:             false,1153	}1154	inputChan <- &FileJob{1155		Language:           "Go",1156		Filename:           "aaaa.go",1157		Extension:          "go",1158		Location:           "./",1159		Bytes:              1000,1160		Lines:              1000,1161		Code:               1000,1162		Comment:            1000,1163		Blank:              1000,1164		Complexity:         1000,1165		WeightedComplexity: 1000,1166		Binary:             false,1167	}1168	close(inputChan)1169	res := fileSummarizeLong(inputChan)11701171	if !strings.Contains(res, `Language`) {1172		t.Error("Expected Summary return", res)1173	}1174}11751176func TestFileSummarizeShort(t *testing.T) {1177	inputChan := make(chan *FileJob, 1000)1178	inputChan <- &FileJob{1179		Language:           "Go",1180		Filename:           "bbbb.go",1181		Extension:          "go",1182		Location:           "./",1183		Bytes:              1000,1184		Lines:              1000,1185		Code:               1000,1186		Comment:            1000,1187		Blank:              1000,1188		Complexity:         1000,1189		WeightedComplexity: 1000,1190		Binary:             false,1191	}1192	inputChan <- &FileJob{1193		Language:           "Go",1194		Filename:           "aaaa.go",1195		Extension:          "go",1196		Location:           "./",1197		Bytes:              1000,1198		Lines:              1000,1199		Code:               1000,1200		Comment:            1000,1201		Blank:              1000,1202		Complexity:         1000,1203		WeightedComplexity: 1000,1204		Binary:             false,1205	}1206	close(inputChan)1207	res := fileSummarizeShort(inputChan)12081209	if !strings.Contains(res, `Language`) {1210		t.Error("Expected Summary return", res)1211	}1212}12131214func TestFileSummarizeShortSort(t *testing.T) {1215	inputChan := make(chan *FileJob, 1000)1216	inputChan <- &FileJob{1217		Language:           "Go",1218		Filename:           "bbbb.go",1219		Extension:          "go",1220		Location:           "./",1221		Bytes:              1000,1222		Lines:              1000,1223		Code:               1000,1224		Comment:            1000,1225		Blank:              1000,1226		Complexity:         1000,1227		WeightedComplexity: 1000,1228		Binary:             false,1229	}1230	inputChan <- &FileJob{1231		Language:           "Go",1232		Filename:           "bbbb.go",1233		Extension:          "go",1234		Location:           "./",1235		Bytes:              1000,1236		Lines:              1000,1237		Code:               1000,1238		Comment:            1000,1239		Blank:              1000,1240		Complexity:         1000,1241		WeightedComplexity: 1000,1242		Binary:             false,1243	}1244	close(inputChan)12451246	sortBy := []string{"name", "line", "blank", "code", "comment"}12471248	Files = true1249	for _, sort := range sortBy {1250		SortBy = sort1251		res := fileSummarizeShort(inputChan)12521253		if !strings.Contains(res, `Language`) {1254			t.Error("Expected Summary return", res)1255		}1256	}1257}12581259func TestFileSummarizeLongSort(t *testing.T) {1260	inputChan := make(chan *FileJob, 1000)1261	inputChan <- &FileJob{1262		Language:           "Go",1263		Filename:           "bbbb.go",1264		Extension:          "go",1265		Location:           "./",1266		Bytes:              1000,1267		Lines:              1000,1268		Code:               1000,1269		Comment:            1000,1270		Blank:              1000,1271		Complexity:         1000,1272		WeightedComplexity: 1000,1273		Binary:             false,1274	}1275	inputChan <- &FileJob{1276		Language:           "Go",1277		Filename:           "bbbb.go",1278		Extension:          "go",1279		Location:           "./",1280		Bytes:              1000,1281		Lines:              1000,1282		Code:               1000,1283		Comment:            1000,1284		Blank:              1000,1285		Complexity:         1000,1286		WeightedComplexity: 1000,1287		Binary:             false,1288	}1289	close(inputChan)12901291	sortBy := []string{"name", "line", "blank", "code", "comment"}12921293	Files = true1294	for _, sort := range sortBy {1295		SortBy = sort1296		res := fileSummarizeLong(inputChan)12971298		if !strings.Contains(res, `Language`) {1299			t.Error("Expected Summary return", res)1300		}1301	}1302}13031304func TestGetTabularShortBreak(t *testing.T) {1305	Ci = false1306	r := getTabularShortBreak()13071308	if !strings.Contains(r, "─") {1309		t.Errorf("Expected to have box line")1310	}13111312	Ci = true1313	r = getTabularShortBreak()13141315	if !strings.Contains(r, "-") {1316		t.Errorf("Expected to have hyphen")1317	}13181319	Ci = false1320}13211322func TestGetTabularWideBreak(t *testing.T) {1323	{1324		Ci, HBorder = false, false1325		r := getTabularWideBreak()1326		if !strings.Contains(r, "─") {1327			t.Errorf("Expected to have box line")1328		}1329	}1330	{1331		Ci, HBorder = false, true1332		r := getTabularWideBreak()1333		if strings.Contains(r, "─") {1334			t.Errorf("Didn't expect to have box line")1335		}1336	}1337	{1338		Ci, HBorder = true, false1339		r := getTabularWideBreak()1340		if !strings.Contains(r, "-") {1341			t.Errorf("Expected to have hyphen")1342		}1343	}1344	{1345		Ci, HBorder = true, true1346		r := getTabularWideBreak()1347		if strings.Contains(r, "-") {1348			t.Errorf("Didn't expect to have hyphen")1349		}1350	}13511352	Ci, HBorder = false, false1353}13541355func TestToHTML(t *testing.T) {1356	inputChan := make(chan *FileJob, 1000)1357	inputChan <- &FileJob{1358		Language:           "Go",1359		Filename:           "bbbb.go",1360		Extension:          "go",1361		Location:           "./",1362		Bytes:              1000,1363		Lines:              1000,1364		Code:               1000,1365		Comment:            1000,1366		Blank:              1000,1367		Complexity:         1000,1368		WeightedComplexity: 1000,1369		Binary:             false,1370	}1371	close(inputChan)1372	res := toHtml(inputChan)13731374	if !strings.Contains(res, `<html lang="en">`) {1375		t.Error("Expected to have HTML wrapper")1376	}13771378	if !strings.Contains(res, "<th>Language</th>") {1379		t.Error("html Language check failed")1380	}1381	if !strings.Contains(res, "<th>Files</th>") {1382		t.Error("html Files check failed")1383	}1384	if !strings.Contains(res, "<th>Lines</th>") {1385		t.Error("html Lines check failed")1386	}1387	if !strings.Contains(res, "<th>Blank</th>") {1388		t.Error("html Blank check failed")1389	}1390	if !strings.Contains(res, "<th>Comment</th>") {1391		t.Error("html Comment check failed")1392	}1393	if !strings.Contains(res, "<th>Code</th>") {1394		t.Error("html Code check failed")1395	}1396	if !strings.Contains(res, "<th>Complexity</th>") {1397		t.Error("html Complexity check failed")1398	}1399	if !strings.Contains(res, "<th>Bytes</th>") {1400		t.Error("html Bytes check failed")1401	}1402	if !strings.Contains(res, "<th>Uloc</th>") {1403		t.Error("html Uloc check failed")1404	}1405}14061407func TestToHTMLTable(t *testing.T) {1408	inputChan := make(chan *FileJob, 1000)1409	inputChan <- &FileJob{1410		Language:           "Go",1411		Filename:           "bbbb.go",1412		Extension:          "go",1413		Location:           "./",1414		Bytes:              1000,1415		Lines:              1000,1416		Code:               1000,1417		Comment:            1000,1418		Blank:              1000,1419		Complexity:         1000,1420		WeightedComplexity: 1000,1421		Binary:             false,1422	}1423	close(inputChan)1424	res := toHtmlTable(inputChan)14251426	if strings.Contains(res, `<html lang="en">`) {1427		t.Error("Expected to not have wrapper")1428	}14291430	if !strings.Contains(res, `<table id="scc-table">`) {1431		t.Error("Expected to have table element")1432	}1433}14341435func TestUnicodeAwareTrimAscii(t *testing.T) {1436	tmp := "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.md"1437	res := unicodeAwareTrim(tmp, shortFormatFileTruncate)1438	if res != "~aaaaaaaaaaaaaaaaaaaaaaa.md" {1439		t.Error("expected ~aaaaaaaaaaaaaaaaaaaaaaa.md got", res)1440	}1441}14421443func TestUnicodeAwareTrimExactSizeAscii(t *testing.T) {1444	tmp := "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.md"1445	res := unicodeAwareTrim(tmp, len(tmp))1446	if res != tmp {1447		t.Errorf("expected %s got %s", tmp, res)1448	}1449}14501451func TestUnicodeAwareTrimUnicode(t *testing.T) {1452	tmp := "中文中文中文中文中文中文中文中文中文中文中文中文中文中文中文中文.md"1453	res := unicodeAwareTrim(tmp, shortFormatFileTruncate)1454	if res != "~文中文中文中文中文中文.md" {1455		t.Error("expected ~文中文中文中文中文中文.md got", res)1456	}1457}14581459func TestUnicodeAwareRightPad(t *testing.T) {1460	tmp := unicodeAwareRightPad("", 10)1461	if runewidth.StringWidth(tmp) != 10 {1462		t.Errorf("expected length of 10")1463	}1464}14651466func TestUnicodeAwareRightPadUnicode(t *testing.T) {1467	tmp := unicodeAwareRightPad("中文", 10)1468	if runewidth.StringWidth(tmp) != 10 {1469		t.Errorf("expected length of 10")1470	}1471}14721473func BenchmarkUnicodeAwareTrimExactSizeAscii(b *testing.B) {1474	tmp := "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.md"1475	for b.Loop() {1476		res := unicodeAwareTrim(tmp, len(tmp))1477		if res != tmp {1478			b.Fatalf("expected %s got %s", tmp, res)1479		}1480	}1481}14821483func BenchmarkUnicodeAwareTrimUnicode(b *testing.B) {1484	tmp := "中文中文中文中文中文中文中文中文中文中文中文中文中文中文中文中文.md"1485	for b.Loop() {1486		res := unicodeAwareTrim(tmp, shortFormatFileTruncate)1487		if res != "~文中文中文中文中文中文.md" {1488			b.Fatalf("expected ~文中文中文中文中文中文.md got %s", res)1489		}1490	}1491}14921493func BenchmarkUnicodeAwareRightPad(b *testing.B) {1494	for b.Loop() {1495		tmp := unicodeAwareRightPad("", 10)1496		if runewidth.StringWidth(tmp) != 10 {1497			b.Fatal("expected length of 10")1498		}1499	}1500}15011502func BenchmarkUnicodeAwareRightPadUnicode(b *testing.B) {1503	for b.Loop() {1504		tmp := unicodeAwareRightPad("中文", 10)1505		if runewidth.StringWidth(tmp) != 10 {1506			b.Fatal("expected length of 10")1507		}1508	}1509}15101511// When using columise  ~28726 ns/op1512// When using optimised ~14293 ns/op1513func BenchmarkFileSummerize(b *testing.B) {1514	for i := 0; i < b.N; i++ {1515		b.StopTimer()1516		fileSummaryJobQueue := make(chan *FileJob, 1000)15171518		fileSummaryJobQueue <- &FileJob{1519			Blank:      1,1520			Bytes:      1,1521			Code:       1,1522			Comment:    1,1523			Complexity: 1,1524			Language:   "Go",1525			Lines:      10,1526		}1527		fileSummaryJobQueue <- &FileJob{1528			Blank:      2,1529			Bytes:      2,1530			Code:       2,1531			Comment:    2,1532			Complexity: 2,1533			Language:   "Python",1534			Lines:      20,1535		}1536		close(fileSummaryJobQueue)1537		b.StartTimer()15381539		fileSummarize(fileSummaryJobQueue)1540	}1541}15421543func TestGetCSVFilesSortFunc(t *testing.T) {1544	records := [][]string{1545		// Language,Provider,Filename,Lines,Code,Comments,Blanks,Complexity,Bytes,ULOC1546		{"Go", "/path/to/file", "go.go", "10", "10", "0", "1", "1", "1024", "0"},1547		{"Python", "/path/to/file", "python.py", "20", "20", "1", "2", "2", "2048", "0"},1548		{"C#", "/path/to/file", "csharp.cs", "30", "30", "2", "3", "3", "4096", "0"},1549		{"C++", "/path/to/file", "cpp.cpp", "40", "40", "3", "4", "4", "8192", "0"},1550	}1551	testCases := []struct {1552		sortBy   string1553		expected []string1554	}{1555		{1556			sortBy:   "names",1557			expected: []string{"C++", "C#", "Go", "Python"},1558		},1559		{1560			sortBy:   "langs",1561			expected: []string{"C#", "C++", "Go", "Python"},1562		},1563		{1564			sortBy:   "lines",1565			expected: []string{"C++", "C#", "Python", "Go"},1566		},1567		{1568			sortBy:   "code",1569			expected: []string{"C++", "C#", "Python", "Go"},1570		},1571		{1572			sortBy:   "comments",1573			expected: []string{"C++", "C#", "Python", "Go"},1574		},1575		{1576			sortBy:   "blanks",1577			expected: []string{"C++", "C#", "Python", "Go"},1578		},1579		{1580			sortBy:   "complexity",1581			expected: []string{"C++", "C#", "Python", "Go"},1582		},1583		{1584			sortBy:   "bytes",1585			expected: []string{"C++", "C#", "Python", "Go"},1586		},1587		{1588			sortBy:   "default",1589			expected: []string{"C++", "C#", "Go", "Python"},1590		},1591	}1592	for _, tc := range testCases {1593		data := slices.Clone(records) // always use an unordered records1594		slices.SortFunc(data, getCSVFilesSortFunc(tc.sortBy))1595		sortedRecords := make([]string, 0, len(data))1596		for i := range data {1597			sortedRecords = append(sortedRecords, data[i][0])1598		}1599		if !slices.Equal(sortedRecords, tc.expected) {1600			t.Errorf("sortBy: %s failed, expected: %v, got: %v", tc.sortBy, tc.expected, sortedRecords)1601		}1602	}1603}16041605func TestToCSVFilesHeader(t *testing.T) {1606	inputChan := make(chan *FileJob, 1000)1607	inputChan <- &FileJob{1608		Language:           "Go",1609		Filename:           "bbbb.go",1610		Extension:          "go",1611		Location:           "./",1612		Bytes:              1000,1613		Lines:              1000,1614		Code:               1000,1615		Comment:            1000,1616		Blank:              1000,1617		Complexity:         1000,1618		WeightedComplexity: 1000,1619		Binary:             false,1620	}1621	inputChan <- &FileJob{1622		Language:           "Go",1623		Filename:           "aaaa.go",1624		Extension:          "go",1625		Location:           "./",1626		Bytes:              1000,1627		Lines:              1000,1628		Code:               1000,1629		Comment:            1000,1630		Blank:              1000,1631		Complexity:         1000,1632		WeightedComplexity: 1000,1633		Binary:             false,1634	}1635	close(inputChan)1636	res := toCSVFiles(inputChan)1637	header, _, _ := strings.Cut(res, "\n")1638	const expected = "Language,Provider,Filename,Lines,Code,Comments,Blanks,Complexity,Bytes,ULOC"1639	if header != expected {1640		t.Errorf("check toCSVFiles header failed, expected: %v, got: %v", expected, header)1641	}1642}16431644func TestToCSVStreamHeader(t *testing.T) {1645	inputChan := make(chan *FileJob, 1000)1646	inputChan <- &FileJob{1647		Language:           "Go",1648		Filename:           "bbbb.go",1649		Extension:          "go",1650		Location:           "./",1651		Bytes:              1000,1652		Lines:              1000,1653		Code:               1000,1654		Comment:            1000,1655		Blank:              1000,1656		Complexity:         1000,1657		WeightedComplexity: 1000,1658		Binary:             false,1659	}1660	inputChan <- &FileJob{1661		Language:           "Go",1662		Filename:           "aaaa.go",1663		Extension:          "go",1664		Location:           "./",1665		Bytes:              1000,1666		Lines:              1000,1667		Code:               1000,1668		Comment:            1000,1669		Blank:              1000,1670		Complexity:         1000,1671		WeightedComplexity: 1000,1672		Binary:             false,1673	}1674	close(inputChan)16751676	originStdout := os.Stdout1677	t.Cleanup(func() {1678		os.Stdout = originStdout1679	})1680	r, w, err := os.Pipe()1681	if err != nil {1682		t.Fatal(err)1683	}1684	os.Stdout = w1685	go func() {1686		toCSVStream(inputChan)1687		_ = w.Close()1688	}()1689	output, err := io.ReadAll(r)1690	if err != nil {1691		t.Fatal(err)1692	}16931694	header, _, _ := strings.Cut(string(output), "\n")1695	const expected = "Language,Provider,Filename,Lines,Code,Comments,Blanks,Complexity,Bytes,Uloc"1696	if header != expected {1697		t.Errorf("check toCSVStream header failed, expected: %v, got: %v", expected, header)1698	}1699}17001701func TestToJSONKeys(t *testing.T) {1702	inputChan := make(chan *FileJob, 1000)1703	inputChan <- &FileJob{1704		Language:           "Go",1705		Filename:           "bbbb.go",1706		Extension:          "go",1707		Location:           "./",1708		Bytes:              1000,1709		Lines:              1000,1710		Code:               1000,1711		Comment:            1000,1712		Blank:              1000,1713		Complexity:         1000,1714		WeightedComplexity: 1000,1715		Binary:             false,1716	}1717	inputChan <- &FileJob{1718		Language:           "Go",1719		Filename:           "aaaa.go",1720		Extension:          "go",1721		Location:           "./",1722		Bytes:              1000,1723		Lines:              1000,1724		Code:               1000,1725		Comment:            1000,1726		Blank:              1000,1727		Complexity:         1000,1728		WeightedComplexity: 1000,1729		Binary:             false,1730	}1731	close(inputChan)17321733	res := toJSON(inputChan)1734	if !strings.Contains(res, `"Name":`) {1735		t.Error("JSON Name check failed")1736	}1737	if !strings.Contains(res, `"Files":`) {1738		t.Error("JSON Files check failed")1739	}1740	if !strings.Contains(res, `"Lines":`) {1741		t.Error("JSON Lines check failed")1742	}1743	if !strings.Contains(res, `"Blank":`) {1744		t.Error("JSON Blank check failed")1745	}1746	if !strings.Contains(res, `"Comment":`) {1747		t.Error("JSON Comment check failed")1748	}1749	if !strings.Contains(res, `"Code":`) {1750		t.Error("JSON Code check failed")1751	}1752	if !strings.Contains(res, `"Complexity":`) {1753		t.Error("JSON Complexity check failed")1754	}1755	if !strings.Contains(res, `"Bytes":`) {1756		t.Error("JSON Bytes check failed")1757	}1758	if !strings.Contains(res, `"ULOC":`) {1759		t.Error("JSON Uloc check failed")1760	}1761}17621763func TestToJSON2Keys(t *testing.T) {1764	inputChan := make(chan *FileJob, 1000)1765	inputChan <- &FileJob{1766		Language:           "Go",1767		Filename:           "bbbb.go",1768		Extension:          "go",1769		Location:           "./",1770		Bytes:              1000,1771		Lines:              1000,1772		Code:               1000,1773		Comment:            1000,1774		Blank:              1000,1775		Complexity:         1000,1776		WeightedComplexity: 1000,1777		Binary:             false,1778	}1779	inputChan <- &FileJob{1780		Language:           "Go",1781		Filename:           "aaaa.go",1782		Extension:          "go",1783		Location:           "./",1784		Bytes:              1000,1785		Lines:              1000,1786		Code:               1000,1787		Comment:            1000,1788		Blank:              1000,1789		Complexity:         1000,1790		WeightedComplexity: 1000,1791		Binary:             false,1792	}1793	close(inputChan)17941795	res := toJSON2(inputChan)1796	if !strings.Contains(res, `"Name":`) {1797		t.Error("JSON2 Name check failed")1798	}1799	if !strings.Contains(res, `"Files":`) {1800		t.Error("JSON2 Files check failed")1801	}1802	if !strings.Contains(res, `"Lines":`) {1803		t.Error("JSON2 Lines check failed")1804	}1805	if !strings.Contains(res, `"Blank":`) {1806		t.Error("JSON2 Blank check failed")1807	}1808	if !strings.Contains(res, `"Comment":`) {1809		t.Error("JSON2 Comment check failed")1810	}1811	if !strings.Contains(res, `"Code":`) {1812		t.Error("JSON2 Code check failed")1813	}1814	if !strings.Contains(res, `"Complexity":`) {1815		t.Error("JSON2 Complexity check failed")1816	}1817	if !strings.Contains(res, `"Bytes":`) {1818		t.Error("JSON2 Bytes check failed")1819	}1820	if !strings.Contains(res, `"ULOC":`) {1821		t.Error("JSON2 Uloc check failed")1822	}1823	if !strings.Contains(res, `"languageSummary":`) {1824		t.Error("JSON2 languageSummary check failed")1825	}1826	if !strings.Contains(res, `"estimatedCost":`) {1827		t.Error("JSON2 estimatedCost check failed")1828	}1829	if !strings.Contains(res, `"estimatedScheduleMonths":`) {1830		t.Error("JSON2 estimatedScheduleMonths check failed")1831	}1832	if !strings.Contains(res, `"estimatedPeople":`) {1833		t.Error("JSON2 estimatedPeople check failed")1834	}1835}

Code quality findings 2

Goroutine without waitgroup or channel; risks resource leaks or race conditions
warning correctness goroutine-without-sync
go func() {
Multiple appends without pre-allocation; use make() with capacity when size is known
info performance append-without-prealloc
sortedRecords = append(sortedRecords, data[i][0])

Get this view in your editor

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