blob: 680991429af31107bac220608eef8c8127b4edd1 [file] [log] [blame]
Paul Duffin2e61fa62019-03-28 14:10:57 +00001package android
2
3import (
4 "github.com/google/blueprint"
5 "io/ioutil"
6 "os"
7 "testing"
8)
9
10var visibilityTests = []struct {
11 name string
12 fs map[string][]byte
13 expectedErrors []string
14}{
15 {
16 name: "invalid visibility: empty list",
17 fs: map[string][]byte{
18 "top/Blueprints": []byte(`
19 mock_library {
20 name: "libexample",
21 visibility: [],
22 }`),
23 },
24 expectedErrors: []string{`visibility: must contain at least one visibility rule`},
25 },
26 {
27 name: "invalid visibility: empty rule",
28 fs: map[string][]byte{
29 "top/Blueprints": []byte(`
30 mock_library {
31 name: "libexample",
32 visibility: [""],
33 }`),
34 },
35 expectedErrors: []string{`visibility: invalid visibility pattern ""`},
36 },
37 {
38 name: "invalid visibility: unqualified",
39 fs: map[string][]byte{
40 "top/Blueprints": []byte(`
41 mock_library {
42 name: "libexample",
43 visibility: ["target"],
44 }`),
45 },
46 expectedErrors: []string{`visibility: invalid visibility pattern "target"`},
47 },
48 {
49 name: "invalid visibility: empty namespace",
50 fs: map[string][]byte{
51 "top/Blueprints": []byte(`
52 mock_library {
53 name: "libexample",
54 visibility: ["//"],
55 }`),
56 },
57 expectedErrors: []string{`visibility: invalid visibility pattern "//"`},
58 },
59 {
60 name: "invalid visibility: empty module",
61 fs: map[string][]byte{
62 "top/Blueprints": []byte(`
63 mock_library {
64 name: "libexample",
65 visibility: [":"],
66 }`),
67 },
68 expectedErrors: []string{`visibility: invalid visibility pattern ":"`},
69 },
70 {
71 name: "invalid visibility: empty namespace and module",
72 fs: map[string][]byte{
73 "top/Blueprints": []byte(`
74 mock_library {
75 name: "libexample",
76 visibility: ["//:"],
77 }`),
78 },
79 expectedErrors: []string{`visibility: invalid visibility pattern "//:"`},
80 },
81 {
82 name: "//visibility:unknown",
83 fs: map[string][]byte{
84 "top/Blueprints": []byte(`
85 mock_library {
86 name: "libexample",
87 visibility: ["//visibility:unknown"],
88 }`),
89 },
90 expectedErrors: []string{`unrecognized visibility rule "//visibility:unknown"`},
91 },
92 {
93 name: "//visibility:public mixed",
94 fs: map[string][]byte{
95 "top/Blueprints": []byte(`
96 mock_library {
97 name: "libexample",
98 visibility: ["//visibility:public", "//namespace"],
99 }
100
101 mock_library {
102 name: "libother",
103 visibility: ["//visibility:private", "//namespace"],
104 }`),
105 },
106 expectedErrors: []string{
107 `module "libother" variant "android_common": visibility: cannot mix "//visibility:private"` +
108 ` with any other visibility rules`,
109 `module "libexample" variant "android_common": visibility: cannot mix` +
110 ` "//visibility:public" with any other visibility rules`,
111 },
112 },
113 {
114 name: "//visibility:legacy_public",
115 fs: map[string][]byte{
116 "top/Blueprints": []byte(`
117 mock_library {
118 name: "libexample",
119 visibility: ["//visibility:legacy_public"],
120 }`),
121 },
122 expectedErrors: []string{
123 `module "libexample" variant "android_common": visibility: //visibility:legacy_public must` +
124 ` not be used`,
125 },
126 },
127 {
128 // Verify that //visibility:public will allow the module to be referenced from anywhere, e.g.
129 // the current directory, a nested directory and a directory in a separate tree.
130 name: "//visibility:public",
131 fs: map[string][]byte{
132 "top/Blueprints": []byte(`
133 mock_library {
134 name: "libexample",
135 visibility: ["//visibility:public"],
136 }
137
138 mock_library {
139 name: "libsamepackage",
140 deps: ["libexample"],
141 }`),
142 "top/nested/Blueprints": []byte(`
143 mock_library {
144 name: "libnested",
145 deps: ["libexample"],
146 }`),
147 "other/Blueprints": []byte(`
148 mock_library {
149 name: "libother",
150 deps: ["libexample"],
151 }`),
152 },
153 },
154 {
155 // Verify that //visibility:public will allow the module to be referenced from anywhere, e.g.
156 // the current directory, a nested directory and a directory in a separate tree.
157 name: "//visibility:public",
158 fs: map[string][]byte{
159 "top/Blueprints": []byte(`
160 mock_library {
161 name: "libexample",
162 visibility: ["//visibility:public"],
163 }
164
165 mock_library {
166 name: "libsamepackage",
167 deps: ["libexample"],
168 }`),
169 "top/nested/Blueprints": []byte(`
170 mock_library {
171 name: "libnested",
172 deps: ["libexample"],
173 }`),
174 "other/Blueprints": []byte(`
175 mock_library {
176 name: "libother",
177 deps: ["libexample"],
178 }`),
179 },
180 },
181 {
182 // Verify that //visibility:private allows the module to be referenced from the current
183 // directory only.
184 name: "//visibility:private",
185 fs: map[string][]byte{
186 "top/Blueprints": []byte(`
187 mock_library {
188 name: "libexample",
189 visibility: ["//visibility:private"],
190 }
191
192 mock_library {
193 name: "libsamepackage",
194 deps: ["libexample"],
195 }`),
196 "top/nested/Blueprints": []byte(`
197 mock_library {
198 name: "libnested",
199 deps: ["libexample"],
200 }`),
201 },
202 expectedErrors: []string{
203 `module "libnested" variant "android_common": depends on //top:libexample which is not` +
204 ` visible to this module; //top:libexample is only visible to \[//top:__pkg__\]`,
205 },
206 },
207 {
208 // Verify that :__pkg__ allows the module to be referenced from the current directory only.
209 name: ":__pkg__",
210 fs: map[string][]byte{
211 "top/Blueprints": []byte(`
212 mock_library {
213 name: "libexample",
214 visibility: [":__pkg__"],
215 }
216
217 mock_library {
218 name: "libsamepackage",
219 deps: ["libexample"],
220 }`),
221 "top/nested/Blueprints": []byte(`
222 mock_library {
223 name: "libnested",
224 deps: ["libexample"],
225 }`),
226 },
227 expectedErrors: []string{
228 `module "libnested" variant "android_common": depends on //top:libexample which is not` +
229 ` visible to this module; //top:libexample is only visible to \[//top:__pkg__\]`,
230 },
231 },
232 {
233 // Verify that //top/nested allows the module to be referenced from the current directory and
234 // the top/nested directory only, not a subdirectory of top/nested and not peak directory.
235 name: "//top/nested",
236 fs: map[string][]byte{
237 "top/Blueprints": []byte(`
238 mock_library {
239 name: "libexample",
240 visibility: ["//top/nested"],
241 }
242
243 mock_library {
244 name: "libsamepackage",
245 deps: ["libexample"],
246 }`),
247 "top/nested/Blueprints": []byte(`
248 mock_library {
249 name: "libnested",
250 deps: ["libexample"],
251 }`),
252 "top/nested/again/Blueprints": []byte(`
253 mock_library {
254 name: "libnestedagain",
255 deps: ["libexample"],
256 }`),
257 "peak/Blueprints": []byte(`
258 mock_library {
259 name: "libother",
260 deps: ["libexample"],
261 }`),
262 },
263 expectedErrors: []string{
264 `module "libother" variant "android_common": depends on //top:libexample which is not` +
265 ` visible to this module; //top:libexample is only visible to \[//top/nested:__pkg__\]`,
266 `module "libnestedagain" variant "android_common": depends on //top:libexample which is not` +
267 ` visible to this module; //top:libexample is only visible to \[//top/nested:__pkg__\]`,
268 },
269 },
270 {
271 // Verify that :__subpackages__ allows the module to be referenced from the current directory
272 // and sub directories but nowhere else.
273 name: ":__subpackages__",
274 fs: map[string][]byte{
275 "top/Blueprints": []byte(`
276 mock_library {
277 name: "libexample",
278 visibility: [":__subpackages__"],
279 }
280
281 mock_library {
282 name: "libsamepackage",
283 deps: ["libexample"],
284 }`),
285 "top/nested/Blueprints": []byte(`
286 mock_library {
287 name: "libnested",
288 deps: ["libexample"],
289 }`),
290 "peak/other/Blueprints": []byte(`
291 mock_library {
292 name: "libother",
293 deps: ["libexample"],
294 }`),
295 },
296 expectedErrors: []string{
297 `module "libother" variant "android_common": depends on //top:libexample which is not` +
298 ` visible to this module; //top:libexample is only visible to \[//top:__subpackages__\]`,
299 },
300 },
301 {
302 // Verify that //top/nested:__subpackages__ allows the module to be referenced from the current
303 // directory and sub directories but nowhere else.
304 name: "//top/nested:__subpackages__",
305 fs: map[string][]byte{
306 "top/Blueprints": []byte(`
307 mock_library {
308 name: "libexample",
309 visibility: ["//top/nested:__subpackages__", "//other"],
310 }
311
312 mock_library {
313 name: "libsamepackage",
314 deps: ["libexample"],
315 }`),
316 "top/nested/Blueprints": []byte(`
317 mock_library {
318 name: "libnested",
319 deps: ["libexample"],
320 }`),
321 "top/other/Blueprints": []byte(`
322 mock_library {
323 name: "libother",
324 deps: ["libexample"],
325 }`),
326 },
327 expectedErrors: []string{
328 `module "libother" variant "android_common": depends on //top:libexample which is not` +
329 ` visible to this module; //top:libexample is only visible to` +
330 ` \[//top/nested:__subpackages__, //other:__pkg__\]`,
331 },
332 },
333 {
334 // Verify that ["//top/nested", "//peak:__subpackages"] allows the module to be referenced from
335 // the current directory, top/nested and peak and all its subpackages.
336 name: `["//top/nested", "//peak:__subpackages__"]`,
337 fs: map[string][]byte{
338 "top/Blueprints": []byte(`
339 mock_library {
340 name: "libexample",
341 visibility: ["//top/nested", "//peak:__subpackages__"],
342 }
343
344 mock_library {
345 name: "libsamepackage",
346 deps: ["libexample"],
347 }`),
348 "top/nested/Blueprints": []byte(`
349 mock_library {
350 name: "libnested",
351 deps: ["libexample"],
352 }`),
353 "peak/other/Blueprints": []byte(`
354 mock_library {
355 name: "libother",
356 deps: ["libexample"],
357 }`),
358 },
359 },
360 {
361 // Verify that //vendor... cannot be used outside vendor apart from //vendor:__subpackages__
362 name: `//vendor`,
363 fs: map[string][]byte{
364 "top/Blueprints": []byte(`
365 mock_library {
366 name: "libexample",
367 visibility: ["//vendor:__subpackages__"],
368 }
369
370 mock_library {
371 name: "libsamepackage",
372 visibility: ["//vendor/apps/AcmeSettings"],
373 }`),
374 "vendor/Blueprints": []byte(`
375 mock_library {
376 name: "libvendorexample",
377 deps: ["libexample"],
378 visibility: ["//vendor/nested"],
379 }`),
380 "vendor/nested/Blueprints": []byte(`
381 mock_library {
382 name: "libvendornested",
383 deps: ["libexample", "libvendorexample"],
384 }`),
385 },
386 expectedErrors: []string{
387 `module "libsamepackage" variant "android_common": visibility: "//vendor/apps/AcmeSettings"` +
388 ` is not allowed. Packages outside //vendor cannot make themselves visible to specific` +
389 ` targets within //vendor, they can only use //vendor:__subpackages__.`,
390 },
391 },
392}
393
394func TestVisibility(t *testing.T) {
395 buildDir, err := ioutil.TempDir("", "soong_neverallow_test")
396 if err != nil {
397 t.Fatal(err)
398 }
399 defer os.RemoveAll(buildDir)
400
401 for _, test := range visibilityTests {
402 t.Run(test.name, func(t *testing.T) {
403 _, errs := testVisibility(buildDir, test.fs)
404
405 expectedErrors := test.expectedErrors
406 if expectedErrors == nil {
407 FailIfErrored(t, errs)
408 } else {
409 for _, expectedError := range expectedErrors {
410 FailIfNoMatchingErrors(t, expectedError, errs)
411 }
412 if len(errs) > len(expectedErrors) {
413 t.Errorf("additional errors found, expected %d, found %d", len(expectedErrors), len(errs))
414 for i, expectedError := range expectedErrors {
415 t.Errorf("expectedErrors[%d] = %s", i, expectedError)
416 }
417 for i, err := range errs {
418 t.Errorf("errs[%d] = %s", i, err)
419 }
420 }
421 }
422 })
423 }
424}
425
426func testVisibility(buildDir string, fs map[string][]byte) (*TestContext, []error) {
427
428 // Create a new config per test as visibility information is stored in the config.
429 config := TestArchConfig(buildDir, nil)
430
431 ctx := NewTestArchContext()
432 ctx.RegisterModuleType("mock_library", ModuleFactoryAdaptor(newMockLibraryModule))
433 ctx.PreDepsMutators(registerVisibilityRuleGatherer)
434 ctx.PostDepsMutators(registerVisibilityRuleEnforcer)
435 ctx.Register()
436
437 ctx.MockFileSystem(fs)
438
439 _, errs := ctx.ParseBlueprintsFiles(".")
440 if len(errs) > 0 {
441 return ctx, errs
442 }
443
444 _, errs = ctx.PrepareBuildActions(config)
445 return ctx, errs
446}
447
448type mockLibraryProperties struct {
449 Deps []string
450}
451
452type mockLibraryModule struct {
453 ModuleBase
454 properties mockLibraryProperties
455}
456
457func newMockLibraryModule() Module {
458 m := &mockLibraryModule{}
459 m.AddProperties(&m.properties)
460 InitAndroidArchModule(m, HostAndDeviceSupported, MultilibCommon)
461 return m
462}
463
464type dependencyTag struct {
465 blueprint.BaseDependencyTag
466 name string
467}
468
469func (j *mockLibraryModule) DepsMutator(ctx BottomUpMutatorContext) {
470 ctx.AddVariationDependencies(nil, dependencyTag{name: "mockdeps"}, j.properties.Deps...)
471}
472
473func (p *mockLibraryModule) GenerateAndroidBuildActions(ModuleContext) {
474}