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