lang_test.go
package main
import (
"testing"
)
// TestClassifyPath pins the filename-only classification pass, one row per rule:
// plain classification, the markup case, the skip heuristics (vendored, dotfile,
// documentation), unclassifiable paths, and the ambiguous-extension escape hatch.
func TestClassifyPath(t *testing.T) {
t.Parallel()
for _, c := range []struct {
path string
lang string
needsContent bool
}{
{"src/hello.go", "Go", false},
{"src/style.css", "CSS", false},
{"Makefile", "Makefile", false},
{"design.md", "Markdown", false}, // classified fine; prose is excluded later, by countsToward
{"src/lib.rs", "", true}, // several candidate languages: classify by content
{"vendor/mini.js", "", false}, // vendored
{"testdata/gen.go", "", false}, // vendored (linguist treats test fixtures so)
{".gitignore", "", false}, // dotfile
{"docs/guide.html", "", false}, // documentation
{"README.md", "", false}, // documentation too, READMEs count as docs
{"LICENSE", "", false}, // unclassifiable by name
{"notes", "", false}, // no extension, no known filename
} {
lang, needsContent := classifyPath(c.path)
if lang != c.lang || needsContent != c.needsContent {
t.Errorf("classifyPath(%q) = %q, %v; want %q, %v", c.path, lang, needsContent, c.lang, c.needsContent)
}
}
}
// TestCountsToward pins the type filter: programming and markup make the meter;
// data, prose, and the unknown empty string do not.
func TestCountsToward(t *testing.T) {
t.Parallel()
for _, c := range []struct {
lang string
want bool
}{
{"Go", true},
{"Rust", true},
{"CSS", true}, // markup
{"HTML", true}, // markup
{"Markdown", false}, // prose
{"Text", false}, // prose
{"YAML", false}, // data
{"Go Module", false}, // data
{"", false},
} {
if got := countsToward(c.lang); got != c.want {
t.Errorf("countsToward(%q) = %v, want %v", c.lang, got, c.want)
}
}
}
// TestLanguageStats runs the whole computation against the fixture repo:
// Go, Rust (via the content sniff), and CSS counted by blob size, descending;
// prose, data, vendored, and dotfile content absent.
func TestLanguageStats(t *testing.T) {
t.Parallel()
r := fixtureRepo(t)
size := func(path string) int64 {
t.Helper()
n, err := r.blobSize(fixtureShaMain, path)
if err != nil {
t.Fatal(err)
}
return n
}
want := []langStat{
{"Go", size("src/hello.go")},
{"Rust", size("src/lib.rs")},
{"CSS", size("src/style.css")},
}
got, err := languageStats(r, fixtureShaMain)
if err != nil {
t.Fatal(err)
}
if len(got) != len(want) {
t.Fatalf("languageStats = %+v, want %+v", got, want)
}
for i := range want {
if got[i] != want[i] {
t.Errorf("stat %d = %+v, want %+v", i, got[i], want[i])
}
}
}
// TestLangCacheBounds pins the cache's FIFO eviction: the capacity holds,
// the oldest key leaves first, and lookups tell hits from misses.
func TestLangCacheBounds(t *testing.T) {
t.Parallel()
c := newLangCache(2)
c.put("a", []langStat{{"Go", 1}})
c.put("b", []langStat{{"Go", 2}})
if _, ok := c.get("a"); !ok {
t.Error("a missing before capacity was exceeded")
}
c.put("c", []langStat{{"Go", 3}})
if _, ok := c.get("a"); ok {
t.Error("a survived eviction; want FIFO drop of the oldest")
}
for _, key := range []string{"b", "c"} {
if _, ok := c.get(key); !ok {
t.Errorf("%s missing after eviction of a", key)
}
}
if _, ok := c.get("never"); ok {
t.Error("get invented an entry")
}
}
// TestLangStatsCached proves the server-level path consults the cache:
// a planted cache entry is returned instead of a fresh computation.
func TestLangStatsCached(t *testing.T) {
t.Parallel()
s := fixtureServer(t)
rp := s.snap.Load().repos["fixturama"]
planted := []langStat{{"Ada", 42}}
s.langs.put(langCacheKey(rp.name, fixtureShaMain), planted)
got, err := s.langStats(rp, fixtureShaMain)
if err != nil {
t.Fatal(err)
}
if len(got) != 1 || got[0] != planted[0] {
t.Errorf("langStats = %+v, want the planted %+v", got, planted)
}
}
lang_test.go · 4.0 KiB · fba503ffd6