1#!/usr/bin/env python3
2
3from __future__ import print_function
4
5import tempfile
6import unittest
7
8from vndk_definition_tool import TaggedDict, TaggedPathDict, TaggedLibDict, \
9                                 NUM_PARTITIONS, PT_SYSTEM, PT_VENDOR
10
11from .compat import StringIO
12
13
14_TEST_DATA = '''Path,Tag
15/system/lib/lib_ll_ndk.so,ll-ndk
16/system/lib/lib_ll_ndk_private.so,ll-ndk-private
17/system/lib/lib_vndk_sp.so,vndk-sp
18/system/lib/lib_vndk_sp_private.so,vndk-sp-private
19/system/lib/lib_vndk.so,vndk
20/system/lib/lib_system_only.so,system-only
21/system/lib/lib_system_only_rs.so,system-only-rs
22/vendor/lib/lib_sp_hal.so,sp-hal
23/vendor/lib/lib_sp_hal_dep.so,sp-hal-dep
24/vendor/lib/lib_vendor_only.so,vendor-only
25/product/lib/lib_product_only.so,product-only
26/system_ext/lib/lib_system_ext_only.so,system_ext-only
27/system/lib/lib_remove.so,remove
28/system/lib/lib_hl_ndk.so,hl-ndk
29/system/lib/lib_vndk_private.so,vndk-private
30/system/lib/lib_vndk_sp_both.so,vndk-sp-both
31/system/lib/lib_vndk_sp_hal.so,vndk-sp-hal
32/system/lib/lib_vndk_indirect.so,vndk-private
33/system/lib/lib_vndk_sp_indirect.so,vndk-sp
34/system/lib/lib_vndk_sp_indirect_private.so,vndk-sp-private
35/system/lib/lib_fwk_only.so,system-only
36/system/lib/lib_fwk_only_rs.so,system-only-rs
37/vendor/lib/lib_vnd_only.so,vendor-only
38'''
39
40_TEST_DATA_ALIAS_PATHS = {
41    '/system/lib/lib_hl_ndk.so',
42    '/system/lib/lib_vndk_indirect.so',
43    '/system/lib/lib_vndk_sp_both.so',
44    '/system/lib/lib_vndk_sp_hal.so',
45    '/system/lib/lib_vndk_sp_indirect.so',
46    '/system/lib/lib_vndk_sp_indirect_private.so',
47    '/system/lib/lib_fwk_only.so',
48    '/system/lib/lib_fwk_only_rs.so',
49    '/vendor/lib/lib_vnd_only.so',
50}
51
52
53class TaggedDictTest(unittest.TestCase):
54    def test_normalize_tag(self):
55        self.assertEqual('ll_ndk', TaggedDict._normalize_tag('ll-ndk'))
56        self.assertEqual('ll_ndk', TaggedDict._normalize_tag('LL-NDK'))
57        self.assertEqual('ll_ndk', TaggedDict._normalize_tag('LL_NDK'))
58        with self.assertRaises(ValueError):
59            TaggedDict._normalize_tag('unknown-lib-tag')
60
61
62    def _check_tag_visibility(self, d, from_tag, visible_tags):
63        for to_tag in visible_tags:
64            self.assertTrue(d.is_tag_visible(from_tag, to_tag))
65        for to_tag in TaggedDict.TAGS:
66            self.assertEqual(d.is_tag_visible(from_tag, to_tag),
67                             to_tag in visible_tags)
68
69
70    def test_is_tag_visible(self):
71        d = TaggedDict()
72
73        # LL-NDK
74        visible_tags = {'ll_ndk', 'll_ndk_private'}
75        self._check_tag_visibility(d, 'll_ndk', visible_tags)
76        self._check_tag_visibility(d, 'll_ndk_private', visible_tags)
77
78        # VNDK-SP
79        visible_tags = {
80            'll_ndk', 'vndk_sp', 'vndk_sp_private', 'system_only_rs',
81        }
82        self._check_tag_visibility(d, 'vndk_sp', visible_tags)
83        self._check_tag_visibility(d, 'vndk_sp_private', visible_tags)
84
85        # VNDK
86        visible_tags = {
87            'll_ndk', 'vndk_sp', 'vndk_sp_private', 'vndk', 'vndk_private',
88        }
89        self._check_tag_visibility(d, 'vndk', visible_tags)
90
91        # SYSTEM-ONLY and SYSTEM_EXT-ONLY
92        visible_tags = {
93            'll_ndk', 'll_ndk_private',
94            'vndk_sp', 'vndk_sp_private',
95            'vndk', 'vndk_private',
96            'system_only', 'system_only_rs',
97            'system_ext_only',
98            'sp_hal'
99        }
100        self._check_tag_visibility(d, 'system_only', visible_tags)
101        self._check_tag_visibility(d, 'system_only_rs', visible_tags)
102        self._check_tag_visibility(d, 'system_ext_only', visible_tags)
103
104        # SP-HAL
105        visible_tags = {'ll_ndk', 'vndk_sp', 'sp_hal', 'sp_hal_dep'}
106        self._check_tag_visibility(d, 'sp_hal', visible_tags)
107        self._check_tag_visibility(d, 'sp_hal_dep', visible_tags)
108
109        # VENDOR-ONLY
110        visible_tags = {
111            'll_ndk', 'vndk_sp', 'vndk', 'sp_hal', 'sp_hal_dep', 'vendor_only',
112        }
113        self._check_tag_visibility(d, 'vendor_only', visible_tags)
114
115        # PRODUCT-ONLY
116        visible_tags = {
117            'll_ndk', 'vndk_sp', 'vndk', 'sp_hal',
118            # Remove the following after VNDK-ext can be checked separately.
119            'sp_hal_dep', 'vendor_only',
120        }
121        self._check_tag_visibility(d, 'vendor_only', visible_tags)
122
123        # Remove
124        self._check_tag_visibility(d, 'remove', set())
125
126
127class TaggedPathDictTest(unittest.TestCase):
128    def test_enumerate_paths(self):
129        tagged_paths = TaggedPathDict()
130
131        self.assertEqual(
132            ['/system/lib/libc.so'],
133            list(tagged_paths._enumerate_paths('/system/lib/libc.so')))
134
135        self.assertEqual(
136            ['/system/lib/libc.so', '/system/lib64/libc.so'],
137            list(tagged_paths._enumerate_paths('/system/${LIB}/libc.so')))
138
139        self.assertEqual(
140            ['/system/lib/vndk/libutils.so',
141             '/system/lib64/vndk/libutils.so'],
142            list(tagged_paths._enumerate_paths(
143                '/system/${LIB}/vndk${VNDK_VER}/libutils.so')))
144
145        tagged_paths = TaggedPathDict(['current', '27', '28'])
146
147        self.assertEqual(
148            ['/system/lib/vndk/libutils.so',
149             '/system/lib64/vndk/libutils.so',
150             '/system/lib/vndk-27/libutils.so',
151             '/system/lib64/vndk-27/libutils.so',
152             '/system/lib/vndk-28/libutils.so',
153             '/system/lib64/vndk-28/libutils.so'],
154            list(tagged_paths._enumerate_paths(
155                '/system/${LIB}/vndk${VNDK_VER}/libutils.so')))
156
157        self.assertEqual(
158            ['/system/lib/vndk/libutils.so',
159             '/system/lib/vndk-27/libutils.so',
160             '/system/lib/vndk-28/libutils.so'],
161            list(tagged_paths._enumerate_paths(
162                '/system/lib/vndk${VNDK_VER}/libutils.so')))
163
164
165    def test_load_from_csv_empty(self):
166        try:
167            TaggedPathDict().load_from_csv(StringIO(''))
168        except StopIteration:
169            self.fail('empty file should be considered as a valid input')
170
171
172    def test_load_from_csv_with_header(self):
173        fp = StringIO('Path,Tag\nliba.so,system-only\n')
174        d = TaggedPathDict()
175        d.load_from_csv(fp)
176        self.assertIn('liba.so', d.system_only)
177
178        fp = StringIO('Tag,Path\nsystem-only,liba.so\n')
179        d = TaggedPathDict()
180        d.load_from_csv(fp)
181        self.assertIn('liba.so', d.system_only)
182
183
184    def test_load_from_csv_without_header(self):
185        fp = StringIO('liba.so,system-only\n')
186        d = TaggedPathDict()
187        d.load_from_csv(fp)
188        self.assertIn('liba.so', d.system_only)
189
190
191    def _check_test_data_loaded(self, d):
192        # Paths
193        self.assertIn('/system/lib/lib_ll_ndk.so', d.ll_ndk)
194        self.assertIn('/system/lib/lib_ll_ndk_private.so', d.ll_ndk_private)
195        self.assertIn('/system/lib/lib_vndk_sp.so', d.vndk_sp)
196        self.assertIn('/system/lib/lib_vndk_sp_private.so', d.vndk_sp_private)
197        self.assertIn('/system/lib/lib_vndk.so', d.vndk)
198        self.assertIn('/system/lib/lib_vndk_private.so', d.vndk_private)
199        self.assertIn('/system/lib/lib_system_only.so', d.system_only)
200        self.assertIn('/system/lib/lib_system_only_rs.so', d.system_only_rs)
201        self.assertIn('/vendor/lib/lib_sp_hal.so', d.sp_hal)
202        self.assertIn('/vendor/lib/lib_sp_hal_dep.so', d.sp_hal_dep)
203        self.assertIn('/vendor/lib/lib_vendor_only.so', d.vendor_only)
204        self.assertIn('/system_ext/lib/lib_system_ext_only.so',
205                      d.system_ext_only)
206        self.assertIn('/product/lib/lib_product_only.so', d.product_only)
207        self.assertIn('/system/lib/lib_remove.so', d.remove)
208
209        # Aliases
210        self.assertIn('/system/lib/lib_hl_ndk.so', d.system_only)
211        self.assertIn('/system/lib/lib_vndk_sp_both.so', d.vndk_sp)
212        self.assertIn('/system/lib/lib_vndk_sp_hal.so', d.vndk_sp)
213
214
215    def test_load_from_csv_tags(self):
216        fp = StringIO(_TEST_DATA)
217        d = TaggedPathDict()
218        d.load_from_csv(fp)
219        self._check_test_data_loaded(d)
220
221
222    def test_create_from_csv(self):
223        d = TaggedPathDict.create_from_csv(StringIO(_TEST_DATA))
224        self._check_test_data_loaded(d)
225
226
227    def test_create_from_csv_path(self):
228        with tempfile.NamedTemporaryFile('w+') as f:
229            f.write(_TEST_DATA)
230            f.flush()
231
232            d = TaggedPathDict.create_from_csv_path(f.name)
233            self._check_test_data_loaded(d)
234
235
236    def test_get_path_tag(self):
237        fp = StringIO(_TEST_DATA)
238        d = TaggedPathDict()
239        d.load_from_csv(fp)
240
241        self.assertEqual('ll_ndk', d.get_path_tag('/system/lib/lib_ll_ndk.so'))
242        self.assertEqual('ll_ndk_private',
243                         d.get_path_tag('/system/lib/lib_ll_ndk_private.so'))
244        self.assertEqual('vndk_sp',
245                         d.get_path_tag('/system/lib/lib_vndk_sp.so'))
246        self.assertEqual('vndk_sp_private',
247                         d.get_path_tag('/system/lib/lib_vndk_sp_private.so'))
248        self.assertEqual('vndk', d.get_path_tag('/system/lib/lib_vndk.so'))
249        self.assertEqual('vndk_private',
250                         d.get_path_tag('/system/lib/lib_vndk_private.so'))
251        self.assertEqual('system_only',
252                         d.get_path_tag('/system/lib/lib_system_only.so'))
253        self.assertEqual('system_only_rs',
254                         d.get_path_tag('/system/lib/lib_system_only_rs.so'))
255        self.assertEqual('sp_hal',
256                         d.get_path_tag('/vendor/lib/lib_sp_hal.so'))
257        self.assertEqual('sp_hal_dep',
258                         d.get_path_tag('/vendor/lib/lib_sp_hal_dep.so'))
259        self.assertEqual('vendor_only',
260                         d.get_path_tag('/vendor/lib/lib_vendor_only.so'))
261        self.assertEqual('remove',
262                         d.get_path_tag('/system/lib/lib_remove.so'))
263
264        # Aliases
265        self.assertEqual('system_only',
266                         d.get_path_tag('/system/lib/lib_hl_ndk.so'))
267        self.assertEqual('vndk_sp',
268                         d.get_path_tag('/system/lib/lib_vndk_sp_hal.so'))
269        self.assertEqual('vndk_sp',
270                         d.get_path_tag('/system/lib/lib_vndk_sp_both.so'))
271        self.assertEqual('vndk_private',
272                         d.get_path_tag('/system/lib/lib_vndk_indirect.so'))
273        self.assertEqual('vndk_sp',
274                         d.get_path_tag('/system/lib/lib_vndk_sp_indirect.so'))
275        self.assertEqual('vndk_sp_private',
276                         d.get_path_tag('/system/lib/' +
277                                        'lib_vndk_sp_indirect_private.so'))
278        self.assertEqual('system_only',
279                         d.get_path_tag('/system/lib/lib_fwk_only.so'))
280        self.assertEqual('system_only_rs',
281                         d.get_path_tag('/system/lib/lib_fwk_only_rs.so'))
282        self.assertEqual('vendor_only',
283                         d.get_path_tag('/vendor/lib/lib_vendor_only.so'))
284
285        # Unmatched paths
286        self.assertEqual('system_only',
287                         d.get_path_tag('/system/lib/unknown.so'))
288        self.assertEqual('system_only',
289                         d.get_path_tag('/data/lib/unknown.so'))
290        self.assertEqual('vendor_only',
291                         d.get_path_tag('/vendor/lib/unknown.so'))
292
293
294    def _check_path_visibility(self, d, all_paths, from_paths, visible_paths):
295        for from_path in from_paths:
296            for to_path in all_paths:
297                self.assertEqual(d.is_path_visible(from_path, to_path),
298                                 to_path in visible_paths)
299
300
301    def test_is_path_visible(self):
302        fp = StringIO(_TEST_DATA)
303        d = TaggedPathDict()
304        d.load_from_csv(fp)
305
306        # Collect path universe set.
307        all_paths = set()
308        for tag in TaggedPathDict.TAGS:
309            all_paths |= getattr(d, tag)
310        all_paths -= _TEST_DATA_ALIAS_PATHS
311
312        # LL-NDK
313        from_paths = {
314            '/system/lib/lib_ll_ndk.so',
315            '/system/lib/lib_ll_ndk_private.so',
316        }
317        visible_paths = {
318            '/system/lib/lib_ll_ndk.so',
319            '/system/lib/lib_ll_ndk_private.so',
320        }
321        self._check_path_visibility(d, all_paths, from_paths, visible_paths)
322
323        # VNDK-SP
324        from_paths = {
325            '/system/lib/lib_vndk_sp.so',
326            '/system/lib/lib_vndk_sp_private.so',
327        }
328        visible_paths = {
329            '/system/lib/lib_ll_ndk.so',
330            '/system/lib/lib_vndk_sp.so',
331            '/system/lib/lib_vndk_sp_private.so',
332            '/system/lib/lib_system_only_rs.so',
333        }
334        self._check_path_visibility(d, all_paths, from_paths, visible_paths)
335
336        # VNDK
337        from_paths = {
338            '/system/lib/lib_vndk.so',
339        }
340        visible_paths = {
341            '/system/lib/lib_ll_ndk.so',
342            '/system/lib/lib_vndk_sp.so',
343            '/system/lib/lib_vndk_sp_private.so',
344            '/system/lib/lib_vndk.so',
345            '/system/lib/lib_vndk_private.so',
346        }
347        self._check_path_visibility(d, all_paths, from_paths, visible_paths)
348
349        # SYSTEM-ONLY
350        from_paths = {
351            '/system/lib/lib_system_only.so',
352            '/system/lib/lib_system_only_rs.so',
353        }
354        visible_paths = {
355            '/system/lib/lib_ll_ndk.so',
356            '/system/lib/lib_ll_ndk_private.so',
357            '/system/lib/lib_vndk_sp.so',
358            '/system/lib/lib_vndk_sp_private.so',
359            '/system/lib/lib_vndk.so',
360            '/system/lib/lib_vndk_private.so',
361            '/system/lib/lib_system_only.so',
362            '/system/lib/lib_system_only_rs.so',
363            '/vendor/lib/lib_sp_hal.so',
364            '/system_ext/lib/lib_system_ext_only.so',
365        }
366        self._check_path_visibility(d, all_paths, from_paths, visible_paths)
367
368        # SP-HAL
369        from_paths = {
370            '/vendor/lib/lib_sp_hal.so',
371            '/vendor/lib/lib_sp_hal_dep.so',
372        }
373        visible_paths = {
374            '/system/lib/lib_ll_ndk.so',
375            '/system/lib/lib_vndk_sp.so',
376            '/vendor/lib/lib_sp_hal.so',
377            '/vendor/lib/lib_sp_hal_dep.so',
378        }
379        self._check_path_visibility(d, all_paths, from_paths, visible_paths)
380
381        # VENDOR-ONLY
382        from_paths = {
383            '/vendor/lib/lib_vendor_only.so',
384        }
385        visible_paths = {
386            '/system/lib/lib_ll_ndk.so',
387            '/system/lib/lib_vndk_sp.so',
388            '/system/lib/lib_vndk.so',
389            '/vendor/lib/lib_sp_hal.so',
390            '/vendor/lib/lib_sp_hal_dep.so',
391            '/vendor/lib/lib_vendor_only.so',
392        }
393        self._check_path_visibility(d, all_paths, from_paths, visible_paths)
394
395
396class MockSPLibResult(object):
397    def __init__(self, sp_hal, sp_hal_dep):
398        self.sp_hal = sp_hal
399        self.sp_hal_dep = sp_hal_dep
400
401
402class MockELFLinkData(object):
403    def __init__(self, path):
404        self.path = path
405
406
407class MockELFGraph(object):
408    def __init__(self):
409        self.lib_pt = [dict() for i in range(NUM_PARTITIONS)]
410
411
412    def add(self, path):
413        partition = PT_VENDOR if path.startswith('/vendor') else PT_SYSTEM
414        lib = MockELFLinkData(path)
415        self.lib_pt[partition][path] = lib
416        return lib
417
418
419    def compute_sp_lib(self, generic_refs=None):
420        vendor_libs = self.lib_pt[PT_VENDOR].values()
421        return MockSPLibResult(
422            set(v for v in vendor_libs if 'lib_sp_hal.so' in v.path),
423            set(v for v in vendor_libs if 'lib_sp_hal_dep.so' in v.path))
424
425
426class TaggedLibDictTest(unittest.TestCase):
427    def setUp(self):
428        self.tagged_paths = TaggedPathDict.create_from_csv(StringIO(_TEST_DATA))
429
430        self.graph = MockELFGraph()
431
432        self.lib_ll_ndk = self.graph.add('/system/lib/lib_ll_ndk.so')
433        self.lib_ll_ndk_private = \
434            self.graph.add('/system/lib/lib_ll_ndk_private.so')
435
436        self.lib_vndk_sp = self.graph.add('/system/lib/lib_vndk_sp.so')
437        self.lib_vndk_sp_private = \
438            self.graph.add('/system/lib/lib_vndk_sp_private.so')
439
440        self.lib_vndk = self.graph.add('/system/lib/lib_vndk.so')
441
442        self.lib_system_only = \
443            self.graph.add('/system/lib/lib_system_only.so')
444        self.lib_system_only_rs = \
445            self.graph.add('/system/lib/lib_system_only_rs.so')
446
447        self.lib_sp_hal = self.graph.add('/vendor/lib/lib_sp_hal.so')
448        self.lib_sp_hal_dep = self.graph.add('/vendor/lib/lib_sp_hal_dep.so')
449
450        self.lib_vendor_only = self.graph.add('/vendor/lib/lib_vendor_only.so')
451
452        self.tagged_libs = TaggedLibDict.create_from_graph(
453            self.graph, self.tagged_paths)
454
455
456    def test_create_from_graph(self):
457        self.assertIn(self.lib_ll_ndk, self.tagged_libs.ll_ndk)
458        self.assertIn(self.lib_ll_ndk_private,
459                      self.tagged_libs.ll_ndk_private)
460        self.assertIn(self.lib_vndk_sp, self.tagged_libs.vndk_sp)
461        self.assertIn(self.lib_vndk_sp_private,
462                      self.tagged_libs.vndk_sp_private)
463
464        self.assertIn(self.lib_vndk, self.tagged_libs.vndk)
465
466        self.assertIn(self.lib_system_only, self.tagged_libs.system_only)
467        self.assertIn(self.lib_system_only_rs, self.tagged_libs.system_only_rs)
468
469        self.assertIn(self.lib_sp_hal, self.tagged_libs.sp_hal)
470        self.assertIn(self.lib_sp_hal_dep, self.tagged_libs.sp_hal_dep)
471        self.assertIn(self.lib_vendor_only, self.tagged_libs.vendor_only)
472
473
474    def test_get_path_tag(self):
475        d = self.tagged_libs
476
477        self.assertEqual('ll_ndk', d.get_path_tag(self.lib_ll_ndk))
478        self.assertEqual('ll_ndk_private',
479                         d.get_path_tag(self.lib_ll_ndk_private))
480        self.assertEqual('vndk_sp', d.get_path_tag(self.lib_vndk_sp))
481        self.assertEqual('vndk_sp_private',
482                         d.get_path_tag(self.lib_vndk_sp_private))
483        self.assertEqual('vndk', d.get_path_tag(self.lib_vndk))
484        self.assertEqual('system_only', d.get_path_tag(self.lib_system_only))
485        self.assertEqual('system_only_rs',
486                         d.get_path_tag(self.lib_system_only_rs))
487        self.assertEqual('sp_hal', d.get_path_tag(self.lib_sp_hal))
488        self.assertEqual('sp_hal_dep', d.get_path_tag(self.lib_sp_hal_dep))
489        self.assertEqual('vendor_only', d.get_path_tag(self.lib_vendor_only))
490
491        # Unmatched paths
492        tag = d.get_path_tag(MockELFLinkData('/system/lib/unknown.so'))
493        self.assertEqual('system_only', tag)
494        tag = d.get_path_tag(MockELFLinkData('/data/lib/unknown.so'))
495        self.assertEqual('system_only', tag)
496        tag = d.get_path_tag(MockELFLinkData('/vendor/lib/unknown.so'))
497        self.assertEqual('vendor_only', tag)
498