package app import ( "os" "path/filepath" "testing" "rickub.com/turbo-editors/turbo-core/lsp" ) func TestDiagnosticsAreFoundForAFileOpenedByARelativePath(t *testing.T) { // Found by driving the real editor against a real gopls: the Problems // window listed two errors and the gutter showed none, because the two // sides spell the file differently. A server publishes absolute URIs; a // buffer opened as "turbo-go main.go" holds the relative path it was // given. The status bar had been failing the same way since long before // the marks existed, silently, because an editor with no error to show and // an editor that cannot find the error look identical. project := t.TempDir() t.Chdir(project) t.Setenv(testProfile().SnippetDirEnvVar(), t.TempDir()) a, _ := newTestApp(t) writeTestFile(t, filepath.Join(project, "main.go"), "package main\n\nfunc main() { x }\n") a.Open("main.go") // relative, as the command line gives it a.language.receiveDiagnostics(filepath.Join(project, "main.go"), []lsp.Diagnostic{ {Message: "undefined: x", Severity: lsp.SeverityError, Range: lsp.Range{Start: lsp.Position{Line: 2}}}, }) a.Tick() if _, found := a.language.FirstError(a.ActiveView().Buffer().Path()); !found { t.Errorf("no error found for %q, though one was published for the same file", a.ActiveView().Buffer().Path()) } if got := a.ActiveView().Marks()[2]; got == 0 { t.Errorf("line 3 is unmarked, though an error was published for it") } } func TestDiagnosticsAreFoundWhicheverWaySpelt(t *testing.T) { // Both directions, since either side may be the relative one. project := t.TempDir() t.Chdir(project) a, _ := newTestApp(t) a.language.receiveDiagnostics("main.go", []lsp.Diagnostic{{Message: "relative in"}}) if got := a.language.Diagnostics(filepath.Join(project, "main.go")); len(got) != 1 { t.Errorf("published relative, read absolute: got %d diagnostics", len(got)) } a.language.receiveDiagnostics(filepath.Join(project, "other.go"), []lsp.Diagnostic{{Message: "absolute in"}}) if got := a.language.Diagnostics("other.go"); len(got) != 1 { t.Errorf("published absolute, read relative: got %d diagnostics", len(got)) } } func TestKnowsDoesNotDependOnTheSpellingOfThePath(t *testing.T) { // The map of open documents is keyed the same way as the diagnostics: // absolute. Keyed by whatever string DidOpen was given, a file opened as // "main.go" would not be known as "/project/main.go" — and saving, which // asks Knows to decide between announcing a document and reporting a // write, would announce the same document twice. project := t.TempDir() t.Chdir(project) a, _ := newTestApp(t) client, _ := newFakeLanguage(t, a) connectLanguage(a, client) a.language.DidOpen("main.go", "package main\n") absolute := filepath.Join(project, "main.go") if !a.language.Knows(absolute) { t.Errorf("opened as %q, not known as %q", "main.go", absolute) } if !a.language.Knows("main.go") { t.Errorf("opened as %q, not known under that very spelling", "main.go") } a.language.DidClose(absolute) if a.language.Knows("main.go") { t.Errorf("closed as %q, still known as %q", absolute, "main.go") } } func TestDiagnosticsPublishedUnderTheRealPathReachAFileOpenedThroughALink(t *testing.T) { // Found on macOS, the first time Turbo MoonBit's suite ran there: every // temporary directory is /var/folders/…, a link to /private/var/…, and // moon-lsp canonicalises paths. It published the file's problems under // /private/var and answered no completion about a buffer announced as // /var — to it, a file that belonged to no package. Absolute was not // enough; the key has to be canonical, and so has the URI the server is // sent, or the two sides spell the same file differently again. real := t.TempDir() link := filepath.Join(t.TempDir(), "link") if err := os.Symlink(real, link); err != nil { t.Skipf("cannot create a symbolic link here: %v", err) } a, _ := newTestApp(t) client, fake := newFakeLanguage(t, a) connectLanguage(a, client) throughLink := filepath.Join(link, "main.mbt") a.language.DidOpen(throughLink, "fn main {}\n") resolvedReal, err := filepath.EvalSymlinks(real) if err != nil { t.Fatal(err) } if !a.language.Knows(filepath.Join(resolvedReal, "main.mbt")) { t.Errorf("opened through the link, not known under the real path") } if got, want := fake.lastOpenedURI(), lsp.PathToURI(filepath.Join(resolvedReal, "main.mbt")); got != want { t.Errorf("the server was told %q, want the real path's %q", got, want) } a.language.receiveDiagnostics(filepath.Join(resolvedReal, "main.mbt"), []lsp.Diagnostic{ {Message: "published under the real path", Severity: lsp.SeverityError}, }) if got := a.language.Diagnostics(throughLink); len(got) != 1 { t.Errorf("published under the real path, read through the link: got %d diagnostics", len(got)) } } func TestWindowForFindsAFileOpenedThroughALink(t *testing.T) { // A location the server sends back names the file as the server spells // it. Compared as spelt, a jump to a definition in a file already on // screen opened it a second time, and the references list quoted the disk // instead of the unsaved window — both seen on macOS, where every // temporary directory is a link. real := t.TempDir() link := filepath.Join(t.TempDir(), "link") if err := os.Symlink(real, link); err != nil { t.Skipf("cannot create a symbolic link here: %v", err) } t.Setenv(testProfile().SnippetDirEnvVar(), t.TempDir()) a, _ := newTestApp(t) writeTestFile(t, filepath.Join(real, "main.go"), "package main\n") a.Open(filepath.Join(link, "main.go")) resolvedReal, err := filepath.EvalSymlinks(real) if err != nil { t.Fatal(err) } if a.windowFor(filepath.Join(resolvedReal, "main.go")) == nil { t.Errorf("opened through the link, the window is not found under the real path") } if a.windowFor(filepath.Join(link, "main.go")) == nil { t.Errorf("opened through the link, the window is not found under the link either") } }