blob: d81ac2cbf83168f9401b14b3d390dc61ecea6be2 [file] [log] [blame]
Ulya Trafimovich69612672020-10-20 17:41:54 +01001// Copyright 2020 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 dexpreopt
16
17// This file contains unit tests for class loader context structure.
18// For class loader context tests involving .bp files, see TestUsesLibraries in java package.
19
20import (
Ulya Trafimovich7bc1cf52021-01-05 15:41:55 +000021 "fmt"
Ulya Trafimovich69612672020-10-20 17:41:54 +010022 "reflect"
23 "strings"
24 "testing"
25
26 "android/soong/android"
27)
28
29func TestCLC(t *testing.T) {
30 // Construct class loader context with the following structure:
31 // .
32 // ├── 29
33 // │   ├── android.hidl.manager
34 // │   └── android.hidl.base
35 // │
36 // └── any
37 // ├── a
38 // ├── b
39 // ├── c
40 // ├── d
Ulya Trafimovich8cbc5d22020-11-03 15:15:46 +000041 // │   ├── a2
42 // │   ├── b2
43 // │   └── c2
44 // │   ├── a1
45 // │   └── b1
Ulya Trafimovich69612672020-10-20 17:41:54 +010046 // ├── f
47 // ├── a3
48 // └── b3
49 //
50 ctx := testContext()
51
Ulya Trafimovichfc0f6e32021-08-12 16:16:11 +010052 optional := false
Ulya Trafimovich0b1c70e2021-08-20 15:39:12 +010053 implicit := true
Ulya Trafimovichfc0f6e32021-08-12 16:16:11 +010054
Ulya Trafimovich8cbc5d22020-11-03 15:15:46 +000055 m := make(ClassLoaderContextMap)
Ulya Trafimovich69612672020-10-20 17:41:54 +010056
Ulya Trafimovich0b1c70e2021-08-20 15:39:12 +010057 m.AddContext(ctx, AnySdkVersion, "a", optional, implicit, buildPath(ctx, "a"), installPath(ctx, "a"), nil)
58 m.AddContext(ctx, AnySdkVersion, "b", optional, implicit, buildPath(ctx, "b"), installPath(ctx, "b"), nil)
59 m.AddContext(ctx, AnySdkVersion, "c", optional, implicit, buildPath(ctx, "c"), installPath(ctx, "c"), nil)
Ulya Trafimovich69612672020-10-20 17:41:54 +010060
61 // Add some libraries with nested subcontexts.
62
Ulya Trafimovich8cbc5d22020-11-03 15:15:46 +000063 m1 := make(ClassLoaderContextMap)
Ulya Trafimovich0b1c70e2021-08-20 15:39:12 +010064 m1.AddContext(ctx, AnySdkVersion, "a1", optional, implicit, buildPath(ctx, "a1"), installPath(ctx, "a1"), nil)
65 m1.AddContext(ctx, AnySdkVersion, "b1", optional, implicit, buildPath(ctx, "b1"), installPath(ctx, "b1"), nil)
Ulya Trafimovich69612672020-10-20 17:41:54 +010066
Ulya Trafimovich8cbc5d22020-11-03 15:15:46 +000067 m2 := make(ClassLoaderContextMap)
Ulya Trafimovich0b1c70e2021-08-20 15:39:12 +010068 m2.AddContext(ctx, AnySdkVersion, "a2", optional, implicit, buildPath(ctx, "a2"), installPath(ctx, "a2"), nil)
69 m2.AddContext(ctx, AnySdkVersion, "b2", optional, implicit, buildPath(ctx, "b2"), installPath(ctx, "b2"), nil)
70 m2.AddContext(ctx, AnySdkVersion, "c2", optional, implicit, buildPath(ctx, "c2"), installPath(ctx, "c2"), m1)
Ulya Trafimovich69612672020-10-20 17:41:54 +010071
Ulya Trafimovich8cbc5d22020-11-03 15:15:46 +000072 m3 := make(ClassLoaderContextMap)
Ulya Trafimovich0b1c70e2021-08-20 15:39:12 +010073 m3.AddContext(ctx, AnySdkVersion, "a3", optional, implicit, buildPath(ctx, "a3"), installPath(ctx, "a3"), nil)
74 m3.AddContext(ctx, AnySdkVersion, "b3", optional, implicit, buildPath(ctx, "b3"), installPath(ctx, "b3"), nil)
Ulya Trafimovich69612672020-10-20 17:41:54 +010075
Ulya Trafimovich0b1c70e2021-08-20 15:39:12 +010076 m.AddContext(ctx, AnySdkVersion, "d", optional, implicit, buildPath(ctx, "d"), installPath(ctx, "d"), m2)
Ulya Trafimovich8cbc5d22020-11-03 15:15:46 +000077 // When the same library is both in conditional and unconditional context, it should be removed
78 // from conditional context.
Ulya Trafimovich0b1c70e2021-08-20 15:39:12 +010079 m.AddContext(ctx, 42, "f", optional, implicit, buildPath(ctx, "f"), installPath(ctx, "f"), nil)
80 m.AddContext(ctx, AnySdkVersion, "f", optional, implicit, buildPath(ctx, "f"), installPath(ctx, "f"), nil)
Ulya Trafimovich18554242020-11-03 15:55:11 +000081
82 // Merge map with implicit root library that is among toplevel contexts => does nothing.
83 m.AddContextMap(m1, "c")
84 // Merge map with implicit root library that is not among toplevel contexts => all subcontexts
85 // of the other map are added as toplevel contexts.
86 m.AddContextMap(m3, "m_g")
Ulya Trafimovich69612672020-10-20 17:41:54 +010087
88 // Compatibility libraries with unknown install paths get default paths.
Ulya Trafimovich0b1c70e2021-08-20 15:39:12 +010089 m.AddContext(ctx, 29, AndroidHidlManager, optional, implicit, buildPath(ctx, AndroidHidlManager), nil, nil)
90 m.AddContext(ctx, 29, AndroidHidlBase, optional, implicit, buildPath(ctx, AndroidHidlBase), nil, nil)
Ulya Trafimovich69612672020-10-20 17:41:54 +010091
92 // Add "android.test.mock" to conditional CLC, observe that is gets removed because it is only
93 // needed as a compatibility library if "android.test.runner" is in CLC as well.
Ulya Trafimovich0b1c70e2021-08-20 15:39:12 +010094 m.AddContext(ctx, 30, AndroidTestMock, optional, implicit, buildPath(ctx, AndroidTestMock), nil, nil)
Ulya Trafimovich69612672020-10-20 17:41:54 +010095
Ulya Trafimovich8cbc5d22020-11-03 15:15:46 +000096 valid, validationError := validateClassLoaderContext(m)
Ulya Trafimovich69612672020-10-20 17:41:54 +010097
Ulya Trafimovich8cbc5d22020-11-03 15:15:46 +000098 fixClassLoaderContext(m)
Ulya Trafimovich69612672020-10-20 17:41:54 +010099
100 var haveStr string
101 var havePaths android.Paths
Ulya Trafimovichfc0f6e32021-08-12 16:16:11 +0100102 var haveUsesLibsReq, haveUsesLibsOpt []string
Ulya Trafimovich8cbc5d22020-11-03 15:15:46 +0000103 if valid && validationError == nil {
104 haveStr, havePaths = ComputeClassLoaderContext(m)
Ulya Trafimovichfc0f6e32021-08-12 16:16:11 +0100105 haveUsesLibsReq, haveUsesLibsOpt = m.UsesLibs()
Ulya Trafimovich69612672020-10-20 17:41:54 +0100106 }
107
108 // Test that validation is successful (all paths are known).
109 t.Run("validate", func(t *testing.T) {
Ulya Trafimovich8cbc5d22020-11-03 15:15:46 +0000110 if !(valid && validationError == nil) {
Ulya Trafimovich69612672020-10-20 17:41:54 +0100111 t.Errorf("invalid class loader context")
112 }
113 })
114
115 // Test that class loader context structure is correct.
116 t.Run("string", func(t *testing.T) {
117 wantStr := " --host-context-for-sdk 29 " +
118 "PCL[out/" + AndroidHidlManager + ".jar]#" +
119 "PCL[out/" + AndroidHidlBase + ".jar]" +
120 " --target-context-for-sdk 29 " +
121 "PCL[/system/framework/" + AndroidHidlManager + ".jar]#" +
122 "PCL[/system/framework/" + AndroidHidlBase + ".jar]" +
123 " --host-context-for-sdk any " +
Ulya Trafimovich8cbc5d22020-11-03 15:15:46 +0000124 "PCL[out/a.jar]#PCL[out/b.jar]#PCL[out/c.jar]#PCL[out/d.jar]" +
125 "{PCL[out/a2.jar]#PCL[out/b2.jar]#PCL[out/c2.jar]" +
126 "{PCL[out/a1.jar]#PCL[out/b1.jar]}}#" +
Ulya Trafimovich69612672020-10-20 17:41:54 +0100127 "PCL[out/f.jar]#PCL[out/a3.jar]#PCL[out/b3.jar]" +
128 " --target-context-for-sdk any " +
Ulya Trafimovich8cbc5d22020-11-03 15:15:46 +0000129 "PCL[/system/a.jar]#PCL[/system/b.jar]#PCL[/system/c.jar]#PCL[/system/d.jar]" +
130 "{PCL[/system/a2.jar]#PCL[/system/b2.jar]#PCL[/system/c2.jar]" +
131 "{PCL[/system/a1.jar]#PCL[/system/b1.jar]}}#" +
Ulya Trafimovich69612672020-10-20 17:41:54 +0100132 "PCL[/system/f.jar]#PCL[/system/a3.jar]#PCL[/system/b3.jar]"
133 if wantStr != haveStr {
134 t.Errorf("\nwant class loader context: %s\nhave class loader context: %s", wantStr, haveStr)
135 }
136 })
137
138 // Test that all expected build paths are gathered.
139 t.Run("paths", func(t *testing.T) {
140 wantPaths := []string{
141 "out/android.hidl.manager-V1.0-java.jar", "out/android.hidl.base-V1.0-java.jar",
142 "out/a.jar", "out/b.jar", "out/c.jar", "out/d.jar",
143 "out/a2.jar", "out/b2.jar", "out/c2.jar",
144 "out/a1.jar", "out/b1.jar",
145 "out/f.jar", "out/a3.jar", "out/b3.jar",
146 }
147 if !reflect.DeepEqual(wantPaths, havePaths.Strings()) {
148 t.Errorf("\nwant paths: %s\nhave paths: %s", wantPaths, havePaths)
149 }
150 })
151
152 // Test for libraries that are added by the manifest_fixer.
153 t.Run("uses libs", func(t *testing.T) {
Ulya Trafimovichfc0f6e32021-08-12 16:16:11 +0100154 wantUsesLibsReq := []string{"a", "b", "c", "d", "f", "a3", "b3"}
155 wantUsesLibsOpt := []string{}
156 if !reflect.DeepEqual(wantUsesLibsReq, haveUsesLibsReq) {
157 t.Errorf("\nwant required uses libs: %s\nhave required uses libs: %s", wantUsesLibsReq, haveUsesLibsReq)
158 }
159 if !reflect.DeepEqual(wantUsesLibsOpt, haveUsesLibsOpt) {
160 t.Errorf("\nwant optional uses libs: %s\nhave optional uses libs: %s", wantUsesLibsOpt, haveUsesLibsOpt)
Ulya Trafimovich69612672020-10-20 17:41:54 +0100161 }
162 })
163}
164
Jeongik Cha19ade892021-04-22 20:55:21 +0900165func TestCLCJson(t *testing.T) {
166 ctx := testContext()
Ulya Trafimovichfc0f6e32021-08-12 16:16:11 +0100167 optional := false
Ulya Trafimovich0b1c70e2021-08-20 15:39:12 +0100168 implicit := true
Jeongik Cha19ade892021-04-22 20:55:21 +0900169 m := make(ClassLoaderContextMap)
Ulya Trafimovich0b1c70e2021-08-20 15:39:12 +0100170 m.AddContext(ctx, 28, "a", optional, implicit, buildPath(ctx, "a"), installPath(ctx, "a"), nil)
171 m.AddContext(ctx, 29, "b", optional, implicit, buildPath(ctx, "b"), installPath(ctx, "b"), nil)
172 m.AddContext(ctx, 30, "c", optional, implicit, buildPath(ctx, "c"), installPath(ctx, "c"), nil)
173 m.AddContext(ctx, AnySdkVersion, "d", optional, implicit, buildPath(ctx, "d"), installPath(ctx, "d"), nil)
Jeongik Cha19ade892021-04-22 20:55:21 +0900174 jsonCLC := toJsonClassLoaderContext(m)
175 restored := fromJsonClassLoaderContext(ctx, jsonCLC)
176 android.AssertIntEquals(t, "The size of the maps should be the same.", len(m), len(restored))
177 for k := range m {
178 a, _ := m[k]
179 b, ok := restored[k]
180 android.AssertBoolEquals(t, "The both maps should have the same keys.", ok, true)
181 android.AssertIntEquals(t, "The size of the elements should be the same.", len(a), len(b))
182 for i, elemA := range a {
183 before := fmt.Sprintf("%v", *elemA)
184 after := fmt.Sprintf("%v", *b[i])
185 android.AssertStringEquals(t, "The content should be the same.", before, after)
186 }
187 }
188}
189
Ulya Trafimovich7bc1cf52021-01-05 15:41:55 +0000190// Test that unknown library paths cause a validation error.
191func testCLCUnknownPath(t *testing.T, whichPath string) {
Ulya Trafimovich69612672020-10-20 17:41:54 +0100192 ctx := testContext()
Ulya Trafimovichfc0f6e32021-08-12 16:16:11 +0100193 optional := false
Ulya Trafimovich0b1c70e2021-08-20 15:39:12 +0100194 implicit := true
Ulya Trafimovich69612672020-10-20 17:41:54 +0100195
Ulya Trafimovich8cbc5d22020-11-03 15:15:46 +0000196 m := make(ClassLoaderContextMap)
Ulya Trafimovich7bc1cf52021-01-05 15:41:55 +0000197 if whichPath == "build" {
Ulya Trafimovich0b1c70e2021-08-20 15:39:12 +0100198 m.AddContext(ctx, AnySdkVersion, "a", optional, implicit, nil, nil, nil)
Ulya Trafimovich7bc1cf52021-01-05 15:41:55 +0000199 } else {
Ulya Trafimovich0b1c70e2021-08-20 15:39:12 +0100200 m.AddContext(ctx, AnySdkVersion, "a", optional, implicit, buildPath(ctx, "a"), nil, nil)
Ulya Trafimovich7bc1cf52021-01-05 15:41:55 +0000201 }
Ulya Trafimovich69612672020-10-20 17:41:54 +0100202
Ulya Trafimovich8cbc5d22020-11-03 15:15:46 +0000203 // The library should be added to <uses-library> tags by the manifest_fixer.
Ulya Trafimovich7bc1cf52021-01-05 15:41:55 +0000204 t.Run("uses libs", func(t *testing.T) {
Ulya Trafimovichfc0f6e32021-08-12 16:16:11 +0100205 haveUsesLibsReq, haveUsesLibsOpt := m.UsesLibs()
206 wantUsesLibsReq := []string{"a"}
207 wantUsesLibsOpt := []string{}
208 if !reflect.DeepEqual(wantUsesLibsReq, haveUsesLibsReq) {
209 t.Errorf("\nwant required uses libs: %s\nhave required uses libs: %s", wantUsesLibsReq, haveUsesLibsReq)
210 }
211 if !reflect.DeepEqual(wantUsesLibsOpt, haveUsesLibsOpt) {
212 t.Errorf("\nwant optional uses libs: %s\nhave optional uses libs: %s", wantUsesLibsOpt, haveUsesLibsOpt)
Ulya Trafimovich8cbc5d22020-11-03 15:15:46 +0000213 }
214 })
Ulya Trafimovich69612672020-10-20 17:41:54 +0100215
Ulya Trafimovich7bc1cf52021-01-05 15:41:55 +0000216 // But CLC cannot be constructed: there is a validation error.
217 _, err := validateClassLoaderContext(m)
218 checkError(t, err, fmt.Sprintf("invalid %s path for <uses-library> \"a\"", whichPath))
219}
220
221// Test that unknown build path is an error.
222func TestCLCUnknownBuildPath(t *testing.T) {
223 testCLCUnknownPath(t, "build")
224}
225
226// Test that unknown install path is an error.
227func TestCLCUnknownInstallPath(t *testing.T) {
228 testCLCUnknownPath(t, "install")
Ulya Trafimovich69612672020-10-20 17:41:54 +0100229}
230
Ulya Trafimovich5e13a732020-11-03 15:33:03 +0000231// An attempt to add conditional nested subcontext should fail.
232func TestCLCNestedConditional(t *testing.T) {
233 ctx := testContext()
Ulya Trafimovichfc0f6e32021-08-12 16:16:11 +0100234 optional := false
Ulya Trafimovich0b1c70e2021-08-20 15:39:12 +0100235 implicit := true
Ulya Trafimovich5e13a732020-11-03 15:33:03 +0000236 m1 := make(ClassLoaderContextMap)
Ulya Trafimovich0b1c70e2021-08-20 15:39:12 +0100237 m1.AddContext(ctx, 42, "a", optional, implicit, buildPath(ctx, "a"), installPath(ctx, "a"), nil)
Ulya Trafimovich5e13a732020-11-03 15:33:03 +0000238 m := make(ClassLoaderContextMap)
Ulya Trafimovich0b1c70e2021-08-20 15:39:12 +0100239 err := m.addContext(ctx, AnySdkVersion, "b", optional, implicit, buildPath(ctx, "b"), installPath(ctx, "b"), m1)
Ulya Trafimovich5e13a732020-11-03 15:33:03 +0000240 checkError(t, err, "nested class loader context shouldn't have conditional part")
241}
242
Ulya Trafimovichc9f2b942020-12-23 15:41:29 +0000243// Test for SDK version order in conditional CLC: no matter in what order the libraries are added,
244// they end up in the order that agrees with PackageManager.
245func TestCLCSdkVersionOrder(t *testing.T) {
246 ctx := testContext()
Ulya Trafimovichfc0f6e32021-08-12 16:16:11 +0100247 optional := false
Ulya Trafimovich0b1c70e2021-08-20 15:39:12 +0100248 implicit := true
Ulya Trafimovichc9f2b942020-12-23 15:41:29 +0000249 m := make(ClassLoaderContextMap)
Ulya Trafimovich0b1c70e2021-08-20 15:39:12 +0100250 m.AddContext(ctx, 28, "a", optional, implicit, buildPath(ctx, "a"), installPath(ctx, "a"), nil)
251 m.AddContext(ctx, 29, "b", optional, implicit, buildPath(ctx, "b"), installPath(ctx, "b"), nil)
252 m.AddContext(ctx, 30, "c", optional, implicit, buildPath(ctx, "c"), installPath(ctx, "c"), nil)
253 m.AddContext(ctx, AnySdkVersion, "d", optional, implicit, buildPath(ctx, "d"), installPath(ctx, "d"), nil)
Ulya Trafimovichc9f2b942020-12-23 15:41:29 +0000254
255 valid, validationError := validateClassLoaderContext(m)
256
257 fixClassLoaderContext(m)
258
259 var haveStr string
260 if valid && validationError == nil {
261 haveStr, _ = ComputeClassLoaderContext(m)
262 }
263
264 // Test that validation is successful (all paths are known).
265 t.Run("validate", func(t *testing.T) {
266 if !(valid && validationError == nil) {
267 t.Errorf("invalid class loader context")
268 }
269 })
270
271 // Test that class loader context structure is correct.
272 t.Run("string", func(t *testing.T) {
273 wantStr := " --host-context-for-sdk 30 PCL[out/c.jar]" +
274 " --target-context-for-sdk 30 PCL[/system/c.jar]" +
275 " --host-context-for-sdk 29 PCL[out/b.jar]" +
276 " --target-context-for-sdk 29 PCL[/system/b.jar]" +
277 " --host-context-for-sdk 28 PCL[out/a.jar]" +
278 " --target-context-for-sdk 28 PCL[/system/a.jar]" +
279 " --host-context-for-sdk any PCL[out/d.jar]" +
280 " --target-context-for-sdk any PCL[/system/d.jar]"
281 if wantStr != haveStr {
282 t.Errorf("\nwant class loader context: %s\nhave class loader context: %s", wantStr, haveStr)
283 }
284 })
285}
286
Ulya Trafimovich69612672020-10-20 17:41:54 +0100287func checkError(t *testing.T, have error, want string) {
288 if have == nil {
289 t.Errorf("\nwant error: '%s'\nhave: none", want)
290 } else if msg := have.Error(); !strings.HasPrefix(msg, want) {
291 t.Errorf("\nwant error: '%s'\nhave error: '%s'\n", want, msg)
292 }
293}
294
295func testContext() android.ModuleInstallPathContext {
296 config := android.TestConfig("out", nil, "", nil)
297 return android.ModuleInstallPathContextForTesting(config)
298}
299
300func buildPath(ctx android.PathContext, lib string) android.Path {
301 return android.PathForOutput(ctx, lib+".jar")
302}
303
304func installPath(ctx android.ModuleInstallPathContext, lib string) android.InstallPath {
305 return android.PathForModuleInstall(ctx, lib+".jar")
306}