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. 14package main 15 16import ( 17 "strings" 18 "testing" 19 20 "github.com/google/blueprint/parser" 21 "github.com/google/blueprint/proptools" 22) 23 24var testCases = []struct { 25 name string 26 input string 27 output string 28 property string 29 addSet string 30 removeSet string 31 addLiteral *string 32 setString *string 33 setBool *string 34 removeProperty bool 35 replaceProperty string 36 moveProperty bool 37 newLocation string 38}{ 39 { 40 name: "add", 41 input: ` 42 cc_foo { 43 name: "foo", 44 } 45 `, 46 output: ` 47 cc_foo { 48 name: "foo", 49 deps: ["bar"], 50 } 51 `, 52 property: "deps", 53 addSet: "bar", 54 }, 55 { 56 name: "remove", 57 input: ` 58 cc_foo { 59 name: "foo", 60 deps: ["bar"], 61 } 62 `, 63 output: ` 64 cc_foo { 65 name: "foo", 66 deps: [], 67 } 68 `, 69 property: "deps", 70 removeSet: "bar", 71 }, 72 { 73 name: "nested add", 74 input: ` 75 cc_foo { 76 name: "foo", 77 } 78 `, 79 output: ` 80 cc_foo { 81 name: "foo", 82 arch: { 83 arm: { 84 deps: [ 85 "dep2", 86 "nested_dep",], 87 }, 88 }, 89 } 90 `, 91 property: "arch.arm.deps", 92 addSet: "nested_dep,dep2", 93 }, 94 { 95 name: "nested remove", 96 input: ` 97 cc_foo { 98 name: "foo", 99 arch: { 100 arm: { 101 deps: [ 102 "dep2", 103 "nested_dep", 104 ], 105 }, 106 }, 107 } 108 `, 109 output: ` 110 cc_foo { 111 name: "foo", 112 arch: { 113 arm: { 114 deps: [ 115 ], 116 }, 117 }, 118 } 119 `, 120 property: "arch.arm.deps", 121 removeSet: "nested_dep,dep2", 122 }, 123 { 124 name: "add existing", 125 input: ` 126 cc_foo { 127 name: "foo", 128 arch: { 129 arm: { 130 deps: [ 131 "nested_dep", 132 "dep2", 133 ], 134 }, 135 }, 136 } 137 `, 138 output: ` 139 cc_foo { 140 name: "foo", 141 arch: { 142 arm: { 143 deps: [ 144 "nested_dep", 145 "dep2", 146 ], 147 }, 148 }, 149 } 150 `, 151 property: "arch.arm.deps", 152 addSet: "dep2,dep2", 153 }, 154 { 155 name: "remove missing", 156 input: ` 157 cc_foo { 158 name: "foo", 159 arch: { 160 arm: { 161 deps: [ 162 "nested_dep", 163 "dep2", 164 ], 165 }, 166 }, 167 } 168 `, 169 output: ` 170 cc_foo { 171 name: "foo", 172 arch: { 173 arm: { 174 deps: [ 175 "nested_dep", 176 "dep2", 177 ], 178 }, 179 }, 180 } 181 `, 182 property: "arch.arm.deps", 183 removeSet: "dep3,dep4", 184 }, 185 { 186 name: "remove non existent", 187 input: ` 188 cc_foo { 189 name: "foo", 190 } 191 `, 192 output: ` 193 cc_foo { 194 name: "foo", 195 } 196 `, 197 property: "deps", 198 removeSet: "bar", 199 }, 200 { 201 name: "remove non existent nested", 202 input: ` 203 cc_foo { 204 name: "foo", 205 arch: {}, 206 } 207 `, 208 output: ` 209 cc_foo { 210 name: "foo", 211 arch: {}, 212 } 213 `, 214 property: "arch.arm.deps", 215 removeSet: "dep3,dep4", 216 }, 217 { 218 name: "add numeric sorted", 219 input: ` 220 cc_foo { 221 name: "foo", 222 versions: ["1", "2"], 223 } 224 `, 225 output: ` 226 cc_foo { 227 name: "foo", 228 versions: [ 229 "1", 230 "2", 231 "10", 232 ], 233 } 234 `, 235 property: "versions", 236 addSet: "10", 237 }, 238 { 239 name: "add mixed sorted", 240 input: ` 241 cc_foo { 242 name: "foo", 243 deps: ["bar-v1-bar", "bar-v2-bar"], 244 } 245 `, 246 output: ` 247 cc_foo { 248 name: "foo", 249 deps: [ 250 "bar-v1-bar", 251 "bar-v2-bar", 252 "bar-v10-bar", 253 ], 254 } 255 `, 256 property: "deps", 257 addSet: "bar-v10-bar", 258 }, 259 { 260 name: "add a struct with literal", 261 input: `cc_foo {name: "foo"}`, 262 output: `cc_foo { 263 name: "foo", 264 structs: [ 265 { 266 version: "1", 267 imports: [ 268 "bar1", 269 "bar2", 270 ], 271 }, 272 ], 273} 274`, 275 property: "structs", 276 addLiteral: proptools.StringPtr(`{version: "1", imports: ["bar1", "bar2"]}`), 277 }, 278 { 279 name: "set string", 280 input: ` 281 cc_foo { 282 name: "foo", 283 } 284 `, 285 output: ` 286 cc_foo { 287 name: "foo", 288 foo: "bar", 289 } 290 `, 291 property: "foo", 292 setString: proptools.StringPtr("bar"), 293 }, 294 { 295 name: "set existing string", 296 input: ` 297 cc_foo { 298 name: "foo", 299 foo: "baz", 300 } 301 `, 302 output: ` 303 cc_foo { 304 name: "foo", 305 foo: "bar", 306 } 307 `, 308 property: "foo", 309 setString: proptools.StringPtr("bar"), 310 }, 311 { 312 name: "set bool", 313 input: ` 314 cc_foo { 315 name: "foo", 316 } 317 `, 318 output: ` 319 cc_foo { 320 name: "foo", 321 foo: true, 322 } 323 `, 324 property: "foo", 325 setBool: proptools.StringPtr("true"), 326 }, 327 { 328 name: "set existing bool", 329 input: ` 330 cc_foo { 331 name: "foo", 332 foo: true, 333 } 334 `, 335 output: ` 336 cc_foo { 337 name: "foo", 338 foo: false, 339 } 340 `, 341 property: "foo", 342 setBool: proptools.StringPtr("false"), 343 }, 344 { 345 name: "remove existing property", 346 input: ` 347 cc_foo { 348 name: "foo", 349 foo: "baz", 350 } 351 `, 352 output: ` 353 cc_foo { 354 name: "foo", 355 } 356 `, 357 property: "foo", 358 removeProperty: true, 359 }, { 360 name: "remove nested property", 361 input: ` 362 cc_foo { 363 name: "foo", 364 foo: { 365 bar: "baz", 366 }, 367 } 368 `, 369 output: ` 370 cc_foo { 371 name: "foo", 372 foo: {}, 373 } 374 `, 375 property: "foo.bar", 376 removeProperty: true, 377 }, { 378 name: "remove non-existing property", 379 input: ` 380 cc_foo { 381 name: "foo", 382 foo: "baz", 383 } 384 `, 385 output: ` 386 cc_foo { 387 name: "foo", 388 foo: "baz", 389 } 390 `, 391 property: "bar", 392 removeProperty: true, 393 }, { 394 name: "replace property", 395 property: "deps", 396 input: ` 397 cc_foo { 398 name: "foo", 399 deps: ["baz", "unchanged"], 400 } 401 `, 402 output: ` 403 cc_foo { 404 name: "foo", 405 deps: [ 406 "baz_lib", 407 "unchanged", 408 ], 409 } 410 `, 411 replaceProperty: "baz=baz_lib,foobar=foobar_lib", 412 }, { 413 name: "replace property multiple modules", 414 property: "deps,required", 415 input: ` 416 cc_foo { 417 name: "foo", 418 deps: ["baz", "unchanged"], 419 unchanged: ["baz"], 420 required: ["foobar"], 421 } 422 `, 423 output: ` 424 cc_foo { 425 name: "foo", 426 deps: [ 427 "baz_lib", 428 "unchanged", 429 ], 430 unchanged: ["baz"], 431 required: ["foobar_lib"], 432 } 433 `, 434 replaceProperty: "baz=baz_lib,foobar=foobar_lib", 435 }, { 436 name: "replace property string value", 437 property: "name", 438 input: ` 439 cc_foo { 440 name: "foo", 441 deps: ["baz"], 442 unchanged: ["baz"], 443 required: ["foobar"], 444 } 445 `, 446 output: ` 447 cc_foo { 448 name: "foo_lib", 449 deps: ["baz"], 450 unchanged: ["baz"], 451 required: ["foobar"], 452 } 453 `, 454 replaceProperty: "foo=foo_lib", 455 }, { 456 name: "replace property string and list values", 457 property: "name,deps", 458 input: ` 459 cc_foo { 460 name: "foo", 461 deps: ["baz"], 462 unchanged: ["baz"], 463 required: ["foobar"], 464 } 465 `, 466 output: ` 467 cc_foo { 468 name: "foo_lib", 469 deps: ["baz_lib"], 470 unchanged: ["baz"], 471 required: ["foobar"], 472 } 473 `, 474 replaceProperty: "foo=foo_lib,baz=baz_lib", 475 }, { 476 name: "move contents of property into non-existing property", 477 input: ` 478 cc_foo { 479 name: "foo", 480 bar: ["barContents"], 481 } 482`, 483 output: ` 484 cc_foo { 485 name: "foo", 486 baz: ["barContents"], 487 } 488 `, 489 property: "bar", 490 moveProperty: true, 491 newLocation: "baz", 492 }, { 493 name: "move contents of property into existing property", 494 input: ` 495 cc_foo { 496 name: "foo", 497 baz: ["bazContents"], 498 bar: ["barContents"], 499 } 500 `, 501 output: ` 502 cc_foo { 503 name: "foo", 504 baz: [ 505 "bazContents", 506 "barContents", 507 ], 508 509 } 510 `, 511 property: "bar", 512 moveProperty: true, 513 newLocation: "baz", 514 }, { 515 name: "replace nested", 516 input: ` 517 cc_foo { 518 name: "foo", 519 foo: { 520 bar: "baz", 521 }, 522 } 523 `, 524 output: ` 525 cc_foo { 526 name: "foo", 527 foo: { 528 bar: "baz2", 529 }, 530 } 531 `, 532 property: "foo.bar", 533 replaceProperty: "baz=baz2", 534 }, 535} 536 537func simplifyModuleDefinition(def string) string { 538 var result string 539 for _, line := range strings.Split(def, "\n") { 540 result += strings.TrimSpace(line) 541 } 542 return result 543} 544func TestProcessModule(t *testing.T) { 545 for i, testCase := range testCases { 546 t.Run(testCase.name, func(t *testing.T) { 547 targetedProperties.Set(testCase.property) 548 addIdents.Set(testCase.addSet) 549 removeIdents.Set(testCase.removeSet) 550 removeProperty = &testCase.removeProperty 551 moveProperty = &testCase.moveProperty 552 newLocation = testCase.newLocation 553 setString = testCase.setString 554 setBool = testCase.setBool 555 addLiteral = testCase.addLiteral 556 replaceProperty.Set(testCase.replaceProperty) 557 558 inAst, errs := parser.ParseAndEval("", strings.NewReader(testCase.input), parser.NewScope(nil)) 559 if len(errs) > 0 { 560 for _, err := range errs { 561 t.Errorf(" %s", err) 562 } 563 t.Errorf("failed to parse:") 564 t.Errorf("%+v", testCase) 565 t.FailNow() 566 } 567 if inModule, ok := inAst.Defs[0].(*parser.Module); !ok { 568 t.Fatalf(" input must only contain a single module definition: %s", testCase.input) 569 } else { 570 for _, p := range targetedProperties.properties { 571 _, errs := processModuleProperty(inModule, "", inAst, p) 572 if len(errs) > 0 { 573 t.Errorf("test case %d:", i) 574 for _, err := range errs { 575 t.Errorf(" %s", err) 576 } 577 } 578 579 } 580 inModuleText, _ := parser.Print(inAst) 581 inModuleString := string(inModuleText) 582 if simplifyModuleDefinition(inModuleString) != simplifyModuleDefinition(testCase.output) { 583 t.Errorf("test case %d:", i) 584 t.Errorf("expected module definition:") 585 t.Errorf(" %s", testCase.output) 586 t.Errorf("actual module definition:") 587 t.Errorf(" %s", inModuleString) 588 } 589 } 590 }) 591 } 592} 593 594func TestReplacementsCycleError(t *testing.T) { 595 cycleString := "old1=new1,new1=old1" 596 err := replaceProperty.Set(cycleString) 597 598 if err.Error() != "Duplicated replacement name new1" { 599 t.Errorf("Error message did not match") 600 t.Errorf("Expected ") 601 t.Errorf(" Duplicated replacement name new1") 602 t.Errorf("actual error:") 603 t.Errorf(" %s", err.Error()) 604 t.FailNow() 605 } 606} 607 608func TestReplacementsDuplicatedError(t *testing.T) { 609 cycleString := "a=b,a=c" 610 err := replaceProperty.Set(cycleString) 611 612 if err.Error() != "Duplicated replacement name a" { 613 t.Errorf("Error message did not match") 614 t.Errorf("Expected ") 615 t.Errorf(" Duplicated replacement name a") 616 t.Errorf("actual error:") 617 t.Errorf(" %s", err.Error()) 618 t.FailNow() 619 } 620} 621 622func TestReplacementsMultipleReplacedToSame(t *testing.T) { 623 cycleString := "a=c,d=c" 624 err := replaceProperty.Set(cycleString) 625 626 if err.Error() != "Duplicated replacement name c" { 627 t.Errorf("Error message did not match") 628 t.Errorf("Expected ") 629 t.Errorf(" Duplicated replacement name c") 630 t.Errorf("actual error:") 631 t.Errorf(" %s", err.Error()) 632 t.FailNow() 633 } 634} 635