1// Copyright 2018 Google Inc. All rights reserved.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15package android
16
17import (
18	"testing"
19
20	"github.com/google/blueprint"
21)
22
23var neverallowTests = []struct {
24	// The name of the test.
25	name string
26
27	// Optional test specific rules. If specified then they are used instead of the default rules.
28	rules []Rule
29
30	// Additional contents to add to the virtual filesystem used by the tests.
31	fs MockFS
32
33	// The expected error patterns. If empty then no errors are expected, otherwise each error
34	// reported must be matched by at least one of these patterns. A pattern matches if the error
35	// message contains the pattern. A pattern does not have to match the whole error message.
36	expectedErrors []string
37}{
38	// Test General Functionality
39
40	// in direct deps tests
41	{
42		name: "not_allowed_in_direct_deps",
43		rules: []Rule{
44			NeverAllow().InDirectDeps("not_allowed_in_direct_deps"),
45		},
46		fs: map[string][]byte{
47			"top/Android.bp": []byte(`
48				cc_library {
49					name: "not_allowed_in_direct_deps",
50				}`),
51			"other/Android.bp": []byte(`
52				cc_library {
53					name: "libother",
54					static_libs: ["not_allowed_in_direct_deps"],
55				}`),
56		},
57		expectedErrors: []string{
58			`module "libother": violates neverallow deps:not_allowed_in_direct_deps`,
59		},
60	},
61
62	// Test android specific rules
63
64	// include_dir rule tests
65	{
66		name: "include_dir not allowed to reference art",
67		fs: map[string][]byte{
68			"other/Android.bp": []byte(`
69				cc_library {
70					name: "libother",
71					include_dirs: ["art/libdexfile/include"],
72				}`),
73		},
74		expectedErrors: []string{
75			"all usages of 'art' have been migrated",
76		},
77	},
78	{
79		name: "include_dir not allowed to reference art",
80		fs: map[string][]byte{
81			"system/libfmq/Android.bp": []byte(`
82				cc_library {
83					name: "libother",
84					include_dirs: ["any/random/file"],
85				}`),
86		},
87		expectedErrors: []string{
88			"all usages of them in 'system/libfmq' have been migrated",
89		},
90	},
91	{
92		name: "include_dir can work",
93		fs: map[string][]byte{
94			"other/Android.bp": []byte(`
95				cc_library {
96					name: "libother",
97					include_dirs: ["another/include"],
98				}`),
99		},
100	},
101	// Treble rule tests
102	{
103		name: "no vndk.enabled under vendor directory",
104		fs: map[string][]byte{
105			"vendor/Android.bp": []byte(`
106				cc_library {
107					name: "libvndk",
108					vendor_available: true,
109					vndk: {
110						enabled: true,
111					},
112				}`),
113		},
114		expectedErrors: []string{
115			"VNDK can never contain a library that is device dependent",
116		},
117	},
118	{
119		name: "no vndk.enabled under device directory",
120		fs: map[string][]byte{
121			"device/Android.bp": []byte(`
122				cc_library {
123					name: "libvndk",
124					vendor_available: true,
125					vndk: {
126						enabled: true,
127					},
128				}`),
129		},
130		expectedErrors: []string{
131			"VNDK can never contain a library that is device dependent",
132		},
133	},
134	{
135		name: "vndk-ext under vendor or device directory",
136		fs: map[string][]byte{
137			"device/Android.bp": []byte(`
138				cc_library {
139					name: "libvndk1_ext",
140					vendor: true,
141					vndk: {
142						enabled: true,
143					},
144				}`),
145			"vendor/Android.bp": []byte(`
146				cc_library {
147					name: "libvndk2_ext",
148					vendor: true,
149					vndk: {
150						enabled: true,
151					},
152				}`),
153		},
154	},
155
156	{
157		name: "no enforce_vintf_manifest.cflags",
158		fs: map[string][]byte{
159			"Android.bp": []byte(`
160				cc_library {
161					name: "libexample",
162					product_variables: {
163						enforce_vintf_manifest: {
164							cflags: ["-DSHOULD_NOT_EXIST"],
165						},
166					},
167				}`),
168		},
169		expectedErrors: []string{
170			"manifest enforcement should be independent",
171		},
172	},
173
174	{
175		name: "no treble_linker_namespaces.cflags",
176		fs: map[string][]byte{
177			"Android.bp": []byte(`
178				cc_library {
179					name: "libexample",
180					product_variables: {
181						treble_linker_namespaces: {
182							cflags: ["-DSHOULD_NOT_EXIST"],
183						},
184					},
185				}`),
186		},
187		expectedErrors: []string{
188			"nothing should care if linker namespaces are enabled or not",
189		},
190	},
191	{
192		name: "libc_bionic_ndk treble_linker_namespaces.cflags",
193		fs: map[string][]byte{
194			"Android.bp": []byte(`
195				cc_library {
196					name: "libc_bionic_ndk",
197					product_variables: {
198						treble_linker_namespaces: {
199							cflags: ["-DSHOULD_NOT_EXIST"],
200						},
201					},
202				}`),
203		},
204	},
205	{
206		name: "java_device_for_host",
207		fs: map[string][]byte{
208			"Android.bp": []byte(`
209				java_device_for_host {
210					name: "device_for_host",
211					libs: ["core-libart"],
212				}`),
213		},
214		expectedErrors: []string{
215			"java_device_for_host can only be used in allowed projects",
216		},
217	},
218	// CC sdk rule tests
219	{
220		name: `"sdk_variant_only" outside allowed list`,
221		fs: map[string][]byte{
222			"Android.bp": []byte(`
223				cc_library {
224					name: "outside_allowed_list",
225					sdk_version: "current",
226					sdk_variant_only: true,
227				}`),
228		},
229		expectedErrors: []string{
230			`module "outside_allowed_list": violates neverallow`,
231		},
232	},
233	{
234		name: `"sdk_variant_only: false" outside allowed list`,
235		fs: map[string][]byte{
236			"Android.bp": []byte(`
237				cc_library {
238					name: "outside_allowed_list",
239					sdk_version: "current",
240					sdk_variant_only: false,
241				}`),
242		},
243		expectedErrors: []string{
244			`module "outside_allowed_list": violates neverallow`,
245		},
246	},
247	{
248		name: `"platform" outside allowed list`,
249		fs: map[string][]byte{
250			"Android.bp": []byte(`
251				cc_library {
252					name: "outside_allowed_list",
253					platform: {
254						shared_libs: ["libfoo"],
255					},
256				}`),
257		},
258		expectedErrors: []string{
259			`module "outside_allowed_list": violates neverallow`,
260		},
261	},
262	{
263		name: "uncompress_dex inside art",
264		fs: map[string][]byte{
265			"art/Android.bp": []byte(`
266				java_library {
267					name: "inside_art_libraries",
268					uncompress_dex: true,
269				}`),
270		},
271	},
272	{
273		name: "uncompress_dex outside art",
274		fs: map[string][]byte{
275			"other/Android.bp": []byte(`
276				java_library {
277					name: "outside_art_libraries",
278					uncompress_dex: true,
279				}`),
280		},
281		expectedErrors: []string{
282			"module \"outside_art_libraries\": violates neverallow",
283		},
284	},
285	{
286		name: "disallowed makefile_goal",
287		fs: map[string][]byte{
288			"Android.bp": []byte(`
289				makefile_goal {
290					name: "foo",
291					product_out_path: "boot/trap.img"
292				}
293			`),
294		},
295		expectedErrors: []string{
296			"Only boot images may be imported as a makefile goal.",
297		},
298	},
299}
300
301var prepareForNeverAllowTest = GroupFixturePreparers(
302	FixtureRegisterWithContext(func(ctx RegistrationContext) {
303		ctx.RegisterModuleType("cc_library", newMockCcLibraryModule)
304		ctx.RegisterModuleType("java_library", newMockJavaLibraryModule)
305		ctx.RegisterModuleType("java_library_host", newMockJavaLibraryModule)
306		ctx.RegisterModuleType("java_device_for_host", newMockJavaLibraryModule)
307		ctx.RegisterModuleType("makefile_goal", newMockMakefileGoalModule)
308	}),
309)
310
311func TestNeverallow(t *testing.T) {
312	for _, test := range neverallowTests {
313		t.Run(test.name, func(t *testing.T) {
314			GroupFixturePreparers(
315				prepareForNeverAllowTest,
316				PrepareForTestWithNeverallowRules(test.rules),
317				test.fs.AddToFixture(),
318			).
319				ExtendWithErrorHandler(FixtureExpectsAllErrorsToMatchAPattern(test.expectedErrors)).
320				RunTest(t)
321		})
322	}
323}
324
325type mockCcLibraryProperties struct {
326	Include_dirs     []string
327	Vendor_available *bool
328	Static_libs      []string
329	Sdk_version      *string
330	Sdk_variant_only *bool
331
332	Vndk struct {
333		Enabled                *bool
334		Support_system_process *bool
335		Extends                *string
336	}
337
338	Product_variables struct {
339		Enforce_vintf_manifest struct {
340			Cflags []string
341		}
342
343		Treble_linker_namespaces struct {
344			Cflags []string
345		}
346	}
347
348	Platform struct {
349		Shared_libs []string
350	}
351}
352
353type mockCcLibraryModule struct {
354	ModuleBase
355	properties mockCcLibraryProperties
356}
357
358func newMockCcLibraryModule() Module {
359	m := &mockCcLibraryModule{}
360	m.AddProperties(&m.properties)
361	InitAndroidModule(m)
362	return m
363}
364
365type neverallowTestDependencyTag struct {
366	blueprint.BaseDependencyTag
367	name string
368}
369
370var staticDepTag = neverallowTestDependencyTag{name: "static"}
371
372func (c *mockCcLibraryModule) DepsMutator(ctx BottomUpMutatorContext) {
373	for _, lib := range c.properties.Static_libs {
374		ctx.AddDependency(ctx.Module(), staticDepTag, lib)
375	}
376}
377
378func (p *mockCcLibraryModule) GenerateAndroidBuildActions(ModuleContext) {
379}
380
381type mockJavaLibraryProperties struct {
382	Libs           []string
383	Sdk_version    *string
384	Uncompress_dex *bool
385}
386
387type mockJavaLibraryModule struct {
388	ModuleBase
389	properties mockJavaLibraryProperties
390}
391
392func newMockJavaLibraryModule() Module {
393	m := &mockJavaLibraryModule{}
394	m.AddProperties(&m.properties)
395	InitAndroidModule(m)
396	return m
397}
398
399func (p *mockJavaLibraryModule) GenerateAndroidBuildActions(ModuleContext) {
400}
401
402type mockMakefileGoalProperties struct {
403	Product_out_path *string
404}
405
406type mockMakefileGoalModule struct {
407	ModuleBase
408	properties mockMakefileGoalProperties
409}
410
411func newMockMakefileGoalModule() Module {
412	m := &mockMakefileGoalModule{}
413	m.AddProperties(&m.properties)
414	InitAndroidModule(m)
415	return m
416}
417
418func (p *mockMakefileGoalModule) GenerateAndroidBuildActions(ModuleContext) {
419}
420