1#!/usr/bin/python 2# 3# Copyright (C) 2015 The Android Open Source Project 4# 5# Licensed under the Apache License, Version 2.0 (the "License"); 6# you may not use this file except in compliance with the License. 7# You may obtain a copy of the License at 8# 9# http://www.apache.org/licenses/LICENSE-2.0 10# 11# Unless required by applicable law or agreed to in writing, software 12# distributed under the License is distributed on an "AS IS" BASIS, 13# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14# See the License for the specific language governing permissions and 15# limitations under the License. 16# 17 18import copy 19import unittest 20 21import api_analysis 22 23 24class APIAnalysisTests(unittest.TestCase): 25 def test_guest_symbol_not_present_in_host(self): 26 guest_api = \ 27 { 28 "symbols": {"guest_only": {"type": "guest_only"}}, 29 "types": {"guest_only": {"kind": "incomplete"}} 30 } 31 host_api = {"symbols": {}, "types": {}} 32 api_analysis.mark_incompatible_api(guest_api, host_api) 33 self.assertFalse(guest_api['symbols']['guest_only']['is_compatible']) 34 35 def test_compatible_int(self): 36 guest_api = \ 37 { 38 "symbols": { 39 "int": {"type": "int32"} 40 }, 41 "types": { 42 "int32": {"align": 64, 43 "kind": "int", 44 "size": 32} 45 } 46 } 47 host_api = copy.deepcopy(guest_api) 48 # We allow host alignment to be less than guest one. 49 # See comments in api_analysis.py for details. 50 host_api['types']['int32']['align'] = 32 51 api_analysis.mark_incompatible_api(guest_api, host_api) 52 self.assertTrue(guest_api['symbols']['int']['is_compatible']) 53 54 def test_compatible_loop_reference(self): 55 guest_api = \ 56 { 57 "symbols": { 58 "pointer1": {"type": "pointer1"}, 59 "pointer2": {"type": "pointer2"}, 60 }, 61 "types": { 62 "pointer1": {"align": 32, 63 "kind": "pointer", 64 "pointee_type": "pointer2", 65 "size": 32}, 66 "pointer2": {"align": 32, 67 "kind": "pointer", 68 "pointee_type": "pointer1", 69 "size": 32} 70 } 71 } 72 host_api = copy.deepcopy(guest_api) 73 api_analysis.mark_incompatible_api(guest_api, host_api) 74 self.assertTrue(guest_api['symbols']['pointer1']['is_compatible']) 75 self.assertTrue(guest_api['symbols']['pointer2']['is_compatible']) 76 77 78 def test_incompatible_kind(self): 79 guest_api = \ 80 { 81 "symbols": {"symb": {"type": "t"}}, 82 "types": {"t": {"kind": "incomplete"}} 83 } 84 host_api = copy.deepcopy(guest_api) 85 host_api['types']['t']['kind'] = 'fp' 86 api_analysis.mark_incompatible_api(guest_api, host_api) 87 self.assertFalse(guest_api['symbols']['symb']['is_compatible']) 88 89 90 def test_incompatible_size(self): 91 guest_api = \ 92 { 93 "symbols": {"symb": {"type": "t"}}, 94 "types": {"t": {"kind": "int", "size": 32, "align": 32}} 95 } 96 host_api = copy.deepcopy(guest_api) 97 host_api['types']['t']['size'] = 64 98 api_analysis.mark_incompatible_api(guest_api, host_api) 99 self.assertFalse(guest_api['symbols']['symb']['is_compatible']) 100 101 102 def test_incompatible_align(self): 103 guest_api = \ 104 { 105 "symbols": {"symb": {"type": "t"}}, 106 "types": {"t": {"kind": "int", "size": 32, "align": 32}} 107 } 108 host_api = copy.deepcopy(guest_api) 109 host_api['types']['t']['align'] = 64 110 api_analysis.mark_incompatible_api(guest_api, host_api) 111 self.assertFalse(guest_api['symbols']['symb']['is_compatible']) 112 113 114 def test_incompatible_fields_num(self): 115 guest_api = \ 116 { 117 "symbols": {"symb": {"type": "t"}}, 118 "types": {"t": {"kind": "struct", 119 "is_polymorphic": False, 120 "fields": [{"offset": 0, 121 "type": "t2"}], 122 "size": 32, 123 "align": 32}, 124 "t2": {"kind": "int", "size": 32, "align": 32}} 125 } 126 host_api = copy.deepcopy(guest_api) 127 host_api['types']['t']['fields'] = [] 128 api_analysis.mark_incompatible_api(guest_api, host_api) 129 self.assertFalse(guest_api['symbols']['symb']['is_compatible']) 130 131 132 def test_incompatible_field_type(self): 133 guest_api = \ 134 { 135 "symbols": {"symb": {"type": "t"}}, 136 "types": {"t": {"kind": "struct", 137 "is_polymorphic": False, 138 "fields": [{"offset": 0, 139 "type": "t2"}], 140 "size": 32, 141 "align": 32}, 142 "t2": {"kind": "int", "size": 32, "align": 32}} 143 } 144 host_api = copy.deepcopy(guest_api) 145 host_api['types']['t2']['kind'] = 'fp' 146 api_analysis.mark_incompatible_api(guest_api, host_api) 147 self.assertFalse(guest_api['symbols']['symb']['is_compatible']) 148 149 150 def test_incompatible_field_offset(self): 151 guest_api = \ 152 { 153 "symbols": {"symb": {"type": "t"}}, 154 "types": {"t": {"kind": "struct", 155 "is_polymorphic": False, 156 "fields": [{"offset": 0, 157 "type": "t2"}], 158 "size": 32, 159 "align": 32}, 160 "t2": {"kind": "int", "size": 32, "align": 32}} 161 } 162 host_api = copy.deepcopy(guest_api) 163 host_api['types']['t']['fields'][0]["offset"] = 32 164 api_analysis.mark_incompatible_api(guest_api, host_api) 165 self.assertFalse(guest_api['symbols']['symb']['is_compatible']) 166 167 168 def test_incompatible_polymorphic(self): 169 guest_api = \ 170 { 171 "symbols": {"symb": {"type": "t"}}, 172 "types": {"t": {"kind": "struct", 173 "is_polymorphic": True, 174 "fields": [], 175 "size": 32, 176 "align": 32}} 177 } 178 host_api = copy.deepcopy(guest_api) 179 api_analysis.mark_incompatible_api(guest_api, host_api) 180 self.assertFalse(guest_api['symbols']['symb']['is_compatible']) 181 182 183 def test_incompatible_func_variadic_args(self): 184 guest_api = \ 185 { 186 "symbols": { 187 "symb": { 188 "type": "t" 189 } 190 }, 191 "types": { 192 "t": { 193 "align": 32, 194 "has_variadic_args": True, 195 "is_virtual_method": False, 196 "kind": "function", 197 "params": [], 198 "return_type": "void", 199 }, 200 "void": { 201 "kind": "incomplete" 202 } 203 } 204 } 205 host_api = copy.deepcopy(guest_api) 206 api_analysis.mark_incompatible_api(guest_api, host_api) 207 self.assertFalse(guest_api['symbols']['symb']['is_compatible']) 208 209 210 def test_incompatible_virtual_method(self): 211 guest_api = \ 212 { 213 "symbols": { 214 "symb": { 215 "type": "t" 216 } 217 }, 218 "types": { 219 "t": { 220 "align": 32, 221 "has_variadic_args": True, 222 "is_virtual_method": True, 223 "kind": "function", 224 "params": [], 225 "return_type": "void", 226 }, 227 "void": { 228 "kind": "incomplete" 229 } 230 } 231 } 232 host_api = copy.deepcopy(guest_api) 233 api_analysis.mark_incompatible_api(guest_api, host_api) 234 self.assertFalse(guest_api['symbols']['symb']['is_compatible']) 235 236 237 def test_incompatible_func_params_num(self): 238 guest_api = \ 239 { 240 "symbols": { 241 "symb": { 242 "type": "t" 243 } 244 }, 245 "types": { 246 "t": { 247 "align": 32, 248 "has_variadic_args": False, 249 "is_virtual_method": False, 250 "kind": "function", 251 "params": ["t2"], 252 "return_type": "t2", 253 }, 254 "t2": { 255 "kind": "int", 256 "size": 32, 257 "align": 32 258 } 259 } 260 } 261 host_api = copy.deepcopy(guest_api) 262 host_api['types']['t']['params'] = [] 263 api_analysis.mark_incompatible_api(guest_api, host_api) 264 self.assertFalse(guest_api['symbols']['symb']['is_compatible']) 265 266 267 def test_incompatible_func_param_type(self): 268 guest_api = \ 269 { 270 "symbols": { 271 "symb": { 272 "type": "t" 273 } 274 }, 275 "types": { 276 "t": { 277 "has_variadic_args": False, 278 "is_virtual_method": False, 279 "kind": "function", 280 "params": ["t2"], 281 "return_type": "void", 282 }, 283 "void": { 284 "kind": "incomplete" 285 }, 286 "t2": { 287 "kind": "int", 288 "size": 32, 289 "align": 32 290 } 291 } 292 } 293 host_api = copy.deepcopy(guest_api) 294 host_api['types']['t2']['kind'] = 'fp' 295 api_analysis.mark_incompatible_api(guest_api, host_api) 296 self.assertFalse(guest_api['symbols']['symb']['is_compatible']) 297 298 299 def test_unallowed_func_param_type(self): 300 guest_api = \ 301 { 302 "symbols": { 303 "symb": { 304 "type": "t" 305 } 306 }, 307 "types": { 308 "t": { 309 "align": 32, 310 "has_variadic_args": False, 311 "is_virtual_method": False, 312 "kind": "function", 313 "params": ["t2"], 314 "return_type": "void", 315 "size": 0 316 }, 317 "void": { 318 "kind": "incomplete" 319 }, 320 # Structs are not supported in trampolines. 321 "t2": { 322 "kind": "struct", 323 "is_polymorphic": False, 324 "fields": [], 325 "size": 32, 326 "align": 32 327 } 328 } 329 } 330 host_api = copy.deepcopy(guest_api) 331 api_analysis.mark_incompatible_api(guest_api, host_api) 332 self.assertFalse(guest_api['symbols']['symb']['is_compatible']) 333 334 335 def test_incompatible_func_return_type(self): 336 guest_api = \ 337 { 338 "symbols": { 339 "symb": { 340 "type": "t" 341 } 342 }, 343 "types": { 344 "t": { 345 "has_variadic_args": False, 346 "is_virtual_method": False, 347 "kind": "function", 348 "params": [], 349 "return_type": "t2", 350 }, 351 "t2": { 352 "kind": "int", 353 "size": 32, 354 "align": 32 355 } 356 } 357 } 358 host_api = copy.deepcopy(guest_api) 359 host_api['types']['t2']['kind'] = 'fp' 360 api_analysis.mark_incompatible_api(guest_api, host_api) 361 self.assertFalse(guest_api['symbols']['symb']['is_compatible']) 362 363 364 def test_unallowed_func_return_type(self): 365 guest_api = \ 366 { 367 "symbols": { 368 "symb": { 369 "type": "t" 370 } 371 }, 372 "types": { 373 "t": { 374 "has_variadic_args": False, 375 "is_virtual_method": False, 376 "kind": "function", 377 "params": [], 378 "return_type": "t2", 379 }, 380 # Structs are not supported in trampolines. 381 "t2": { 382 "kind": "struct", 383 "is_polymorphic": False, 384 "fields": [], 385 "size": 32, 386 "align": 32 387 } 388 } 389 } 390 host_api = copy.deepcopy(guest_api) 391 api_analysis.mark_incompatible_api(guest_api, host_api) 392 self.assertFalse(guest_api['symbols']['symb']['is_compatible']) 393 394 395 def test_incompatible_pointee_type(self): 396 guest_api = \ 397 { 398 "symbols": {"symb": {"type": "t"}}, 399 "types": {"t": {"kind": "pointer", 400 "pointee_type": "t2", 401 "size": 32, 402 "align": 32}, 403 "t2": {"kind": "int", "size": 32, "align": 32}} 404 } 405 host_api = copy.deepcopy(guest_api) 406 host_api['types']['t2']['kind'] = 'fp' 407 api_analysis.mark_incompatible_api(guest_api, host_api) 408 self.assertFalse(guest_api['symbols']['symb']['is_compatible']) 409 410 411 def test_incompatible_volatile_type(self): 412 guest_api = \ 413 { 414 "symbols": {"symb": {"type": "t"}}, 415 "types": {"t": {"kind": "volatile", 416 "base_type" : "t2", 417 "size": 32, 418 "align": 32}, 419 "t2": {"kind": "int", "size": 32, "align": 32}} 420 } 421 host_api = copy.deepcopy(guest_api) 422 host_api['types']['t2']['kind'] = 'char8' 423 api_analysis.mark_incompatible_api(guest_api, host_api) 424 self.assertFalse(guest_api['symbols']['symb']['is_compatible']) 425 426 427 def test_incompatible_restrict_type(self): 428 guest_api = \ 429 { 430 "symbols": {"symb": {"type": "t"}}, 431 "types": {"t": {"kind": "restrict", 432 "base_type" : "t2", 433 "size": 32, 434 "align": 32}, 435 "t2": {"kind": "int", "size": 32, "align": 32}} 436 } 437 host_api = copy.deepcopy(guest_api) 438 host_api['types']['t2']['kind'] = 'char8' 439 api_analysis.mark_incompatible_api(guest_api, host_api) 440 self.assertFalse(guest_api['symbols']['symb']['is_compatible']) 441 442 443 def test_incompatible_const_type(self): 444 guest_api = \ 445 { 446 "symbols": { 447 "symb": { 448 "type": "t" 449 } 450 }, 451 "types": { 452 "t": { 453 "kind": "const", 454 "base_type" : "t2" 455 }, 456 "t2": { 457 "kind": "int", 458 "size": 32, 459 "align": 32 460 } 461 } 462 } 463 host_api = copy.deepcopy(guest_api) 464 host_api['types']['t2']['kind'] = 'char8' 465 api_analysis.mark_incompatible_api(guest_api, host_api) 466 self.assertFalse(guest_api['symbols']['symb']['is_compatible']) 467 468 469 def test_incompatible_pointer_to_function(self): 470 guest_api = \ 471 { 472 "symbols": { 473 "symb": { 474 "type": "t" 475 } 476 }, 477 "types": { 478 "t": { 479 "kind": "pointer", 480 "pointee_type": "func", 481 "size": 32, 482 "align": 32 483 }, 484 "func": { 485 "has_variadic_args": False, 486 "is_virtual_method": False, 487 "kind": "function", 488 "params": [], 489 "return_type": "void", 490 }, 491 "void": { 492 "kind": "incomplete" 493 } 494 } 495 } 496 host_api = copy.deepcopy(guest_api) 497 api_analysis.mark_incompatible_api(guest_api, host_api) 498 self.assertFalse(guest_api['symbols']['symb']['is_compatible']) 499 500 501 def test_incompatible_array_element_type(self): 502 guest_api = \ 503 { 504 "symbols": { 505 "symb": { 506 "type": "t" 507 } 508 }, 509 "types": { 510 "t": { 511 "align": 32, 512 "kind": "array", 513 "element_type": "t2", 514 "incomplete": False, 515 "size": 32 516 }, 517 "t2": { 518 "kind": "int", 519 "size": 32, 520 "align": 32 521 } 522 } 523 } 524 host_api = copy.deepcopy(guest_api) 525 host_api['types']['t2']['kind'] = 'fp' 526 api_analysis.mark_incompatible_api(guest_api, host_api) 527 self.assertFalse(guest_api['symbols']['symb']['is_compatible']) 528 529 530 def test_incompatible_array_incompleteness(self): 531 guest_api = \ 532 { 533 "symbols": {"symb": {"type": "t"}}, 534 "types": {"t": {"align": 32, 535 "kind": "array", 536 "element_type": "t2", 537 "incomplete": False, 538 "size": 32}, 539 "t2": {"kind": "int", "size": 32, "align": 32}} 540 } 541 host_api = copy.deepcopy(guest_api) 542 host_api['types']['t']['incomplete'] = True 543 api_analysis.mark_incompatible_api(guest_api, host_api) 544 self.assertFalse(guest_api['symbols']['symb']['is_compatible']) 545 546 547 def test_comparison_context(self): 548 guest_api = \ 549 { 550 "symbols": {"good_symb": {"type": "good_pointer_type"}, 551 "bad_symb": {"type": "bad_pointer_type"}}, 552 "types": {"good_pointer_type": {"kind": "pointer", 553 "pointee_type": "int_type", 554 "size": 32, 555 "align": 32}, 556 "bad_pointer_type": {"kind": "pointer", 557 "pointee_type": "int_type", 558 "size": 32, 559 "align": 32}, 560 "int_type": {"kind": "int", "size": 32, "align": 32}, 561 "fp_type": {"kind": "fp", "size": 32, "align": 32}} 562 } 563 host_api = copy.deepcopy(guest_api) 564 host_api['types']['bad_pointer_type']['pointee_type'] = 'fp_type' 565 comparator = api_analysis.APIComparator( 566 guest_api['types'], host_api['types']) 567 api_analysis.mark_incompatible_api_with_comparator( 568 comparator, guest_api['symbols'], host_api['symbols']) 569 # Though int_type is compared against incompatible type in case of 570 # 'bad_symb' the type itself may still be compatible in other contexts. 571 # Thus this doesn't affect 'good_symb' compatibility. 572 self.assertTrue(guest_api['symbols']['good_symb']['is_compatible']) 573 self.assertFalse(guest_api['symbols']['bad_symb']['is_compatible']) 574 self.assertTrue(comparator.are_types_compatible('int_type', 'int_type')) 575 self.assertFalse(comparator.are_types_compatible('int_type', 'fp_type')) 576 577 578 def test_loop_references(self): 579 guest_api = \ 580 { 581 "symbols": {"symb": {"type": "ref_loop_head"}}, 582 "types": {"ref_loop_head": {"kind": "struct", 583 "is_polymorphic": False, 584 "fields": [{"offset": 0, 585 "type": "ref_loop_body"}, 586 {"offset": 0, 587 "type": "ref_post_loop"}], 588 "size": 32, 589 "align": 32}, 590 "ref_loop_body": {"kind": "pointer", 591 "pointee_type": "ref_loop_head", 592 "size": 32, 593 "align": 32}, 594 "ref_post_loop": {"kind": "int", 595 "size": 32, 596 "align": 32}} 597 } 598 host_api = copy.deepcopy(guest_api) 599 host_api['types']['ref_post_loop']['kind'] = 'fp' 600 comparator = api_analysis.APIComparator( 601 guest_api['types'], host_api['types']) 602 api_analysis.mark_incompatible_api_with_comparator( 603 comparator, guest_api['symbols'], host_api['symbols']) 604 # 'ref_loop_body' is incompatible due to referencing 'ref_loop_head' which 605 # references 'ref_post_loop' incompatible due to different type kind. 606 # This is true even though reference from 'ref_loop_body' is back edge if to 607 # consider graph traversal from 'symb'. 608 self.assertFalse(guest_api['symbols']['symb']['is_compatible']) 609 self.assertFalse( 610 comparator.are_types_compatible('ref_loop_body', 'ref_loop_body')) 611 612 613 def test_force_compatibility(self): 614 guest_api = \ 615 { 616 "symbols": { 617 "symb": { 618 "type": "t" 619 }, 620 "symb_p": { 621 "type": "t_p" 622 }, 623 "symb_c_p" : { 624 "type": "t_c_p" 625 } 626 }, 627 "types": { 628 "t": { 629 "kind": "incomplete", 630 "force_compatible": True 631 }, 632 "t_p" : { 633 "kind" : "pointer", 634 "pointee_type": "t", 635 "size" : 32 636 }, 637 "t_c" : { 638 "kind" : "const", 639 "base_type": "t", 640 }, 641 "t_c_p" : { 642 "kind" : "pointer", 643 "pointee_type": "t_c", 644 "size" : 32 645 } 646 } 647 } 648 host_api = copy.deepcopy(guest_api) 649 host_api['types']['t']['kind'] = 'fp' 650 api_analysis.mark_incompatible_api(guest_api, host_api) 651 self.assertTrue(guest_api['symbols']['symb']['is_compatible']) 652 self.assertTrue(guest_api['symbols']['symb_p']['is_compatible']) 653 self.assertTrue(guest_api['symbols']['symb_c_p']['is_compatible']) 654 self.assertTrue(guest_api['types']['t']['useful_force_compatible']) 655 656 657 def test_force_compatibility_for_referencing_incompatible(self): 658 guest_api = \ 659 { 660 "symbols": { 661 "symb": { 662 "type": "t" 663 }, 664 "symb_p": { 665 "type": "t_p" 666 }, 667 "symb_c_p" : { 668 "type": "t_c_p" 669 } 670 }, 671 "types": { 672 "t": { 673 "kind": "incomplete" 674 }, 675 "t_p" : { 676 "kind" : "pointer", 677 "pointee_type": "t", 678 "size" : 32, 679 "force_compatible": True 680 }, 681 "t_c" : { 682 "kind" : "const", 683 "base_type": "t", 684 }, 685 "t_c_p" : { 686 "kind" : "pointer", 687 "pointee_type": "t_c", 688 "size" : 32, 689 "force_compatible": True 690 } 691 } 692 } 693 host_api = copy.deepcopy(guest_api) 694 host_api['types']['t']['kind'] = 'fp' 695 api_analysis.mark_incompatible_api(guest_api, host_api) 696 self.assertFalse(guest_api['symbols']['symb']['is_compatible']) 697 self.assertTrue(guest_api['symbols']['symb_p']['is_compatible']) 698 self.assertTrue(guest_api['symbols']['symb_c_p']['is_compatible']) 699 self.assertTrue(guest_api['types']['t_p']['useful_force_compatible']) 700 self.assertTrue(guest_api['types']['t_c_p']['useful_force_compatible']) 701 702 def test_useless_force_compatibility(self): 703 guest_api = \ 704 { 705 "symbols": { 706 "symb": { 707 "type": "t" 708 }, 709 }, 710 "types": { 711 "t": { 712 "kind": "incomplete", 713 "force_compatible": True 714 }, 715 } 716 } 717 host_api = copy.deepcopy(guest_api) 718 api_analysis.mark_incompatible_api(guest_api, host_api) 719 self.assertTrue(guest_api['symbols']['symb']['is_compatible']) 720 self.assertFalse(guest_api['types']['t'].get('useful_force_compatible', False)) 721 722 723 def test_incompatible_type_referenced_by_incompatible_type(self): 724 guest_api = \ 725 { 726 "symbols": {"symb": {"type": "t"}}, 727 "types": {"t": {"kind": "struct", 728 "is_polymorphic": False, 729 "fields": [{"offset": 0, 730 "type": "t2"}], 731 "size": 32, 732 "align": 32}, 733 "t2": {"kind": "int", "size": 32, "align": 32}} 734 } 735 host_api = copy.deepcopy(guest_api) 736 host_api['types']['t']['size'] = 64 737 host_api['types']['t2']['kind'] = 'fp' 738 api_analysis.mark_incompatible_api(guest_api, host_api) 739 self.assertFalse(guest_api['symbols']['symb']['is_compatible']) 740 self.assertFalse(guest_api['types']['t']['is_compatible']) 741 self.assertFalse(guest_api['types']['t2']['is_compatible']) 742 743if __name__ == '__main__': 744 unittest.main() 745