1// Copyright 2021 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 bazel 16 17import ( 18 "reflect" 19 "strings" 20 "testing" 21 22 "github.com/google/blueprint/proptools" 23) 24 25func TestUniqueBazelLabels(t *testing.T) { 26 testCases := []struct { 27 originalLabels []Label 28 expectedUniqueLabels []Label 29 }{ 30 { 31 originalLabels: []Label{ 32 {Label: "a"}, 33 {Label: "b"}, 34 {Label: "a"}, 35 {Label: "c"}, 36 }, 37 expectedUniqueLabels: []Label{ 38 {Label: "a"}, 39 {Label: "b"}, 40 {Label: "c"}, 41 }, 42 }, 43 } 44 for _, tc := range testCases { 45 actualUniqueLabels := UniqueSortedBazelLabels(tc.originalLabels) 46 if !reflect.DeepEqual(tc.expectedUniqueLabels, actualUniqueLabels) { 47 t.Fatalf("Expected %v, got %v", tc.expectedUniqueLabels, actualUniqueLabels) 48 } 49 } 50} 51 52func TestSubtractStrings(t *testing.T) { 53 testCases := []struct { 54 haystack []string 55 needle []string 56 expectedResult []string 57 }{ 58 { 59 haystack: []string{ 60 "a", 61 "b", 62 "c", 63 }, 64 needle: []string{ 65 "a", 66 }, 67 expectedResult: []string{ 68 "b", "c", 69 }, 70 }, 71 } 72 for _, tc := range testCases { 73 actualResult := SubtractStrings(tc.haystack, tc.needle) 74 if !reflect.DeepEqual(tc.expectedResult, actualResult) { 75 t.Fatalf("Expected %v, got %v", tc.expectedResult, actualResult) 76 } 77 } 78} 79 80func TestSubtractBazelLabelList(t *testing.T) { 81 testCases := []struct { 82 haystack LabelList 83 needle LabelList 84 expectedResult LabelList 85 }{ 86 { 87 haystack: LabelList{ 88 Includes: []Label{ 89 {Label: "a"}, 90 {Label: "b"}, 91 {Label: "c"}, 92 }, 93 Excludes: []Label{ 94 {Label: "x"}, 95 {Label: "y"}, 96 {Label: "z"}, 97 }, 98 }, 99 needle: LabelList{ 100 Includes: []Label{ 101 {Label: "a"}, 102 }, 103 Excludes: []Label{ 104 {Label: "z"}, 105 }, 106 }, 107 // NOTE: Excludes are intentionally not subtracted 108 expectedResult: LabelList{ 109 Includes: []Label{ 110 {Label: "b"}, 111 {Label: "c"}, 112 }, 113 Excludes: []Label{ 114 {Label: "x"}, 115 {Label: "y"}, 116 {Label: "z"}, 117 }, 118 }, 119 }, 120 } 121 for _, tc := range testCases { 122 actualResult := SubtractBazelLabelList(tc.haystack, tc.needle) 123 if !reflect.DeepEqual(tc.expectedResult, actualResult) { 124 t.Fatalf("Expected %v, got %v", tc.expectedResult, actualResult) 125 } 126 } 127} 128func TestFirstUniqueBazelLabelList(t *testing.T) { 129 testCases := []struct { 130 originalLabelList LabelList 131 expectedUniqueLabelList LabelList 132 }{ 133 { 134 originalLabelList: LabelList{ 135 Includes: []Label{ 136 {Label: "a"}, 137 {Label: "b"}, 138 {Label: "a"}, 139 {Label: "c"}, 140 }, 141 Excludes: []Label{ 142 {Label: "x"}, 143 {Label: "x"}, 144 {Label: "y"}, 145 {Label: "z"}, 146 }, 147 }, 148 expectedUniqueLabelList: LabelList{ 149 Includes: []Label{ 150 {Label: "a"}, 151 {Label: "b"}, 152 {Label: "c"}, 153 }, 154 Excludes: []Label{ 155 {Label: "x"}, 156 {Label: "y"}, 157 {Label: "z"}, 158 }, 159 }, 160 }, 161 } 162 for _, tc := range testCases { 163 actualUniqueLabelList := FirstUniqueBazelLabelList(tc.originalLabelList) 164 if !reflect.DeepEqual(tc.expectedUniqueLabelList, actualUniqueLabelList) { 165 t.Fatalf("Expected %v, got %v", tc.expectedUniqueLabelList, actualUniqueLabelList) 166 } 167 } 168} 169 170func TestUniqueSortedBazelLabelList(t *testing.T) { 171 testCases := []struct { 172 originalLabelList LabelList 173 expectedUniqueLabelList LabelList 174 }{ 175 { 176 originalLabelList: LabelList{ 177 Includes: []Label{ 178 {Label: "c"}, 179 {Label: "a"}, 180 {Label: "a"}, 181 {Label: "b"}, 182 }, 183 Excludes: []Label{ 184 {Label: "y"}, 185 {Label: "z"}, 186 {Label: "x"}, 187 {Label: "x"}, 188 }, 189 }, 190 expectedUniqueLabelList: LabelList{ 191 Includes: []Label{ 192 {Label: "a"}, 193 {Label: "b"}, 194 {Label: "c"}, 195 }, 196 Excludes: []Label{ 197 {Label: "x"}, 198 {Label: "y"}, 199 {Label: "z"}, 200 }, 201 }, 202 }, 203 } 204 for _, tc := range testCases { 205 actualUniqueLabelList := UniqueSortedBazelLabelList(tc.originalLabelList) 206 if !reflect.DeepEqual(tc.expectedUniqueLabelList, actualUniqueLabelList) { 207 t.Fatalf("Expected %v, got %v", tc.expectedUniqueLabelList, actualUniqueLabelList) 208 } 209 } 210} 211 212func makeLabels(labels ...string) []Label { 213 var ret []Label 214 for _, l := range labels { 215 ret = append(ret, Label{Label: l}) 216 } 217 return ret 218} 219 220func makeLabelList(includes, excludes []string) LabelList { 221 return LabelList{ 222 Includes: makeLabels(includes...), 223 Excludes: makeLabels(excludes...), 224 } 225} 226 227func TestResolveExcludes(t *testing.T) { 228 attr := LabelListAttribute{ 229 Value: makeLabelList( 230 []string{ 231 "all_include", 232 "arm_exclude", 233 "android_exclude", 234 }, 235 []string{"all_exclude"}, 236 ), 237 ConfigurableValues: configurableLabelLists{ 238 ArchConfigurationAxis: labelListSelectValues{ 239 "arm": makeLabelList([]string{}, []string{"arm_exclude"}), 240 "x86": makeLabelList([]string{"x86_include"}, []string{}), 241 ConditionsDefaultConfigKey: makeLabelList([]string{"default_include"}, []string{}), 242 }, 243 OsConfigurationAxis: labelListSelectValues{ 244 "android": makeLabelList([]string{}, []string{"android_exclude"}), 245 "linux": makeLabelList([]string{"linux_include"}, []string{}), 246 }, 247 OsArchConfigurationAxis: labelListSelectValues{ 248 "linux_x86": makeLabelList([]string{"linux_x86_include"}, []string{}), 249 }, 250 ProductVariableConfigurationAxis("product_with_defaults"): labelListSelectValues{ 251 "a": makeLabelList([]string{}, []string{"not_in_value"}), 252 "b": makeLabelList([]string{"b_val"}, []string{}), 253 "c": makeLabelList([]string{"c_val"}, []string{}), 254 ConditionsDefaultConfigKey: makeLabelList([]string{"c_val", "default", "default2"}, []string{}), 255 }, 256 ProductVariableConfigurationAxis("product_only_with_excludes"): labelListSelectValues{ 257 "a": makeLabelList([]string{}, []string{"not_in_value"}), 258 }, 259 }, 260 } 261 262 attr.ResolveExcludes() 263 264 expectedBaseIncludes := []Label{{Label: "all_include"}} 265 if !reflect.DeepEqual(expectedBaseIncludes, attr.Value.Includes) { 266 t.Errorf("Expected Value includes %q, got %q", attr.Value.Includes, expectedBaseIncludes) 267 } 268 var nilLabels []Label 269 expectedConfiguredIncludes := map[ConfigurationAxis]map[string][]Label{ 270 ArchConfigurationAxis: { 271 "arm": nilLabels, 272 "x86": makeLabels("arm_exclude", "x86_include"), 273 ConditionsDefaultConfigKey: makeLabels("arm_exclude", "default_include"), 274 }, 275 OsConfigurationAxis: { 276 "android": nilLabels, 277 "linux": makeLabels("android_exclude", "linux_include"), 278 ConditionsDefaultConfigKey: makeLabels("android_exclude"), 279 }, 280 OsArchConfigurationAxis: { 281 "linux_x86": makeLabels("linux_x86_include"), 282 ConditionsDefaultConfigKey: nilLabels, 283 }, 284 ProductVariableConfigurationAxis("product_with_defaults"): { 285 "a": nilLabels, 286 "b": makeLabels("b_val"), 287 "c": makeLabels("c_val"), 288 ConditionsDefaultConfigKey: makeLabels("c_val", "default", "default2"), 289 }, 290 } 291 for _, axis := range attr.SortedConfigurationAxes() { 292 if _, ok := expectedConfiguredIncludes[axis]; !ok { 293 t.Errorf("Found unexpected axis %s", axis) 294 continue 295 } 296 expectedForAxis := expectedConfiguredIncludes[axis] 297 gotForAxis := attr.ConfigurableValues[axis] 298 if len(expectedForAxis) != len(gotForAxis) { 299 t.Errorf("Expected %d configs for %s, got %d: %s", len(expectedForAxis), axis, len(gotForAxis), gotForAxis) 300 } 301 for config, value := range gotForAxis { 302 if expected, ok := expectedForAxis[config]; ok { 303 if !reflect.DeepEqual(expected, value.Includes) { 304 t.Errorf("For %s,\nexpected: %#v\ngot %#v", axis, expected, value.Includes) 305 } 306 } else { 307 t.Errorf("Got unexpected config %q for %s", config, axis) 308 } 309 } 310 } 311} 312 313// labelAddSuffixForTypeMapper returns a LabelMapper that adds suffix to label name for modules of 314// typ 315func labelAddSuffixForTypeMapper(suffix, typ string) LabelMapper { 316 return func(omc OtherModuleContext, label Label) (string, bool) { 317 m, ok := omc.ModuleFromName(label.Label) 318 if !ok { 319 return label.Label, false 320 } 321 mTyp := omc.OtherModuleType(m) 322 if typ == mTyp { 323 return label.Label + suffix, true 324 } 325 return label.Label, false 326 } 327} 328 329func TestPartitionLabelListAttribute(t *testing.T) { 330 testCases := []struct { 331 name string 332 ctx *OtherModuleTestContext 333 labelList LabelListAttribute 334 filters LabelPartitions 335 expected PartitionToLabelListAttribute 336 expectedErrMsg *string 337 }{ 338 { 339 name: "no configurable values", 340 ctx: &OtherModuleTestContext{}, 341 labelList: LabelListAttribute{ 342 Value: makeLabelList([]string{"a.a", "b.b", "c.c", "d.d", "e.e"}, []string{}), 343 }, 344 filters: LabelPartitions{ 345 "A": LabelPartition{Extensions: []string{".a"}}, 346 "B": LabelPartition{Extensions: []string{".b"}}, 347 "C": LabelPartition{Extensions: []string{".c"}}, 348 }, 349 expected: PartitionToLabelListAttribute{ 350 "A": LabelListAttribute{Value: makeLabelList([]string{"a.a"}, []string{})}, 351 "B": LabelListAttribute{Value: makeLabelList([]string{"b.b"}, []string{})}, 352 "C": LabelListAttribute{Value: makeLabelList([]string{"c.c"}, []string{})}, 353 }, 354 }, 355 { 356 name: "no configurable values, remainder partition", 357 ctx: &OtherModuleTestContext{}, 358 labelList: LabelListAttribute{ 359 Value: makeLabelList([]string{"a.a", "b.b", "c.c", "d.d", "e.e"}, []string{}), 360 }, 361 filters: LabelPartitions{ 362 "A": LabelPartition{Extensions: []string{".a"}, Keep_remainder: true}, 363 "B": LabelPartition{Extensions: []string{".b"}}, 364 "C": LabelPartition{Extensions: []string{".c"}}, 365 }, 366 expected: PartitionToLabelListAttribute{ 367 "A": LabelListAttribute{Value: makeLabelList([]string{"a.a", "d.d", "e.e"}, []string{})}, 368 "B": LabelListAttribute{Value: makeLabelList([]string{"b.b"}, []string{})}, 369 "C": LabelListAttribute{Value: makeLabelList([]string{"c.c"}, []string{})}, 370 }, 371 }, 372 { 373 name: "no configurable values, empty partition", 374 ctx: &OtherModuleTestContext{}, 375 labelList: LabelListAttribute{ 376 Value: makeLabelList([]string{"a.a", "c.c"}, []string{}), 377 }, 378 filters: LabelPartitions{ 379 "A": LabelPartition{Extensions: []string{".a"}}, 380 "B": LabelPartition{Extensions: []string{".b"}}, 381 "C": LabelPartition{Extensions: []string{".c"}}, 382 }, 383 expected: PartitionToLabelListAttribute{ 384 "A": LabelListAttribute{Value: makeLabelList([]string{"a.a"}, []string{})}, 385 "C": LabelListAttribute{Value: makeLabelList([]string{"c.c"}, []string{})}, 386 }, 387 }, 388 { 389 name: "no configurable values, has map", 390 ctx: &OtherModuleTestContext{ 391 Modules: []TestModuleInfo{{ModuleName: "srcs", Typ: "fg", Dir: "dir"}}, 392 }, 393 labelList: LabelListAttribute{ 394 Value: makeLabelList([]string{"a.a", "srcs", "b.b", "c.c"}, []string{}), 395 }, 396 filters: LabelPartitions{ 397 "A": LabelPartition{Extensions: []string{".a"}, LabelMapper: labelAddSuffixForTypeMapper("_a", "fg")}, 398 "B": LabelPartition{Extensions: []string{".b"}}, 399 "C": LabelPartition{Extensions: []string{".c"}}, 400 }, 401 expected: PartitionToLabelListAttribute{ 402 "A": LabelListAttribute{Value: makeLabelList([]string{"a.a", "srcs_a"}, []string{})}, 403 "B": LabelListAttribute{Value: makeLabelList([]string{"b.b"}, []string{})}, 404 "C": LabelListAttribute{Value: makeLabelList([]string{"c.c"}, []string{})}, 405 }, 406 }, 407 { 408 name: "configurable values, keeps empty if excludes", 409 ctx: &OtherModuleTestContext{}, 410 labelList: LabelListAttribute{ 411 ConfigurableValues: configurableLabelLists{ 412 ArchConfigurationAxis: labelListSelectValues{ 413 "x86": makeLabelList([]string{"a.a", "c.c"}, []string{}), 414 "arm": makeLabelList([]string{"b.b"}, []string{}), 415 "x86_64": makeLabelList([]string{"b.b"}, []string{"d.d"}), 416 }, 417 }, 418 }, 419 filters: LabelPartitions{ 420 "A": LabelPartition{Extensions: []string{".a"}}, 421 "B": LabelPartition{Extensions: []string{".b"}}, 422 "C": LabelPartition{Extensions: []string{".c"}}, 423 }, 424 expected: PartitionToLabelListAttribute{ 425 "A": LabelListAttribute{ 426 ConfigurableValues: configurableLabelLists{ 427 ArchConfigurationAxis: labelListSelectValues{ 428 "x86": makeLabelList([]string{"a.a"}, []string{}), 429 "x86_64": makeLabelList([]string{}, []string{"c.c"}), 430 }, 431 }, 432 }, 433 "B": LabelListAttribute{ 434 ConfigurableValues: configurableLabelLists{ 435 ArchConfigurationAxis: labelListSelectValues{ 436 "arm": makeLabelList([]string{"b.b"}, []string{}), 437 "x86_64": makeLabelList([]string{"b.b"}, []string{"c.c"}), 438 }, 439 }, 440 }, 441 "C": LabelListAttribute{ 442 ConfigurableValues: configurableLabelLists{ 443 ArchConfigurationAxis: labelListSelectValues{ 444 "x86": makeLabelList([]string{"c.c"}, []string{}), 445 "x86_64": makeLabelList([]string{}, []string{"c.c"}), 446 }, 447 }, 448 }, 449 }, 450 }, 451 { 452 name: "error for multiple partitions same value", 453 ctx: &OtherModuleTestContext{}, 454 labelList: LabelListAttribute{ 455 Value: makeLabelList([]string{"a.a", "b.b", "c.c", "d.d", "e.e"}, []string{}), 456 }, 457 filters: LabelPartitions{ 458 "A": LabelPartition{Extensions: []string{".a"}}, 459 "other A": LabelPartition{Extensions: []string{".a"}}, 460 }, 461 expected: PartitionToLabelListAttribute{}, 462 expectedErrMsg: proptools.StringPtr(`"a.a" was found in multiple partitions:`), 463 }, 464 } 465 466 for _, tc := range testCases { 467 t.Run(tc.name, func(t *testing.T) { 468 got := PartitionLabelListAttribute(tc.ctx, &tc.labelList, tc.filters) 469 470 if hasErrors, expectsErr := len(tc.ctx.errors) > 0, tc.expectedErrMsg != nil; hasErrors != expectsErr { 471 t.Errorf("Unexpected error(s): %q, expected: %q", tc.ctx.errors, *tc.expectedErrMsg) 472 } else if tc.expectedErrMsg != nil { 473 found := false 474 for _, err := range tc.ctx.errors { 475 if strings.Contains(err, *tc.expectedErrMsg) { 476 found = true 477 break 478 } 479 } 480 481 if !found { 482 t.Errorf("Expected error message: %q, got %q", *tc.expectedErrMsg, tc.ctx.errors) 483 } 484 return 485 } 486 487 if len(tc.expected) != len(got) { 488 t.Errorf("Expected %d partitions, got %d partitions", len(tc.expected), len(got)) 489 } 490 for partition, expectedLla := range tc.expected { 491 gotLla, ok := got[partition] 492 if !ok { 493 t.Errorf("Expected partition %q, but it was not found %v", partition, got) 494 continue 495 } 496 expectedLabelList := expectedLla.Value 497 gotLabelList := gotLla.Value 498 if !reflect.DeepEqual(expectedLabelList.Includes, gotLabelList.Includes) { 499 t.Errorf("Expected no config includes %v, got %v", expectedLabelList.Includes, gotLabelList.Includes) 500 } 501 expectedAxes := expectedLla.SortedConfigurationAxes() 502 gotAxes := gotLla.SortedConfigurationAxes() 503 if !reflect.DeepEqual(expectedAxes, gotAxes) { 504 t.Errorf("Expected axes %v, got %v (%#v)", expectedAxes, gotAxes, gotLla) 505 } 506 for _, axis := range expectedLla.SortedConfigurationAxes() { 507 if _, exists := gotLla.ConfigurableValues[axis]; !exists { 508 t.Errorf("Expected %s to be a supported axis, but it was not found", axis) 509 } 510 if expected, got := expectedLla.ConfigurableValues[axis], gotLla.ConfigurableValues[axis]; len(expected) != len(got) { 511 t.Errorf("For axis %q: expected configs %v, got %v", axis, expected, got) 512 } 513 for config, expectedLabelList := range expectedLla.ConfigurableValues[axis] { 514 gotLabelList, exists := gotLla.ConfigurableValues[axis][config] 515 if !exists { 516 t.Errorf("Expected %s to be a supported config, but config was not found", config) 517 continue 518 } 519 if !reflect.DeepEqual(expectedLabelList.Includes, gotLabelList.Includes) { 520 t.Errorf("Expected %s %s includes %v, got %v", axis, config, expectedLabelList.Includes, gotLabelList.Includes) 521 } 522 } 523 } 524 } 525 }) 526 } 527} 528 529func TestDeduplicateAxesFromBase(t *testing.T) { 530 attr := StringListAttribute{ 531 Value: []string{ 532 "all_include", 533 "arm_include", 534 "android_include", 535 "linux_x86_include", 536 }, 537 ConfigurableValues: configurableStringLists{ 538 ArchConfigurationAxis: stringListSelectValues{ 539 "arm": []string{"arm_include"}, 540 "x86": []string{"x86_include"}, 541 }, 542 OsConfigurationAxis: stringListSelectValues{ 543 "android": []string{"android_include"}, 544 "linux": []string{"linux_include"}, 545 }, 546 OsArchConfigurationAxis: stringListSelectValues{ 547 "linux_x86": {"linux_x86_include"}, 548 }, 549 ProductVariableConfigurationAxis("a"): stringListSelectValues{ 550 "a": []string{"not_in_value"}, 551 }, 552 }, 553 } 554 555 attr.DeduplicateAxesFromBase() 556 557 expectedBaseIncludes := []string{ 558 "all_include", 559 "arm_include", 560 "android_include", 561 "linux_x86_include", 562 } 563 if !reflect.DeepEqual(expectedBaseIncludes, attr.Value) { 564 t.Errorf("Expected Value includes %q, got %q", attr.Value, expectedBaseIncludes) 565 } 566 expectedConfiguredIncludes := configurableStringLists{ 567 ArchConfigurationAxis: stringListSelectValues{ 568 "x86": []string{"x86_include"}, 569 }, 570 OsConfigurationAxis: stringListSelectValues{ 571 "linux": []string{"linux_include"}, 572 }, 573 OsArchConfigurationAxis: stringListSelectValues{}, 574 ProductVariableConfigurationAxis("a"): stringListSelectValues{ 575 "a": []string{"not_in_value"}, 576 }, 577 } 578 for _, axis := range attr.SortedConfigurationAxes() { 579 if _, ok := expectedConfiguredIncludes[axis]; !ok { 580 t.Errorf("Found unexpected axis %s", axis) 581 continue 582 } 583 expectedForAxis := expectedConfiguredIncludes[axis] 584 gotForAxis := attr.ConfigurableValues[axis] 585 if len(expectedForAxis) != len(gotForAxis) { 586 t.Errorf("Expected %d configs for %s, got %d: %s", len(expectedForAxis), axis, len(gotForAxis), gotForAxis) 587 } 588 for config, value := range gotForAxis { 589 if expected, ok := expectedForAxis[config]; ok { 590 if !reflect.DeepEqual(expected, value) { 591 t.Errorf("For %s, expected: %#v, got %#v", axis, expected, value) 592 } 593 } else { 594 t.Errorf("Got unexpected config %q for %s", config, axis) 595 } 596 } 597 } 598} 599