1// Copyright 2019 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 sdk
16
17import (
18	"android/soong/android"
19	"log"
20	"os"
21	"testing"
22
23	"github.com/google/blueprint/proptools"
24)
25
26// Needed in an _test.go file in this package to ensure tests run correctly, particularly in IDE.
27func TestMain(m *testing.M) {
28	if android.BuildOs != android.Linux {
29		// b/145598135 - Generating host snapshots for anything other than linux is not supported.
30		log.Printf("Skipping as sdk snapshot generation is only supported on %s not %s", android.Linux, android.BuildOs)
31		os.Exit(0)
32	}
33
34	os.Exit(m.Run())
35}
36
37func TestDepNotInRequiredSdks(t *testing.T) {
38	testSdkError(t, `module "myjavalib".*depends on "otherlib".*that isn't part of the required SDKs:.*`, `
39		sdk {
40			name: "mysdk",
41			java_header_libs: ["sdkmember"],
42		}
43
44		sdk_snapshot {
45			name: "mysdk@1",
46			java_header_libs: ["sdkmember_mysdk_1"],
47		}
48
49		java_import {
50			name: "sdkmember",
51			prefer: false,
52			host_supported: true,
53		}
54
55		java_import {
56			name: "sdkmember_mysdk_1",
57			sdk_member_name: "sdkmember",
58			host_supported: true,
59		}
60
61		java_library {
62			name: "myjavalib",
63			srcs: ["Test.java"],
64			libs: [
65				"sdkmember",
66				"otherlib",
67			],
68			system_modules: "none",
69			sdk_version: "none",
70			compile_dex: true,
71			host_supported: true,
72			apex_available: ["myapex"],
73		}
74
75		// this lib is no in mysdk
76		java_library {
77			name: "otherlib",
78			srcs: ["Test.java"],
79			system_modules: "none",
80			sdk_version: "none",
81			compile_dex: true,
82			host_supported: true,
83		}
84
85		apex {
86			name: "myapex",
87			java_libs: ["myjavalib"],
88			uses_sdks: ["mysdk@1"],
89			key: "myapex.key",
90			certificate: ":myapex.cert",
91		}
92	`)
93}
94
95// Ensure that prebuilt modules have the same effective visibility as the source
96// modules.
97func TestSnapshotVisibility(t *testing.T) {
98	packageBp := `
99		package {
100			default_visibility: ["//other/foo"],
101		}
102
103		sdk {
104			name: "mysdk",
105			visibility: [
106				"//other/foo",
107				// This short form will be replaced with //package:__subpackages__ in the
108				// generated sdk_snapshot.
109				":__subpackages__",
110			],
111			prebuilt_visibility: [
112				"//prebuilts/mysdk",
113			],
114			java_header_libs: [
115				"myjavalib",
116				"mypublicjavalib",
117				"mydefaultedjavalib",
118				"myprivatejavalib",
119			],
120		}
121
122		java_library {
123			name: "myjavalib",
124			// Uses package default visibility
125			srcs: ["Test.java"],
126			system_modules: "none",
127			sdk_version: "none",
128		}
129
130		java_defaults {
131			name: "java-defaults",
132			visibility: ["//other/bar"],
133		}
134
135		java_library {
136			name: "mypublicjavalib",
137			defaults: ["java-defaults"],
138      visibility: ["//visibility:public"],
139			srcs: ["Test.java"],
140			system_modules: "none",
141			sdk_version: "none",
142		}
143
144		java_defaults {
145			name: "myjavadefaults",
146			visibility: ["//other/bar"],
147		}
148
149		java_library {
150			name: "mydefaultedjavalib",
151			defaults: ["myjavadefaults"],
152			srcs: ["Test.java"],
153			system_modules: "none",
154			sdk_version: "none",
155		}
156
157		java_library {
158			name: "myprivatejavalib",
159			srcs: ["Test.java"],
160			visibility: ["//visibility:private"],
161			system_modules: "none",
162			sdk_version: "none",
163		}
164	`
165
166	result := testSdkWithFs(t, ``,
167		map[string][]byte{
168			"package/Test.java":  nil,
169			"package/Android.bp": []byte(packageBp),
170		})
171
172	CheckSnapshot(t, result, "mysdk", "package",
173		checkAndroidBpContents(`
174// This is auto-generated. DO NOT EDIT.
175
176java_import {
177    name: "mysdk_myjavalib@current",
178    sdk_member_name: "myjavalib",
179    visibility: [
180        "//other/foo",
181        "//package",
182        "//prebuilts/mysdk",
183    ],
184    apex_available: ["//apex_available:platform"],
185    jars: ["java/myjavalib.jar"],
186}
187
188java_import {
189    name: "myjavalib",
190    prefer: false,
191    visibility: [
192        "//other/foo",
193        "//package",
194        "//prebuilts/mysdk",
195    ],
196    apex_available: ["//apex_available:platform"],
197    jars: ["java/myjavalib.jar"],
198}
199
200java_import {
201    name: "mysdk_mypublicjavalib@current",
202    sdk_member_name: "mypublicjavalib",
203    visibility: ["//visibility:public"],
204    apex_available: ["//apex_available:platform"],
205    jars: ["java/mypublicjavalib.jar"],
206}
207
208java_import {
209    name: "mypublicjavalib",
210    prefer: false,
211    visibility: ["//visibility:public"],
212    apex_available: ["//apex_available:platform"],
213    jars: ["java/mypublicjavalib.jar"],
214}
215
216java_import {
217    name: "mysdk_mydefaultedjavalib@current",
218    sdk_member_name: "mydefaultedjavalib",
219    visibility: [
220        "//other/bar",
221        "//package",
222        "//prebuilts/mysdk",
223    ],
224    apex_available: ["//apex_available:platform"],
225    jars: ["java/mydefaultedjavalib.jar"],
226}
227
228java_import {
229    name: "mydefaultedjavalib",
230    prefer: false,
231    visibility: [
232        "//other/bar",
233        "//package",
234        "//prebuilts/mysdk",
235    ],
236    apex_available: ["//apex_available:platform"],
237    jars: ["java/mydefaultedjavalib.jar"],
238}
239
240java_import {
241    name: "mysdk_myprivatejavalib@current",
242    sdk_member_name: "myprivatejavalib",
243    visibility: [
244        "//package",
245        "//prebuilts/mysdk",
246    ],
247    apex_available: ["//apex_available:platform"],
248    jars: ["java/myprivatejavalib.jar"],
249}
250
251java_import {
252    name: "myprivatejavalib",
253    prefer: false,
254    visibility: [
255        "//package",
256        "//prebuilts/mysdk",
257    ],
258    apex_available: ["//apex_available:platform"],
259    jars: ["java/myprivatejavalib.jar"],
260}
261
262sdk_snapshot {
263    name: "mysdk@current",
264    visibility: [
265        "//other/foo",
266        "//package:__subpackages__",
267    ],
268    java_header_libs: [
269        "mysdk_myjavalib@current",
270        "mysdk_mypublicjavalib@current",
271        "mysdk_mydefaultedjavalib@current",
272        "mysdk_myprivatejavalib@current",
273    ],
274}
275`))
276}
277
278func TestPrebuiltVisibilityProperty_IsValidated(t *testing.T) {
279	testSdkError(t, `prebuilt_visibility: cannot mix "//visibility:private" with any other visibility rules`, `
280		sdk {
281			name: "mysdk",
282			prebuilt_visibility: [
283				"//foo",
284				"//visibility:private",
285			],
286		}
287`)
288}
289
290func TestPrebuiltVisibilityProperty_AddPrivate(t *testing.T) {
291	testSdkError(t, `prebuilt_visibility: "//visibility:private" does not widen the visibility`, `
292		sdk {
293			name: "mysdk",
294			prebuilt_visibility: [
295				"//visibility:private",
296			],
297			java_header_libs: [
298				"myjavalib",
299			],
300		}
301
302		java_library {
303			name: "myjavalib",
304			// Uses package default visibility
305			srcs: ["Test.java"],
306			system_modules: "none",
307			sdk_version: "none",
308		}
309`)
310}
311
312func TestSdkInstall(t *testing.T) {
313	sdk := `
314		sdk {
315			name: "mysdk",
316		}
317	`
318	result := testSdkWithFs(t, sdk, nil)
319
320	CheckSnapshot(t, result, "mysdk", "",
321		checkAllOtherCopyRules(`.intermediates/mysdk/common_os/mysdk-current.zip -> mysdk-current.zip`))
322}
323
324type EmbeddedPropertiesStruct struct {
325	S_Embedded_Common    string `android:"arch_variant"`
326	S_Embedded_Different string `android:"arch_variant"`
327}
328
329type testPropertiesStruct struct {
330	name        string
331	private     string
332	Public_Kept string `sdk:"keep"`
333	S_Common    string
334	S_Different string `android:"arch_variant"`
335	A_Common    []string
336	A_Different []string `android:"arch_variant"`
337	F_Common    *bool
338	F_Different *bool `android:"arch_variant"`
339	EmbeddedPropertiesStruct
340}
341
342func (p *testPropertiesStruct) optimizableProperties() interface{} {
343	return p
344}
345
346func (p *testPropertiesStruct) String() string {
347	return p.name
348}
349
350var _ propertiesContainer = (*testPropertiesStruct)(nil)
351
352func TestCommonValueOptimization(t *testing.T) {
353	common := &testPropertiesStruct{name: "common"}
354	structs := []propertiesContainer{
355		&testPropertiesStruct{
356			name:        "struct-0",
357			private:     "common",
358			Public_Kept: "common",
359			S_Common:    "common",
360			S_Different: "upper",
361			A_Common:    []string{"first", "second"},
362			A_Different: []string{"alpha", "beta"},
363			F_Common:    proptools.BoolPtr(false),
364			F_Different: proptools.BoolPtr(false),
365			EmbeddedPropertiesStruct: EmbeddedPropertiesStruct{
366				S_Embedded_Common:    "embedded_common",
367				S_Embedded_Different: "embedded_upper",
368			},
369		},
370		&testPropertiesStruct{
371			name:        "struct-1",
372			private:     "common",
373			Public_Kept: "common",
374			S_Common:    "common",
375			S_Different: "lower",
376			A_Common:    []string{"first", "second"},
377			A_Different: []string{"alpha", "delta"},
378			F_Common:    proptools.BoolPtr(false),
379			F_Different: proptools.BoolPtr(true),
380			EmbeddedPropertiesStruct: EmbeddedPropertiesStruct{
381				S_Embedded_Common:    "embedded_common",
382				S_Embedded_Different: "embedded_lower",
383			},
384		},
385	}
386
387	extractor := newCommonValueExtractor(common)
388
389	err := extractor.extractCommonProperties(common, structs)
390	android.AssertDeepEquals(t, "unexpected error", nil, err)
391
392	android.AssertDeepEquals(t, "common properties not correct",
393		&testPropertiesStruct{
394			name:        "common",
395			private:     "",
396			Public_Kept: "",
397			S_Common:    "common",
398			S_Different: "",
399			A_Common:    []string{"first", "second"},
400			A_Different: []string(nil),
401			F_Common:    proptools.BoolPtr(false),
402			F_Different: nil,
403			EmbeddedPropertiesStruct: EmbeddedPropertiesStruct{
404				S_Embedded_Common:    "embedded_common",
405				S_Embedded_Different: "",
406			},
407		},
408		common)
409
410	android.AssertDeepEquals(t, "updated properties[0] not correct",
411		&testPropertiesStruct{
412			name:        "struct-0",
413			private:     "common",
414			Public_Kept: "common",
415			S_Common:    "",
416			S_Different: "upper",
417			A_Common:    nil,
418			A_Different: []string{"alpha", "beta"},
419			F_Common:    nil,
420			F_Different: proptools.BoolPtr(false),
421			EmbeddedPropertiesStruct: EmbeddedPropertiesStruct{
422				S_Embedded_Common:    "",
423				S_Embedded_Different: "embedded_upper",
424			},
425		},
426		structs[0])
427
428	android.AssertDeepEquals(t, "updated properties[1] not correct",
429		&testPropertiesStruct{
430			name:        "struct-1",
431			private:     "common",
432			Public_Kept: "common",
433			S_Common:    "",
434			S_Different: "lower",
435			A_Common:    nil,
436			A_Different: []string{"alpha", "delta"},
437			F_Common:    nil,
438			F_Different: proptools.BoolPtr(true),
439			EmbeddedPropertiesStruct: EmbeddedPropertiesStruct{
440				S_Embedded_Common:    "",
441				S_Embedded_Different: "embedded_lower",
442			},
443		},
444		structs[1])
445}
446
447func TestCommonValueOptimization_InvalidArchSpecificVariants(t *testing.T) {
448	common := &testPropertiesStruct{name: "common"}
449	structs := []propertiesContainer{
450		&testPropertiesStruct{
451			name:     "struct-0",
452			S_Common: "should-be-but-is-not-common0",
453		},
454		&testPropertiesStruct{
455			name:     "struct-1",
456			S_Common: "should-be-but-is-not-common1",
457		},
458	}
459
460	extractor := newCommonValueExtractor(common)
461
462	err := extractor.extractCommonProperties(common, structs)
463	android.AssertErrorMessageEquals(t, "unexpected error", `field "S_Common" is not tagged as "arch_variant" but has arch specific properties:
464    "struct-0" has value "should-be-but-is-not-common0"
465    "struct-1" has value "should-be-but-is-not-common1"`, err)
466}
467
468// Ensure that sdk snapshot related environment variables work correctly.
469func TestSnapshot_EnvConfiguration(t *testing.T) {
470	bp := `
471		sdk {
472			name: "mysdk",
473			java_header_libs: ["myjavalib"],
474		}
475
476		java_library {
477			name: "myjavalib",
478			srcs: ["Test.java"],
479			system_modules: "none",
480			sdk_version: "none",
481			compile_dex: true,
482			host_supported: true,
483		}
484	`
485	preparer := android.GroupFixturePreparers(
486		prepareForSdkTestWithJava,
487		android.FixtureWithRootAndroidBp(bp),
488	)
489
490	checkZipFile := func(t *testing.T, result *android.TestResult, expected string) {
491		zipRule := result.ModuleForTests("mysdk", "common_os").Rule("SnapshotZipFiles")
492		android.AssertStringEquals(t, "snapshot zip file", expected, zipRule.Output.String())
493	}
494
495	t.Run("no env variables", func(t *testing.T) {
496		result := preparer.RunTest(t)
497
498		checkZipFile(t, result, "out/soong/.intermediates/mysdk/common_os/mysdk-current.zip")
499
500		CheckSnapshot(t, result, "mysdk", "",
501			checkAndroidBpContents(`
502// This is auto-generated. DO NOT EDIT.
503
504java_import {
505    name: "mysdk_myjavalib@current",
506    sdk_member_name: "myjavalib",
507    visibility: ["//visibility:public"],
508    apex_available: ["//apex_available:platform"],
509    jars: ["java/myjavalib.jar"],
510}
511
512java_import {
513    name: "myjavalib",
514    prefer: false,
515    visibility: ["//visibility:public"],
516    apex_available: ["//apex_available:platform"],
517    jars: ["java/myjavalib.jar"],
518}
519
520sdk_snapshot {
521    name: "mysdk@current",
522    visibility: ["//visibility:public"],
523    java_header_libs: ["mysdk_myjavalib@current"],
524}
525			`),
526		)
527	})
528
529	t.Run("SOONG_SDK_SNAPSHOT_PREFER=true", func(t *testing.T) {
530		result := android.GroupFixturePreparers(
531			preparer,
532			android.FixtureMergeEnv(map[string]string{
533				"SOONG_SDK_SNAPSHOT_PREFER": "true",
534			}),
535		).RunTest(t)
536
537		checkZipFile(t, result, "out/soong/.intermediates/mysdk/common_os/mysdk-current.zip")
538
539		CheckSnapshot(t, result, "mysdk", "",
540			checkAndroidBpContents(`
541// This is auto-generated. DO NOT EDIT.
542
543java_import {
544    name: "mysdk_myjavalib@current",
545    sdk_member_name: "myjavalib",
546    visibility: ["//visibility:public"],
547    apex_available: ["//apex_available:platform"],
548    jars: ["java/myjavalib.jar"],
549}
550
551java_import {
552    name: "myjavalib",
553    prefer: true,
554    visibility: ["//visibility:public"],
555    apex_available: ["//apex_available:platform"],
556    jars: ["java/myjavalib.jar"],
557}
558
559sdk_snapshot {
560    name: "mysdk@current",
561    visibility: ["//visibility:public"],
562    java_header_libs: ["mysdk_myjavalib@current"],
563}
564			`),
565		)
566	})
567
568	t.Run("SOONG_SDK_SNAPSHOT_VERSION=unversioned", func(t *testing.T) {
569		result := android.GroupFixturePreparers(
570			preparer,
571			android.FixtureMergeEnv(map[string]string{
572				"SOONG_SDK_SNAPSHOT_VERSION": "unversioned",
573			}),
574		).RunTest(t)
575
576		checkZipFile(t, result, "out/soong/.intermediates/mysdk/common_os/mysdk.zip")
577
578		CheckSnapshot(t, result, "mysdk", "",
579			checkAndroidBpContents(`
580// This is auto-generated. DO NOT EDIT.
581
582java_import {
583    name: "myjavalib",
584    prefer: false,
585    visibility: ["//visibility:public"],
586    apex_available: ["//apex_available:platform"],
587    jars: ["java/myjavalib.jar"],
588}
589			`),
590		)
591	})
592
593	t.Run("SOONG_SDK_SNAPSHOT_VERSION=current", func(t *testing.T) {
594		result := android.GroupFixturePreparers(
595			preparer,
596			android.FixtureMergeEnv(map[string]string{
597				"SOONG_SDK_SNAPSHOT_VERSION": "current",
598			}),
599		).RunTest(t)
600
601		checkZipFile(t, result, "out/soong/.intermediates/mysdk/common_os/mysdk-current.zip")
602
603		CheckSnapshot(t, result, "mysdk", "",
604			checkAndroidBpContents(`
605// This is auto-generated. DO NOT EDIT.
606
607java_import {
608    name: "mysdk_myjavalib@current",
609    sdk_member_name: "myjavalib",
610    visibility: ["//visibility:public"],
611    apex_available: ["//apex_available:platform"],
612    jars: ["java/myjavalib.jar"],
613}
614
615java_import {
616    name: "myjavalib",
617    prefer: false,
618    visibility: ["//visibility:public"],
619    apex_available: ["//apex_available:platform"],
620    jars: ["java/myjavalib.jar"],
621}
622
623sdk_snapshot {
624    name: "mysdk@current",
625    visibility: ["//visibility:public"],
626    java_header_libs: ["mysdk_myjavalib@current"],
627}
628			`),
629		)
630	})
631
632	t.Run("SOONG_SDK_SNAPSHOT_VERSION=2", func(t *testing.T) {
633		result := android.GroupFixturePreparers(
634			preparer,
635			android.FixtureMergeEnv(map[string]string{
636				"SOONG_SDK_SNAPSHOT_VERSION": "2",
637			}),
638		).RunTest(t)
639
640		checkZipFile(t, result, "out/soong/.intermediates/mysdk/common_os/mysdk-2.zip")
641
642		CheckSnapshot(t, result, "mysdk", "",
643			checkAndroidBpContents(`
644// This is auto-generated. DO NOT EDIT.
645
646java_import {
647    name: "mysdk_myjavalib@2",
648    sdk_member_name: "myjavalib",
649    visibility: ["//visibility:public"],
650    apex_available: ["//apex_available:platform"],
651    jars: ["java/myjavalib.jar"],
652}
653
654sdk_snapshot {
655    name: "mysdk@2",
656    visibility: ["//visibility:public"],
657    java_header_libs: ["mysdk_myjavalib@2"],
658}
659			`),
660			// A versioned snapshot cannot be used on its own so add the source back in.
661			snapshotTestPreparer(checkSnapshotWithoutSource, android.FixtureWithRootAndroidBp(bp)),
662		)
663	})
664}
665