Home | History | Annotate | Download | only in overlaytests
      1 #!/usr/bin/python
      2 import hashlib
      3 import optparse
      4 import os
      5 import re
      6 import shlex
      7 import subprocess
      8 import sys
      9 import threading
     10 import time
     11 
     12 TASK_COMPILATION = 'compile'
     13 TASK_DISABLE_OVERLAYS = 'disable overlays'
     14 TASK_ENABLE_MULTIPLE_OVERLAYS = 'enable multiple overlays'
     15 TASK_ENABLE_SINGLE_OVERLAY = 'enable single overlay'
     16 TASK_ENABLE_FILTERED_OVERLAYS = 'enable filtered overlays'
     17 TASK_FILE_EXISTS_TEST = 'test (file exists)'
     18 TASK_GREP_IDMAP_TEST = 'test (grep idmap)'
     19 TASK_MD5_TEST = 'test (md5)'
     20 TASK_IDMAP_PATH = 'idmap --path'
     21 TASK_IDMAP_SCAN = 'idmap --scan'
     22 TASK_INSTRUMENTATION = 'instrumentation'
     23 TASK_INSTRUMENTATION_TEST = 'test (instrumentation)'
     24 TASK_MKDIR = 'mkdir'
     25 TASK_PUSH = 'push'
     26 TASK_ROOT = 'root'
     27 TASK_REMOUNT = 'remount'
     28 TASK_RM = 'rm'
     29 TASK_SETPROP = 'setprop'
     30 TASK_SETUP_IDMAP_PATH = 'setup idmap --path'
     31 TASK_SETUP_IDMAP_SCAN = 'setup idmap --scan'
     32 TASK_START = 'start'
     33 TASK_STOP = 'stop'
     34 
     35 adb = 'adb'
     36 
     37 def _adb_shell(cmd):
     38     argv = shlex.split(adb + " shell '" + cmd + "; echo $?'")
     39     proc = subprocess.Popen(argv, bufsize=1, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
     40     (stdout, stderr) = proc.communicate()
     41     (stdout, stderr) = (stdout.replace('\r', ''), stderr.replace('\r', ''))
     42     tmp = stdout.rsplit('\n', 2)
     43     if len(tmp) == 2:
     44         stdout == ''
     45         returncode = int(tmp[0])
     46     else:
     47         stdout = tmp[0] + '\n'
     48         returncode = int(tmp[1])
     49     return returncode, stdout, stderr
     50 
     51 class VerbosePrinter:
     52     class Ticker(threading.Thread):
     53         def _print(self):
     54             s = '\r' + self.text + '[' + '.' * self.i + ' ' * (4 - self.i) + ']'
     55             sys.stdout.write(s)
     56             sys.stdout.flush()
     57             self.i = (self.i + 1) % 5
     58 
     59         def __init__(self, cond_var, text):
     60             threading.Thread.__init__(self)
     61             self.text = text
     62             self.setDaemon(True)
     63             self.cond_var = cond_var
     64             self.running = False
     65             self.i = 0
     66             self._print()
     67             self.running = True
     68 
     69         def run(self):
     70             self.cond_var.acquire()
     71             while True:
     72                 self.cond_var.wait(0.25)
     73                 running = self.running
     74                 if not running:
     75                     break
     76                 self._print()
     77             self.cond_var.release()
     78 
     79         def stop(self):
     80             self.cond_var.acquire()
     81             self.running = False
     82             self.cond_var.notify_all()
     83             self.cond_var.release()
     84 
     85     def _start_ticker(self):
     86         self.ticker = VerbosePrinter.Ticker(self.cond_var, self.text)
     87         self.ticker.start()
     88 
     89     def _stop_ticker(self):
     90         self.ticker.stop()
     91         self.ticker.join()
     92         self.ticker = None
     93 
     94     def _format_begin(self, type, name):
     95         N = self.width - len(type) - len(' [    ] ')
     96         fmt = '%%s %%-%ds ' % N
     97         return fmt % (type, name)
     98 
     99     def __init__(self, use_color):
    100         self.cond_var = threading.Condition()
    101         self.ticker = None
    102         if use_color:
    103             self.color_RED = '\033[1;31m'
    104             self.color_red = '\033[0;31m'
    105             self.color_reset = '\033[0;37m'
    106         else:
    107             self.color_RED = ''
    108             self.color_red = ''
    109             self.color_reset = ''
    110 
    111         argv = shlex.split('stty size') # get terminal width
    112         proc = subprocess.Popen(argv, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
    113         (stdout, stderr) = proc.communicate()
    114         if proc.returncode == 0:
    115             (h, w) = stdout.split()
    116             self.width = int(w)
    117         else:
    118             self.width = 72 # conservative guesstimate
    119 
    120     def begin(self, type, name):
    121         self.text = self._format_begin(type, name)
    122         sys.stdout.write(self.text + '[    ]')
    123         sys.stdout.flush()
    124         self._start_ticker()
    125 
    126     def end_pass(self, type, name):
    127         self._stop_ticker()
    128         sys.stdout.write('\r' + self.text + '[ OK ]\n')
    129         sys.stdout.flush()
    130 
    131     def end_fail(self, type, name, msg):
    132         self._stop_ticker()
    133         sys.stdout.write('\r' + self.color_RED + self.text + '[FAIL]\n')
    134         sys.stdout.write(self.color_red)
    135         sys.stdout.write(msg)
    136         sys.stdout.write(self.color_reset)
    137         sys.stdout.flush()
    138 
    139 class QuietPrinter:
    140     def begin(self, type, name):
    141         pass
    142 
    143     def end_pass(self, type, name):
    144         sys.stdout.write('PASS ' + type + ' ' + name + '\n')
    145         sys.stdout.flush()
    146 
    147     def end_fail(self, type, name, msg):
    148         sys.stdout.write('FAIL ' + type + ' ' + name + '\n')
    149         sys.stdout.flush()
    150 
    151 class CompilationTask:
    152     def __init__(self, makefile):
    153         self.makefile = makefile
    154 
    155     def get_type(self):
    156         return TASK_COMPILATION
    157 
    158     def get_name(self):
    159         return self.makefile
    160 
    161     def execute(self):
    162         os.putenv('ONE_SHOT_MAKEFILE', os.getcwd() + "/" + self.makefile)
    163         argv = shlex.split('make -C "../../../../../" files')
    164         proc = subprocess.Popen(argv, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
    165         (stdout, stderr) = proc.communicate()
    166         return proc.returncode, stdout, stderr
    167 
    168 class InstrumentationTask:
    169     def __init__(self, instrumentation_class):
    170         self.instrumentation_class = instrumentation_class
    171 
    172     def get_type(self):
    173         return TASK_INSTRUMENTATION
    174 
    175     def get_name(self):
    176         return self.instrumentation_class
    177 
    178     def execute(self):
    179         return _adb_shell('am instrument -r -w -e class %s com.android.overlaytest/android.test.InstrumentationTestRunner' % self.instrumentation_class)
    180 
    181 class PushTask:
    182     def __init__(self, src, dest):
    183         self.src = src
    184         self.dest = dest
    185 
    186     def get_type(self):
    187         return TASK_PUSH
    188 
    189     def get_name(self):
    190         return "%s -> %s" % (self.src, self.dest)
    191 
    192     def execute(self):
    193         src = os.getenv('OUT')
    194         if (src is None):
    195           return 1, "", "Unable to proceed - $OUT environment var not set\n"
    196         src += "/" + self.src
    197         argv = shlex.split(adb + ' push %s %s' % (src, self.dest))
    198         proc = subprocess.Popen(argv, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
    199         (stdout, stderr) = proc.communicate()
    200         return proc.returncode, stdout, stderr
    201 
    202 class MkdirTask:
    203     def __init__(self, path):
    204         self.path = path
    205 
    206     def get_type(self):
    207         return TASK_MKDIR
    208 
    209     def get_name(self):
    210         return self.path
    211 
    212     def execute(self):
    213         return _adb_shell('mkdir -p %s' % self.path)
    214 
    215 class RmTask:
    216     def __init__(self, path):
    217         self.path = path
    218 
    219     def get_type(self):
    220         return TASK_RM
    221 
    222     def get_name(self):
    223         return self.path
    224 
    225     def execute(self):
    226         returncode, stdout, stderr = _adb_shell('ls %s' % self.path)
    227         if returncode != 0 and stderr.endswith(': No such file or directory\n'):
    228             return 0, "", ""
    229         return _adb_shell('rm -r %s' % self.path)
    230 
    231 class SetPropTask:
    232     def __init__(self, prop, value):
    233         self.prop = prop
    234         self.value = value
    235 
    236     def get_type(self):
    237         return TASK_SETPROP
    238 
    239     def get_name(self):
    240         return self.prop
    241 
    242     def execute(self):
    243       return _adb_shell('setprop %s %s' % (self.prop, self.value))
    244 
    245 class IdmapPathTask:
    246     def __init__(self, path_target_apk, path_overlay_apk, path_idmap):
    247         self.path_target_apk = path_target_apk
    248         self.path_overlay_apk = path_overlay_apk
    249         self.path_idmap = path_idmap
    250 
    251     def get_type(self):
    252         return TASK_IDMAP_PATH
    253 
    254     def get_name(self):
    255         return self.path_idmap
    256 
    257     def execute(self):
    258         return _adb_shell('su system idmap --scan "%s" "%s" "%s" "%s"' % (self.target_pkg_name, self.target_pkg, self.idmap_dir, self.overlay_dir))
    259 
    260 class IdmapScanTask:
    261     def __init__(self, overlay_dir, target_pkg_name, target_pkg, idmap_dir, symlink_dir):
    262         self.overlay_dir = overlay_dir
    263         self.target_pkg_name = target_pkg_name
    264         self.target_pkg = target_pkg
    265         self.idmap_dir = idmap_dir
    266         self.symlink_dir = symlink_dir
    267 
    268     def get_type(self):
    269         return TASK_IDMAP_SCAN
    270 
    271     def get_name(self):
    272         return self.target_pkg_name
    273 
    274     def execute(self):
    275         return _adb_shell('su system idmap --scan "%s" "%s" "%s" "%s"' % (self.overlay_dir, self.target_pkg_name, self.target_pkg, self.idmap_dir))
    276 
    277 class FileExistsTest:
    278     def __init__(self, path):
    279         self.path = path
    280 
    281     def get_type(self):
    282         return TASK_FILE_EXISTS_TEST
    283 
    284     def get_name(self):
    285         return self.path
    286 
    287     def execute(self):
    288         return _adb_shell('ls %s' % self.path)
    289 
    290 class GrepIdmapTest:
    291     def __init__(self, path_idmap, pattern, expected_n):
    292         self.path_idmap = path_idmap
    293         self.pattern = pattern
    294         self.expected_n = expected_n
    295 
    296     def get_type(self):
    297         return TASK_GREP_IDMAP_TEST
    298 
    299     def get_name(self):
    300         return self.pattern
    301 
    302     def execute(self):
    303         returncode, stdout, stderr = _adb_shell('idmap --inspect %s' % self.path_idmap)
    304         if returncode != 0:
    305             return returncode, stdout, stderr
    306         all_matches = re.findall('\s' + self.pattern + '$', stdout, flags=re.MULTILINE)
    307         if len(all_matches) != self.expected_n:
    308             return 1, 'pattern=%s idmap=%s expected=%d found=%d\n' % (self.pattern, self.path_idmap, self.expected_n, len(all_matches)), ''
    309         return 0, "", ""
    310 
    311 class Md5Test:
    312     def __init__(self, path, expected_content):
    313         self.path = path
    314         self.expected_md5 = hashlib.md5(expected_content).hexdigest()
    315 
    316     def get_type(self):
    317         return TASK_MD5_TEST
    318 
    319     def get_name(self):
    320         return self.path
    321 
    322     def execute(self):
    323         returncode, stdout, stderr = _adb_shell('md5sum %s' % self.path)
    324         if returncode != 0:
    325             return returncode, stdout, stderr
    326         actual_md5 = stdout.split()[0]
    327         if actual_md5 != self.expected_md5:
    328             return 1, 'expected %s, got %s\n' % (self.expected_md5, actual_md5), ''
    329         return 0, "", ""
    330 
    331 class StartTask:
    332     def get_type(self):
    333         return TASK_START
    334 
    335     def get_name(self):
    336         return ""
    337 
    338     def execute(self):
    339         (returncode, stdout, stderr) = _adb_shell('start')
    340         if returncode != 0:
    341             return returncode, stdout, stderr
    342 
    343         while True:
    344             (returncode, stdout, stderr) = _adb_shell('getprop dev.bootcomplete')
    345             if returncode != 0:
    346                 return returncode, stdout, stderr
    347             if stdout.strip() == "1":
    348                 break
    349             time.sleep(0.5)
    350 
    351         return 0, "", ""
    352 
    353 class StopTask:
    354     def get_type(self):
    355         return TASK_STOP
    356 
    357     def get_name(self):
    358         return ""
    359 
    360     def execute(self):
    361         (returncode, stdout, stderr) = _adb_shell('stop')
    362         if returncode != 0:
    363             return returncode, stdout, stderr
    364         return _adb_shell('setprop dev.bootcomplete 0')
    365 
    366 class RootTask:
    367     def get_type(self):
    368         return TASK_ROOT
    369 
    370     def get_name(self):
    371         return ""
    372 
    373     def execute(self):
    374         (returncode, stdout, stderr) = _adb_shell('getprop service.adb.root 0')
    375         if returncode != 0:
    376             return returncode, stdout, stderr
    377         if stdout.strip() == '1': # already root
    378             return 0, "", ""
    379 
    380         argv = shlex.split(adb + ' root')
    381         proc = subprocess.Popen(argv, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
    382         (stdout, stderr) = proc.communicate()
    383         if proc.returncode != 0:
    384             return proc.returncode, stdout, stderr
    385 
    386         argv = shlex.split(adb + ' wait-for-device')
    387         proc = subprocess.Popen(argv, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
    388         (stdout, stderr) = proc.communicate()
    389         return proc.returncode, stdout, stderr
    390 
    391 class RemountTask:
    392     def get_type(self):
    393         return TASK_REMOUNT
    394 
    395     def get_name(self):
    396         return ""
    397 
    398     def execute(self):
    399         argv = shlex.split(adb + ' remount')
    400         proc = subprocess.Popen(argv, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
    401         (stdout, stderr) = proc.communicate()
    402         # adb remount returns 0 even if the operation failed, so check stdout
    403         if stdout.startswith('remount failed:'):
    404             return 1, stdout, stderr
    405         return proc.returncode, stdout, stderr
    406 
    407 class CompoundTask:
    408     def __init__(self, type, tasks):
    409         self.type = type
    410         self.tasks = tasks
    411 
    412     def get_type(self):
    413         return self.type
    414 
    415     def get_name(self):
    416         return ""
    417 
    418     def execute(self):
    419         for t in self.tasks:
    420             (returncode, stdout, stderr) = t.execute()
    421             if returncode != 0:
    422                 return returncode, stdout, stderr
    423         return 0, "", ""
    424 
    425 def _create_disable_overlays_task():
    426     tasks = [
    427         RmTask("/vendor/overlay/framework_a.apk"),
    428         RmTask("/vendor/overlay/framework_b.apk"),
    429         RmTask("/data/resource-cache/vendor@overlay (at] framework_a.apk@idmap"),
    430         RmTask("/data/resource-cache/vendor@overlay (at] framework_b.apk@idmap"),
    431         RmTask("/vendor/overlay/app_a.apk"),
    432         RmTask("/vendor/overlay/app_b.apk"),
    433         RmTask("/vendor/overlay/app_c.apk"),
    434         RmTask("/data/resource-cache/vendor@overlay (at] app_a.apk@idmap"),
    435         RmTask("/data/resource-cache/vendor@overlay (at] app_b.apk@idmap"),
    436         RmTask("/data/resource-cache/vendor@overlay (at] app_c.apk@idmap"),
    437         SetPropTask('persist.oem.overlay.test', '""'),
    438         RmTask("/data/property/persist.oem.overlay.test"),
    439     ]
    440     return CompoundTask(TASK_DISABLE_OVERLAYS, tasks)
    441 
    442 def _create_enable_single_overlay_task():
    443     tasks = [
    444         _create_disable_overlays_task(),
    445         MkdirTask('/system/vendor'),
    446         MkdirTask('/vendor/overlay'),
    447         PushTask('/data/app/com.android.overlaytest.overlay/com.android.overlaytest.overlay.apk', '/vendor/overlay/framework_a.apk'),
    448         PushTask('/data/app/com.android.overlaytest.first_app_overlay/com.android.overlaytest.first_app_overlay.apk', '/vendor/overlay/app_a.apk'),
    449     ]
    450     return CompoundTask(TASK_ENABLE_SINGLE_OVERLAY, tasks)
    451 
    452 def _create_enable_multiple_overlays_task():
    453     tasks = [
    454         _create_disable_overlays_task(),
    455         MkdirTask('/system/vendor'),
    456         MkdirTask('/vendor/overlay'),
    457 
    458         PushTask('/data/app/com.android.overlaytest.overlay/com.android.overlaytest.overlay.apk', '/vendor/overlay/framework_b.apk'),
    459         PushTask('/data/app/com.android.overlaytest.first_app_overlay/com.android.overlaytest.first_app_overlay.apk', '/vendor/overlay/app_a.apk'),
    460         PushTask('/data/app/com.android.overlaytest.second_app_overlay/com.android.overlaytest.second_app_overlay.apk', '/vendor/overlay/app_b.apk'),
    461         PushTask('/data/app/com.android.overlaytest.filtered_app_overlay/com.android.overlaytest.filtered_app_overlay.apk', '/vendor/overlay/app_c.apk'),
    462     ]
    463     return CompoundTask(TASK_ENABLE_MULTIPLE_OVERLAYS, tasks)
    464 
    465 def _create_enable_filtered_overlays_task():
    466       tasks = [
    467         _create_disable_overlays_task(),
    468         SetPropTask('persist.oem.overlay.test', 'foo'),
    469         MkdirTask('/system/vendor'),
    470         MkdirTask('/vendor/overlay'),
    471         PushTask('/data/app/com.android.overlaytest.overlay/com.android.overlaytest.overlay.apk', '/vendor/overlay/framework_b.apk'),
    472         PushTask('/data/app/com.android.overlaytest.first_app_overlay/com.android.overlaytest.first_app_overlay.apk', '/vendor/overlay/app_a.apk'),
    473         PushTask('/data/app/com.android.overlaytest.second_app_overlay/com.android.overlaytest.second_app_overlay.apk', '/vendor/overlay/app_b.apk'),
    474         PushTask('/data/app/com.android.overlaytest.filtered_app_overlay/com.android.overlaytest.filtered_app_overlay.apk', '/vendor/overlay/app_c.apk'),
    475       ]
    476       return CompoundTask(TASK_ENABLE_FILTERED_OVERLAYS, tasks)
    477 
    478 def _create_setup_idmap_path_task(idmaps, symlinks):
    479     tasks = [
    480         _create_enable_single_overlay_task(),
    481         RmTask(symlinks),
    482         RmTask(idmaps),
    483         MkdirTask(idmaps),
    484         MkdirTask(symlinks),
    485     ]
    486     return CompoundTask(TASK_SETUP_IDMAP_PATH, tasks)
    487 
    488 def _create_setup_idmap_scan_task(idmaps, symlinks):
    489     tasks = [
    490         _create_enable_filtered_overlays_task(),
    491         RmTask(symlinks),
    492         RmTask(idmaps),
    493         MkdirTask(idmaps),
    494         MkdirTask(symlinks),
    495     ]
    496     return CompoundTask(TASK_SETUP_IDMAP_SCAN, tasks)
    497 
    498 def _handle_instrumentation_task_output(stdout, printer):
    499     regex_status_code = re.compile(r'^INSTRUMENTATION_STATUS_CODE: -?(\d+)')
    500     regex_name = re.compile(r'^INSTRUMENTATION_STATUS: test=(.*)')
    501     regex_begin_stack = re.compile(r'^INSTRUMENTATION_STATUS: stack=(.*)')
    502     regex_end_stack = re.compile(r'^$')
    503 
    504     failed_tests = 0
    505     current_test = None
    506     current_stack = []
    507     mode_stack = False
    508     for line in stdout.split("\n"):
    509         line = line.rstrip() # strip \r from adb output
    510         m = regex_status_code.match(line)
    511         if m:
    512             c = int(m.group(1))
    513             if c == 1:
    514                 printer.begin(TASK_INSTRUMENTATION_TEST, current_test)
    515             elif c == 0:
    516                 printer.end_pass(TASK_INSTRUMENTATION_TEST, current_test)
    517             else:
    518                 failed_tests += 1
    519                 current_stack.append("\n")
    520                 msg = "\n".join(current_stack)
    521                 printer.end_fail(TASK_INSTRUMENTATION_TEST, current_test, msg.rstrip() + '\n')
    522             continue
    523 
    524         m = regex_name.match(line)
    525         if m:
    526             current_test = m.group(1)
    527             continue
    528 
    529         m = regex_begin_stack.match(line)
    530         if m:
    531             mode_stack = True
    532             current_stack = []
    533             current_stack.append("  " + m.group(1))
    534             continue
    535 
    536         m = regex_end_stack.match(line)
    537         if m:
    538             mode_stack = False
    539             continue
    540 
    541         if mode_stack:
    542             current_stack.append("    " + line.strip())
    543 
    544     return failed_tests
    545 
    546 def _set_adb_device(option, opt, value, parser):
    547     global adb
    548     if opt == '-d' or opt == '--device':
    549         adb = 'adb -d'
    550     if opt == '-e' or opt == '--emulator':
    551         adb = 'adb -e'
    552     if opt == '-s' or opt == '--serial':
    553         adb = 'adb -s ' + value
    554 
    555 def _create_opt_parser():
    556     parser = optparse.OptionParser()
    557     parser.add_option('-d', '--device', action='callback', callback=_set_adb_device,
    558             help='pass -d to adb')
    559     parser.add_option('-e', '--emulator', action='callback', callback=_set_adb_device,
    560             help='pass -e to adb')
    561     parser.add_option('-s', '--serial', type="str", action='callback', callback=_set_adb_device,
    562             help='pass -s <serical> to adb')
    563     parser.add_option('-C', '--no-color', action='store_false',
    564             dest='use_color', default=True,
    565             help='disable color escape sequences in output')
    566     parser.add_option('-q', '--quiet', action='store_true',
    567             dest='quiet_mode', default=False,
    568             help='quiet mode, output only results')
    569     parser.add_option('-b', '--no-build', action='store_false',
    570             dest='do_build', default=True,
    571             help='do not rebuild test projects')
    572     parser.add_option('-k', '--continue', action='store_true',
    573             dest='do_continue', default=False,
    574             help='do not rebuild test projects')
    575     parser.add_option('-i', '--test-idmap', action='store_true',
    576             dest='test_idmap', default=False,
    577             help='run tests for idmap')
    578     parser.add_option('-0', '--test-no-overlay', action='store_true',
    579             dest='test_no_overlay', default=False,
    580             help='run tests without any overlay')
    581     parser.add_option('-1', '--test-single-overlay', action='store_true',
    582             dest='test_single_overlay', default=False,
    583             help='run tests for single overlay')
    584     parser.add_option('-2', '--test-multiple-overlays', action='store_true',
    585             dest='test_multiple_overlays', default=False,
    586             help='run tests for multiple overlays')
    587     parser.add_option('-3', '--test-filtered-overlays', action='store_true',
    588             dest='test_filtered_overlays', default=False,
    589             help='run tests for filtered (sys prop) overlays')
    590     return parser
    591 
    592 if __name__ == '__main__':
    593     opt_parser = _create_opt_parser()
    594     opts, args = opt_parser.parse_args(sys.argv[1:])
    595     if not opts.test_idmap and not opts.test_no_overlay and not opts.test_single_overlay and not opts.test_multiple_overlays and not opts.test_filtered_overlays:
    596         opts.test_idmap = True
    597         opts.test_no_overlay = True
    598         opts.test_single_overlay = True
    599         opts.test_multiple_overlays = True
    600         opts.test_filtered_overlays = True
    601 
    602     if len(args) > 0:
    603         opt_parser.error("unexpected arguments: %s" % " ".join(args))
    604         # will never reach this: opt_parser.error will call sys.exit
    605 
    606     if opts.quiet_mode:
    607         printer = QuietPrinter()
    608     else:
    609         printer = VerbosePrinter(opts.use_color)
    610     tasks = []
    611 
    612     # must be in the same directory as this script for compilation tasks to work
    613     script = sys.argv[0]
    614     dirname = os.path.dirname(script)
    615     wd = os.path.realpath(dirname)
    616     os.chdir(wd)
    617 
    618     # build test cases
    619     if opts.do_build:
    620         tasks.append(CompilationTask('OverlayTest/Android.mk'))
    621         tasks.append(CompilationTask('OverlayTestOverlay/Android.mk'))
    622         tasks.append(CompilationTask('OverlayAppFirst/Android.mk'))
    623         tasks.append(CompilationTask('OverlayAppSecond/Android.mk'))
    624         tasks.append(CompilationTask('OverlayAppFiltered/Android.mk'))
    625 
    626     # remount filesystem, install test project
    627     tasks.append(RootTask())
    628     tasks.append(RemountTask())
    629     tasks.append(PushTask('/system/app/OverlayTest/OverlayTest.apk', '/system/app/OverlayTest.apk'))
    630 
    631     # test idmap
    632     if opts.test_idmap:
    633         idmaps='/data/local/tmp/idmaps'
    634         symlinks='/data/local/tmp/symlinks'
    635 
    636         # idmap --path
    637         tasks.append(StopTask())
    638         tasks.append(_create_setup_idmap_path_task(idmaps, symlinks))
    639         tasks.append(StartTask())
    640         tasks.append(IdmapPathTask('/vendor/overlay/framework_a.apk', '/system/framework/framework-res.apk', idmaps + '/a.idmap'))
    641         tasks.append(FileExistsTest(idmaps + '/a.idmap'))
    642         tasks.append(GrepIdmapTest(idmaps + '/a.idmap', 'bool/config_annoy_dianne', 1))
    643 
    644         # idmap --scan
    645         tasks.append(StopTask())
    646         tasks.append(_create_setup_idmap_scan_task(idmaps, symlinks))
    647         tasks.append(StartTask())
    648         tasks.append(IdmapScanTask('/vendor/overlay', 'android', '/system/framework/framework-res.apk', idmaps, symlinks))
    649         tasks.append(FileExistsTest(idmaps + '/vendor@overlay (at] framework_b.apk@idmap'))
    650         tasks.append(GrepIdmapTest(idmaps + '/vendor@overlay (at] framework_b.apk@idmap', 'bool/config_annoy_dianne', 1))
    651 
    652 
    653         # overlays.list
    654         overlays_list_path = idmaps + '/overlays.list'
    655         expected_content = '''\
    656 /vendor/overlay/framework_b.apk /data/local/tmp/idmaps/vendor@overlay (at] framework_b.apk@idmap
    657 '''
    658         tasks.append(FileExistsTest(overlays_list_path))
    659         tasks.append(Md5Test(overlays_list_path, expected_content))
    660 
    661         # idmap cleanup
    662         tasks.append(RmTask(symlinks))
    663         tasks.append(RmTask(idmaps))
    664 
    665     # test no overlay: all overlays cleared
    666     if opts.test_no_overlay:
    667         tasks.append(StopTask())
    668         tasks.append(_create_disable_overlays_task())
    669         tasks.append(StartTask())
    670         tasks.append(InstrumentationTask('com.android.overlaytest.WithoutOverlayTest'))
    671 
    672     # test single overlay: one overlay (a)
    673     if opts.test_single_overlay:
    674         tasks.append(StopTask())
    675         tasks.append(_create_enable_single_overlay_task())
    676         tasks.append(StartTask())
    677         tasks.append(InstrumentationTask('com.android.overlaytest.WithOverlayTest'))
    678 
    679     # test multiple overlays: all overlays - including 'disabled' filtered
    680     # overlay (system property unset) so expect 'b[p=2]' overrides 'a[p=1]' but
    681     # 'c[p=3]' should be ignored
    682     if opts.test_multiple_overlays:
    683         tasks.append(StopTask())
    684         tasks.append(_create_enable_multiple_overlays_task())
    685         tasks.append(StartTask())
    686         tasks.append(InstrumentationTask('com.android.overlaytest.WithMultipleOverlaysTest'))
    687 
    688     # test filtered overlays: all overlays - including 'enabled' filtered
    689     # overlay (system property set/matched) so expect c[p=3] to override both a
    690     # & b where applicable
    691     if opts.test_filtered_overlays:
    692         tasks.append(StopTask())
    693         tasks.append(_create_enable_filtered_overlays_task())
    694         tasks.append(StartTask())
    695         tasks.append(InstrumentationTask('com.android.overlaytest.WithFilteredOverlaysTest'))
    696 
    697     ignored_errors = 0
    698     for t in tasks:
    699         type = t.get_type()
    700         name = t.get_name()
    701         if type == TASK_INSTRUMENTATION:
    702             # InstrumentationTask will run several tests, but we want it
    703             # to appear as if each test was run individually. Calling
    704             # "am instrument" with a single test method is prohibitively
    705             # expensive, so let's instead post-process the output to
    706             # emulate individual calls.
    707             retcode, stdout, stderr = t.execute()
    708             if retcode != 0:
    709                 printer.begin(TASK_INSTRUMENTATION, name)
    710                 printer.end_fail(TASK_INSTRUMENTATION, name, stderr)
    711                 sys.exit(retcode)
    712             retcode = _handle_instrumentation_task_output(stdout, printer)
    713             if retcode != 0:
    714                 if not opts.do_continue:
    715                     sys.exit(retcode)
    716                 else:
    717                     ignored_errors += retcode
    718         else:
    719             printer.begin(type, name)
    720             retcode, stdout, stderr = t.execute()
    721             if retcode == 0:
    722                 printer.end_pass(type, name)
    723             if retcode != 0:
    724                 if len(stderr) == 0:
    725                     # hope for output from stdout instead (true for eg adb shell rm)
    726                     stderr = stdout
    727                 printer.end_fail(type, name, stderr)
    728                 if not opts.do_continue:
    729                     sys.exit(retcode)
    730                 else:
    731                     ignored_errors += retcode
    732     sys.exit(ignored_errors)
    733