turbo-editors/turbo-corepublic Fork 0
v1.0.2
Commits
Clone
git clone https://git.rickub.com/turbo-editors/turbo-core.git
git clone ssh://git@rickub.com/turbo-editors/turbo-core.git

Host key fingerprint (ed25519): SHA256:iycHnxEyq0Q7uyVpB7JlznP0G7JrTPXLYRcAU5CSLhc — verify it before your first connect.

save_test.go · 196 lines · 6.7 KBGo Blame HistoryRaw
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
package app

import (
	"path/filepath"
	"testing"
	"time"

	"rickub.com/turbo-editors/turbo-core/lsp"
)

// These tests cover what saving says to the language server. The defect they
// were written against: a window that started Untitled gains its path through
// Save As, afterSave sent only didSave, and a server ignores didSave for a
// document it was never told is open — so the window had no completion until
// the editor was restarted.

func TestSavingAnUntitledWindowAnnouncesItsDocument(t *testing.T) {
	project := t.TempDir()
	t.Chdir(project)
	a, _ := newTestApp(t)
	client, server := newFakeLanguage(t, a)
	connectLanguage(a, client)
	a.Tick() // the start-up announcement runs, and finds nothing to tell

	a.NewFile()
	typeText(a, "package main")
	path := filepath.Join(project, "main.go")
	a.save(a.ActiveView(), path)

	waitForMethod(t, server, "textDocument/didOpen")
	if got := server.methodCount("textDocument/didSave"); got != 0 {
		t.Errorf("didSave was sent %d times for a document the server was never told is open", got)
	}
	if !a.language.Knows(path) {
		t.Errorf("after the save, the server still does not know %q", path)
	}
}

func TestSaveAsUnderANewNameClosesTheOldDocument(t *testing.T) {
	project := t.TempDir()
	t.Chdir(project)
	a, _ := newTestApp(t)
	client, server := newFakeLanguage(t, a)
	connectLanguage(a, client)

	writeTestFile(t, filepath.Join(project, "old.go"), "package main\n")
	a.Open("old.go")
	waitForMethod(t, server, "textDocument/didOpen")

	newPath := filepath.Join(project, "new.go")
	a.save(a.ActiveView(), newPath)

	waitForMethod(t, server, "textDocument/didClose")
	if a.language.Knows("old.go") {
		t.Errorf("the old document is still open on the server after Save As gave the buffer a new name")
	}
	if !a.language.Knows(newPath) {
		t.Errorf("the new document was never announced to the server")
	}
}

func TestSaveAsToTheSamePathClosesNothing(t *testing.T) {
	// Save As offered the file's own name and the user accepted it: that is a
	// write, not a rename, however the dialog spelt the path.
	project := t.TempDir()
	t.Chdir(project)
	a, _ := newTestApp(t)
	client, server := newFakeLanguage(t, a)
	connectLanguage(a, client)

	writeTestFile(t, filepath.Join(project, "main.go"), "package main\n")
	a.Open("main.go") // relative, as the command line gives it
	waitForMethod(t, server, "textDocument/didOpen")

	a.save(a.ActiveView(), filepath.Join(project, "main.go")) // absolute, as the dialog gives it

	waitForMethod(t, server, "textDocument/didSave")
	if got := server.methodCount("textDocument/didClose"); got != 0 {
		t.Errorf("didClose was sent %d times for a save that renamed nothing", got)
	}
}

func TestSavingAKnownDocumentReportsTheWrite(t *testing.T) {
	project := t.TempDir()
	t.Chdir(project)
	a, _ := newTestApp(t)
	client, server := newFakeLanguage(t, a)
	connectLanguage(a, client)

	writeTestFile(t, filepath.Join(project, "main.go"), "package main\n")
	a.Open("main.go")
	waitForMethod(t, server, "textDocument/didOpen")

	a.save(a.ActiveView(), a.ActiveView().Buffer().Path())

	waitForMethod(t, server, "textDocument/didSave")
	if got := server.methodCount("textDocument/didOpen"); got != 1 {
		t.Errorf("didOpen was sent %d times; saving an open document must not re-announce it", got)
	}
}

