a06f331eb8
install-script / powershell-syntax (push) Waiting to run
CI / benchmark (push) Has been skipped
install-script / posix-syntax (push) Successful in 6m1s
install-script / install (macos-14) (push) Waiting to run
install-script / install (ubuntu-latest) (push) Waiting to run
CI / build-onnx (push) Failing after 6m43s
init-smoke / dry-run (push) Failing after 15m57s
security / govulncheck (push) Has been cancelled
security / trivy-fs (push) Has been cancelled
CI / test (1.26, ubuntu-latest) (push) Has been cancelled
Scorecard supply-chain security / Scorecard analysis (push) Has been cancelled
CI / test (1.26, macos-latest) (push) Has been cancelled
CI / build-windows (push) Has been cancelled
CI / lint (push) Has been cancelled
179 lines
6.7 KiB
Go
179 lines
6.7 KiB
Go
package indexer
|
|
|
|
import (
|
|
"os"
|
|
"path/filepath"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/require"
|
|
)
|
|
|
|
// writeCompileDB writes a compile_commands.json at path and registers cleanup of
|
|
// the per-root include-dir cache so each test starts cold.
|
|
func writeCompileDB(t *testing.T, repoRoot, path, body string) {
|
|
t.Helper()
|
|
require.NoError(t, os.MkdirAll(filepath.Dir(path), 0o755))
|
|
require.NoError(t, os.WriteFile(path, []byte(body), 0o644))
|
|
t.Cleanup(func() { clearCppIncludeDirCache(repoRoot) })
|
|
}
|
|
|
|
func TestLoadCompileCommands_CommandString(t *testing.T) {
|
|
root := t.TempDir()
|
|
writeCompileDB(t, root, filepath.Join(root, "compile_commands.json"), `[
|
|
{
|
|
"directory": "`+root+`/build",
|
|
"file": "../src/main.c",
|
|
"command": "clang -I../gen/include -isystem /usr/include -c ../src/main.c -o main.o"
|
|
}
|
|
]`)
|
|
|
|
tus := loadCompileCommands(root)
|
|
tu, ok := tus["src/main.c"]
|
|
require.True(t, ok, "main.c TU resolved repo-relative from build/ directory")
|
|
assert.Equal(t, "src/main.c", tu.file)
|
|
// `-I../gen/include` becomes repo-relative; the toolchain `-isystem` path is
|
|
// outside the repo and dropped.
|
|
assert.Equal(t, []string{"gen/include"}, tu.includeDirs)
|
|
}
|
|
|
|
func TestLoadCompileCommands_ArgumentsArray(t *testing.T) {
|
|
root := t.TempDir()
|
|
writeCompileDB(t, root, filepath.Join(root, "compile_commands.json"), `[
|
|
{
|
|
"directory": "`+root+`",
|
|
"file": "src/main.cpp",
|
|
"arguments": ["clang++", "-Igen/include", "-I", "vendor/inc", "-iquote", "local", "-c", "src/main.cpp"]
|
|
}
|
|
]`)
|
|
|
|
tus := loadCompileCommands(root)
|
|
tu, ok := tus["src/main.cpp"]
|
|
require.True(t, ok)
|
|
assert.Equal(t, []string{"gen/include", "vendor/inc", "local"}, tu.includeDirs)
|
|
}
|
|
|
|
func TestLoadCompileCommands_BuildDirGlob(t *testing.T) {
|
|
root := t.TempDir()
|
|
// No root compile_commands.json — only build-debug/compile_commands.json.
|
|
writeCompileDB(t, root, filepath.Join(root, "build-debug", "compile_commands.json"), `[
|
|
{
|
|
"directory": "`+root+`/build-debug",
|
|
"file": "../src/app.c",
|
|
"command": "cc -I../include -c ../src/app.c"
|
|
}
|
|
]`)
|
|
|
|
tus := loadCompileCommands(root)
|
|
tu, ok := tus["src/app.c"]
|
|
require.True(t, ok, "TU discovered via build*/compile_commands.json glob")
|
|
assert.Equal(t, []string{"include"}, tu.includeDirs)
|
|
}
|
|
|
|
func TestLoadCompileCommands_None(t *testing.T) {
|
|
root := t.TempDir()
|
|
t.Cleanup(func() { clearCppIncludeDirCache(root) })
|
|
assert.Empty(t, loadCompileCommands(root), "no compile_commands.json yields empty map")
|
|
}
|
|
|
|
// TestHeuristicIncludeDirs pins the no-compile-DB fallback: conventional roots
|
|
// in priority order, followed by any other top-level dir directly containing a
|
|
// C/C++ header; dirs without headers and dotfile dirs are skipped.
|
|
func TestHeuristicIncludeDirs(t *testing.T) {
|
|
root := t.TempDir()
|
|
mk := func(parts ...string) {
|
|
require.NoError(t, os.MkdirAll(filepath.Join(append([]string{root}, parts...)...), 0o755))
|
|
}
|
|
wr := func(rel, body string) {
|
|
require.NoError(t, os.WriteFile(filepath.Join(root, rel), []byte(body), 0o644))
|
|
}
|
|
|
|
mk("include", "proj") // conventional; header is nested, not direct
|
|
wr("include/proj/api.h", "// h")
|
|
mk("src") // conventional, exists
|
|
mk("thirdparty")
|
|
wr("thirdparty/lib.hpp", "// h") // non-conventional dir with a direct header
|
|
mk("docs")
|
|
wr("docs/readme.md", "x") // no header → ignored
|
|
mk(".git")
|
|
wr(".git/x.h", "x") // dotfile dir → skipped
|
|
|
|
got := heuristicIncludeDirs(root)
|
|
assert.Equal(t, []string{"include", "src", "thirdparty"}, got)
|
|
}
|
|
|
|
func TestHeuristicIncludeDirs_Empty(t *testing.T) {
|
|
assert.Nil(t, heuristicIncludeDirs(""))
|
|
assert.Empty(t, heuristicIncludeDirs(t.TempDir()), "no conventional roots and no header dirs")
|
|
}
|
|
|
|
// TestLoadCompileCommands_CacheAndClear pins the cache-and-invalidate contract:
|
|
// a second load returns the cached include dirs even after the file changes on
|
|
// disk, and clearing the cache (as an incremental reindex does) re-reads it —
|
|
// so an edited compile_commands.json takes effect without a daemon restart.
|
|
func TestLoadCompileCommands_CacheAndClear(t *testing.T) {
|
|
root := t.TempDir()
|
|
path := filepath.Join(root, "compile_commands.json")
|
|
t.Cleanup(func() { clearCppIncludeDirCache(root) })
|
|
|
|
// write replaces the DB body while preserving its modtime, so this test
|
|
// isolates the explicit-clear path from the mtime-keyed reload (covered by
|
|
// TestLoadCompileCommands_MtimeReload).
|
|
write := func(incDir string) {
|
|
var keep time.Time
|
|
if fi, err := os.Stat(path); err == nil {
|
|
keep = fi.ModTime()
|
|
}
|
|
body := `[{"directory":"` + root + `","file":"src/main.c","command":"cc -I` + incDir + ` -c src/main.c"}]`
|
|
require.NoError(t, os.WriteFile(path, []byte(body), 0o644))
|
|
if !keep.IsZero() {
|
|
require.NoError(t, os.Chtimes(path, keep, keep))
|
|
}
|
|
}
|
|
|
|
write("inc1")
|
|
require.Equal(t, []string{"inc1"}, loadCompileCommands(root)["src/main.c"].includeDirs)
|
|
|
|
// Editing the DB on disk (without bumping its modtime) is not observed until
|
|
// the cache is invalidated.
|
|
write("inc2")
|
|
assert.Equal(t, []string{"inc1"}, loadCompileCommands(root)["src/main.c"].includeDirs,
|
|
"second load returns the cached result")
|
|
|
|
// Clearing the cache (the incremental-reindex hook) re-reads the new dir.
|
|
clearCppIncludeDirCache(root)
|
|
assert.Equal(t, []string{"inc2"}, loadCompileCommands(root)["src/main.c"].includeDirs,
|
|
"after clear the edited -I dir is picked up")
|
|
}
|
|
|
|
// TestLoadCompileCommands_MtimeReload pins the mtime-keyed invalidation: editing
|
|
// only compile_commands.json (changing an -I dir) and re-loading picks up the new
|
|
// include dir without any explicit cache clear / full reindex — the cache reloads
|
|
// because the file's modtime advanced.
|
|
func TestLoadCompileCommands_MtimeReload(t *testing.T) {
|
|
root := t.TempDir()
|
|
path := filepath.Join(root, "compile_commands.json")
|
|
t.Cleanup(func() { clearCppIncludeDirCache(root) })
|
|
|
|
write := func(incDir string, mtime time.Time) {
|
|
body := `[{"directory":"` + root + `","file":"src/main.c","command":"cc -I` + incDir + ` -c src/main.c"}]`
|
|
require.NoError(t, os.WriteFile(path, []byte(body), 0o644))
|
|
require.NoError(t, os.Chtimes(path, mtime, mtime))
|
|
}
|
|
|
|
t0 := time.Now().Add(-time.Hour)
|
|
write("inc1", t0)
|
|
require.Equal(t, []string{"inc1"}, loadCompileCommands(root)["src/main.c"].includeDirs)
|
|
|
|
// Edit only compile_commands.json, advancing its modtime. No clearCppIncludeDirCache
|
|
// call — the next load must observe the newer file and re-read it.
|
|
write("inc2", t0.Add(time.Minute))
|
|
assert.Equal(t, []string{"inc2"}, loadCompileCommands(root)["src/main.c"].includeDirs,
|
|
"a newer compile_commands.json is re-read without an explicit cache clear")
|
|
|
|
// A subsequent load with no further edit is served from the (now-warm) cache.
|
|
assert.Equal(t, []string{"inc2"}, loadCompileCommands(root)["src/main.c"].includeDirs,
|
|
"unchanged compile_commands.json stays cached")
|
|
}
|