1
15
16 package proto
17
18 import (
19 "path/filepath"
20 "reflect"
21 "strings"
22 "testing"
23
24 "github.com/bazelbuild/bazel-gazelle/label"
25 "github.com/bazelbuild/bazel-gazelle/repo"
26 "github.com/bazelbuild/bazel-gazelle/resolve"
27 "github.com/bazelbuild/bazel-gazelle/rule"
28 bzl "github.com/bazelbuild/buildtools/build"
29 )
30
31 func TestResolveProto(t *testing.T) {
32 type buildFile struct {
33 rel, content string
34 }
35 type testCase struct {
36 desc string
37 index []buildFile
38 old, want string
39 }
40 for _, tc := range []testCase{
41 {
42 desc: "well_known",
43 index: []buildFile{{
44 rel: "google/protobuf",
45 content: `
46 proto_library(
47 name = "bad_proto",
48 srcs = ["any.proto"],
49 )
50 `,
51 }},
52 old: `
53 proto_library(
54 name = "dep_proto",
55 _imports = [
56 "google/protobuf/any.proto",
57 "google/protobuf/timestamp.proto",
58 ],
59 )
60 `,
61 want: `
62 proto_library(
63 name = "dep_proto",
64 deps = [
65 "@com_google_protobuf//:any_proto",
66 "@com_google_protobuf//:timestamp_proto",
67 ],
68 )
69 `,
70 }, {
71 desc: "override",
72 index: []buildFile{
73 {
74 rel: "google/rpc",
75 content: `
76 proto_library(
77 name = "bad_proto",
78 srcs = ["status.proto"],
79 )
80 `,
81 }, {
82 rel: "",
83 content: `
84 # gazelle:resolve proto google/rpc/status.proto //:good_proto
85 `,
86 },
87 },
88 old: `
89 proto_library(
90 name = "dep_proto",
91 _imports = ["google/rpc/status.proto"],
92 )
93 `,
94 want: `
95 proto_library(
96 name = "dep_proto",
97 deps = ["//:good_proto"],
98 )
99 `,
100 }, {
101 desc: "index",
102 index: []buildFile{{
103 rel: "foo",
104 content: `
105 proto_library(
106 name = "foo_proto",
107 srcs = ["foo.proto"],
108 )
109 `,
110 }},
111 old: `
112 proto_library(
113 name = "dep_proto",
114 _imports = ["foo/foo.proto"],
115 )
116 `,
117 want: `
118 proto_library(
119 name = "dep_proto",
120 deps = ["//foo:foo_proto"],
121 )
122 `,
123 }, {
124 desc: "index_local",
125 old: `
126 proto_library(
127 name = "foo_proto",
128 srcs = ["foo.proto"],
129 )
130
131 proto_library(
132 name = "dep_proto",
133 _imports = ["test/foo.proto"],
134 )
135 `,
136 want: `
137 proto_library(
138 name = "foo_proto",
139 srcs = ["foo.proto"],
140 )
141
142 proto_library(
143 name = "dep_proto",
144 deps = [":foo_proto"],
145 )
146 `,
147 }, {
148 desc: "index_ambiguous",
149 index: []buildFile{{
150 rel: "foo",
151 content: `
152 proto_library(
153 name = "a_proto",
154 srcs = ["foo.proto"],
155 )
156
157 proto_library(
158 name = "b_proto",
159 srcs = ["foo.proto"],
160 )
161 `,
162 }},
163 old: `
164 proto_library(
165 name = "dep_proto",
166 _imports = ["foo/foo.proto"],
167 )
168 `,
169 want: `proto_library(name = "dep_proto")`,
170 }, {
171 desc: "index_self",
172 old: `
173 proto_library(
174 name = "dep_proto",
175 srcs = ["foo.proto"],
176 _imports = ["test/foo.proto"],
177 )
178 `,
179 want: `
180 proto_library(
181 name = "dep_proto",
182 srcs = ["foo.proto"],
183 )
184 `,
185 }, {
186 desc: "index_dedup",
187 index: []buildFile{{
188 rel: "foo",
189 content: `
190 proto_library(
191 name = "foo_proto",
192 srcs = [
193 "a.proto",
194 "b.proto",
195 ],
196 )
197 `,
198 }},
199 old: `
200 proto_library(
201 name = "dep_proto",
202 srcs = ["dep.proto"],
203 _imports = [
204 "foo/a.proto",
205 "foo/b.proto",
206 ],
207 )
208 `,
209 want: `
210 proto_library(
211 name = "dep_proto",
212 srcs = ["dep.proto"],
213 deps = ["//foo:foo_proto"],
214 )
215 `,
216 }, {
217 desc: "unknown",
218 old: `
219 proto_library(
220 name = "dep_proto",
221 _imports = ["foo/bar/unknown.proto"],
222 )
223 `,
224 want: `
225 proto_library(
226 name = "dep_proto",
227 deps = ["//foo/bar:bar_proto"],
228 )
229 `,
230 }, {
231 desc: "strip_import_prefix",
232 index: []buildFile{{
233 rel: "foo/bar/sub",
234 content: `
235 proto_library(
236 name = "foo_proto",
237 srcs = ["foo.proto"],
238 strip_import_prefix = "/foo/bar",
239 )
240 `,
241 }},
242 old: `
243 proto_library(
244 name = "dep_proto",
245 _imports = ["sub/foo.proto"],
246 )
247 `,
248 want: `
249 proto_library(
250 name = "dep_proto",
251 deps = ["//foo/bar/sub:foo_proto"],
252 )
253 `,
254 }, {
255 desc: "skip bad strip_import_prefix",
256 index: []buildFile{{
257 rel: "bar",
258 content: `
259 proto_library(
260 name = "foo_proto",
261 srcs = ["foo.proto"],
262 strip_import_prefix = "/foo",
263 )
264 `,
265 }},
266 old: `
267 proto_library(
268 name = "dep_proto",
269 _imports = ["bar/foo.proto"],
270 )
271 `,
272 want: `
273 proto_library(
274 name = "dep_proto",
275 deps = ["//bar:bar_proto"],
276 )
277 `,
278 }, {
279 desc: "import_prefix",
280 index: []buildFile{{
281 rel: "bar",
282 content: `
283 proto_library(
284 name = "foo_proto",
285 srcs = ["foo.proto"],
286 import_prefix = "foo/",
287 )
288 `,
289 }},
290 old: `
291 proto_library(
292 name = "dep_proto",
293 _imports = ["foo/bar/foo.proto"],
294 )
295 `,
296 want: `
297 proto_library(
298 name = "dep_proto",
299 deps = ["//bar:foo_proto"],
300 )
301 `,
302 }, {
303 desc: "strip_import_prefix and import_prefix",
304 index: []buildFile{{
305 rel: "foo",
306 content: `
307 proto_library(
308 name = "foo_proto",
309 srcs = ["foo.proto"],
310 import_prefix = "bar/",
311 strip_import_prefix = "/foo",
312 )
313 `,
314 }},
315 old: `
316 proto_library(
317 name = "dep_proto",
318 _imports = ["bar/foo.proto"],
319 )
320 `,
321 want: `
322 proto_library(
323 name = "dep_proto",
324 deps = ["//foo:foo_proto"],
325 )
326 `,
327 }, {
328 desc: "test single file resolution in file mode",
329 index: []buildFile{{
330 rel: "somedir",
331 content: `
332 # gazelle:proto file
333
334 proto_library(
335 name = "foo_proto",
336 srcs = ["foo.proto"],
337 )
338
339 proto_library(
340 name = "bar_proto",
341 srcs = ["bar.proto"],
342 )
343
344 proto_library(
345 name = "baz_proto",
346 srcs = ["baz.proto"],
347 )
348 `,
349 }},
350 old: `
351 proto_library(
352 name = "other_proto",
353 _imports = ["somedir/bar.proto"],
354 )
355 `,
356 want: `
357 proto_library(
358 name = "other_proto",
359 deps = ["//somedir:bar_proto"],
360 )
361 `,
362 }, {
363 desc: "test single file resolution in same package",
364 old: `
365 proto_library(
366 name = "qwerty_proto",
367 srcs = ["qwerty.proto"],
368 )
369
370 proto_library(
371 name = "other_proto",
372 _imports = ["test/qwerty.proto"],
373 )
374 `,
375 want: `
376 proto_library(
377 name = "qwerty_proto",
378 srcs = ["qwerty.proto"],
379 )
380
381 proto_library(
382 name = "other_proto",
383 deps = [":qwerty_proto"],
384 )
385 `,
386 },
387 } {
388 t.Run(tc.desc, func(t *testing.T) {
389 c, lang, cexts := testConfig(t, ".")
390 mrslv := make(mapResolver)
391 mrslv["proto_library"] = lang
392 ix := resolve.NewRuleIndex(mrslv.Resolver, []resolve.CrossResolver{lang.(resolve.CrossResolver)})
393 rc := (*repo.RemoteCache)(nil)
394 for _, bf := range tc.index {
395 f, err := rule.LoadData(filepath.Join(bf.rel, "BUILD.bazel"), bf.rel, []byte(bf.content))
396 if err != nil {
397 t.Fatal(err)
398 }
399 if bf.rel == "" {
400 for _, cext := range cexts {
401 cext.Configure(c, "", f)
402 }
403 }
404 for _, r := range f.Rules {
405 ix.AddRule(c, r, f)
406 }
407 }
408 f, err := rule.LoadData("test/BUILD.bazel", "test", []byte(tc.old))
409 if err != nil {
410 t.Fatal(err)
411 }
412 imports := make([]interface{}, len(f.Rules))
413 for i, r := range f.Rules {
414 imports[i] = convertImportsAttr(r)
415 ix.AddRule(c, r, f)
416 }
417 ix.Finish()
418 for i, r := range f.Rules {
419 lang.Resolve(c, ix, rc, r, imports[i], label.New("", "test", r.Name()))
420 }
421 f.Sync()
422 got := strings.TrimSpace(string(bzl.Format(f.File)))
423 want := strings.TrimSpace(tc.want)
424 if got != want {
425 t.Errorf("got:\n%s\nwant:\n%s", got, want)
426 }
427 })
428 }
429 }
430
431 func TestCrossResolve(t *testing.T) {
432 type testCase struct {
433 desc string
434 protoMode Mode
435 imp resolve.ImportSpec
436 lang string
437 want []resolve.FindResult
438 }
439 for _, tc := range []testCase{
440 {
441 desc: "disable global mode go",
442 protoMode: DisableGlobalMode,
443 imp: resolve.ImportSpec{Lang: "go", Imp: "github.com/golang/protobuf/proto"},
444 lang: "go",
445 want: nil,
446 },
447 {
448 desc: "disable global mode proto",
449 protoMode: DisableGlobalMode,
450 imp: resolve.ImportSpec{Lang: "proto", Imp: "google/protobuf/any.proto"},
451 lang: "go",
452 want: nil,
453 },
454 {
455 desc: "proto source lang",
456 protoMode: DefaultMode,
457 imp: resolve.ImportSpec{Lang: "proto", Imp: "google/protobuf/any.proto"},
458 lang: "proto",
459 want: nil,
460 },
461 {
462 desc: "unsupported import lang",
463 protoMode: DefaultMode,
464 imp: resolve.ImportSpec{Lang: "foo", Imp: "foo"},
465 lang: "go",
466 want: nil,
467 },
468 {
469 desc: "go known import",
470 protoMode: DefaultMode,
471 imp: resolve.ImportSpec{Lang: "go", Imp: "github.com/golang/protobuf/proto"},
472 lang: "go",
473 want: []resolve.FindResult{{Label: label.New("com_github_golang_protobuf", "proto", "go_default_library")}},
474 },
475 {
476 desc: "go unknown import",
477 protoMode: DefaultMode,
478 imp: resolve.ImportSpec{Lang: "go", Imp: "foo"},
479 lang: "go",
480 want: nil,
481 },
482 {
483 desc: "proto known import",
484 protoMode: DefaultMode,
485 imp: resolve.ImportSpec{Lang: "proto", Imp: "google/protobuf/any.proto"},
486 lang: "go",
487 want: []resolve.FindResult{{Label: label.New("com_github_golang_protobuf", "ptypes/any", "any")}},
488 },
489 {
490 desc: "proto unknown import",
491 protoMode: DefaultMode,
492 imp: resolve.ImportSpec{Lang: "proto", Imp: "foo.proto"},
493 lang: "go",
494 want: nil,
495 },
496 } {
497 t.Run(tc.desc, func(t *testing.T) {
498 c, lang, _ := testConfig(t, ".")
499 pc := GetProtoConfig(c)
500 pc.Mode = tc.protoMode
501 ix := (*resolve.RuleIndex)(nil)
502 got := lang.(resolve.CrossResolver).CrossResolve(c, ix, tc.imp, tc.lang)
503 if !reflect.DeepEqual(got, tc.want) {
504 t.Errorf("got %#v ; want %#v", got, tc.want)
505 }
506 })
507 }
508 }
509
510 func convertImportsAttr(r *rule.Rule) interface{} {
511 value := r.AttrStrings("_imports")
512 if value == nil {
513 value = []string(nil)
514 }
515 r.DelAttr("_imports")
516 return value
517 }
518
519 type mapResolver map[string]resolve.Resolver
520
521 func (mr mapResolver) Resolver(r *rule.Rule, f string) resolve.Resolver {
522 return mr[r.Kind()]
523 }
524
View as plain text