|
import re
|
import os
|
import glob
|
import sys
|
import subprocess
|
import argparse
|
|
# test command line arguments
|
argParser = argparse.ArgumentParser(description = 'dexter end-to-end test driver')
|
argParser.add_argument('-cmd', default = 'dexter')
|
argParser.add_argument('-root', help = 'Root location of the test data files')
|
argParser.add_argument('-update', action = 'store_true', help = 'Update the expected files')
|
args = argParser.parse_args()
|
|
# the bazel sandbox test data root
|
data_root = args.root or 'tools/dexter/testdata'
|
|
# update expected (golden) output?
|
if args.update:
|
if args.root is None:
|
print('ERROR: -update requires -root value')
|
exit(1)
|
print('\nUpdating expected output (test data root: %s)' % data_root)
|
|
# list of test cases
|
# ( <test_name> : { <test_case_config> } )
|
test_cases = {
|
'map' : { 'args' : '-m', 'input' : ['*.dex'] },
|
'stats' : { 'args' : '-s', 'input' : ['*.dex'] },
|
'asm' : { 'args' : '-d', 'input' : ['*.dex'] },
|
'hello_stats' : { 'args' : '-s -e Hello', 'input' : ['hello.dex'] },
|
'am_stats' : { 'args' : '-s -e android.app.ActivityManager', 'input' : ['large.dex'] },
|
'rewrite' : { 'args' : '-d -x full_rewrite', 'input' : ['*.dex'] },
|
'entry_hook' : { 'args' : '-d -x stress_entry_hook', 'input' : [
|
'entry_hooks.dex', 'hello.dex', 'medium.dex', 'min.dex' ] },
|
'exit_hook' : { 'args' : '-d -x stress_exit_hook', 'input' : [
|
'exit_hooks.dex', 'medium.dex', 'try_catch.dex' ] },
|
'wrap_invoke' : { 'args' : '-d -x stress_wrap_invoke', 'input' : [
|
'hello.dex', 'hello_nodebug.dex', 'medium.dex' ] },
|
'mi' : { 'args' : '-d -x test_method_instrumenter', 'input' : ['mi.dex'] },
|
'find_method' : { 'args' : '-x stress_find_method', 'input' : [
|
'hello.dex', 'entry_hooks.dex', 'medium.dex', 'large.dex', 'try_catch.dex' ] },
|
'verbose_cfg' : { 'args' : '-d --cfg=verbose', 'input' : ['*.dex'] },
|
'compact_cfg' : { 'args' : '-d --cfg=compact', 'input' : ['*.dex'] },
|
'scratch_regs' : { 'args' : '-d -x stress_scratch_regs', 'input' : ['*.dex'] },
|
'regs_usage' : { 'args' : '-x regs_histogram', 'input' : ['*.dex'] },
|
'code_coverage' : { 'args' : '-d -x code_coverage', 'input' : ['*.dex'] },
|
}
|
|
# run a shell command and returns the stdout content
|
def Run(cmd, stdin_content=None):
|
return subprocess.Popen(
|
args = cmd,
|
shell = True,
|
stdin = subprocess.PIPE,
|
stdout = subprocess.PIPE,
|
stderr = subprocess.STDOUT).communicate(input = stdin_content)[0]
|
|
tests = 0
|
failures = 0
|
|
# for each test_case, run dexter over the specified input (ex. *.dex)
|
#
|
# the expected ('golden') output has the same base name as the input .dex,
|
# for example (test_name = 'map') :
|
#
|
# 'hello.dex' -> 'expected/hello.map'
|
#
|
for test_name, test_config in sorted(test_cases.iteritems()):
|
for input_pattern in test_config['input']:
|
input_files = glob.glob(os.path.join(data_root, input_pattern))
|
|
for input in input_files:
|
tests = tests + 1
|
|
# run dexter with the test arguments
|
cmd = '%s %s %s' % (args.cmd, test_config['args'], input)
|
actual_output = Run(cmd)
|
|
# build the expected filename
|
expected_filename = re.sub(r'\.dex', ('.%s' % test_name), os.path.basename(input))
|
expected_filename = os.path.join(data_root, 'expected', expected_filename)
|
|
if args.update:
|
# update expected output file
|
with open(expected_filename, "w") as f:
|
f.write(actual_output)
|
else:
|
# compare the actual output with the expected output
|
cmp_output = Run('diff "%s" -' % expected_filename, actual_output)
|
if cmp_output:
|
print('\nFAILED: expected output mismatch (%s)' % os.path.basename(expected_filename))
|
print(cmp_output)
|
failures = failures + 1
|
else:
|
print('ok: output matching (%s)' % os.path.basename(expected_filename))
|
|
if args.update:
|
print('\nSUMMARY: updated expected output for %d tests\n' % tests)
|
else:
|
print('\nSUMMARY: %d failure(s), %d test cases\n' % (failures, tests))
|
|
if failures != 0:
|
exit(1)
|