processor/file_test.go GO 436 lines View on github.com → Search inside
1// SPDX-License-Identifier: MIT23package processor45import (6	"math/rand/v2"7	"os"8	"path/filepath"9	"runtime"10	"testing"11)1213func TestGetExtension(t *testing.T) {14	got := getExtension("something.c")15	expected := "c"1617	if got != expected {18		t.Errorf("Expected %s got %s", expected, got)19	}20}2122func TestGetExtensionNoExtension(t *testing.T) {23	got := getExtension("something")24	expected := "something"2526	if got != expected {27		t.Errorf("Expected %s got %s", expected, got)28	}29}3031func TestGetExtensionMultipleDots(t *testing.T) {32	got := getExtension(".travis.yml")33	expected := "travis.yml"3435	if got != expected {36		t.Errorf("Expected %s got %s", expected, got)37	}38}3940func TestGetExtensionMultipleExtensions(t *testing.T) {41	got := getExtension("something.go.yml")42	expected := "go.yml"4344	if got != expected {45		t.Errorf("Expected %s got %s", expected, got)46	}47}4849func TestGetExtensionStartsWith(t *testing.T) {50	got := getExtension(".gitignore")51	expected := ".gitignore"5253	if got != expected {54		t.Errorf("Expected %s got %s", expected, got)55	}56}5758func TestGetExtensionTypeScriptDefinition(t *testing.T) {59	got := getExtension("test.d.ts")60	expected := "d.ts"6162	if got != expected {63		t.Errorf("Expected %s got %s", expected, got)64	}65}6667func TestGetExtensionSecondPass(t *testing.T) {68	got := getExtension("test.d.ts")69	got = getExtension(got)70	expected := "ts"7172	if got != expected {73		t.Errorf("Expected %s got %s", expected, got)74	}75}7677func TestNewFileJobCountAsPattern(t *testing.T) {78	defer func() {79		CountAsPattern = nil80		CountRules = nil81		compiledCountRules = nil82		delete(languageDatabase, "Ruby Spec")83		delete(languageDatabase, "Ruby App")84		delete(languageDatabase, "Rescued")85	}()8687	AllowListExtensions = []string{}88	// First matching rule wins, so the spec rule is ordered before a broad rule89	CountAsPattern = []string{90		"glob:*_spec.rb:Ruby Spec:Ruby",91		"glob:*.rb:Ruby App:Ruby",92		"glob:*.unknownext:Rescued:JavaScript",93	}94	CountRules = nil95	compiledCountRules = nil96	ProcessConstants()97	cleanVisitedPaths()9899	// A *_spec.rb path is relabelled to the new minted category100	fi, _ := os.Stat("../examples/countaspattern/foo_spec.rb")101	job := newFileJob("../examples/countaspattern/foo_spec.rb", "foo_spec.rb", fi)102	if job == nil || job.PossibleLanguages[0] != "Ruby Spec" {103		t.Fatalf("expected Ruby Spec, got %+v", job)104	}105106	// First-match-wins: app.rb matches *.rb only, so falls to the second rule107	cleanVisitedPaths()108	fi, _ = os.Stat("../examples/countaspattern/app.rb")109	job = newFileJob("../examples/countaspattern/app.rb", "app.rb", fi)110	if job == nil || job.PossibleLanguages[0] != "Ruby App" {111		t.Fatalf("expected Ruby App, got %+v", job)112	}113}114115func TestNewFileJobFullname(t *testing.T) {116	ProcessConstants()117	cleanVisitedPaths()118	AllowListExtensions = []string{}119120	fi, _ := os.Stat("../examples/issue114/makefile")121	job := newFileJob("../examples/issue114/", "makefile", fi)122123	if job.PossibleLanguages[0] != "Makefile" {124		t.Error("Expected makefile got", job.PossibleLanguages[0])125	}126}127128func TestNewFileJob(t *testing.T) {129	ProcessConstants()130	cleanVisitedPaths()131132	fi, _ := os.Stat("../examples/issue114/java")133	job := newFileJob("../examples/issue114/", "java", fi)134135	if job.PossibleLanguages[0] != "#!" {136		t.Error("Expected special value #! got", job.PossibleLanguages[0])137	}138}139140func TestNewFileJobGitIgnore(t *testing.T) {141	AllowListExtensions = []string{}142	ProcessConstants()143	cleanVisitedPaths()144	CountIgnore = true145146	fi, _ := os.Stat("../examples/issue114/.gitignore")147	job := newFileJob("../examples/issue114/", ".gitignore", fi)148149	if job.PossibleLanguages[0] != "gitignore" {150		t.Error("Expected gitignore got", job.PossibleLanguages[0])151	}152}153154func TestNewFileJobIgnore(t *testing.T) {155	AllowListExtensions = []string{}156	ProcessConstants()157	cleanVisitedPaths()158159	fi, _ := os.Stat("../examples/issue114/.ignore")160	job := newFileJob("../examples/issue114/", ".ignore", fi)161162	if job.PossibleLanguages[0] != "ignore" {163		t.Error("Expected ignore got", job.PossibleLanguages[0])164	}165}166167func TestNewFileJobLicense(t *testing.T) {168	ProcessConstants()169	cleanVisitedPaths()170171	fi, _ := os.Stat("../examples/issue114/license")172	job := newFileJob("../examples/issue114/", "license", fi)173174	if job.PossibleLanguages[0] != "License" {175		t.Error("Expected License got", job.PossibleLanguages[0])176	}177}178179func TestNewFileJobYAML(t *testing.T) {180	ProcessConstants()181	cleanVisitedPaths()182183	fi, _ := os.Stat("../examples/issue114/.travis.yml")184	job := newFileJob("../examples/issue114/", ".travis.yml", fi)185186	found := false187	for _, j := range job.PossibleLanguages {188		if j == "YAML" {189			found = true190		}191	}192193	if !found {194		t.Error("Expected YAML in but didn't find", job.PossibleLanguages)195	}196}197198func TestNewFileJobYAMLCloudformation(t *testing.T) {199	ProcessConstants()200	cleanVisitedPaths()201202	fi, _ := os.Stat("../examples/issue114/.travis.yml")203	job := newFileJob("../examples/issue114/", ".travis.yml", fi)204205	found := false206	for _, j := range job.PossibleLanguages {207		if j == "CloudFormation (YAML)" {208			found = true209		}210	}211212	if !found {213		t.Error("Expected CloudFormation in but didn't find", job.PossibleLanguages)214	}215}216217func TestNewFileJobSize(t *testing.T) {218	ProcessConstants()219	cleanVisitedPaths()220	NoLarge = true221	LargeByteCount = 1222223	fi, _ := os.Stat("file_test.go")224	job := newFileJob("file_test.go", "file_test.go", fi)225226	if job != nil {227		t.Error("Expected nil got", job)228	}229230	NoLarge = false231	LargeByteCount = 1000000232}233234func TestNewFileJobBrokenSymlink(t *testing.T) {235	if runtime.GOOS == "windows" {236		t.Skip("skipping symlink test on Windows due to privilege requirements")237	}238239	ProcessConstants()240	cleanVisitedPaths()241	IncludeSymLinks = true242	defer func() {243		IncludeSymLinks = false244	}()245246	// Create a temp directory to work in247	file := filepath.Join(t.TempDir(), "source.go")248	err := os.WriteFile(file, []byte("package main\n"), 0644)249	if err != nil {250		t.Fatal(err)251	}252	symPath := filepath.Join(t.TempDir(), "broken.go")253	err = os.Symlink(file, symPath)254	if err != nil {255		t.Fatal(err)256	}257	// Delete the file breaks the symlink258	err = os.Remove(file)259	if err != nil {260		t.Fatal(err)261	}262263	fi, _ := os.Lstat(symPath)264	job := newFileJob(symPath, "broken.go", fi)265266	if job != nil {267		t.Error("Expected nil for broken symlink got", job)268	}269}270271func TestNewFileJobUnsupportedSkippedByDefault(t *testing.T) {272	ProcessConstants()273	cleanVisitedPaths()274	AllowListExtensions = []string{}275	CountUnsupported = false276277	dir, _ := filepath.EvalSymlinks(t.TempDir())278	file := filepath.Join(dir, "data.unknownext")279	if err := os.WriteFile(file, []byte("one\ntwo\n"), 0644); err != nil {280		t.Fatal(err)281	}282283	fi, _ := os.Stat(file)284	job := newFileJob(file, "data.unknownext", fi)285286	if job != nil {287		t.Error("Expected nil for unsupported file without --count-unsupported, got", job)288	}289}290291func TestNewFileJobUnsupportedCounted(t *testing.T) {292	ProcessConstants()293	cleanVisitedPaths()294	AllowListExtensions = []string{}295	CountUnsupported = true296	defer func() { CountUnsupported = false }()297298	dir, _ := filepath.EvalSymlinks(t.TempDir())299	file := filepath.Join(dir, "data.unknownext")300	if err := os.WriteFile(file, []byte("one\ntwo\n"), 0644); err != nil {301		t.Fatal(err)302	}303304	fi, _ := os.Stat(file)305	job := newFileJob(file, "data.unknownext", fi)306307	if job == nil {308		t.Fatal("Expected a FileJob for unsupported file with --count-unsupported, got nil")309	}310	if len(job.PossibleLanguages) != 1 || job.PossibleLanguages[0] != UnknownLanguage {311		t.Errorf("Expected language %q got %v", UnknownLanguage, job.PossibleLanguages)312	}313314	// It should count as plain text: lines and code populated, no comments or315	// complexity since the Unknown category carries no language features.316	job.Content = []byte("one\ntwo\n")317	job.Language = DetermineLanguage(job.Filename, job.Language, job.PossibleLanguages, job.Content)318	if job.Language != UnknownLanguage {319		t.Errorf("Expected resolved language %q got %q", UnknownLanguage, job.Language)320	}321	CountStats(job)322	if job.Lines != 2 {323		t.Errorf("Expected 2 lines got %d", job.Lines)324	}325	if job.Code != 2 {326		t.Errorf("Expected 2 code lines got %d", job.Code)327	}328	if job.Comment != 0 || job.Complexity != 0 {329		t.Errorf("Expected no comments/complexity got comment=%d complexity=%d", job.Comment, job.Complexity)330	}331}332333func BenchmarkGetExtensionDifferent(b *testing.B) {334	for i := 0; i < b.N; i++ {335336		b.StopTimer()337		name := randStringBytes(3) + "." + randStringBytes(2)338		b.StartTimer()339340		getExtension(name)341	}342}343344func BenchmarkGetExtensionSame(b *testing.B) {345	name := randStringBytes(7) + "." + randStringBytes(3)346347	for i := 0; i < b.N; i++ {348		getExtension(name)349	}350}351352const letterBytes = "abcdefghijklmnopqrstuvwxyz"353354func randStringBytes(n int) string {355	b := make([]byte, n)356	for i := range b {357		b[i] = letterBytes[rand.IntN(len(letterBytes))]358	}359	return string(b)360}361362func TestNewFileJobCircularSymlink(t *testing.T) {363	if runtime.GOOS == "windows" {364		t.Skip("skipping symlink test on Windows due to privilege requirements")365	}366	ProcessConstants()367	cleanVisitedPaths()368	IncludeSymLinks = true369	defer func() { IncludeSymLinks = false }()370371	// Create a temp directory to work in372	dir, _ := filepath.EvalSymlinks(t.TempDir())373	link1 := filepath.Join(dir, "link1.go")374	link2 := filepath.Join(dir, "link2.go")375	// Create a loop: link1 -> link2 and link2 -> link1376	if err := os.Symlink(link2, link1); err != nil {377		t.Fatal("Failed to create first link:", err)378	}379	if err := os.Symlink(link1, link2); err != nil {380		t.Fatal("Failed to create circular link:", err)381	}382383	fi, err := os.Lstat(link1)384	if err != nil {385		t.Fatal(err)386	}387	// It should return the 'too many links' error.388	job := newFileJob(link1, "link1.go", fi)389390	if job != nil {391		t.Error("Expected nil for circular symlink, but got a FileJob")392	}393}394395func TestNewFileJobDuplicateCounting(t *testing.T) {396	if runtime.GOOS == "windows" {397		t.Skip("skipping symlink test on Windows due to privilege requirements")398	}399	ProcessConstants()400	cleanVisitedPaths()401	IncludeSymLinks = true402	defer func() { IncludeSymLinks = false }()403404	// on some systems like macOS, t.TempDir is also a symlink405	dir, _ := filepath.EvalSymlinks(t.TempDir())406	// Create a test file407	testFile := filepath.Join(dir, "file.go")408409	if err := os.WriteFile(testFile, []byte("package main"), 0644); err != nil {410		t.Fatal(err)411	}412413	// Create a symlink to the same file414	linkFile := filepath.Join(dir, "link.go")415	if err := os.Symlink(testFile, linkFile); err != nil {416		t.Fatalf("Failed to create link file: %s", err)417	}418	// Process the test file419	fi1, _ := os.Lstat(testFile)420	job1 := newFileJob(testFile, "file.go", fi1)421422	// Process the symlink (same target)423	fi2, _ := os.Lstat(linkFile)424	job2 := newFileJob(linkFile, "link.go", fi2)425426	// First count should go through427	if job1 == nil {428		t.Fatal("Expected first file job to be created")429	}430431	// Second count should be skipped432	if job2 != nil {433		t.Error("Expected nil for duplicate file through symlink, but got a FileJob")434	}435}

