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