blob: 6b6b1629a8696be5f6884b6d305b178df3affaaa [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 (
21 "reflect"
22 "strings"
23 "testing"
24
25 "android/soong/android"
26)
27
28func TestCLC(t *testing.T) {
29 // Construct class loader context with the following structure:
30 // .
31 // ├── 29
32 // │   ├── android.hidl.manager
33 // │   └── android.hidl.base
34 // │
35 // └── any
36 // ├── a
37 // ├── b
38 // ├── c
39 // ├── d
Ulya Trafimovich8cbc5d22020-11-03 15:15:46 +000040 // │   ├── a2
41 // │   ├── b2
42 // │   └── c2
43 // │   ├── a1
44 // │   └── b1
Ulya Trafimovich69612672020-10-20 17:41:54 +010045 // ├── f
46 // ├── a3
47 // └── b3
48 //
49 ctx := testContext()
50
Ulya Trafimovich8cbc5d22020-11-03 15:15:46 +000051 m := make(ClassLoaderContextMap)
Ulya Trafimovich69612672020-10-20 17:41:54 +010052
Ulya Trafimovich78a71552020-11-25 14:20:52 +000053 m.AddContext(ctx, "a", buildPath(ctx, "a"), installPath(ctx, "a"))
54 m.AddContext(ctx, "b", buildPath(ctx, "b"), installPath(ctx, "b"))
Ulya Trafimovich69612672020-10-20 17:41:54 +010055
56 // "Maybe" variant in the good case: add as usual.
57 c := "c"
Ulya Trafimovich78a71552020-11-25 14:20:52 +000058 m.MaybeAddContext(ctx, &c, buildPath(ctx, "c"), installPath(ctx, "c"))
Ulya Trafimovich69612672020-10-20 17:41:54 +010059
60 // "Maybe" variant in the bad case: don't add library with unknown name, keep going.
Ulya Trafimovich78a71552020-11-25 14:20:52 +000061 m.MaybeAddContext(ctx, nil, nil, nil)
Ulya Trafimovich69612672020-10-20 17:41:54 +010062
63 // Add some libraries with nested subcontexts.
64
Ulya Trafimovich8cbc5d22020-11-03 15:15:46 +000065 m1 := make(ClassLoaderContextMap)
Ulya Trafimovich78a71552020-11-25 14:20:52 +000066 m1.AddContext(ctx, "a1", buildPath(ctx, "a1"), installPath(ctx, "a1"))
67 m1.AddContext(ctx, "b1", buildPath(ctx, "b1"), installPath(ctx, "b1"))
Ulya Trafimovich69612672020-10-20 17:41:54 +010068
Ulya Trafimovich8cbc5d22020-11-03 15:15:46 +000069 m2 := make(ClassLoaderContextMap)
Ulya Trafimovich78a71552020-11-25 14:20:52 +000070 m2.AddContext(ctx, "a2", buildPath(ctx, "a2"), installPath(ctx, "a2"))
71 m2.AddContext(ctx, "b2", buildPath(ctx, "b2"), installPath(ctx, "b2"))
72 m2.AddContextForSdk(ctx, AnySdkVersion, "c2", buildPath(ctx, "c2"), installPath(ctx, "c2"), m1)
Ulya Trafimovich69612672020-10-20 17:41:54 +010073
Ulya Trafimovich8cbc5d22020-11-03 15:15:46 +000074 m3 := make(ClassLoaderContextMap)
Ulya Trafimovich78a71552020-11-25 14:20:52 +000075 m3.AddContext(ctx, "a3", buildPath(ctx, "a3"), installPath(ctx, "a3"))
76 m3.AddContext(ctx, "b3", buildPath(ctx, "b3"), installPath(ctx, "b3"))
Ulya Trafimovich69612672020-10-20 17:41:54 +010077
Ulya Trafimovich78a71552020-11-25 14:20:52 +000078 m.AddContextForSdk(ctx, AnySdkVersion, "d", buildPath(ctx, "d"), installPath(ctx, "d"), m2)
Ulya Trafimovich8cbc5d22020-11-03 15:15:46 +000079 // When the same library is both in conditional and unconditional context, it should be removed
80 // from conditional context.
Ulya Trafimovich78a71552020-11-25 14:20:52 +000081 m.AddContextForSdk(ctx, 42, "f", buildPath(ctx, "f"), installPath(ctx, "f"), nil)
82 m.AddContextForSdk(ctx, AnySdkVersion, "f", buildPath(ctx, "f"), installPath(ctx, "f"), nil)
Ulya Trafimovich18554242020-11-03 15:55:11 +000083
84 // Merge map with implicit root library that is among toplevel contexts => does nothing.
85 m.AddContextMap(m1, "c")
86 // Merge map with implicit root library that is not among toplevel contexts => all subcontexts
87 // of the other map are added as toplevel contexts.
88 m.AddContextMap(m3, "m_g")
Ulya Trafimovich69612672020-10-20 17:41:54 +010089
90 // Compatibility libraries with unknown install paths get default paths.
Ulya Trafimovich78a71552020-11-25 14:20:52 +000091 m.AddContextForSdk(ctx, 29, AndroidHidlManager, buildPath(ctx, AndroidHidlManager), nil, nil)
92 m.AddContextForSdk(ctx, 29, AndroidHidlBase, buildPath(ctx, AndroidHidlBase), nil, nil)
Ulya Trafimovich69612672020-10-20 17:41:54 +010093
94 // Add "android.test.mock" to conditional CLC, observe that is gets removed because it is only
95 // needed as a compatibility library if "android.test.runner" is in CLC as well.
Ulya Trafimovich78a71552020-11-25 14:20:52 +000096 m.AddContextForSdk(ctx, 30, AndroidTestMock, buildPath(ctx, AndroidTestMock), nil, nil)
Ulya Trafimovich69612672020-10-20 17:41:54 +010097
Ulya Trafimovich8cbc5d22020-11-03 15:15:46 +000098 valid, validationError := validateClassLoaderContext(m)
Ulya Trafimovich69612672020-10-20 17:41:54 +010099
Ulya Trafimovich8cbc5d22020-11-03 15:15:46 +0000100 fixClassLoaderContext(m)
Ulya Trafimovich69612672020-10-20 17:41:54 +0100101
102 var haveStr string
103 var havePaths android.Paths
104 var haveUsesLibs []string
Ulya Trafimovich8cbc5d22020-11-03 15:15:46 +0000105 if valid && validationError == nil {
106 haveStr, havePaths = ComputeClassLoaderContext(m)
107 haveUsesLibs = m.UsesLibs()
Ulya Trafimovich69612672020-10-20 17:41:54 +0100108 }
109
110 // Test that validation is successful (all paths are known).
111 t.Run("validate", func(t *testing.T) {
Ulya Trafimovich8cbc5d22020-11-03 15:15:46 +0000112 if !(valid && validationError == nil) {
Ulya Trafimovich69612672020-10-20 17:41:54 +0100113 t.Errorf("invalid class loader context")
114 }
115 })
116
117 // Test that class loader context structure is correct.
118 t.Run("string", func(t *testing.T) {
119 wantStr := " --host-context-for-sdk 29 " +
120 "PCL[out/" + AndroidHidlManager + ".jar]#" +
121 "PCL[out/" + AndroidHidlBase + ".jar]" +
122 " --target-context-for-sdk 29 " +
123 "PCL[/system/framework/" + AndroidHidlManager + ".jar]#" +
124 "PCL[/system/framework/" + AndroidHidlBase + ".jar]" +
125 " --host-context-for-sdk any " +
Ulya Trafimovich8cbc5d22020-11-03 15:15:46 +0000126 "PCL[out/a.jar]#PCL[out/b.jar]#PCL[out/c.jar]#PCL[out/d.jar]" +
127 "{PCL[out/a2.jar]#PCL[out/b2.jar]#PCL[out/c2.jar]" +
128 "{PCL[out/a1.jar]#PCL[out/b1.jar]}}#" +
Ulya Trafimovich69612672020-10-20 17:41:54 +0100129 "PCL[out/f.jar]#PCL[out/a3.jar]#PCL[out/b3.jar]" +
130 " --target-context-for-sdk any " +
Ulya Trafimovich8cbc5d22020-11-03 15:15:46 +0000131 "PCL[/system/a.jar]#PCL[/system/b.jar]#PCL[/system/c.jar]#PCL[/system/d.jar]" +
132 "{PCL[/system/a2.jar]#PCL[/system/b2.jar]#PCL[/system/c2.jar]" +
133 "{PCL[/system/a1.jar]#PCL[/system/b1.jar]}}#" +
Ulya Trafimovich69612672020-10-20 17:41:54 +0100134 "PCL[/system/f.jar]#PCL[/system/a3.jar]#PCL[/system/b3.jar]"
135 if wantStr != haveStr {
136 t.Errorf("\nwant class loader context: %s\nhave class loader context: %s", wantStr, haveStr)
137 }
138 })
139
140 // Test that all expected build paths are gathered.
141 t.Run("paths", func(t *testing.T) {
142 wantPaths := []string{
143 "out/android.hidl.manager-V1.0-java.jar", "out/android.hidl.base-V1.0-java.jar",
144 "out/a.jar", "out/b.jar", "out/c.jar", "out/d.jar",
145 "out/a2.jar", "out/b2.jar", "out/c2.jar",
146 "out/a1.jar", "out/b1.jar",
147 "out/f.jar", "out/a3.jar", "out/b3.jar",
148 }
149 if !reflect.DeepEqual(wantPaths, havePaths.Strings()) {
150 t.Errorf("\nwant paths: %s\nhave paths: %s", wantPaths, havePaths)
151 }
152 })
153
154 // Test for libraries that are added by the manifest_fixer.
155 t.Run("uses libs", func(t *testing.T) {
Ulya Trafimovich78a71552020-11-25 14:20:52 +0000156 wantUsesLibs := []string{"a", "b", "c", "d", "f", "a3", "b3"}
Ulya Trafimovich69612672020-10-20 17:41:54 +0100157 if !reflect.DeepEqual(wantUsesLibs, haveUsesLibs) {
158 t.Errorf("\nwant uses libs: %s\nhave uses libs: %s", wantUsesLibs, haveUsesLibs)
159 }
160 })
161}
162
163// Test that an unexpected unknown build path causes immediate error.
164func TestCLCUnknownBuildPath(t *testing.T) {
165 ctx := testContext()
Ulya Trafimovich8cbc5d22020-11-03 15:15:46 +0000166 m := make(ClassLoaderContextMap)
Ulya Trafimovich78a71552020-11-25 14:20:52 +0000167 err := m.addContext(ctx, AnySdkVersion, "a", nil, nil, true, nil)
Ulya Trafimovich8cbc5d22020-11-03 15:15:46 +0000168 checkError(t, err, "unknown build path to <uses-library> \"a\"")
Ulya Trafimovich69612672020-10-20 17:41:54 +0100169}
170
171// Test that an unexpected unknown install path causes immediate error.
172func TestCLCUnknownInstallPath(t *testing.T) {
173 ctx := testContext()
Ulya Trafimovich8cbc5d22020-11-03 15:15:46 +0000174 m := make(ClassLoaderContextMap)
Ulya Trafimovich78a71552020-11-25 14:20:52 +0000175 err := m.addContext(ctx, AnySdkVersion, "a", buildPath(ctx, "a"), nil, true, nil)
Ulya Trafimovich8cbc5d22020-11-03 15:15:46 +0000176 checkError(t, err, "unknown install path to <uses-library> \"a\"")
Ulya Trafimovich69612672020-10-20 17:41:54 +0100177}
178
179func TestCLCMaybeAdd(t *testing.T) {
180 ctx := testContext()
181
Ulya Trafimovich8cbc5d22020-11-03 15:15:46 +0000182 m := make(ClassLoaderContextMap)
Ulya Trafimovich69612672020-10-20 17:41:54 +0100183 a := "a"
Ulya Trafimovich78a71552020-11-25 14:20:52 +0000184 m.MaybeAddContext(ctx, &a, nil, nil)
Ulya Trafimovich69612672020-10-20 17:41:54 +0100185
Ulya Trafimovich8cbc5d22020-11-03 15:15:46 +0000186 // The library should be added to <uses-library> tags by the manifest_fixer.
187 t.Run("maybe add", func(t *testing.T) {
188 haveUsesLibs := m.UsesLibs()
189 wantUsesLibs := []string{"a"}
190 if !reflect.DeepEqual(wantUsesLibs, haveUsesLibs) {
191 t.Errorf("\nwant uses libs: %s\nhave uses libs: %s", wantUsesLibs, haveUsesLibs)
192 }
193 })
Ulya Trafimovich69612672020-10-20 17:41:54 +0100194
Ulya Trafimovich8cbc5d22020-11-03 15:15:46 +0000195 // But class loader context in such cases should raise an error on validation.
196 t.Run("validate", func(t *testing.T) {
197 _, err := validateClassLoaderContext(m)
Ulya Trafimovich78210f62020-12-02 13:06:47 +0000198 checkError(t, err, "invalid build path for <uses-library> \"a\"")
Ulya Trafimovich8cbc5d22020-11-03 15:15:46 +0000199 })
Ulya Trafimovich69612672020-10-20 17:41:54 +0100200}
201
Ulya Trafimovich5e13a732020-11-03 15:33:03 +0000202// An attempt to add conditional nested subcontext should fail.
203func TestCLCNestedConditional(t *testing.T) {
204 ctx := testContext()
205 m1 := make(ClassLoaderContextMap)
Ulya Trafimovich78a71552020-11-25 14:20:52 +0000206 m1.AddContextForSdk(ctx, 42, "a", buildPath(ctx, "a"), installPath(ctx, "a"), nil)
Ulya Trafimovich5e13a732020-11-03 15:33:03 +0000207 m := make(ClassLoaderContextMap)
Ulya Trafimovich78a71552020-11-25 14:20:52 +0000208 err := m.addContext(ctx, AnySdkVersion, "b", buildPath(ctx, "b"), installPath(ctx, "b"), true, m1)
Ulya Trafimovich5e13a732020-11-03 15:33:03 +0000209 checkError(t, err, "nested class loader context shouldn't have conditional part")
210}
211
Ulya Trafimovichc9f2b942020-12-23 15:41:29 +0000212// Test for SDK version order in conditional CLC: no matter in what order the libraries are added,
213// they end up in the order that agrees with PackageManager.
214func TestCLCSdkVersionOrder(t *testing.T) {
215 ctx := testContext()
216 m := make(ClassLoaderContextMap)
217 m.AddContextForSdk(ctx, 28, "a", buildPath(ctx, "a"), installPath(ctx, "a"), nil)
218 m.AddContextForSdk(ctx, 29, "b", buildPath(ctx, "b"), installPath(ctx, "b"), nil)
219 m.AddContextForSdk(ctx, 30, "c", buildPath(ctx, "c"), installPath(ctx, "c"), nil)
220 m.AddContextForSdk(ctx, AnySdkVersion, "d", buildPath(ctx, "d"), installPath(ctx, "d"), nil)
221
222 valid, validationError := validateClassLoaderContext(m)
223
224 fixClassLoaderContext(m)
225
226 var haveStr string
227 if valid && validationError == nil {
228 haveStr, _ = ComputeClassLoaderContext(m)
229 }
230
231 // Test that validation is successful (all paths are known).
232 t.Run("validate", func(t *testing.T) {
233 if !(valid && validationError == nil) {
234 t.Errorf("invalid class loader context")
235 }
236 })
237
238 // Test that class loader context structure is correct.
239 t.Run("string", func(t *testing.T) {
240 wantStr := " --host-context-for-sdk 30 PCL[out/c.jar]" +
241 " --target-context-for-sdk 30 PCL[/system/c.jar]" +
242 " --host-context-for-sdk 29 PCL[out/b.jar]" +
243 " --target-context-for-sdk 29 PCL[/system/b.jar]" +
244 " --host-context-for-sdk 28 PCL[out/a.jar]" +
245 " --target-context-for-sdk 28 PCL[/system/a.jar]" +
246 " --host-context-for-sdk any PCL[out/d.jar]" +
247 " --target-context-for-sdk any PCL[/system/d.jar]"
248 if wantStr != haveStr {
249 t.Errorf("\nwant class loader context: %s\nhave class loader context: %s", wantStr, haveStr)
250 }
251 })
252}
253
Ulya Trafimovich69612672020-10-20 17:41:54 +0100254func checkError(t *testing.T, have error, want string) {
255 if have == nil {
256 t.Errorf("\nwant error: '%s'\nhave: none", want)
257 } else if msg := have.Error(); !strings.HasPrefix(msg, want) {
258 t.Errorf("\nwant error: '%s'\nhave error: '%s'\n", want, msg)
259 }
260}
261
262func testContext() android.ModuleInstallPathContext {
263 config := android.TestConfig("out", nil, "", nil)
264 return android.ModuleInstallPathContextForTesting(config)
265}
266
267func buildPath(ctx android.PathContext, lib string) android.Path {
268 return android.PathForOutput(ctx, lib+".jar")
269}
270
271func installPath(ctx android.ModuleInstallPathContext, lib string) android.InstallPath {
272 return android.PathForModuleInstall(ctx, lib+".jar")
273}