github.com/tiagovtristao/plz@v13.4.0+incompatible/tools/build_langserver/langserver/analyzer_test.go (about) 1 package langserver 2 3 import ( 4 "context" 5 6 "path" 7 "path/filepath" 8 "testing" 9 10 "github.com/thought-machine/please/src/core" 11 "github.com/thought-machine/please/src/parse/asp" 12 "github.com/thought-machine/please/src/parse/rules" 13 "github.com/thought-machine/please/tools/build_langserver/lsp" 14 15 "github.com/stretchr/testify/assert" 16 "strings" 17 ) 18 19 func TestNewAnalyzer(t *testing.T) { 20 a, err := newAnalyzer() 21 assert.Equal(t, err, nil) 22 23 assert.NotEqual(t, nil, a.BuiltIns) 24 25 goLibrary := a.BuiltIns["go_library"] 26 assert.Equal(t, 15, len(goLibrary.ArgMap)) 27 assert.Equal(t, true, goLibrary.ArgMap["name"].Required) 28 29 // check preloadBuildDefs has being loaded 30 goBinData := a.BuiltIns["go_bindata"] 31 assert.Equal(t, 10, len(goBinData.ArgMap)) 32 assert.Equal(t, true, goBinData.ArgMap["name"].Required) 33 assert.Equal(t, "input_dir=None", goBinData.ArgMap["input_dir"].Repr) 34 35 // Ensure private funcDefs are not loaded 36 for name := range a.BuiltIns { 37 assert.False(t, strings.HasPrefix(name, "_")) 38 } 39 40 // Check for methods map 41 _, ok := a.Attributes["str"] 42 assert.True(t, ok) 43 } 44 45 func TestAspStatementFromFile(t *testing.T) { 46 a, err := newAnalyzer() 47 assert.Equal(t, err, nil) 48 49 filePath := "tools/build_langserver/langserver/test_data/example.build" 50 a.State.Config.Parse.BuildFileName = append(a.State.Config.Parse.BuildFileName, "example.build") 51 uri := lsp.DocumentURI("file://" + filePath) 52 53 stmts, err := a.AspStatementFromFile(uri) 54 assert.Equal(t, err, nil) 55 assert.Equal(t, stmts[0].Ident.Name, "go_library") 56 57 assert.Equal(t, stmts[1].Ident.Name, "go_test") 58 } 59 60 func TestNewRuleDef(t *testing.T) { 61 a, err := newAnalyzer() 62 assert.Equal(t, err, nil) 63 64 // Test header the definition for build_rule 65 expected := "def go_library(name:str, srcs:list, asm_srcs:list=None, hdrs:list=None, out:str=None, deps:list=[],\n" + 66 " visibility:list=None, test_only:bool&testonly=False, complete:bool=True, cover:bool=True,\n" + 67 " filter_srcs:bool=True)" 68 69 ruleContent := rules.MustAsset("go_rules.build_defs") 70 71 statements, err := a.parser.ParseData(ruleContent, "go_rules.build_defs") 72 assert.Equal(t, err, nil) 73 74 stmt := getStatementByName(statements, "go_library") 75 ruleDef := newRuleDef(string(ruleContent), stmt) 76 77 assert.Equal(t, ruleDef.Header, expected) 78 assert.Equal(t, len(ruleDef.Arguments), len(ruleDef.ArgMap)) 79 assert.Equal(t, false, ruleDef.ArgMap["_link_private"].Required) 80 assert.Equal(t, true, ruleDef.ArgMap["name"].Required) 81 assert.Equal(t, ruleDef.ArgMap["visibility"].Definition, 82 "visibility required:false, type:list") 83 assert.Equal(t, ruleDef.ArgMap["visibility"].Argument.Name, "visibility") 84 assert.Equal(t, ruleDef.ArgMap["name"].Argument.Name, "name") 85 assert.Equal(t, ruleDef.ArgMap["_link_private"].Argument.Name, "_link_private") 86 87 // Test header for len() 88 ruleContent = rules.MustAsset("builtins.build_defs") 89 90 statements, err = a.parser.ParseData(ruleContent, "builtins.build_defs") 91 assert.Equal(t, err, nil) 92 93 stmt = getStatementByName(statements, "len") 94 ruleDef = newRuleDef(string(ruleContent), stmt) 95 96 assert.Equal(t, ruleDef.Header, "def len(obj)") 97 assert.Equal(t, len(ruleDef.ArgMap), 1) 98 assert.Equal(t, true, ruleDef.ArgMap["obj"].Required) 99 assert.Equal(t, ruleDef.ArgMap["obj"].Definition, "obj required:true") 100 assert.Equal(t, "obj", ruleDef.ArgMap["obj"].Repr) 101 assert.Equal(t, ruleDef.ArgMap["obj"].Argument.Name, "obj") 102 103 // Test header for a string function, startswith() 104 stmt = getStatementByName(statements, "startswith") 105 ruleDef = newRuleDef(string(ruleContent), stmt) 106 107 assert.Equal(t, ruleDef.Header, "str.startswith(s:str)") 108 assert.Equal(t, len(ruleDef.ArgMap), 1) 109 assert.Equal(t, true, ruleDef.ArgMap["s"].Required) 110 assert.Equal(t, "s:str", ruleDef.ArgMap["s"].Repr) 111 112 // Test header for a string function, format() 113 stmt = getStatementByName(statements, "format") 114 ruleDef = newRuleDef(string(ruleContent), stmt) 115 116 assert.Equal(t, ruleDef.Header, "str.format()") 117 assert.Equal(t, len(ruleDef.ArgMap), 0) 118 assert.Equal(t, ruleDef.Object, "str") 119 120 // Test header for a config function, setdefault() 121 stmt = getStatementByName(statements, "setdefault") 122 ruleDef = newRuleDef(string(ruleContent), stmt) 123 124 assert.Equal(t, ruleDef.Header, "config.setdefault(key:str, default=None)") 125 assert.Equal(t, 2, len(ruleDef.ArgMap)) 126 assert.Equal(t, false, ruleDef.ArgMap["default"].Required) 127 } 128 129 func TestGetArgString(t *testing.T) { 130 argWithVal := asp.Argument{ 131 Name: "mystring", 132 Type: []string{"string", "list"}, 133 Value: &asp.Expression{ 134 Optimised: &asp.OptimisedExpression{ 135 Local: "None", 136 }, 137 }, 138 } 139 assert.Equal(t, getArgString(argWithVal), "mystring required:false, type:string|list") 140 141 argWithoutVal := asp.Argument{ 142 Name: "name", 143 Type: []string{"string"}, 144 } 145 assert.Equal(t, getArgString(argWithoutVal), "name required:true, type:string") 146 } 147 148 func TestBuildLabelFromString(t *testing.T) { 149 a, err := newAnalyzer() 150 assert.Equal(t, err, nil) 151 152 ctx := context.Background() 153 filePath := "tools/build_langserver/langserver/test_data/example.build" 154 uri := lsp.DocumentURI("file://" + filePath) 155 156 a.State.Config.Parse.BuildFileName = append(a.State.Config.Parse.BuildFileName, "example.build") 157 158 // Test case for regular and complete BuildLabel path 159 label, err := a.BuildLabelFromString(ctx, uri, "//third_party/go:jsonrpc2") 160 expectedContent := "go_get(\n" + 161 " name = \"jsonrpc2\",\n" + 162 " get = \"github.com/sourcegraph/jsonrpc2\",\n" + 163 " revision = \"549eb959f029d014d623104d40ab966d159a92de\",\n" + 164 ")" 165 assert.Equal(t, err, nil) 166 assert.Equal(t, path.Join(core.RepoRoot, "third_party/go/BUILD"), label.Path) 167 assert.Equal(t, "jsonrpc2", label.Name) 168 assert.Equal(t, expectedContent, label.BuildDef.Content) 169 assert.Equal(t, `BUILD Label: //third_party/go:jsonrpc2`, label.Definition) 170 171 // Test case for relative BuildLabel path 172 label, err = a.BuildLabelFromString(ctx, uri, ":langserver") 173 expectedContent = "go_library(\n" + 174 " name = \"langserver\",\n" + 175 " srcs = glob(\n" + 176 " [\"*.go\"],\n" + 177 " exclude = [\"*_test.go\"],\n" + 178 " ),\n" + 179 " visibility = [\"//tools/build_langserver/...\", \"//src/core\"],\n" + 180 " deps = [\n" + 181 " \"//src/core\",\n" + 182 " \"//src/fs\",\n" + 183 " \"//src/parse\",\n" + 184 " \"//src/parse/asp\",\n" + 185 " \"//src/parse/rules\",\n" + 186 " \"//third_party/go:jsonrpc2\",\n" + 187 " \"//third_party/go:logging\",\n" + 188 " \"//tools/build_langserver/lsp\",\n" + 189 " ],\n" + 190 ")" 191 absPath, err := filepath.Abs(filePath) 192 assert.Equal(t, err, nil) 193 assert.Equal(t, absPath, label.Path) 194 assert.Equal(t, "langserver", label.Name) 195 assert.Equal(t, expectedContent, label.BuildDef.Content) 196 197 // Test case for Allsubpackage BuildLabels: "//src/parse/..." 198 label, err = a.BuildLabelFromString(ctx, 199 uri, "//src/parse/...") 200 assert.Equal(t, err, nil) 201 assert.True(t, nil == label.BuildDef) 202 assert.Equal(t, "BuildLabel includes all subpackages in path: "+path.Join(core.RepoRoot, "src/parse"), 203 label.Definition) 204 205 // Test case for All targets in a BUILD file: "//src/parse:all" 206 label, err = a.BuildLabelFromString(ctx, 207 uri, "//src/parse:all") 208 assert.Equal(t, err, nil) 209 assert.True(t, nil == label.BuildDef) 210 assert.Equal(t, "BuildLabel includes all BuildTargets in BUILD file: "+path.Join(core.RepoRoot, "src/parse/BUILD"), 211 label.Definition) 212 213 // Test case for shortended BuildLabel 214 label, err = a.BuildLabelFromString(ctx, uri, "//src/core") 215 assert.Equal(t, err, nil) 216 217 label2, err := a.BuildLabelFromString(ctx, uri, "//src/core:core") 218 assert.Equal(t, err, nil) 219 220 assert.Equal(t, label.Definition, label2.Definition) 221 222 // Test case for subrepo 223 label, err = a.BuildLabelFromString(ctx, uri, "@mysubrepo//spam/eggs:ham") 224 assert.Equal(t, err, nil) 225 assert.True(t, nil == label.BuildDef) 226 assert.Equal(t, "Subrepo label: @mysubrepo//spam/eggs:ham", label.Definition) 227 } 228 229 func TestAnalyzer_BuildLabelFromStringBogusLabel(t *testing.T) { 230 a, err := newAnalyzer() 231 assert.Equal(t, err, nil) 232 233 ctx := context.Background() 234 235 // Ensure we get an error when we pass in a bogus label 236 label, err := a.BuildLabelFromString(ctx, exampleBuildURI, "//blah/foo") 237 assert.NotEqual(t, err, nil) 238 assert.True(t, nil == label) 239 240 label, err = a.BuildLabelFromString(ctx, exampleBuildURI, "//src/core:blah") 241 assert.NotEqual(t, err, nil) 242 assert.True(t, nil == label) 243 } 244 245 func TestAnalyzer_BuildDefFromUri(t *testing.T) { 246 ctx := context.Background() 247 248 buildDefs, err := analyzer.BuildDefsFromURI(ctx, exampleBuildURI) 249 assert.Equal(t, err, nil) 250 assert.Equal(t, 6, len(buildDefs)) 251 assert.True(t, StringInSlice(buildDefs["langserver"].Visibility, "//tools/build_langserver/...")) 252 assert.True(t, StringInSlice(buildDefs["langserver"].Visibility, "//src/core")) 253 t.Log(buildDefs["langserver_test"].Visibility) 254 255 exampleBuildURI2 := lsp.DocumentURI("file://tools/build_langserver/langserver/test_data/example2.build") 256 buildDefs, err = analyzer.BuildDefsFromURI(ctx, exampleBuildURI2) 257 assert.Equal(t, 2, len(buildDefs)) 258 assert.True(t, StringInSlice(buildDefs["langserver_test"].Visibility, "PUBLIC")) 259 } 260 261 func TestAnalyzer_IsBuildFile(t *testing.T) { 262 a, err := newAnalyzer() 263 assert.Equal(t, err, nil) 264 265 uri := lsp.DocumentURI("file://tools/build_langserver/langserver/test_data/example.build") 266 267 assert.False(t, a.IsBuildFile(uri)) 268 269 a.State.Config.Parse.BuildFileName = append(a.State.Config.Parse.BuildFileName, "example.build") 270 assert.True(t, a.IsBuildFile(uri)) 271 } 272 273 func TestAnalyzer_VariableFromContentGLOBAL(t *testing.T) { 274 a, err := newAnalyzer() 275 assert.Equal(t, err, nil) 276 pos := &lsp.Position{Line: 5, Character: 0} 277 278 // Tests for string variables 279 vars := a.VariablesFromContent(`my_str = "my str"`+" \n"+ 280 `another_str = ""`+"\n "+`more_empty = ''`, pos) 281 assert.Equal(t, "my_str", vars["my_str"].Name) 282 assert.Equal(t, "another_str", vars["another_str"].Name) 283 assert.Equal(t, "more_empty", vars["more_empty"].Name) 284 for _, v := range vars { 285 assert.Equal(t, "str", v.Type) 286 } 287 288 vars = a.VariablesFromContent(`fstr = f"blah"`, pos) 289 assert.Equal(t, "str", vars["fstr"].Type) 290 291 // Tests for int variables 292 vars = a.VariablesFromContent(`my_int = 34`, pos) 293 assert.Equal(t, "my_int", vars["my_int"].Name) 294 assert.Equal(t, "int", vars["my_int"].Type) 295 296 // Tests for list variables 297 vars = a.VariablesFromContent(`my_list = []`, pos) 298 assert.Equal(t, "my_list", vars["my_list"].Name) 299 assert.Equal(t, "list", vars["my_list"].Type) 300 301 vars = a.VariablesFromContent(`my_list2 = [1, 2, 3]`, pos) 302 assert.Equal(t, "my_list2", vars["my_list2"].Name) 303 assert.Equal(t, "list", vars["my_list2"].Type) 304 305 // Tests for dict variables 306 vars = a.VariablesFromContent(`my_dict = {'foo': 1, 'bar': 3}`, pos) 307 assert.Equal(t, "my_dict", vars["my_dict"].Name) 308 assert.Equal(t, "dict", vars["my_dict"].Type) 309 310 // Test for calls 311 vars = a.VariablesFromContent(`my_call = go_library()`, pos) 312 assert.Equal(t, "", vars["my_call"].Type) 313 314 // Test for reassigning variable 315 vars = a.VariablesFromContent(`foo = "hello"`+"\n"+`foo = 90`, pos) 316 assert.Equal(t, "int", vars["foo"].Type) 317 } 318 319 func TestAnalyzer_GetSubinclude(t *testing.T) { 320 a, err := newAnalyzer() 321 assert.Equal(t, err, nil) 322 ctx := context.Background() 323 324 stmts, err := a.AspStatementFromFile(subincludeURI) 325 assert.NoError(t, err) 326 327 subinclude := a.GetSubinclude(ctx, stmts, subincludeURI) 328 assert.Equal(t, len(subinclude), 1) 329 _, ok := subinclude["plz_e2e_test"] 330 assert.True(t, ok) 331 } 332 333 /************************ 334 * Helper functions 335 ************************/ 336 func getStatementByName(statements []*asp.Statement, name string) *asp.Statement { 337 for _, stmt := range statements { 338 if stmt.FuncDef != nil && stmt.FuncDef.Name == name { 339 return stmt 340 } 341 } 342 return nil 343 }