Code quality findings 9

Blank identifier discarding results; verify intentional ignoring of return values
warning correctness blank-identifier-discard
fi, _ = os.Stat("../examples/countaspattern/app.rb")
Can cause issues on Windows consider filepath.Join instead
info correctness path-join-windows
file := filepath.Join(t.TempDir(), "source.go")
Can cause issues on Windows consider filepath.Join instead
info correctness path-join-windows
symPath := filepath.Join(t.TempDir(), "broken.go")
Can cause issues on Windows consider filepath.Join instead
info correctness path-join-windows
file := filepath.Join(dir, "data.unknownext")
Can cause issues on Windows consider filepath.Join instead
info correctness path-join-windows
file := filepath.Join(dir, "data.unknownext")
Can cause issues on Windows consider filepath.Join instead
info correctness path-join-windows
link1 := filepath.Join(dir, "link1.go")
Can cause issues on Windows consider filepath.Join instead
info correctness path-join-windows
link2 := filepath.Join(dir, "link2.go")
Can cause issues on Windows consider filepath.Join instead
info correctness path-join-windows
testFile := filepath.Join(dir, "file.go")
Can cause issues on Windows consider filepath.Join instead
info correctness path-join-windows
linkFile := filepath.Join(dir, "link.go")

Get this view in your editor

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