github.com/tiagovtristao/plz@v13.4.0+incompatible/tools/build_langserver/langserver/diagnostics_test.go (about) 1 package langserver 2 3 import ( 4 "context" 5 "testing" 6 "github.com/thought-machine/please/tools/build_langserver/lsp" 7 8 "github.com/stretchr/testify/assert" 9 ) 10 11 func TestDiagnose(t *testing.T) { 12 ds := getDefaultDS(t) 13 assert.Equal(t, 13, len(ds.stored)) 14 } 15 16 func TestDiagnosisInvalidBuildLabel(t *testing.T) { 17 ds := getDefaultDS(t) 18 19 // Test for invalid build label 20 diag := FindDiagnosticByMsg(ds.stored, 21 "Invalid build label //dummy/buildlabels:foo. error: cannot find the path for build label //dummy/buildlabels:foo") 22 assert.NotNil(t, diag) 23 expected := lsp.Range{ 24 Start: lsp.Position{Line: 30, Character: 8}, 25 End: lsp.Position{Line: 30, Character: 33}, 26 } 27 assert.Equal(t, expected, diag.Range) 28 29 diag = FindDiagnosticByMsg(ds.stored, 30 "Invalid build label //sr. error: cannot find the path for build label //sr:sr") 31 assert.NotNil(t, diag) 32 expected = lsp.Range{ 33 Start: lsp.Position{Line: 45, Character: 0}, 34 End: lsp.Position{Line: 45, Character: 6}, 35 } 36 assert.Equal(t, expected, diag.Range) 37 38 // Test for invisible build label 39 expected = lsp.Range{ 40 Start: lsp.Position{Line: 12, Character: 8}, 41 End: lsp.Position{Line: 12, Character: 27}, 42 } 43 diag = FindDiagnosticByRange(ds.stored, expected) 44 assert.NotNil(t, diag) 45 assert.Equal(t, "build label //src/parse/rules is not visible to current package", diag.Message) 46 47 } 48 49 func TestDiagnosisFuncArgument(t *testing.T) { 50 ds := getDefaultDS(t) 51 52 // Test for unexpected argument 53 diag := FindDiagnosticByMsg(ds.stored, "unexpected argument foo") 54 assert.NotNil(t, diag) 55 expected := lsp.Range{ 56 Start: lsp.Position{Line: 25, Character: 4}, 57 End: lsp.Position{Line: 25, Character: 7}, 58 } 59 assert.Equal(t, expected, diag.Range) 60 61 // Test for invalid argument type 62 diag = FindDiagnosticByMsg(ds.stored, 63 "invalid type for argument type 'dict' for target, expecting one of [str]") 64 assert.NotNil(t, diag) 65 expected = lsp.Range{ 66 Start: lsp.Position{Line: 50, Character: 11}, 67 End: lsp.Position{Line: 50, Character: 35}, 68 } 69 assert.Equal(t, expected, diag.Range) 70 71 } 72 73 func TestDiagnosisVariable(t *testing.T) { 74 ds := getDefaultDS(t) 75 76 // Test for undefined variable, variable later defined 77 diag := FindDiagnosticByMsg(ds.stored, "unexpected variable or config property 'baz'") 78 assert.NotNil(t, diag) 79 expected := lsp.Range{ 80 Start: lsp.Position{Line: 55, Character: 8}, 81 End: lsp.Position{Line: 55, Character: 11}, 82 } 83 assert.Equal(t, expected, diag.Range) 84 85 // Test for undefined variable in function 86 diag = FindDiagnosticByMsg(ds.stored, "unexpected variable or config property 'bar'") 87 assert.NotNil(t, diag) 88 expected = lsp.Range{ 89 Start: lsp.Position{Line: 51, Character: 4}, 90 End: lsp.Position{Line: 51, Character: 7}, 91 } 92 assert.Equal(t, expected, diag.Range) 93 94 } 95 96 func TestDiagnosisFunction(t *testing.T) { 97 ds := getDefaultDS(t) 98 99 // Test for undefined function 100 diag := FindDiagnosticByMsg(ds.stored, "function undefined: blah") 101 assert.NotNil(t, diag) 102 expected := lsp.Range{ 103 Start: lsp.Position{Line: 53, Character: 0}, 104 End: lsp.Position{Line: 53, Character: 4}, 105 } 106 assert.Equal(t, expected, diag.Range) 107 108 // Test for not enough argument 109 diag = FindDiagnosticByMsg(ds.stored, "not enough arguments in call to add_dep") 110 assert.NotNil(t, diag) 111 expected = lsp.Range{ 112 Start: lsp.Position{Line: 62, Character: 7}, 113 End: lsp.Position{Line: 62, Character: 15}, 114 } 115 assert.Equal(t, expected, diag.Range) 116 117 for _, d := range ds.stored { 118 t.Log(d) 119 } 120 } 121 122 func TestDiagnosticsProperty(t *testing.T) { 123 ds := getDefaultDS(t) 124 125 // Test for invalid string call 126 diag := FindDiagnosticByMsg(ds.stored, "function undefined: foo") 127 assert.NotNil(t, diag) 128 expected := lsp.Range{ 129 Start: lsp.Position{Line: 64, Character: 30}, 130 End: lsp.Position{Line: 64, Character: 33}, 131 } 132 assert.Equal(t, expected, diag.Range) 133 134 // Test for invalid config property 135 diag = FindDiagnosticByMsg(ds.stored, "unexpected variable or config property 'BLAH'") 136 assert.NotNil(t, diag) 137 expected = lsp.Range{ 138 Start: lsp.Position{Line: 66, Character: 13}, 139 End: lsp.Position{Line: 66, Character: 17}, 140 } 141 assert.Equal(t, expected, diag.Range) 142 143 // Ensure correct config property is not being listed in diagnostics 144 configRange := lsp.Range{ 145 Start: lsp.Position{Line: 64, Character: 58}, 146 End: lsp.Position{Line: 64, Character: 84}, 147 } 148 assert.Nil(t, FindDiagnosticByRange(ds.stored, configRange)) 149 150 } 151 152 func TestDiagnoseOutOfScope(t *testing.T) { 153 analyzer.State.Config.Parse.BuildFileName = append(analyzer.State.Config.Parse.BuildFileName, "out_of_scope.build") 154 ds := &diagnosticStore{ 155 uri: OutScopeURI, 156 stored: []*lsp.Diagnostic{}, 157 } 158 159 stmts, err := analyzer.AspStatementFromFile(OutScopeURI) 160 assert.NoError(t, err) 161 162 ds.storeDiagnostics(analyzer, stmts) 163 assert.Equal(t, 1, len(ds.stored)) 164 assert.NotNil(t, FindDiagnosticByMsg(ds.stored, "unexpected variable or config property 'blah'")) 165 for _, d := range ds.stored { 166 t.Log(d) 167 } 168 } 169 170 func TestStoreFuncCallDiagnosticsBuildDef(t *testing.T) { 171 ctx := context.Background() 172 ds := &diagnosticStore{ 173 uri: exampleBuildURI, 174 } 175 176 buildDefs, err := analyzer.BuildDefsFromURI(ctx, exampleBuildURI) 177 assert.NoError(t, err) 178 179 // Tests for build def 180 buildDef := buildDefs["langserver_test"] 181 callRange := lsp.Range{ 182 Start: lsp.Position{Line: 19, Character: 1}, 183 End: lsp.Position{Line: 32, Character: 1}, 184 } 185 186 ds.diagnoseFuncCall(analyzer, "go_test", 187 buildDef.Action.Call.Arguments, callRange) 188 expectedRange := lsp.Range{ 189 Start: lsp.Position{Line: 25, Character: 4}, 190 End: lsp.Position{Line: 25, Character: 7}, 191 } 192 diag := FindDiagnosticByMsg(ds.stored, 193 "unexpected argument foo") 194 assert.Equal(t, expectedRange, diag.Range) 195 } 196 197 func TestStoreFuncCallDiagnosticsFuncCall(t *testing.T) { 198 ds := &diagnosticStore{ 199 uri: exampleBuildURI, 200 stored: []*lsp.Diagnostic{}, 201 } 202 203 stmts, err := analyzer.AspStatementFromFile(exampleBuildURI) 204 assert.NoError(t, err) 205 206 // Test for regular function call with correct argument 207 callRange := lsp.Range{ 208 Start: lsp.Position{Line: 49, Character: 0}, 209 End: lsp.Position{Line: 49, Character: 33}, 210 } 211 stmt := analyzer.StatementFromPos(stmts, callRange.Start) 212 ds.diagnoseFuncCall(analyzer, "subinclude", stmt.Ident.Action.Call.Arguments, callRange) 213 assert.Zero(t, len(ds.stored)) 214 215 // Test for function call with incorrect argument value type 216 callRange = lsp.Range{ 217 Start: lsp.Position{Line: 50, Character: 0}, 218 End: lsp.Position{Line: 50, Character: 33}, 219 } 220 stmt = analyzer.StatementFromPos(stmts, callRange.Start) 221 ds.diagnoseFuncCall(analyzer, "subinclude", stmt.Ident.Action.Call.Arguments, callRange) 222 223 expectedRange := lsp.Range{ 224 Start: lsp.Position{Line: 50, Character: 11}, 225 End: lsp.Position{Line: 50, Character: 35}, 226 } 227 diag := FindDiagnosticByMsg(ds.stored, 228 "invalid type for argument type 'dict' for target, expecting one of [str]") 229 assert.Equal(t, expectedRange, diag.Range) 230 231 } 232 233 func TestDiagnosticFromBuildLabel(t *testing.T) { 234 ds := getDefaultDS(t) 235 236 dummyRange := lsp.Range{ 237 Start: lsp.Position{Line: 19, Character: 1}, 238 End: lsp.Position{Line: 32, Character: 1}, 239 } 240 241 // Tests for valid labels 242 diag := ds.diagnosticFromBuildLabel(analyzer, "//src/query", dummyRange) 243 assert.Nil(t, diag) 244 diag = ds.diagnosticFromBuildLabel(analyzer, "//src/query:query", dummyRange) 245 assert.Nil(t, diag) 246 diag = ds.diagnosticFromBuildLabel(analyzer, ":langserver_test", dummyRange) 247 assert.Nil(t, diag) 248 diag = ds.diagnosticFromBuildLabel(analyzer, ":langserver", dummyRange) 249 assert.Nil(t, diag) 250 diag = ds.diagnosticFromBuildLabel(analyzer, "//third_party/go:jsonrpc2", dummyRange) 251 assert.Nil(t, diag) 252 253 // Tests for invalid labels 254 diag = ds.diagnosticFromBuildLabel(analyzer, "//src/blah:foo", dummyRange) 255 assert.Equal(t, "Invalid build label //src/blah:foo. error: cannot find the path for build label //src/blah:foo", diag.Message) 256 257 // Tests for invisible labels 258 diag = ds.diagnosticFromBuildLabel(analyzer, "//src/output:interactive_display_test", dummyRange) 259 assert.Equal(t, "build label //src/output:interactive_display_test is not visible to current package", 260 diag.Message) 261 } 262 263 /************************ 264 * Helper functions 265 ************************/ 266 func getDefaultDS(t testing.TB) *diagnosticStore { 267 if !StringInSlice(analyzer.State.Config.Parse.BuildFileName, "example.build") { 268 analyzer.State.Config.Parse.BuildFileName = append(analyzer.State.Config.Parse.BuildFileName, "example.build") 269 } 270 271 ds := &diagnosticStore{ 272 uri: exampleBuildURI, 273 stored: []*lsp.Diagnostic{}, 274 } 275 276 stmts, err := analyzer.AspStatementFromFile(exampleBuildURI) 277 assert.NoError(t, err) 278 279 ds.storeDiagnostics(analyzer, stmts) 280 281 return ds 282 } 283 284 func FindDiagnosticByMsg(diag []*lsp.Diagnostic, message string) *lsp.Diagnostic { 285 for _, v := range diag { 286 if v.Message == message { 287 return v 288 } 289 } 290 return nil 291 } 292 293 func FindDiagnosticByRange(diag []*lsp.Diagnostic, DRange lsp.Range) *lsp.Diagnostic { 294 for _, v := range diag { 295 if v.Range == DRange { 296 return v 297 } 298 } 299 return nil 300 }