| 🛟 Updated. 28d5985 k33g 20h ago | 1 | package app |
| 2 | |
| 3 | import ( |
| 📦 Turbo Core — canonical paths: symbolic links resolved in URIs and document keys (moon-lsp on macOS) b91316e k33g 11h ago | 4 | "os" |
| 🛟 Updated. 28d5985 k33g 20h ago | 5 | "path/filepath" |
| 6 | "testing" |
| 7 | |
| 📦 Turbo Core f3ade8d k33g 13h ago | 8 | "rickub.com/turbo-editors/turbo-core/lsp" |
| 🛟 Updated. 28d5985 k33g 20h ago | 9 | ) |
| 10 | |
| 11 | func TestDiagnosticsAreFoundForAFileOpenedByARelativePath(t *testing.T) { |
| 12 | // Found by driving the real editor against a real gopls: the Problems |
| 13 | // window listed two errors and the gutter showed none, because the two |
| 14 | // sides spell the file differently. A server publishes absolute URIs; a |
| 15 | // buffer opened as "turbo-go main.go" holds the relative path it was |
| 16 | // given. The status bar had been failing the same way since long before |
| 17 | // the marks existed, silently, because an editor with no error to show and |
| 18 | // an editor that cannot find the error look identical. |
| 19 | project := t.TempDir() |
| 20 | t.Chdir(project) |
| 21 | t.Setenv(testProfile().SnippetDirEnvVar(), t.TempDir()) |
| 22 | a, _ := newTestApp(t) |
| 23 | |
| 24 | writeTestFile(t, filepath.Join(project, "main.go"), "package main\n\nfunc main() { x }\n") |
| 25 | a.Open("main.go") // relative, as the command line gives it |
| 26 | |
| 27 | a.language.receiveDiagnostics(filepath.Join(project, "main.go"), []lsp.Diagnostic{ |
| 28 | {Message: "undefined: x", Severity: lsp.SeverityError, Range: lsp.Range{Start: lsp.Position{Line: 2}}}, |
| 29 | }) |
| 30 | a.Tick() |
| 31 | |
| 32 | if _, found := a.language.FirstError(a.ActiveView().Buffer().Path()); !found { |
| 33 | t.Errorf("no error found for %q, though one was published for the same file", |
| 34 | a.ActiveView().Buffer().Path()) |
| 35 | } |
| 36 | if got := a.ActiveView().Marks()[2]; got == 0 { |
| 37 | t.Errorf("line 3 is unmarked, though an error was published for it") |
| 38 | } |
| 39 | } |
| 40 | |
| 41 | func TestDiagnosticsAreFoundWhicheverWaySpelt(t *testing.T) { |
| 42 | // Both directions, since either side may be the relative one. |
| 43 | project := t.TempDir() |
| 44 | t.Chdir(project) |
| 45 | a, _ := newTestApp(t) |
| 46 | |
| 47 | a.language.receiveDiagnostics("main.go", []lsp.Diagnostic{{Message: "relative in"}}) |
| 48 | if got := a.language.Diagnostics(filepath.Join(project, "main.go")); len(got) != 1 { |
| 49 | t.Errorf("published relative, read absolute: got %d diagnostics", len(got)) |
| 50 | } |
| 51 | |
| 52 | a.language.receiveDiagnostics(filepath.Join(project, "other.go"), []lsp.Diagnostic{{Message: "absolute in"}}) |
| 53 | if got := a.language.Diagnostics("other.go"); len(got) != 1 { |
| 54 | t.Errorf("published absolute, read relative: got %d diagnostics", len(got)) |
| 55 | } |
| 56 | } |
| 57 | |
| 58 | func TestKnowsDoesNotDependOnTheSpellingOfThePath(t *testing.T) { |
| 59 | // The map of open documents is keyed the same way as the diagnostics: |
| 60 | // absolute. Keyed by whatever string DidOpen was given, a file opened as |
| 61 | // "main.go" would not be known as "/project/main.go" — and saving, which |
| 62 | // asks Knows to decide between announcing a document and reporting a |
| 63 | // write, would announce the same document twice. |
| 64 | project := t.TempDir() |
| 65 | t.Chdir(project) |
| 66 | a, _ := newTestApp(t) |
| 67 | client, _ := newFakeLanguage(t, a) |
| 68 | connectLanguage(a, client) |
| 69 | |
| 70 | a.language.DidOpen("main.go", "package main\n") |
| 71 | |
| 72 | absolute := filepath.Join(project, "main.go") |
| 73 | if !a.language.Knows(absolute) { |
| 74 | t.Errorf("opened as %q, not known as %q", "main.go", absolute) |
| 75 | } |
| 76 | if !a.language.Knows("main.go") { |
| 77 | t.Errorf("opened as %q, not known under that very spelling", "main.go") |
| 78 | } |
| 79 | |
| 80 | a.language.DidClose(absolute) |
| 81 | if a.language.Knows("main.go") { |
| 82 | t.Errorf("closed as %q, still known as %q", absolute, "main.go") |
| 83 | } |
| 84 | } |
| 📦 Turbo Core — canonical paths: symbolic links resolved in URIs and document keys (moon-lsp on macOS) b91316e k33g 11h ago | 85 | |
| 86 | func TestDiagnosticsPublishedUnderTheRealPathReachAFileOpenedThroughALink(t *testing.T) { |
| 87 | // Found on macOS, the first time Turbo MoonBit's suite ran there: every |
| 88 | // temporary directory is /var/folders/…, a link to /private/var/…, and |
| 89 | // moon-lsp canonicalises paths. It published the file's problems under |
| 90 | // /private/var and answered no completion about a buffer announced as |
| 91 | // /var — to it, a file that belonged to no package. Absolute was not |
| 92 | // enough; the key has to be canonical, and so has the URI the server is |
| 93 | // sent, or the two sides spell the same file differently again. |
| 94 | real := t.TempDir() |
| 95 | link := filepath.Join(t.TempDir(), "link") |
| 96 | if err := os.Symlink(real, link); err != nil { |
| 97 | t.Skipf("cannot create a symbolic link here: %v", err) |
| 98 | } |
| 99 | a, _ := newTestApp(t) |
| 100 | client, fake := newFakeLanguage(t, a) |
| 101 | connectLanguage(a, client) |
| 102 | |
| 103 | throughLink := filepath.Join(link, "main.mbt") |
| 104 | a.language.DidOpen(throughLink, "fn main {}\n") |
| 105 | |
| 106 | resolvedReal, err := filepath.EvalSymlinks(real) |
| 107 | if err != nil { |
| 108 | t.Fatal(err) |
| 109 | } |
| 110 | if !a.language.Knows(filepath.Join(resolvedReal, "main.mbt")) { |
| 111 | t.Errorf("opened through the link, not known under the real path") |
| 112 | } |
| 113 | if got, want := fake.lastOpenedURI(), lsp.PathToURI(filepath.Join(resolvedReal, "main.mbt")); got != want { |
| 114 | t.Errorf("the server was told %q, want the real path's %q", got, want) |
| 115 | } |
| 116 | |
| 117 | a.language.receiveDiagnostics(filepath.Join(resolvedReal, "main.mbt"), []lsp.Diagnostic{ |
| 118 | {Message: "published under the real path", Severity: lsp.SeverityError}, |
| 119 | }) |
| 120 | if got := a.language.Diagnostics(throughLink); len(got) != 1 { |
| 121 | t.Errorf("published under the real path, read through the link: got %d diagnostics", len(got)) |
| 122 | } |
| 123 | } |
| 124 | |
| 125 | func TestWindowForFindsAFileOpenedThroughALink(t *testing.T) { |
| 126 | // A location the server sends back names the file as the server spells |
| 127 | // it. Compared as spelt, a jump to a definition in a file already on |
| 128 | // screen opened it a second time, and the references list quoted the disk |
| 129 | // instead of the unsaved window — both seen on macOS, where every |
| 130 | // temporary directory is a link. |
| 131 | real := t.TempDir() |
| 132 | link := filepath.Join(t.TempDir(), "link") |
| 133 | if err := os.Symlink(real, link); err != nil { |
| 134 | t.Skipf("cannot create a symbolic link here: %v", err) |
| 135 | } |
| 136 | t.Setenv(testProfile().SnippetDirEnvVar(), t.TempDir()) |
| 137 | a, _ := newTestApp(t) |
| 138 | writeTestFile(t, filepath.Join(real, "main.go"), "package main\n") |
| 139 | |
| 140 | a.Open(filepath.Join(link, "main.go")) |
| 141 | |
| 142 | resolvedReal, err := filepath.EvalSymlinks(real) |
| 143 | if err != nil { |
| 144 | t.Fatal(err) |
| 145 | } |
| 146 | if a.windowFor(filepath.Join(resolvedReal, "main.go")) == nil { |
| 147 | t.Errorf("opened through the link, the window is not found under the real path") |
| 148 | } |
| 149 | if a.windowFor(filepath.Join(link, "main.go")) == nil { |
| 150 | t.Errorf("opened through the link, the window is not found under the link either") |
| 151 | } |
| 152 | } |