vendor tsgo
This commit is contained in:
127
tools/tsgo/internal/lsp/server_shutdown_test.go
Normal file
127
tools/tsgo/internal/lsp/server_shutdown_test.go
Normal file
@@ -0,0 +1,127 @@
|
||||
package lsp
|
||||
|
||||
import (
|
||||
"context"
|
||||
"io"
|
||||
"testing"
|
||||
|
||||
"github.com/microsoft/typescript-go/internal/bundled"
|
||||
"github.com/microsoft/typescript-go/internal/lsp/lsproto"
|
||||
"github.com/microsoft/typescript-go/internal/project"
|
||||
"github.com/microsoft/typescript-go/internal/vfs/vfstest"
|
||||
)
|
||||
|
||||
type shutdownTestReader struct{}
|
||||
|
||||
func (shutdownTestReader) Read() (*lsproto.Message, error) { return nil, io.EOF }
|
||||
|
||||
type shutdownTestWriter struct{}
|
||||
|
||||
func (shutdownTestWriter) Write(*lsproto.Message) error { return nil }
|
||||
|
||||
// TestServerShutdownNoDeadlock verifies that operations after shutdown
|
||||
// don't block.
|
||||
func TestServerShutdownNoDeadlock(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
if !bundled.Embedded {
|
||||
t.Skip("bundled files are not embedded")
|
||||
}
|
||||
|
||||
fs := bundled.WrapFS(vfstest.FromMap(map[string]string{
|
||||
"/test/tsconfig.json": "{}",
|
||||
"/test/index.ts": "const x = 1;",
|
||||
}, false))
|
||||
|
||||
server := NewServer(&ServerOptions{
|
||||
In: shutdownTestReader{},
|
||||
Out: shutdownTestWriter{},
|
||||
Err: io.Discard,
|
||||
Cwd: "/test",
|
||||
FS: fs,
|
||||
DefaultLibraryPath: bundled.LibPath(),
|
||||
})
|
||||
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
server.backgroundCtx = ctx
|
||||
|
||||
// Start write loop to drain queue
|
||||
writeLoopDone := make(chan struct{})
|
||||
go func() {
|
||||
_ = server.writeLoop(ctx)
|
||||
close(writeLoopDone)
|
||||
}()
|
||||
|
||||
// Create session with the server's lifecycle context
|
||||
server.initStarted.Store(true)
|
||||
server.session = project.NewSession(&project.SessionInit{
|
||||
BackgroundCtx: ctx,
|
||||
Options: &project.SessionOptions{
|
||||
CurrentDirectory: "/test",
|
||||
DefaultLibraryPath: bundled.LibPath(),
|
||||
PositionEncoding: lsproto.PositionEncodingKindUTF8,
|
||||
WatchEnabled: false,
|
||||
LoggingEnabled: true,
|
||||
},
|
||||
FS: fs,
|
||||
Logger: server.logger,
|
||||
})
|
||||
|
||||
// Open a file to establish a project
|
||||
server.session.DidOpenFile(ctx, "file:///test/index.ts", 1, "const x = 1;", lsproto.LanguageKindTypeScript)
|
||||
server.session.WaitForBackgroundTasks()
|
||||
|
||||
// Shutdown (cancel context and wait for write loop to exit)
|
||||
cancel()
|
||||
<-writeLoopDone
|
||||
|
||||
// Trigger operations that would log (these should not block)
|
||||
server.session.DidChangeFile(ctx, "file:///test/index.ts", 2, []lsproto.TextDocumentContentChangePartialOrWholeDocument{
|
||||
{
|
||||
WholeDocument: &lsproto.TextDocumentContentChangeWholeDocument{
|
||||
Text: "const x = 2;",
|
||||
},
|
||||
},
|
||||
})
|
||||
_, _ = server.session.GetLanguageService(ctx, "file:///test/index.ts")
|
||||
server.session.WaitForBackgroundTasks()
|
||||
|
||||
server.session.Close()
|
||||
}
|
||||
|
||||
func TestServerOutgoingQueueDoesNotBlockWithoutWriter(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
server := NewServer(&ServerOptions{
|
||||
In: shutdownTestReader{},
|
||||
Out: shutdownTestWriter{},
|
||||
Err: io.Discard,
|
||||
Cwd: "/test",
|
||||
})
|
||||
server.backgroundCtx = t.Context()
|
||||
|
||||
msg := lsproto.WindowLogMessageInfo.NewNotificationMessage(&lsproto.LogMessageParams{
|
||||
Type: lsproto.MessageTypeInfo,
|
||||
Message: "queued",
|
||||
}).Message()
|
||||
|
||||
done := make(chan error, 1)
|
||||
go func() {
|
||||
for range 1000 {
|
||||
if err := server.send(msg); err != nil {
|
||||
done <- err
|
||||
return
|
||||
}
|
||||
}
|
||||
done <- nil
|
||||
}()
|
||||
|
||||
select {
|
||||
case err := <-done:
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
case <-t.Context().Done():
|
||||
t.Fatal("sending outgoing messages blocked without a writer")
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user