github.com/saibing/bingo@v0.0.0-20190331051950-76bcd777316d/langserver/lsp_test.go (about) 1 package langserver 2 3 import ( 4 "context" 5 "encoding/json" 6 "errors" 7 "flag" 8 "fmt" 9 "log" 10 "net" 11 "os" 12 "path/filepath" 13 "strconv" 14 "strings" 15 "testing" 16 17 "golang.org/x/tools/go/packages/packagestest" 18 19 "github.com/saibing/bingo/langserver/internal/cache" 20 "github.com/saibing/bingo/langserver/internal/util" 21 22 "github.com/sourcegraph/go-lsp" 23 "github.com/sourcegraph/jsonrpc2" 24 25 _ "net/http/pprof" 26 ) 27 28 const ( 29 goroot = "goroot" 30 gomodule = "gomodule" 31 rootImportPath = "github.com/saibing/bingo/langserver/test/pkg" 32 ) 33 34 var ( 35 gopathDir = getGOPATH() 36 githubModule = "pkg/mod/github.com/saibing/dep@v1.0.2" 37 gomoduleDir = filepath.Join(gopathDir, githubModule) 38 ) 39 40 func getGOPATH() string { 41 gopath := os.Getenv("GOPATH") 42 if gopath == "" { 43 return filepath.Join(os.Getenv("HOME"), "go") 44 } 45 46 paths := strings.Split(gopath, string(os.PathListSeparator)) 47 return paths[0] 48 } 49 50 func TestMain(m *testing.M) { 51 flag.Parse() 52 code := m.Run() 53 tearDown() 54 os.Exit(code) 55 } 56 57 func tearDown() { 58 completionContext.tearDown() 59 definitionContext.tearDown() 60 symbolContext.tearDown() 61 formatContext.tearDown() 62 hoverContext.tearDown() 63 implementationContext.tearDown() 64 referencesContext.tearDown() 65 renameContext.tearDown() 66 signatureContext.tearDown() 67 typeDefinitionContext.tearDown() 68 workspaceReferencesContext.tearDown() 69 workspaceSymbolContext.tearDown() 70 xDefinitionContext.tearDown() 71 } 72 73 type TestContext struct { 74 h jsonrpc2.Handler 75 conn *jsonrpc2.Conn 76 connServer *jsonrpc2.Conn 77 ctx context.Context 78 exported *packagestest.Exported 79 } 80 81 func newTestContext(style cache.CacheStyle) *TestContext { 82 cfg := NewDefaultConfig() 83 cfg.DisableFuncSnippet = false 84 cfg.GlobalCacheStyle = string(style) 85 86 h := NewHandler(cfg) 87 ctx := context.Background() 88 return &TestContext{ 89 h: h, 90 ctx: ctx, 91 } 92 } 93 94 func (tx *TestContext) setup(t *testing.T) { 95 t.Helper() 96 tx.exported = packagestest.Export(t, packagestest.Modules, testdata) 97 tx.initServer(t) 98 } 99 100 func (tx *TestContext) tearDown() { 101 if tx.exported != nil { 102 fmt.Printf("clean up module project %s\n", tx.root()) 103 tx.exported.Cleanup() 104 } 105 106 if tx.conn != nil { 107 if err := tx.conn.Close(); err != nil { 108 log.Fatal("conn.Close:", err) 109 } 110 } 111 112 if tx.connServer != nil { 113 if err := tx.connServer.Close(); err != nil { 114 log.Fatal("connServer.Close:", err) 115 } 116 } 117 } 118 119 func (tx *TestContext) root() string { 120 return tx.exported.Config.Dir 121 } 122 123 func (tx *TestContext) initServer(t *testing.T) { 124 t.Helper() 125 rootDir := tx.root() 126 os.Chdir(rootDir) 127 rootURI := util.PathToURI(filepath.ToSlash(rootDir)) 128 t.Logf("rootUri:=%q", rootURI) 129 130 // Prepare the connection. 131 client, server := net.Pipe() 132 tx.connServer = jsonrpc2.NewConn(tx.ctx, jsonrpc2.NewBufferedStream(server, jsonrpc2.VSCodeObjectCodec{}), tx.h) 133 tx.conn = jsonrpc2.NewConn(tx.ctx, jsonrpc2.NewBufferedStream(client, jsonrpc2.VSCodeObjectCodec{}), tx.h) 134 135 tdCap := lsp.TextDocumentClientCapabilities{} 136 tdCap.Completion.CompletionItemKind.ValueSet = []lsp.CompletionItemKind{lsp.CIKConstant} 137 params := InitializeParams{ 138 InitializeParams: lsp.InitializeParams{ 139 RootURI: rootURI, 140 Capabilities: lsp.ClientCapabilities{TextDocument: tdCap}, 141 }, 142 143 RootImportPath: rootImportPath, 144 } 145 if err := tx.conn.Call(tx.ctx, "initialize", params, nil); err != nil { 146 t.Fatal("conn.Call initialize:", err) 147 } 148 } 149 150 // tbRun calls (testing.T).Run or (testing.B).Run. 151 func tbRun(t testing.TB, name string, f func(testing.TB)) bool { 152 t.Helper() 153 switch tb := t.(type) { 154 case *testing.B: 155 return tb.Run(name, func(b *testing.B) { 156 b.Helper() 157 f(b) 158 }) 159 case *testing.T: 160 return tb.Run(name, func(t *testing.T) { 161 t.Helper() 162 f(t) 163 }) 164 default: 165 panic(fmt.Sprintf("unexpected %T, want *testing.B or *testing.T", tb)) 166 } 167 } 168 169 func parsePos(s string) (file string, line, char int, err error) { 170 parts := strings.Split(s, ":") 171 if len(parts) != 3 { 172 err = fmt.Errorf("invalid pos %q (%d parts)", s, len(parts)) 173 return 174 } 175 file = parts[0] 176 line, err = strconv.Atoi(parts[1]) 177 if err != nil { 178 err = fmt.Errorf("invalid line in %q: %s", s, err) 179 return 180 } 181 char, err = strconv.Atoi(parts[2]) 182 if err != nil { 183 err = fmt.Errorf("invalid char in %q: %s", s, err) 184 return 185 } 186 return file, line - 1, char - 1, nil // LSP is 0-indexed 187 } 188 189 func uriJoin(base lsp.DocumentURI, file string) lsp.DocumentURI { 190 return lsp.DocumentURI(string(base) + "/" + file) 191 } 192 193 func qualifiedName(s lsp.SymbolInformation) string { 194 if s.ContainerName != "" { 195 return s.ContainerName + "." + s.Name 196 } 197 return s.Name 198 } 199 200 type markedStrings []lsp.MarkedString 201 202 func (v *markedStrings) UnmarshalJSON(data []byte) error { 203 if len(data) == 0 { 204 return errors.New("invalid empty JSON") 205 } 206 if data[0] == '[' { 207 var ms []markedString 208 if err := json.Unmarshal(data, &ms); err != nil { 209 return err 210 } 211 for _, ms := range ms { 212 *v = append(*v, lsp.MarkedString(ms)) 213 } 214 return nil 215 } 216 *v = []lsp.MarkedString{{}} 217 return json.Unmarshal(data, &(*v)[0]) 218 } 219 220 type markedString lsp.MarkedString 221 222 func (v *markedString) UnmarshalJSON(data []byte) error { 223 if len(data) == 0 { 224 return errors.New("invalid empty JSON") 225 } 226 if data[0] == '{' { 227 return json.Unmarshal(data, (*lsp.MarkedString)(v)) 228 } 229 230 // String 231 *v = markedString{} 232 return json.Unmarshal(data, &v.Value) 233 } 234 235 type locations []lsp.Location 236 237 func (v *locations) UnmarshalJSON(data []byte) error { 238 if len(data) == 0 { 239 return errors.New("invalid empty JSON") 240 } 241 if data[0] == '[' { 242 return json.Unmarshal(data, (*[]lsp.Location)(v)) 243 } 244 *v = []lsp.Location{{}} 245 return json.Unmarshal(data, &(*v)[0]) 246 } 247 248 func makePath(elem ...string) string { 249 path := filepath.Join(elem...) 250 return util.LowerDriver(filepath.ToSlash(path)) 251 }