blob: 47a58b1b4d05c95190334c8b9e0acf0a46be7170 [file] [log] [blame]
Paul Duffin82d90432019-11-30 09:24:33 +00001// 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 (
Paul Duffinc3c5d5e2019-11-29 20:45:22 +000018 "fmt"
Paul Duffin82d90432019-11-30 09:24:33 +000019 "io/ioutil"
20 "os"
Paul Duffinc3c5d5e2019-11-29 20:45:22 +000021 "path/filepath"
Paul Duffin82d90432019-11-30 09:24:33 +000022 "strings"
23 "testing"
24
25 "android/soong/android"
26 "android/soong/apex"
27 "android/soong/cc"
28 "android/soong/java"
29)
30
Paul Duffinc3c5d5e2019-11-29 20:45:22 +000031func testSdkContext(bp string, fs map[string][]byte) (*android.TestContext, android.Config) {
Paul Duffin82d90432019-11-30 09:24:33 +000032 config := android.TestArchConfig(buildDir, nil)
33 ctx := android.NewTestArchContext()
34
35 // from android package
36 ctx.PreArchMutators(android.RegisterDefaultsPreArchMutators)
37 ctx.PreArchMutators(func(ctx android.RegisterMutatorsContext) {
38 ctx.BottomUp("prebuilts", android.PrebuiltMutator).Parallel()
39 })
40 ctx.PostDepsMutators(func(ctx android.RegisterMutatorsContext) {
41 ctx.TopDown("prebuilt_select", android.PrebuiltSelectModuleMutator).Parallel()
42 ctx.BottomUp("prebuilt_postdeps", android.PrebuiltPostDepsMutator).Parallel()
43 })
44
45 // from java package
46 ctx.RegisterModuleType("android_app_certificate", java.AndroidAppCertificateFactory)
47 ctx.RegisterModuleType("java_library", java.LibraryFactory)
48 ctx.RegisterModuleType("java_import", java.ImportFactory)
49 ctx.RegisterModuleType("droidstubs", java.DroidstubsFactory)
50 ctx.RegisterModuleType("prebuilt_stubs_sources", java.PrebuiltStubsSourcesFactory)
51
52 // from cc package
53 ctx.RegisterModuleType("cc_library", cc.LibraryFactory)
54 ctx.RegisterModuleType("cc_library_shared", cc.LibrarySharedFactory)
55 ctx.RegisterModuleType("cc_object", cc.ObjectFactory)
56 ctx.RegisterModuleType("cc_prebuilt_library_shared", cc.PrebuiltSharedLibraryFactory)
57 ctx.RegisterModuleType("cc_prebuilt_library_static", cc.PrebuiltStaticLibraryFactory)
58 ctx.RegisterModuleType("llndk_library", cc.LlndkLibraryFactory)
59 ctx.RegisterModuleType("toolchain_library", cc.ToolchainLibraryFactory)
60 ctx.PreDepsMutators(func(ctx android.RegisterMutatorsContext) {
61 ctx.BottomUp("image", android.ImageMutator).Parallel()
62 ctx.BottomUp("link", cc.LinkageMutator).Parallel()
63 ctx.BottomUp("vndk", cc.VndkMutator).Parallel()
64 ctx.BottomUp("test_per_src", cc.TestPerSrcMutator).Parallel()
65 ctx.BottomUp("version", cc.VersionMutator).Parallel()
66 ctx.BottomUp("begin", cc.BeginMutator).Parallel()
67 })
68
69 // from apex package
70 ctx.RegisterModuleType("apex", apex.BundleFactory)
71 ctx.RegisterModuleType("apex_key", apex.ApexKeyFactory)
72 ctx.PostDepsMutators(apex.RegisterPostDepsMutators)
73
74 // from this package
75 ctx.RegisterModuleType("sdk", ModuleFactory)
76 ctx.RegisterModuleType("sdk_snapshot", SnapshotModuleFactory)
77 ctx.PreDepsMutators(RegisterPreDepsMutators)
78 ctx.PostDepsMutators(RegisterPostDepsMutators)
79
80 ctx.Register()
81
82 bp = bp + `
83 apex_key {
84 name: "myapex.key",
85 public_key: "myapex.avbpubkey",
86 private_key: "myapex.pem",
87 }
88
89 android_app_certificate {
90 name: "myapex.cert",
91 certificate: "myapex",
92 }
93 ` + cc.GatherRequiredDepsForTest(android.Android)
94
Paul Duffinc3c5d5e2019-11-29 20:45:22 +000095 mockFS := map[string][]byte{
Paul Duffin82d90432019-11-30 09:24:33 +000096 "Android.bp": []byte(bp),
97 "build/make/target/product/security": nil,
98 "apex_manifest.json": nil,
99 "system/sepolicy/apex/myapex-file_contexts": nil,
100 "system/sepolicy/apex/myapex2-file_contexts": nil,
101 "myapex.avbpubkey": nil,
102 "myapex.pem": nil,
103 "myapex.x509.pem": nil,
104 "myapex.pk8": nil,
Paul Duffinc3c5d5e2019-11-29 20:45:22 +0000105 }
106
107 for k, v := range fs {
108 mockFS[k] = v
109 }
110
111 ctx.MockFileSystem(mockFS)
Paul Duffin82d90432019-11-30 09:24:33 +0000112
113 return ctx, config
114}
115
Paul Duffinc3c5d5e2019-11-29 20:45:22 +0000116func testSdkWithFs(t *testing.T, bp string, fs map[string][]byte) *testSdkResult {
117 t.Helper()
118 ctx, config := testSdkContext(bp, fs)
Paul Duffin82d90432019-11-30 09:24:33 +0000119 _, errs := ctx.ParseFileList(".", []string{"Android.bp"})
120 android.FailIfErrored(t, errs)
121 _, errs = ctx.PrepareBuildActions(config)
122 android.FailIfErrored(t, errs)
Paul Duffinc3c5d5e2019-11-29 20:45:22 +0000123 return &testSdkResult{
124 TestHelper: TestHelper{t: t},
125 ctx: ctx,
126 config: config,
127 }
Paul Duffin82d90432019-11-30 09:24:33 +0000128}
129
130func testSdkError(t *testing.T, pattern, bp string) {
131 t.Helper()
Paul Duffinc3c5d5e2019-11-29 20:45:22 +0000132 ctx, config := testSdkContext(bp, nil)
Paul Duffin82d90432019-11-30 09:24:33 +0000133 _, errs := ctx.ParseFileList(".", []string{"Android.bp"})
134 if len(errs) > 0 {
135 android.FailIfNoMatchingErrors(t, pattern, errs)
136 return
137 }
138 _, errs = ctx.PrepareBuildActions(config)
139 if len(errs) > 0 {
140 android.FailIfNoMatchingErrors(t, pattern, errs)
141 return
142 }
143
144 t.Fatalf("missing expected error %q (0 errors are returned)", pattern)
145}
146
147func ensureListContains(t *testing.T, result []string, expected string) {
148 t.Helper()
149 if !android.InList(expected, result) {
150 t.Errorf("%q is not found in %v", expected, result)
151 }
152}
153
154func pathsToStrings(paths android.Paths) []string {
155 var ret []string
156 for _, p := range paths {
157 ret = append(ret, p.String())
158 }
159 return ret
160}
161
Paul Duffinc3c5d5e2019-11-29 20:45:22 +0000162// Provides general test support.
163type TestHelper struct {
164 t *testing.T
165}
166
167func (h *TestHelper) AssertStringEquals(message string, expected string, actual string) {
168 h.t.Helper()
169 if actual != expected {
170 h.t.Errorf("%s: expected %s, actual %s", message, expected, actual)
Paul Duffin82d90432019-11-30 09:24:33 +0000171 }
172}
173
Paul Duffinc3c5d5e2019-11-29 20:45:22 +0000174func (h *TestHelper) AssertTrimmedStringEquals(message string, expected string, actual string) {
175 h.t.Helper()
176 h.AssertStringEquals(message, strings.TrimSpace(expected), strings.TrimSpace(actual))
177}
178
179// Encapsulates result of processing an SDK definition. Provides support for
180// checking the state of the build structures.
181type testSdkResult struct {
182 TestHelper
183 ctx *android.TestContext
184 config android.Config
185}
186
187// Analyse the sdk build rules to extract information about what it is doing.
188
189// e.g. find the src/dest pairs from each cp command, the various zip files
190// generated, etc.
191func (r *testSdkResult) getSdkSnapshotBuildInfo(sdk *sdk) *snapshotBuildInfo {
192 androidBpContents := strings.NewReplacer("\\n", "\n").Replace(sdk.GetAndroidBpContentsForTests())
193
194 info := &snapshotBuildInfo{
195 r: r,
196 androidBpContents: androidBpContents,
197 }
198
199 buildParams := sdk.BuildParamsForTests()
200 copyRules := &strings.Builder{}
201 for _, bp := range buildParams {
202 switch bp.Rule.String() {
203 case android.Cp.String():
204 // Get source relative to build directory.
205 src := r.pathRelativeToBuildDir(bp.Input)
206 // Get destination relative to the snapshot root
207 dest := bp.Output.Rel()
208 _, _ = fmt.Fprintf(copyRules, "%s -> %s\n", src, dest)
209 info.snapshotContents = append(info.snapshotContents, dest)
210
211 case repackageZip.String():
212 // Add the destdir to the snapshot contents as that is effectively where
213 // the content of the repackaged zip is copied.
214 dest := bp.Args["destdir"]
215 info.snapshotContents = append(info.snapshotContents, dest)
216
217 case zipFiles.String():
218 // This could be an intermediate zip file and not the actual output zip.
219 // In that case this will be overridden when the rule to merge the zips
220 // is processed.
221 info.outputZip = r.pathRelativeToBuildDir(bp.Output)
222
223 case mergeZips.String():
224 // Copy the current outputZip to the intermediateZip.
225 info.intermediateZip = info.outputZip
226 mergeInput := r.pathRelativeToBuildDir(bp.Input)
227 if info.intermediateZip != mergeInput {
228 r.t.Errorf("Expected intermediate zip %s to be an input to merge zips but found %s instead",
229 info.intermediateZip, mergeInput)
230 }
231
232 // Override output zip (which was actually the intermediate zip file) with the actual
233 // output zip.
234 info.outputZip = r.pathRelativeToBuildDir(bp.Output)
235
236 // Save the zips to be merged into the intermediate zip.
237 info.mergeZips = r.pathsRelativeToBuildDir(bp.Inputs)
238 }
239 }
240
241 info.copyRules = copyRules.String()
242
243 return info
244}
245
246func (r *testSdkResult) Module(name string, variant string) android.Module {
247 return r.ctx.ModuleForTests(name, variant).Module()
248}
249
250func (r *testSdkResult) ModuleForTests(name string, variant string) android.TestingModule {
251 return r.ctx.ModuleForTests(name, variant)
252}
253
254func (r *testSdkResult) pathRelativeToBuildDir(path android.Path) string {
255 buildDir := filepath.Clean(r.config.BuildDir()) + "/"
256 return strings.TrimPrefix(filepath.Clean(path.String()), buildDir)
257}
258
259func (r *testSdkResult) pathsRelativeToBuildDir(paths android.Paths) []string {
260 var result []string
261 for _, path := range paths {
262 result = append(result, r.pathRelativeToBuildDir(path))
263 }
264 return result
265}
266
267// Check the snapshot build rules.
268//
269// Takes a list of functions which check different facets of the snapshot build rules.
270// Allows each test to customize what is checked without duplicating lots of code
271// or proliferating check methods of different flavors.
272func (r *testSdkResult) CheckSnapshot(name string, variant string, checkers ...snapshotBuildInfoChecker) {
273 r.t.Helper()
274
275 sdk := r.Module(name, variant).(*sdk)
276
277 snapshotBuildInfo := r.getSdkSnapshotBuildInfo(sdk)
278
279 // Check state of the snapshot build.
280 for _, checker := range checkers {
281 checker(snapshotBuildInfo)
282 }
283
284 // Make sure that the generated zip file is in the correct place.
285 actual := snapshotBuildInfo.outputZip
286 r.AssertStringEquals("Snapshot zip file in wrong place",
287 fmt.Sprintf(".intermediates/%s/%s/%s-current.zip", name, variant, name), actual)
288
289 // Populate a mock filesystem with the files that would have been copied by
290 // the rules.
291 fs := make(map[string][]byte)
292 for _, dest := range snapshotBuildInfo.snapshotContents {
293 fs[dest] = nil
294 }
295
296 // Process the generated bp file to make sure it is valid.
297 testSdkWithFs(r.t, snapshotBuildInfo.androidBpContents, fs)
298}
299
300type snapshotBuildInfoChecker func(info *snapshotBuildInfo)
301
302// Check that the snapshot's generated Android.bp is correct.
303//
304// Both the expected and actual string are both trimmed before comparing.
305func checkAndroidBpContents(expected string) snapshotBuildInfoChecker {
306 return func(info *snapshotBuildInfo) {
307 info.r.t.Helper()
308 info.r.AssertTrimmedStringEquals("Android.bp contents do not match", expected, info.androidBpContents)
309 }
310}
311
312// Check that the snapshot's copy rules are correct.
313//
314// The copy rules are formatted as <src> -> <dest>, one per line and then compared
315// to the supplied expected string. Both the expected and actual string are trimmed
316// before comparing.
317func checkAllCopyRules(expected string) snapshotBuildInfoChecker {
318 return func(info *snapshotBuildInfo) {
319 info.r.t.Helper()
320 info.r.AssertTrimmedStringEquals("Incorrect copy rules", expected, info.copyRules)
321 }
322}
323
324// Check that the specified path is in the list of zips to merge with the intermediate zip.
325func checkMergeZip(expected string) snapshotBuildInfoChecker {
326 return func(info *snapshotBuildInfo) {
327 info.r.t.Helper()
328 if info.intermediateZip == "" {
329 info.r.t.Errorf("No intermediate zip file was created")
330 }
331 ensureListContains(info.r.t, info.mergeZips, expected)
332 }
333}
334
335// Encapsulates information about the snapshot build structure in order to insulate tests from
336// knowing too much about internal structures.
337//
338// All source/input paths are relative either the build directory. All dest/output paths are
339// relative to the snapshot root directory.
340type snapshotBuildInfo struct {
341 r *testSdkResult
342
343 // The contents of the generated Android.bp file
344 androidBpContents string
345
346 // The paths, relative to the snapshot root, of all files and directories copied into the
347 // snapshot.
348 snapshotContents []string
349
350 // A formatted representation of the src/dest pairs, one pair per line, of the format
351 // src -> dest
352 copyRules string
353
354 // The path to the intermediate zip, which is a zip created from the source files copied
355 // into the snapshot directory and which will be merged with other zips to form the final output.
356 // Is am empty string if there is no intermediate zip because there are no zips to merge in.
357 intermediateZip string
358
359 // The paths to the zips to merge into the output zip, does not include the intermediate
360 // zip.
361 mergeZips []string
362
363 // The final output zip.
364 outputZip string
365}
366
Paul Duffin82d90432019-11-30 09:24:33 +0000367var buildDir string
368
369func setUp() {
370 var err error
371 buildDir, err = ioutil.TempDir("", "soong_sdk_test")
372 if err != nil {
373 panic(err)
374 }
375}
376
377func tearDown() {
378 _ = os.RemoveAll(buildDir)
379}
380
381func runTestWithBuildDir(m *testing.M) {
382 run := func() int {
383 setUp()
384 defer tearDown()
385
386 return m.Run()
387 }
388
389 os.Exit(run())
390}
391
392func SkipIfNotLinux(t *testing.T) {
393 t.Helper()
394 if android.BuildOs != android.Linux {
395 t.Skipf("Skipping as sdk snapshot generation is only supported on %s not %s", android.Linux, android.BuildOs)
396 }
397}