blob: ec451edc0d4bf60d52598acedd1a560895d3ea9f [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,
105 "Test.java": nil,
106 "Test.cpp": nil,
107 "include/Test.h": nil,
108 "aidl/foo/bar/Test.aidl": nil,
109 "libfoo.so": nil,
110 "stubs-sources/foo/bar/Foo.java": nil,
111 "foo/bar/Foo.java": nil,
Paul Duffinc3c5d5e2019-11-29 20:45:22 +0000112 }
113
114 for k, v := range fs {
115 mockFS[k] = v
116 }
117
118 ctx.MockFileSystem(mockFS)
Paul Duffin82d90432019-11-30 09:24:33 +0000119
120 return ctx, config
121}
122
Paul Duffinc3c5d5e2019-11-29 20:45:22 +0000123func testSdk(t *testing.T, bp string) *testSdkResult {
124 t.Helper()
125 return testSdkWithFs(t, bp, nil)
126}
127
128func testSdkWithFs(t *testing.T, bp string, fs map[string][]byte) *testSdkResult {
129 t.Helper()
130 ctx, config := testSdkContext(bp, fs)
Paul Duffin82d90432019-11-30 09:24:33 +0000131 _, errs := ctx.ParseFileList(".", []string{"Android.bp"})
132 android.FailIfErrored(t, errs)
133 _, errs = ctx.PrepareBuildActions(config)
134 android.FailIfErrored(t, errs)
Paul Duffinc3c5d5e2019-11-29 20:45:22 +0000135 return &testSdkResult{
136 TestHelper: TestHelper{t: t},
137 ctx: ctx,
138 config: config,
139 }
Paul Duffin82d90432019-11-30 09:24:33 +0000140}
141
142func testSdkError(t *testing.T, pattern, bp string) {
143 t.Helper()
Paul Duffinc3c5d5e2019-11-29 20:45:22 +0000144 ctx, config := testSdkContext(bp, nil)
Paul Duffin82d90432019-11-30 09:24:33 +0000145 _, errs := ctx.ParseFileList(".", []string{"Android.bp"})
146 if len(errs) > 0 {
147 android.FailIfNoMatchingErrors(t, pattern, errs)
148 return
149 }
150 _, errs = ctx.PrepareBuildActions(config)
151 if len(errs) > 0 {
152 android.FailIfNoMatchingErrors(t, pattern, errs)
153 return
154 }
155
156 t.Fatalf("missing expected error %q (0 errors are returned)", pattern)
157}
158
159func ensureListContains(t *testing.T, result []string, expected string) {
160 t.Helper()
161 if !android.InList(expected, result) {
162 t.Errorf("%q is not found in %v", expected, result)
163 }
164}
165
166func pathsToStrings(paths android.Paths) []string {
167 var ret []string
168 for _, p := range paths {
169 ret = append(ret, p.String())
170 }
171 return ret
172}
173
Paul Duffinc3c5d5e2019-11-29 20:45:22 +0000174// Provides general test support.
175type TestHelper struct {
176 t *testing.T
177}
178
179func (h *TestHelper) AssertStringEquals(message string, expected string, actual string) {
180 h.t.Helper()
181 if actual != expected {
182 h.t.Errorf("%s: expected %s, actual %s", message, expected, actual)
Paul Duffin82d90432019-11-30 09:24:33 +0000183 }
184}
185
Paul Duffinc3c5d5e2019-11-29 20:45:22 +0000186func (h *TestHelper) AssertTrimmedStringEquals(message string, expected string, actual string) {
187 h.t.Helper()
188 h.AssertStringEquals(message, strings.TrimSpace(expected), strings.TrimSpace(actual))
189}
190
191// Encapsulates result of processing an SDK definition. Provides support for
192// checking the state of the build structures.
193type testSdkResult struct {
194 TestHelper
195 ctx *android.TestContext
196 config android.Config
197}
198
199// Analyse the sdk build rules to extract information about what it is doing.
200
201// e.g. find the src/dest pairs from each cp command, the various zip files
202// generated, etc.
203func (r *testSdkResult) getSdkSnapshotBuildInfo(sdk *sdk) *snapshotBuildInfo {
204 androidBpContents := strings.NewReplacer("\\n", "\n").Replace(sdk.GetAndroidBpContentsForTests())
205
206 info := &snapshotBuildInfo{
207 r: r,
208 androidBpContents: androidBpContents,
209 }
210
211 buildParams := sdk.BuildParamsForTests()
212 copyRules := &strings.Builder{}
213 for _, bp := range buildParams {
214 switch bp.Rule.String() {
215 case android.Cp.String():
216 // Get source relative to build directory.
217 src := r.pathRelativeToBuildDir(bp.Input)
218 // Get destination relative to the snapshot root
219 dest := bp.Output.Rel()
220 _, _ = fmt.Fprintf(copyRules, "%s -> %s\n", src, dest)
221 info.snapshotContents = append(info.snapshotContents, dest)
222
223 case repackageZip.String():
224 // Add the destdir to the snapshot contents as that is effectively where
225 // the content of the repackaged zip is copied.
226 dest := bp.Args["destdir"]
227 info.snapshotContents = append(info.snapshotContents, dest)
228
229 case zipFiles.String():
230 // This could be an intermediate zip file and not the actual output zip.
231 // In that case this will be overridden when the rule to merge the zips
232 // is processed.
233 info.outputZip = r.pathRelativeToBuildDir(bp.Output)
234
235 case mergeZips.String():
236 // Copy the current outputZip to the intermediateZip.
237 info.intermediateZip = info.outputZip
238 mergeInput := r.pathRelativeToBuildDir(bp.Input)
239 if info.intermediateZip != mergeInput {
240 r.t.Errorf("Expected intermediate zip %s to be an input to merge zips but found %s instead",
241 info.intermediateZip, mergeInput)
242 }
243
244 // Override output zip (which was actually the intermediate zip file) with the actual
245 // output zip.
246 info.outputZip = r.pathRelativeToBuildDir(bp.Output)
247
248 // Save the zips to be merged into the intermediate zip.
249 info.mergeZips = r.pathsRelativeToBuildDir(bp.Inputs)
250 }
251 }
252
253 info.copyRules = copyRules.String()
254
255 return info
256}
257
258func (r *testSdkResult) Module(name string, variant string) android.Module {
259 return r.ctx.ModuleForTests(name, variant).Module()
260}
261
262func (r *testSdkResult) ModuleForTests(name string, variant string) android.TestingModule {
263 return r.ctx.ModuleForTests(name, variant)
264}
265
266func (r *testSdkResult) pathRelativeToBuildDir(path android.Path) string {
267 buildDir := filepath.Clean(r.config.BuildDir()) + "/"
268 return strings.TrimPrefix(filepath.Clean(path.String()), buildDir)
269}
270
271func (r *testSdkResult) pathsRelativeToBuildDir(paths android.Paths) []string {
272 var result []string
273 for _, path := range paths {
274 result = append(result, r.pathRelativeToBuildDir(path))
275 }
276 return result
277}
278
279// Check the snapshot build rules.
280//
281// Takes a list of functions which check different facets of the snapshot build rules.
282// Allows each test to customize what is checked without duplicating lots of code
283// or proliferating check methods of different flavors.
284func (r *testSdkResult) CheckSnapshot(name string, variant string, checkers ...snapshotBuildInfoChecker) {
285 r.t.Helper()
286
287 sdk := r.Module(name, variant).(*sdk)
288
289 snapshotBuildInfo := r.getSdkSnapshotBuildInfo(sdk)
290
291 // Check state of the snapshot build.
292 for _, checker := range checkers {
293 checker(snapshotBuildInfo)
294 }
295
296 // Make sure that the generated zip file is in the correct place.
297 actual := snapshotBuildInfo.outputZip
298 r.AssertStringEquals("Snapshot zip file in wrong place",
299 fmt.Sprintf(".intermediates/%s/%s/%s-current.zip", name, variant, name), actual)
300
301 // Populate a mock filesystem with the files that would have been copied by
302 // the rules.
303 fs := make(map[string][]byte)
304 for _, dest := range snapshotBuildInfo.snapshotContents {
305 fs[dest] = nil
306 }
307
308 // Process the generated bp file to make sure it is valid.
309 testSdkWithFs(r.t, snapshotBuildInfo.androidBpContents, fs)
310}
311
312type snapshotBuildInfoChecker func(info *snapshotBuildInfo)
313
314// Check that the snapshot's generated Android.bp is correct.
315//
316// Both the expected and actual string are both trimmed before comparing.
317func checkAndroidBpContents(expected string) snapshotBuildInfoChecker {
318 return func(info *snapshotBuildInfo) {
319 info.r.t.Helper()
320 info.r.AssertTrimmedStringEquals("Android.bp contents do not match", expected, info.androidBpContents)
321 }
322}
323
324// Check that the snapshot's copy rules are correct.
325//
326// The copy rules are formatted as <src> -> <dest>, one per line and then compared
327// to the supplied expected string. Both the expected and actual string are trimmed
328// before comparing.
329func checkAllCopyRules(expected string) snapshotBuildInfoChecker {
330 return func(info *snapshotBuildInfo) {
331 info.r.t.Helper()
332 info.r.AssertTrimmedStringEquals("Incorrect copy rules", expected, info.copyRules)
333 }
334}
335
336// Check that the specified path is in the list of zips to merge with the intermediate zip.
337func checkMergeZip(expected string) snapshotBuildInfoChecker {
338 return func(info *snapshotBuildInfo) {
339 info.r.t.Helper()
340 if info.intermediateZip == "" {
341 info.r.t.Errorf("No intermediate zip file was created")
342 }
343 ensureListContains(info.r.t, info.mergeZips, expected)
344 }
345}
346
347// Encapsulates information about the snapshot build structure in order to insulate tests from
348// knowing too much about internal structures.
349//
350// All source/input paths are relative either the build directory. All dest/output paths are
351// relative to the snapshot root directory.
352type snapshotBuildInfo struct {
353 r *testSdkResult
354
355 // The contents of the generated Android.bp file
356 androidBpContents string
357
358 // The paths, relative to the snapshot root, of all files and directories copied into the
359 // snapshot.
360 snapshotContents []string
361
362 // A formatted representation of the src/dest pairs, one pair per line, of the format
363 // src -> dest
364 copyRules string
365
366 // The path to the intermediate zip, which is a zip created from the source files copied
367 // into the snapshot directory and which will be merged with other zips to form the final output.
368 // Is am empty string if there is no intermediate zip because there are no zips to merge in.
369 intermediateZip string
370
371 // The paths to the zips to merge into the output zip, does not include the intermediate
372 // zip.
373 mergeZips []string
374
375 // The final output zip.
376 outputZip string
377}
378
Paul Duffin82d90432019-11-30 09:24:33 +0000379var buildDir string
380
381func setUp() {
382 var err error
383 buildDir, err = ioutil.TempDir("", "soong_sdk_test")
384 if err != nil {
385 panic(err)
386 }
387}
388
389func tearDown() {
390 _ = os.RemoveAll(buildDir)
391}
392
393func runTestWithBuildDir(m *testing.M) {
394 run := func() int {
395 setUp()
396 defer tearDown()
397
398 return m.Run()
399 }
400
401 os.Exit(run())
402}
403
404func SkipIfNotLinux(t *testing.T) {
405 t.Helper()
406 if android.BuildOs != android.Linux {
407 t.Skipf("Skipping as sdk snapshot generation is only supported on %s not %s", android.Linux, android.BuildOs)
408 }
409}