func TestSavingANewFileTellsTheServerTheFileExists(t *testing.T) {
	// To moon-lsp a document being open and a file existing are two facts,
	// and it works a package's files out from the directory: a .mbt saved for
	// the first time got no diagnostics, however loudly the document had been
	// announced, until the server was told the file is there. Found on macOS
	// the first time Turbo MoonBit's suite ran there; confirmed at the
	// protocol level — the same file is diagnosed the moment
	// workspace/didChangeWatchedFiles names it.
	project := t.TempDir()
	t.Chdir(project)
	a, _ := newTestApp(t)
	client, server := newFakeLanguage(t, a)
	connectLanguage(a, client)
	a.Tick()

	a.NewFile()
	typeText(a, "package main")
	path := filepath.Join(project, "main.go")
	a.save(a.ActiveView(), path)

	waitForMethod(t, server, "workspace/didChangeWatchedFiles")
	events := server.lastFileEvents()
	if len(events) != 1 || events[0].Type != lsp.FileCreated {
		t.Fatalf("the server was told %+v, want one created file", events)
	}
	if want := lsp.PathToURI(path); events[0].URI != want {
		t.Errorf("the created file is %q, want %q", events[0].URI, want)
	}
	// The document first, then the file: the order the fix was verified in.
	methods := server.methodsSeen()
	if open, created := firstIndexOf(methods, "textDocument/didOpen"), firstIndexOf(methods, "workspace/didChangeWatchedFiles"); open < 0 || created < open {
		t.Errorf("the file was reported before the document was announced: %v", methods)
	}
}

func TestSavingAnExistingFileDoesNotClaimItWasCreated(t *testing.T) {
	// A file that was already on disk is not news to the server, and a save
	// that claimed otherwise would have moon-lsp rescan the package on every
	// Ctrl-S.
	project := t.TempDir()
	t.Chdir(project)
	t.Setenv(testProfile().SnippetDirEnvVar(), t.TempDir())
	a, _ := newTestApp(t)
	client, server := newFakeLanguage(t, a)
	connectLanguage(a, client)
	path := filepath.Join(project, "main.go")
	writeTestFile(t, path, "package main\n")
	a.Open(path)
	a.Tick()

	a.save(a.ActiveView(), path)

	waitForMethod(t, server, "textDocument/didSave")
	if got := server.methodCount("workspace/didChangeWatchedFiles"); got != 0 {
		t.Errorf("saving a file that already existed reported it created %d time(s)", got)
	}
}

func TestAutosaveOfANewlyNamedFileTellsTheServerTheFileExists(t *testing.T) {
	// The other way a file gets written. `turbo-moonbit new.mbt` opens a
	// buffer for a file that is not on disk yet; the first automatic save
	// creates it, and the server has to hear that from this path too.
	project := t.TempDir()
	t.Chdir(project)
	t.Setenv(testProfile().SnippetDirEnvVar(), t.TempDir())
	a, _ := newTestApp(t)
	client, server := newFakeLanguage(t, a)
	connectLanguage(a, client)
	path := filepath.Join(project, "new.go")
	a.Open(path) // not on disk: an empty buffer with that name
	a.Tick()
	a.SetAutosave(true, time.Millisecond)

	typeText(a, "package main")
	deadline := time.Now().Add(2 * time.Second)
	for server.methodCount("workspace/didChangeWatchedFiles") == 0 && time.Now().Before(deadline) {
		time.Sleep(2 * time.Millisecond)
		a.Tick()
	}

	events := server.lastFileEvents()
	if len(events) != 1 || events[0].Type != lsp.FileCreated || events[0].URI != lsp.PathToURI(path) {
		t.Errorf("autosave reported %+v, want %s created", events, path)
	}
}

// firstIndexOf returns where a string first appears in a list, or -1.
func firstIndexOf(list []string, want string) int {
	for i, s := range list {
		if s == want {
			return i
		}
	}
	return -1
}