1// Copyright 2024 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 blueprint 16 17import ( 18 "fmt" 19 "slices" 20 "strings" 21 "testing" 22) 23 24func testTransition(bp string) (*Context, []error) { 25 ctx := newContext() 26 ctx.MockFileSystem(map[string][]byte{ 27 "Android.bp": []byte(bp), 28 }) 29 30 ctx.RegisterBottomUpMutator("deps", depsMutator) 31 ctx.RegisterTransitionMutator("transition", transitionTestMutator{}) 32 ctx.RegisterBottomUpMutator("post_transition_deps", postTransitionDepsMutator) 33 34 ctx.RegisterModuleType("transition_module", newTransitionModule) 35 _, errs := ctx.ParseBlueprintsFiles("Android.bp", nil) 36 if len(errs) > 0 { 37 return nil, errs 38 } 39 40 _, errs = ctx.ResolveDependencies(nil) 41 if len(errs) > 0 { 42 return nil, errs 43 } 44 45 return ctx, nil 46} 47 48func assertNoErrors(t *testing.T, errs []error) { 49 t.Helper() 50 if len(errs) > 0 { 51 t.Errorf("unexpected errors:") 52 for _, err := range errs { 53 t.Errorf(" %s", err) 54 } 55 t.FailNow() 56 } 57} 58 59const testTransitionBp = ` 60 transition_module { 61 name: "A", 62 deps: ["B", "C"], 63 split: ["b", "a"], 64 } 65 66 transition_module { 67 name: "B", 68 deps: ["C"], 69 outgoing: "c", 70 %s 71 } 72 73 transition_module { 74 name: "C", 75 deps: ["D"], 76 } 77 78 transition_module { 79 name: "D", 80 incoming: "d", 81 deps: ["E"], 82 } 83 84 transition_module { 85 name: "E", 86 } 87 88 transition_module { 89 name: "F", 90 } 91 92 transition_module { 93 name: "G", 94 outgoing: "h", 95 %s 96 } 97 98 transition_module { 99 name: "H", 100 split: ["h"], 101 } 102 ` 103 104func getTransitionModule(ctx *Context, name, variant string) *transitionModule { 105 group := ctx.moduleGroupFromName(name, nil) 106 module := group.moduleOrAliasByVariantName(variant).module() 107 return module.logicModule.(*transitionModule) 108} 109 110func checkTransitionVariants(t *testing.T, ctx *Context, name string, expectedVariants []string) { 111 t.Helper() 112 group := ctx.moduleGroupFromName(name, nil) 113 var gotVariants []string 114 for _, variant := range group.modules { 115 gotVariants = append(gotVariants, variant.moduleOrAliasVariant().variations["transition"]) 116 } 117 if !slices.Equal(expectedVariants, gotVariants) { 118 t.Errorf("expected variants of %q to be %q, got %q", name, expectedVariants, gotVariants) 119 } 120} 121 122func checkTransitionDeps(t *testing.T, ctx *Context, m Module, expected ...string) { 123 t.Helper() 124 var got []string 125 ctx.VisitDirectDeps(m, func(m Module) { 126 got = append(got, ctx.ModuleName(m)+"("+ctx.ModuleSubDir(m)+")") 127 }) 128 if !slices.Equal(got, expected) { 129 t.Errorf("unexpected %q dependencies, got %q expected %q", 130 ctx.ModuleName(m), got, expected) 131 } 132} 133 134func checkTransitionMutate(t *testing.T, m *transitionModule, variant string) { 135 t.Helper() 136 if m.properties.Mutated != variant { 137 t.Errorf("unexpected mutated property in %q, expected %q got %q", m.Name(), variant, m.properties.Mutated) 138 } 139} 140 141func TestTransition(t *testing.T) { 142 ctx, errs := testTransition(fmt.Sprintf(testTransitionBp, "", "")) 143 assertNoErrors(t, errs) 144 145 // Module A uses Split to create a and b variants 146 checkTransitionVariants(t, ctx, "A", []string{"b", "a"}) 147 // Module B inherits a and b variants from A 148 checkTransitionVariants(t, ctx, "B", []string{"", "a", "b"}) 149 // Module C inherits a and b variants from A, but gets an outgoing c variant from B 150 checkTransitionVariants(t, ctx, "C", []string{"", "a", "b", "c"}) 151 // Module D always has incoming variant d 152 checkTransitionVariants(t, ctx, "D", []string{"", "d"}) 153 // Module E inherits d from D 154 checkTransitionVariants(t, ctx, "E", []string{"", "d"}) 155 // Module F is untouched 156 checkTransitionVariants(t, ctx, "F", []string{""}) 157 158 A_a := getTransitionModule(ctx, "A", "a") 159 A_b := getTransitionModule(ctx, "A", "b") 160 B_a := getTransitionModule(ctx, "B", "a") 161 B_b := getTransitionModule(ctx, "B", "b") 162 C_a := getTransitionModule(ctx, "C", "a") 163 C_b := getTransitionModule(ctx, "C", "b") 164 C_c := getTransitionModule(ctx, "C", "c") 165 D_d := getTransitionModule(ctx, "D", "d") 166 E_d := getTransitionModule(ctx, "E", "d") 167 F := getTransitionModule(ctx, "F", "") 168 G := getTransitionModule(ctx, "G", "") 169 H_h := getTransitionModule(ctx, "H", "h") 170 171 checkTransitionDeps(t, ctx, A_a, "B(a)", "C(a)") 172 checkTransitionDeps(t, ctx, A_b, "B(b)", "C(b)") 173 checkTransitionDeps(t, ctx, B_a, "C(c)") 174 checkTransitionDeps(t, ctx, B_b, "C(c)") 175 checkTransitionDeps(t, ctx, C_a, "D(d)") 176 checkTransitionDeps(t, ctx, C_b, "D(d)") 177 checkTransitionDeps(t, ctx, C_c, "D(d)") 178 checkTransitionDeps(t, ctx, D_d, "E(d)") 179 checkTransitionDeps(t, ctx, E_d) 180 checkTransitionDeps(t, ctx, F) 181 checkTransitionDeps(t, ctx, G) 182 checkTransitionDeps(t, ctx, H_h) 183 184 checkTransitionMutate(t, A_a, "a") 185 checkTransitionMutate(t, A_b, "b") 186 checkTransitionMutate(t, B_a, "a") 187 checkTransitionMutate(t, B_b, "b") 188 checkTransitionMutate(t, C_a, "a") 189 checkTransitionMutate(t, C_b, "b") 190 checkTransitionMutate(t, C_c, "c") 191 checkTransitionMutate(t, D_d, "d") 192 checkTransitionMutate(t, E_d, "d") 193 checkTransitionMutate(t, F, "") 194 checkTransitionMutate(t, G, "") 195 checkTransitionMutate(t, H_h, "h") 196} 197 198func TestPostTransitionDeps(t *testing.T) { 199 ctx, errs := testTransition(fmt.Sprintf(testTransitionBp, 200 `post_transition_deps: ["C", "D:late", "E:d", "F"],`, 201 `post_transition_deps: ["H"],`)) 202 assertNoErrors(t, errs) 203 204 // Module A uses Split to create a and b variants 205 checkTransitionVariants(t, ctx, "A", []string{"b", "a"}) 206 // Module B inherits a and b variants from A 207 checkTransitionVariants(t, ctx, "B", []string{"", "a", "b"}) 208 // Module C inherits a and b variants from A, but gets an outgoing c variant from B 209 checkTransitionVariants(t, ctx, "C", []string{"", "a", "b", "c"}) 210 // Module D always has incoming variant d 211 checkTransitionVariants(t, ctx, "D", []string{"", "d"}) 212 // Module E inherits d from D 213 checkTransitionVariants(t, ctx, "E", []string{"", "d"}) 214 // Module F is untouched 215 checkTransitionVariants(t, ctx, "F", []string{""}) 216 217 A_a := getTransitionModule(ctx, "A", "a") 218 A_b := getTransitionModule(ctx, "A", "b") 219 B_a := getTransitionModule(ctx, "B", "a") 220 B_b := getTransitionModule(ctx, "B", "b") 221 C_a := getTransitionModule(ctx, "C", "a") 222 C_b := getTransitionModule(ctx, "C", "b") 223 C_c := getTransitionModule(ctx, "C", "c") 224 D_d := getTransitionModule(ctx, "D", "d") 225 E_d := getTransitionModule(ctx, "E", "d") 226 F := getTransitionModule(ctx, "F", "") 227 G := getTransitionModule(ctx, "G", "") 228 H_h := getTransitionModule(ctx, "H", "h") 229 230 checkTransitionDeps(t, ctx, A_a, "B(a)", "C(a)") 231 checkTransitionDeps(t, ctx, A_b, "B(b)", "C(b)") 232 // Verify post-mutator dependencies added to B. The first C(c) is a pre-mutator dependency. 233 // C(c) was added by C and rewritten by OutgoingTransition on B 234 // D(d) was added by D:late and rewritten by IncomingTransition on D 235 // E(d) was added by E:d 236 // F() was added by F, and ignored the existing variation on B 237 checkTransitionDeps(t, ctx, B_a, "C(c)", "C(c)", "D(d)", "E(d)", "F()") 238 checkTransitionDeps(t, ctx, B_b, "C(c)", "C(c)", "D(d)", "E(d)", "F()") 239 checkTransitionDeps(t, ctx, C_a, "D(d)") 240 checkTransitionDeps(t, ctx, C_b, "D(d)") 241 checkTransitionDeps(t, ctx, C_c, "D(d)") 242 checkTransitionDeps(t, ctx, D_d, "E(d)") 243 checkTransitionDeps(t, ctx, E_d) 244 checkTransitionDeps(t, ctx, F) 245 checkTransitionDeps(t, ctx, G, "H(h)") 246 checkTransitionDeps(t, ctx, H_h) 247 248 checkTransitionMutate(t, A_a, "a") 249 checkTransitionMutate(t, A_b, "b") 250 checkTransitionMutate(t, B_a, "a") 251 checkTransitionMutate(t, B_b, "b") 252 checkTransitionMutate(t, C_a, "a") 253 checkTransitionMutate(t, C_b, "b") 254 checkTransitionMutate(t, C_c, "c") 255 checkTransitionMutate(t, D_d, "d") 256 checkTransitionMutate(t, E_d, "d") 257 checkTransitionMutate(t, F, "") 258 checkTransitionMutate(t, G, "") 259 checkTransitionMutate(t, H_h, "h") 260} 261 262func TestPostTransitionDepsMissingVariant(t *testing.T) { 263 // TODO: eventually this will create the missing variant on demand 264 _, errs := testTransition(fmt.Sprintf(testTransitionBp, 265 `post_transition_deps: ["E:missing"],`, "")) 266 expectedError := `Android.bp:8:4: dependency "E" of "B" missing variant: 267 transition:missing 268available variants: 269 transition: 270 transition:d` 271 if len(errs) != 1 || errs[0].Error() != expectedError { 272 t.Errorf("expected error %q, got %q", expectedError, errs) 273 } 274} 275 276type transitionTestMutator struct{} 277 278func (transitionTestMutator) Split(ctx BaseModuleContext) []string { 279 if split := ctx.Module().(*transitionModule).properties.Split; len(split) > 0 { 280 return split 281 } 282 return []string{""} 283} 284 285func (transitionTestMutator) OutgoingTransition(ctx OutgoingTransitionContext, sourceVariation string) string { 286 if outgoing := ctx.Module().(*transitionModule).properties.Outgoing; outgoing != nil { 287 return *outgoing 288 } 289 return sourceVariation 290} 291 292func (transitionTestMutator) IncomingTransition(ctx IncomingTransitionContext, incomingVariation string) string { 293 if incoming := ctx.Module().(*transitionModule).properties.Incoming; incoming != nil { 294 return *incoming 295 } 296 return incomingVariation 297} 298 299func (transitionTestMutator) Mutate(ctx BottomUpMutatorContext, variation string) { 300 ctx.Module().(*transitionModule).properties.Mutated = variation 301} 302 303type transitionModule struct { 304 SimpleName 305 properties struct { 306 Deps []string 307 Post_transition_deps []string 308 Split []string 309 Outgoing *string 310 Incoming *string 311 312 Mutated string `blueprint:"mutated"` 313 } 314} 315 316func newTransitionModule() (Module, []interface{}) { 317 m := &transitionModule{} 318 return m, []interface{}{&m.properties, &m.SimpleName.Properties} 319} 320 321func (f *transitionModule) GenerateBuildActions(ModuleContext) { 322} 323 324func (f *transitionModule) Deps() []string { 325 return f.properties.Deps 326} 327 328func (f *transitionModule) IgnoreDeps() []string { 329 return nil 330} 331 332func postTransitionDepsMutator(mctx BottomUpMutatorContext) { 333 if m, ok := mctx.Module().(*transitionModule); ok { 334 for _, dep := range m.properties.Post_transition_deps { 335 module, variation, _ := strings.Cut(dep, ":") 336 var variations []Variation 337 if variation != "" { 338 variations = append(variations, Variation{"transition", variation}) 339 } 340 mctx.AddVariationDependencies(variations, walkerDepsTag{follow: true}, module) 341 } 342 } 343} 344