292 lines
8.5 KiB
Go
292 lines
8.5 KiB
Go
package symlinks
|
|
|
|
import (
|
|
"testing"
|
|
|
|
"github.com/microsoft/typescript-go/internal/ast"
|
|
"github.com/microsoft/typescript-go/internal/core"
|
|
"github.com/microsoft/typescript-go/internal/module"
|
|
"github.com/microsoft/typescript-go/internal/tspath"
|
|
)
|
|
|
|
func TestNewKnownSymlink(t *testing.T) {
|
|
t.Parallel()
|
|
cache := NewKnownSymlink("/test/dir", true)
|
|
if cache == nil {
|
|
t.Fatal("Expected non-nil cache")
|
|
}
|
|
if cache.cwd != "/test/dir" {
|
|
t.Errorf("Expected cwd to be '/test/dir', got '%s'", cache.cwd)
|
|
}
|
|
if !cache.useCaseSensitiveFileNames {
|
|
t.Error("Expected useCaseSensitiveFileNames to be true")
|
|
}
|
|
}
|
|
|
|
func TestSetDirectory(t *testing.T) {
|
|
t.Parallel()
|
|
cache := NewKnownSymlink("/test/dir", true)
|
|
symlinkPath := tspath.ToPath("/test/symlink", "/test/dir", true).EnsureTrailingDirectorySeparator()
|
|
realDirectory := &KnownDirectoryLink{
|
|
Real: "/real/path/",
|
|
RealPath: tspath.ToPath("/real/path", "/test/dir", true).EnsureTrailingDirectorySeparator(),
|
|
}
|
|
|
|
cache.SetDirectory("/test/symlink", symlinkPath, realDirectory)
|
|
|
|
// Check that directory was stored
|
|
stored, ok := cache.Directories().Load(symlinkPath)
|
|
if !ok {
|
|
t.Fatal("Expected directory to be stored")
|
|
}
|
|
if stored.Real != realDirectory.Real {
|
|
t.Errorf("Expected Real to be '%s', got '%s'", realDirectory.Real, stored.Real)
|
|
}
|
|
if stored.RealPath != realDirectory.RealPath {
|
|
t.Errorf("Expected RealPath to be '%s', got '%s'", realDirectory.RealPath, stored.RealPath)
|
|
}
|
|
|
|
// Check that realpath mapping was created
|
|
set, ok := cache.DirectoriesByRealpath().Load(realDirectory.RealPath)
|
|
if !ok || set.Size() == 0 {
|
|
t.Fatal("Expected realpath mapping to be created")
|
|
}
|
|
if !set.Has("/test/symlink") {
|
|
t.Error("Expected symlink '/test/symlink' to be in set")
|
|
}
|
|
}
|
|
|
|
func TestSetFile(t *testing.T) {
|
|
t.Parallel()
|
|
cache := NewKnownSymlink("/test/dir", true)
|
|
symlink := "/test/symlink/file.ts"
|
|
symlinkPath := tspath.ToPath(symlink, "/test/dir", true)
|
|
realpath := "/real/path/file.ts"
|
|
|
|
cache.SetFile(symlink, symlinkPath, realpath)
|
|
|
|
stored, ok := cache.Files().Load(symlinkPath)
|
|
if !ok {
|
|
t.Fatal("Expected file to be stored")
|
|
}
|
|
if stored != realpath {
|
|
t.Errorf("Expected realpath to be '%s', got '%s'", realpath, stored)
|
|
}
|
|
}
|
|
|
|
func TestProcessResolution(t *testing.T) {
|
|
t.Parallel()
|
|
cache := NewKnownSymlink("/test/dir", true)
|
|
|
|
// Test with empty paths
|
|
cache.ProcessResolution("", "")
|
|
cache.ProcessResolution("original", "")
|
|
cache.ProcessResolution("", "resolved")
|
|
|
|
// Test with valid paths
|
|
originalPath := "/test/original/file.ts"
|
|
resolvedPath := "/test/resolved/file.ts"
|
|
cache.ProcessResolution(originalPath, resolvedPath)
|
|
|
|
// Check that file was stored
|
|
symlinkPath := tspath.ToPath(originalPath, "/test/dir", true)
|
|
stored, ok := cache.Files().Load(symlinkPath)
|
|
if !ok {
|
|
t.Fatal("Expected file to be stored")
|
|
}
|
|
if stored != resolvedPath {
|
|
t.Errorf("Expected resolved path to be '%s', got '%s'", resolvedPath, stored)
|
|
}
|
|
}
|
|
|
|
func TestGuessDirectorySymlink(t *testing.T) {
|
|
t.Parallel()
|
|
cache := NewKnownSymlink("/test/dir", true)
|
|
|
|
tests := []struct {
|
|
name string
|
|
a string
|
|
b string
|
|
cwd string
|
|
expected [2]string // [commonResolved, commonOriginal]
|
|
}{
|
|
{
|
|
name: "identical paths",
|
|
a: "/test/path/file.ts",
|
|
b: "/test/path/file.ts",
|
|
cwd: "/test/dir",
|
|
expected: [2]string{"/", "/"},
|
|
},
|
|
{
|
|
name: "different files same directory",
|
|
a: "/test/path/file1.ts",
|
|
b: "/test/path/file2.ts",
|
|
cwd: "/test/dir",
|
|
expected: [2]string{"", ""},
|
|
},
|
|
{
|
|
name: "different directories",
|
|
a: "/test/path1/file.ts",
|
|
b: "/test/path2/file.ts",
|
|
cwd: "/test/dir",
|
|
expected: [2]string{"/test/path1", "/test/path2"},
|
|
},
|
|
{
|
|
name: "node_modules paths",
|
|
a: "/test/node_modules/pkg/file.ts",
|
|
b: "/test/node_modules/pkg/file.ts",
|
|
cwd: "/test/dir",
|
|
expected: [2]string{"/test/node_modules/pkg", "/test/node_modules/pkg"},
|
|
},
|
|
{
|
|
name: "scoped package paths",
|
|
a: "/test/node_modules/@scope/pkg/file.ts",
|
|
b: "/test/node_modules/@scope/pkg/file.ts",
|
|
cwd: "/test/dir",
|
|
expected: [2]string{"/test/node_modules/@scope/pkg", "/test/node_modules/@scope/pkg"},
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
t.Parallel()
|
|
commonResolved, commonOriginal := cache.guessDirectorySymlink(tt.a, tt.b, tt.cwd)
|
|
if commonResolved != tt.expected[0] {
|
|
t.Errorf("Expected commonResolved to be '%s', got '%s'", tt.expected[0], commonResolved)
|
|
}
|
|
if commonOriginal != tt.expected[1] {
|
|
t.Errorf("Expected commonOriginal to be '%s', got '%s'", tt.expected[1], commonOriginal)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestIsNodeModulesOrScopedPackageDirectory(t *testing.T) {
|
|
t.Parallel()
|
|
cache := NewKnownSymlink("/test/dir", true)
|
|
|
|
tests := []struct {
|
|
name string
|
|
dir string
|
|
expected bool
|
|
}{
|
|
{"node_modules", "node_modules", true},
|
|
{"scoped package", "@scope", true},
|
|
{"regular directory", "src", false},
|
|
{"empty string", "", false},
|
|
{"case insensitive node_modules", "NODE_MODULES", false}, // The function is case sensitive
|
|
{"case insensitive scoped", "@SCOPE", true},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
t.Parallel()
|
|
result := cache.isNodeModulesOrScopedPackageDirectory(tt.dir)
|
|
if result != tt.expected {
|
|
t.Errorf("Expected %v, got %v for directory '%s'", tt.expected, result, tt.dir)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestSetSymlinksFromResolutions(t *testing.T) {
|
|
t.Parallel()
|
|
cache := NewKnownSymlink("/test/dir", true)
|
|
|
|
// Mock resolution data
|
|
resolvedModules := []struct {
|
|
originalPath string
|
|
resolvedPath string
|
|
moduleName string
|
|
mode core.ResolutionMode
|
|
filePath tspath.Path
|
|
}{
|
|
{
|
|
originalPath: "/test/original/file1.ts",
|
|
resolvedPath: "/test/resolved/file1.ts",
|
|
moduleName: "module1",
|
|
mode: core.ResolutionModeNone,
|
|
filePath: tspath.ToPath("/test/source.ts", "/test/dir", true),
|
|
},
|
|
{
|
|
originalPath: "/test/original/file2.ts",
|
|
resolvedPath: "/test/resolved/file2.ts",
|
|
moduleName: "module2",
|
|
mode: core.ResolutionModeNone,
|
|
filePath: tspath.ToPath("/test/source.ts", "/test/dir", true),
|
|
},
|
|
}
|
|
|
|
// Mock callbacks
|
|
forEachResolvedModule := func(callback func(resolution *module.ResolvedModule, moduleName string, mode core.ResolutionMode, filePath tspath.Path), file *ast.SourceFile) {
|
|
for _, res := range resolvedModules {
|
|
resolution := &module.ResolvedModule{
|
|
OriginalPath: res.originalPath,
|
|
ResolvedFileName: res.resolvedPath,
|
|
}
|
|
callback(resolution, res.moduleName, res.mode, res.filePath)
|
|
}
|
|
}
|
|
|
|
forEachResolvedTypeReferenceDirective := func(callback func(resolution *module.ResolvedTypeReferenceDirective, moduleName string, mode core.ResolutionMode, filePath tspath.Path), file *ast.SourceFile) {
|
|
// No type reference directives for this test
|
|
}
|
|
|
|
cache.SetSymlinksFromResolutions(forEachResolvedModule, forEachResolvedTypeReferenceDirective)
|
|
|
|
// Check that files were stored
|
|
for _, res := range resolvedModules {
|
|
symlinkPath := tspath.ToPath(res.originalPath, "/test/dir", true)
|
|
stored, ok := cache.Files().Load(symlinkPath)
|
|
if !ok {
|
|
t.Errorf("Expected file '%s' to be stored", res.originalPath)
|
|
continue
|
|
}
|
|
if stored != res.resolvedPath {
|
|
t.Errorf("Expected resolved path to be '%s', got '%s'", res.resolvedPath, stored)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestKnownSymlinksThreadSafety(t *testing.T) {
|
|
t.Parallel()
|
|
cache := NewKnownSymlink("/test/dir", true)
|
|
|
|
// Test concurrent access
|
|
done := make(chan bool, 10)
|
|
|
|
for i := range 10 {
|
|
go func(id int) {
|
|
defer func() { done <- true }()
|
|
|
|
symlinkPath := tspath.ToPath("/test/symlink"+string(rune(id)), "/test/dir", true).EnsureTrailingDirectorySeparator()
|
|
realDirectory := &KnownDirectoryLink{
|
|
Real: "/real/path" + string(rune(id)) + "/",
|
|
RealPath: tspath.ToPath("/real/path"+string(rune(id)), "/test/dir", true).EnsureTrailingDirectorySeparator(),
|
|
}
|
|
|
|
cache.SetDirectory("/test/symlink"+string(rune(id)), symlinkPath, realDirectory)
|
|
|
|
// Read back
|
|
stored, ok := cache.Directories().Load(symlinkPath)
|
|
if !ok {
|
|
t.Errorf("Goroutine %d: Expected directory to be stored", id)
|
|
return
|
|
}
|
|
if stored.Real != realDirectory.Real {
|
|
t.Errorf("Goroutine %d: Expected Real to be '%s', got '%s'", id, realDirectory.Real, stored.Real)
|
|
}
|
|
}(i)
|
|
}
|
|
|
|
// Wait for all goroutines to complete
|
|
for range 10 {
|
|
<-done
|
|
}
|
|
|
|
// Verify all directories were stored
|
|
if cache.Directories().Size() != 10 {
|
|
t.Errorf("Expected 10 directories to be stored, got %d", cache.Directories().Size())
|
|
}
|
|
}
|