1// 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 "fmt" 22 "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 41 // │ ├── a2 42 // │ ├── b2 43 // │ └── c2 44 // │ ├── a1 45 // │ └── b1 46 // ├── f 47 // ├── a3 48 // └── b3 49 // 50 ctx := testContext() 51 52 m := make(ClassLoaderContextMap) 53 54 m.AddContext(ctx, AnySdkVersion, "a", buildPath(ctx, "a"), installPath(ctx, "a"), nil) 55 m.AddContext(ctx, AnySdkVersion, "b", buildPath(ctx, "b"), installPath(ctx, "b"), nil) 56 m.AddContext(ctx, AnySdkVersion, "c", buildPath(ctx, "c"), installPath(ctx, "c"), nil) 57 58 // Add some libraries with nested subcontexts. 59 60 m1 := make(ClassLoaderContextMap) 61 m1.AddContext(ctx, AnySdkVersion, "a1", buildPath(ctx, "a1"), installPath(ctx, "a1"), nil) 62 m1.AddContext(ctx, AnySdkVersion, "b1", buildPath(ctx, "b1"), installPath(ctx, "b1"), nil) 63 64 m2 := make(ClassLoaderContextMap) 65 m2.AddContext(ctx, AnySdkVersion, "a2", buildPath(ctx, "a2"), installPath(ctx, "a2"), nil) 66 m2.AddContext(ctx, AnySdkVersion, "b2", buildPath(ctx, "b2"), installPath(ctx, "b2"), nil) 67 m2.AddContext(ctx, AnySdkVersion, "c2", buildPath(ctx, "c2"), installPath(ctx, "c2"), m1) 68 69 m3 := make(ClassLoaderContextMap) 70 m3.AddContext(ctx, AnySdkVersion, "a3", buildPath(ctx, "a3"), installPath(ctx, "a3"), nil) 71 m3.AddContext(ctx, AnySdkVersion, "b3", buildPath(ctx, "b3"), installPath(ctx, "b3"), nil) 72 73 m.AddContext(ctx, AnySdkVersion, "d", buildPath(ctx, "d"), installPath(ctx, "d"), m2) 74 // When the same library is both in conditional and unconditional context, it should be removed 75 // from conditional context. 76 m.AddContext(ctx, 42, "f", buildPath(ctx, "f"), installPath(ctx, "f"), nil) 77 m.AddContext(ctx, AnySdkVersion, "f", buildPath(ctx, "f"), installPath(ctx, "f"), nil) 78 79 // Merge map with implicit root library that is among toplevel contexts => does nothing. 80 m.AddContextMap(m1, "c") 81 // Merge map with implicit root library that is not among toplevel contexts => all subcontexts 82 // of the other map are added as toplevel contexts. 83 m.AddContextMap(m3, "m_g") 84 85 // Compatibility libraries with unknown install paths get default paths. 86 m.AddContext(ctx, 29, AndroidHidlManager, buildPath(ctx, AndroidHidlManager), nil, nil) 87 m.AddContext(ctx, 29, AndroidHidlBase, buildPath(ctx, AndroidHidlBase), nil, nil) 88 89 // Add "android.test.mock" to conditional CLC, observe that is gets removed because it is only 90 // needed as a compatibility library if "android.test.runner" is in CLC as well. 91 m.AddContext(ctx, 30, AndroidTestMock, buildPath(ctx, AndroidTestMock), nil, nil) 92 93 valid, validationError := validateClassLoaderContext(m) 94 95 fixClassLoaderContext(m) 96 97 var haveStr string 98 var havePaths android.Paths 99 var haveUsesLibs []string 100 if valid && validationError == nil { 101 haveStr, havePaths = ComputeClassLoaderContext(m) 102 haveUsesLibs = m.UsesLibs() 103 } 104 105 // Test that validation is successful (all paths are known). 106 t.Run("validate", func(t *testing.T) { 107 if !(valid && validationError == nil) { 108 t.Errorf("invalid class loader context") 109 } 110 }) 111 112 // Test that class loader context structure is correct. 113 t.Run("string", func(t *testing.T) { 114 wantStr := " --host-context-for-sdk 29 " + 115 "PCL[out/" + AndroidHidlManager + ".jar]#" + 116 "PCL[out/" + AndroidHidlBase + ".jar]" + 117 " --target-context-for-sdk 29 " + 118 "PCL[/system/framework/" + AndroidHidlManager + ".jar]#" + 119 "PCL[/system/framework/" + AndroidHidlBase + ".jar]" + 120 " --host-context-for-sdk any " + 121 "PCL[out/a.jar]#PCL[out/b.jar]#PCL[out/c.jar]#PCL[out/d.jar]" + 122 "{PCL[out/a2.jar]#PCL[out/b2.jar]#PCL[out/c2.jar]" + 123 "{PCL[out/a1.jar]#PCL[out/b1.jar]}}#" + 124 "PCL[out/f.jar]#PCL[out/a3.jar]#PCL[out/b3.jar]" + 125 " --target-context-for-sdk any " + 126 "PCL[/system/a.jar]#PCL[/system/b.jar]#PCL[/system/c.jar]#PCL[/system/d.jar]" + 127 "{PCL[/system/a2.jar]#PCL[/system/b2.jar]#PCL[/system/c2.jar]" + 128 "{PCL[/system/a1.jar]#PCL[/system/b1.jar]}}#" + 129 "PCL[/system/f.jar]#PCL[/system/a3.jar]#PCL[/system/b3.jar]" 130 if wantStr != haveStr { 131 t.Errorf("\nwant class loader context: %s\nhave class loader context: %s", wantStr, haveStr) 132 } 133 }) 134 135 // Test that all expected build paths are gathered. 136 t.Run("paths", func(t *testing.T) { 137 wantPaths := []string{ 138 "out/android.hidl.manager-V1.0-java.jar", "out/android.hidl.base-V1.0-java.jar", 139 "out/a.jar", "out/b.jar", "out/c.jar", "out/d.jar", 140 "out/a2.jar", "out/b2.jar", "out/c2.jar", 141 "out/a1.jar", "out/b1.jar", 142 "out/f.jar", "out/a3.jar", "out/b3.jar", 143 } 144 if !reflect.DeepEqual(wantPaths, havePaths.Strings()) { 145 t.Errorf("\nwant paths: %s\nhave paths: %s", wantPaths, havePaths) 146 } 147 }) 148 149 // Test for libraries that are added by the manifest_fixer. 150 t.Run("uses libs", func(t *testing.T) { 151 wantUsesLibs := []string{"a", "b", "c", "d", "f", "a3", "b3"} 152 if !reflect.DeepEqual(wantUsesLibs, haveUsesLibs) { 153 t.Errorf("\nwant uses libs: %s\nhave uses libs: %s", wantUsesLibs, haveUsesLibs) 154 } 155 }) 156} 157 158func TestCLCJson(t *testing.T) { 159 ctx := testContext() 160 m := make(ClassLoaderContextMap) 161 m.AddContext(ctx, 28, "a", buildPath(ctx, "a"), installPath(ctx, "a"), nil) 162 m.AddContext(ctx, 29, "b", buildPath(ctx, "b"), installPath(ctx, "b"), nil) 163 m.AddContext(ctx, 30, "c", buildPath(ctx, "c"), installPath(ctx, "c"), nil) 164 m.AddContext(ctx, AnySdkVersion, "d", buildPath(ctx, "d"), installPath(ctx, "d"), nil) 165 jsonCLC := toJsonClassLoaderContext(m) 166 restored := fromJsonClassLoaderContext(ctx, jsonCLC) 167 android.AssertIntEquals(t, "The size of the maps should be the same.", len(m), len(restored)) 168 for k := range m { 169 a, _ := m[k] 170 b, ok := restored[k] 171 android.AssertBoolEquals(t, "The both maps should have the same keys.", ok, true) 172 android.AssertIntEquals(t, "The size of the elements should be the same.", len(a), len(b)) 173 for i, elemA := range a { 174 before := fmt.Sprintf("%v", *elemA) 175 after := fmt.Sprintf("%v", *b[i]) 176 android.AssertStringEquals(t, "The content should be the same.", before, after) 177 } 178 } 179} 180 181// Test that unknown library paths cause a validation error. 182func testCLCUnknownPath(t *testing.T, whichPath string) { 183 ctx := testContext() 184 185 m := make(ClassLoaderContextMap) 186 if whichPath == "build" { 187 m.AddContext(ctx, AnySdkVersion, "a", nil, nil, nil) 188 } else { 189 m.AddContext(ctx, AnySdkVersion, "a", buildPath(ctx, "a"), nil, nil) 190 } 191 192 // The library should be added to <uses-library> tags by the manifest_fixer. 193 t.Run("uses libs", func(t *testing.T) { 194 haveUsesLibs := m.UsesLibs() 195 wantUsesLibs := []string{"a"} 196 if !reflect.DeepEqual(wantUsesLibs, haveUsesLibs) { 197 t.Errorf("\nwant uses libs: %s\nhave uses libs: %s", wantUsesLibs, haveUsesLibs) 198 } 199 }) 200 201 // But CLC cannot be constructed: there is a validation error. 202 _, err := validateClassLoaderContext(m) 203 checkError(t, err, fmt.Sprintf("invalid %s path for <uses-library> \"a\"", whichPath)) 204} 205 206// Test that unknown build path is an error. 207func TestCLCUnknownBuildPath(t *testing.T) { 208 testCLCUnknownPath(t, "build") 209} 210 211// Test that unknown install path is an error. 212func TestCLCUnknownInstallPath(t *testing.T) { 213 testCLCUnknownPath(t, "install") 214} 215 216// An attempt to add conditional nested subcontext should fail. 217func TestCLCNestedConditional(t *testing.T) { 218 ctx := testContext() 219 m1 := make(ClassLoaderContextMap) 220 m1.AddContext(ctx, 42, "a", buildPath(ctx, "a"), installPath(ctx, "a"), nil) 221 m := make(ClassLoaderContextMap) 222 err := m.addContext(ctx, AnySdkVersion, "b", buildPath(ctx, "b"), installPath(ctx, "b"), m1) 223 checkError(t, err, "nested class loader context shouldn't have conditional part") 224} 225 226// Test for SDK version order in conditional CLC: no matter in what order the libraries are added, 227// they end up in the order that agrees with PackageManager. 228func TestCLCSdkVersionOrder(t *testing.T) { 229 ctx := testContext() 230 m := make(ClassLoaderContextMap) 231 m.AddContext(ctx, 28, "a", buildPath(ctx, "a"), installPath(ctx, "a"), nil) 232 m.AddContext(ctx, 29, "b", buildPath(ctx, "b"), installPath(ctx, "b"), nil) 233 m.AddContext(ctx, 30, "c", buildPath(ctx, "c"), installPath(ctx, "c"), nil) 234 m.AddContext(ctx, AnySdkVersion, "d", buildPath(ctx, "d"), installPath(ctx, "d"), nil) 235 236 valid, validationError := validateClassLoaderContext(m) 237 238 fixClassLoaderContext(m) 239 240 var haveStr string 241 if valid && validationError == nil { 242 haveStr, _ = ComputeClassLoaderContext(m) 243 } 244 245 // Test that validation is successful (all paths are known). 246 t.Run("validate", func(t *testing.T) { 247 if !(valid && validationError == nil) { 248 t.Errorf("invalid class loader context") 249 } 250 }) 251 252 // Test that class loader context structure is correct. 253 t.Run("string", func(t *testing.T) { 254 wantStr := " --host-context-for-sdk 30 PCL[out/c.jar]" + 255 " --target-context-for-sdk 30 PCL[/system/c.jar]" + 256 " --host-context-for-sdk 29 PCL[out/b.jar]" + 257 " --target-context-for-sdk 29 PCL[/system/b.jar]" + 258 " --host-context-for-sdk 28 PCL[out/a.jar]" + 259 " --target-context-for-sdk 28 PCL[/system/a.jar]" + 260 " --host-context-for-sdk any PCL[out/d.jar]" + 261 " --target-context-for-sdk any PCL[/system/d.jar]" 262 if wantStr != haveStr { 263 t.Errorf("\nwant class loader context: %s\nhave class loader context: %s", wantStr, haveStr) 264 } 265 }) 266} 267 268func checkError(t *testing.T, have error, want string) { 269 if have == nil { 270 t.Errorf("\nwant error: '%s'\nhave: none", want) 271 } else if msg := have.Error(); !strings.HasPrefix(msg, want) { 272 t.Errorf("\nwant error: '%s'\nhave error: '%s'\n", want, msg) 273 } 274} 275 276func testContext() android.ModuleInstallPathContext { 277 config := android.TestConfig("out", nil, "", nil) 278 return android.ModuleInstallPathContextForTesting(config) 279} 280 281func buildPath(ctx android.PathContext, lib string) android.Path { 282 return android.PathForOutput(ctx, lib+".jar") 283} 284 285func installPath(ctx android.ModuleInstallPathContext, lib string) android.InstallPath { 286 return android.PathForModuleInstall(ctx, lib+".jar") 287} 288