Home | History | Annotate | Download | only in tests
      1 #!/usr/bin/env python3
      2 
      3 from __future__ import print_function
      4 
      5 import os
      6 import subprocess
      7 import unittest
      8 import zipfile
      9 
     10 from vndk_definition_tool import DexFileReader, UnicodeSurrogateDecodeError
     11 
     12 from .compat import TemporaryDirectory
     13 
     14 SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__))
     15 INPUT_DIR = os.path.join(SCRIPT_DIR, 'testdata', 'test_dex_file')
     16 
     17 
     18 class ModifiedUTF8Test(unittest.TestCase):
     19     def test_encode(self):
     20         self.assertEqual(b'\xc0\x80', u'\u0000'.encode('mutf-8'))
     21         self.assertEqual(b'\x09', u'\u0009'.encode('mutf-8'))
     22         self.assertEqual(b'\x7f', u'\u007f'.encode('mutf-8'))
     23         self.assertEqual(b'\xc2\x80', u'\u0080'.encode('mutf-8'))
     24         self.assertEqual(b'\xdf\xbf', u'\u07ff'.encode('mutf-8'))
     25         self.assertEqual(b'\xe0\xa0\x80', u'\u0800'.encode('mutf-8'))
     26         self.assertEqual(b'\xe7\xbf\xbf', u'\u7fff'.encode('mutf-8'))
     27         self.assertEqual(b'\xed\xa0\x81\xed\xb0\x80',
     28                          u'\U00010400'.encode('mutf-8'))
     29 
     30 
     31     def test_decode(self):
     32         self.assertEqual(u'\u0000', b'\xc0\x80'.decode('mutf-8'))
     33         self.assertEqual(u'\u0009', b'\x09'.decode('mutf-8'))
     34         self.assertEqual(u'\u007f', b'\x7f'.decode('mutf-8'))
     35         self.assertEqual(u'\u0080', b'\xc2\x80'.decode('mutf-8'))
     36         self.assertEqual(u'\u07ff', b'\xdf\xbf'.decode('mutf-8'))
     37         self.assertEqual(u'\u0800', b'\xe0\xa0\x80'.decode('mutf-8'))
     38         self.assertEqual(u'\u7fff', b'\xe7\xbf\xbf'.decode('mutf-8'))
     39         self.assertEqual(u'\U00010400',
     40                          b'\xed\xa0\x81\xed\xb0\x80'.decode('mutf-8'))
     41 
     42 
     43     def test_decode_exception(self):
     44         # Low surrogate does not come after high surrogate
     45         with self.assertRaises(UnicodeSurrogateDecodeError):
     46             b'\xed\xa0\x81\x40'.decode('mutf-8')
     47 
     48         # Low surrogate without prior high surrogate
     49         with self.assertRaises(UnicodeSurrogateDecodeError):
     50             b'\xed\xb0\x80\x40'.decode('mutf-8')
     51 
     52         # Unexpected end after high surrogate
     53         with self.assertRaises(UnicodeSurrogateDecodeError):
     54             b'\xed\xa0\x81'.decode('mutf-8')
     55 
     56         # Unexpected end after low surrogate
     57         with self.assertRaises(UnicodeSurrogateDecodeError):
     58             b'\xed\xb0\x80'.decode('mutf-8')
     59 
     60         # Out-of-order surrogate
     61         with self.assertRaises(UnicodeSurrogateDecodeError):
     62             b'\xed\xb0\x80\xed\xa0\x81'.decode('mutf-8')
     63 
     64 
     65 class DexFileTest(unittest.TestCase):
     66     def _assemble_smali(self, dest, source):
     67         """Assemble a smali source file.  Skip the test if the smali command is
     68         unavailable."""
     69         try:
     70             subprocess.check_call(['smali', 'a', source, '-o', dest])
     71         except IOError:
     72             self.skipTest('smali not available')
     73 
     74 
     75     def _create_zip_file(self, dest, paths):
     76         """Create a zip file from several input files."""
     77         with zipfile.ZipFile(dest, 'w') as zip_file:
     78             for path in paths:
     79                 zip_file.write(path, os.path.basename(path))
     80 
     81 
     82     def test_generate_classes_dex_names(self):
     83         seq = DexFileReader.generate_classes_dex_names()
     84         self.assertEqual('classes.dex', next(seq))
     85         self.assertEqual('classes2.dex', next(seq))
     86         self.assertEqual('classes3.dex', next(seq))
     87 
     88 
     89     def test_enumerate_dex_strings_buf(self):
     90         with TemporaryDirectory() as tmp_dir:
     91             smali_file = os.path.join(INPUT_DIR, 'Hello.smali')
     92             classes_dex = os.path.join(tmp_dir, 'classes.dex')
     93             self._assemble_smali(classes_dex, smali_file)
     94 
     95             with open(classes_dex, 'rb') as classes_dex:
     96                 buf = classes_dex.read()
     97 
     98             strs = set(DexFileReader.enumerate_dex_strings_buf(buf))
     99 
    100             self.assertIn(b'hello', strs)
    101             self.assertIn(b'world', strs)
    102 
    103 
    104     def test_enumerate_dex_strings_apk(self):
    105         with TemporaryDirectory() as tmp_dir:
    106             smali_file = os.path.join(INPUT_DIR, 'Hello.smali')
    107             classes_dex = os.path.join(tmp_dir, 'classes.dex')
    108             self._assemble_smali(classes_dex, smali_file)
    109 
    110             smali_file = os.path.join(INPUT_DIR, 'Example.smali')
    111             classes2_dex = os.path.join(tmp_dir, 'classes2.dex')
    112             self._assemble_smali(classes2_dex, smali_file)
    113 
    114             zip_file = os.path.join(tmp_dir, 'example.apk')
    115             self._create_zip_file(zip_file, [classes_dex, classes2_dex])
    116 
    117             strs = set(DexFileReader.enumerate_dex_strings_apk(zip_file))
    118 
    119             self.assertIn(b'hello', strs)
    120             self.assertIn(b'world', strs)
    121             self.assertIn(b'foo', strs)
    122             self.assertIn(b'bar', strs)
    123