177b5baba8349b0741c36f4c0db86660bc0a96991Stephen Hines#!/usr/bin/python2.4 277b5baba8349b0741c36f4c0db86660bc0a96991Stephen Hines# 3a4c54ee5ceb193d514618d341ec6967bff02cb2bStephen Hines# Copyright 2010-2012 Google Inc. All Rights Reserved. 477b5baba8349b0741c36f4c0db86660bc0a96991Stephen Hines 5a4c54ee5ceb193d514618d341ec6967bff02cb2bStephen Hines"""Renderscript Compiler Test. 677b5baba8349b0741c36f4c0db86660bc0a96991Stephen Hines 7a4c54ee5ceb193d514618d341ec6967bff02cb2bStephen HinesRuns subdirectories of tests for the Renderscript compiler. 877b5baba8349b0741c36f4c0db86660bc0a96991Stephen Hines""" 977b5baba8349b0741c36f4c0db86660bc0a96991Stephen Hines 1077b5baba8349b0741c36f4c0db86660bc0a96991Stephen Hinesimport filecmp 1177b5baba8349b0741c36f4c0db86660bc0a96991Stephen Hinesimport glob 1277b5baba8349b0741c36f4c0db86660bc0a96991Stephen Hinesimport os 13a4c54ee5ceb193d514618d341ec6967bff02cb2bStephen Hinesimport re 1477b5baba8349b0741c36f4c0db86660bc0a96991Stephen Hinesimport shutil 1577b5baba8349b0741c36f4c0db86660bc0a96991Stephen Hinesimport subprocess 1677b5baba8349b0741c36f4c0db86660bc0a96991Stephen Hinesimport sys 1777b5baba8349b0741c36f4c0db86660bc0a96991Stephen Hines 1877b5baba8349b0741c36f4c0db86660bc0a96991Stephen Hines__author__ = 'Android' 1977b5baba8349b0741c36f4c0db86660bc0a96991Stephen Hines 2077b5baba8349b0741c36f4c0db86660bc0a96991Stephen Hines 2177b5baba8349b0741c36f4c0db86660bc0a96991Stephen Hinesclass Options(object): 2277b5baba8349b0741c36f4c0db86660bc0a96991Stephen Hines def __init__(self): 2388bb794545936b6915f4ce22fd4404b3cda3bbd7Stephen Hines return 2477b5baba8349b0741c36f4c0db86660bc0a96991Stephen Hines verbose = 0 2588bb794545936b6915f4ce22fd4404b3cda3bbd7Stephen Hines cleanup = 1 26a4c54ee5ceb193d514618d341ec6967bff02cb2bStephen Hines updateCTS = 0 2777b5baba8349b0741c36f4c0db86660bc0a96991Stephen Hines 2877b5baba8349b0741c36f4c0db86660bc0a96991Stephen Hines 29a4c54ee5ceb193d514618d341ec6967bff02cb2bStephen Hinesdef CompareFiles(actual, expect): 30a4c54ee5ceb193d514618d341ec6967bff02cb2bStephen Hines """Compares actual and expect for equality.""" 31dd6206bb61bf8df2ed6b643abe8a29c48a315685Stephen Hines if not os.path.isfile(actual): 32dd6206bb61bf8df2ed6b643abe8a29c48a315685Stephen Hines if Options.verbose: 33dd6206bb61bf8df2ed6b643abe8a29c48a315685Stephen Hines print 'Could not find %s' % actual 34dd6206bb61bf8df2ed6b643abe8a29c48a315685Stephen Hines return False 35dd6206bb61bf8df2ed6b643abe8a29c48a315685Stephen Hines if not os.path.isfile(expect): 36dd6206bb61bf8df2ed6b643abe8a29c48a315685Stephen Hines if Options.verbose: 37dd6206bb61bf8df2ed6b643abe8a29c48a315685Stephen Hines print 'Could not find %s' % expect 38dd6206bb61bf8df2ed6b643abe8a29c48a315685Stephen Hines return False 39dd6206bb61bf8df2ed6b643abe8a29c48a315685Stephen Hines 4077b5baba8349b0741c36f4c0db86660bc0a96991Stephen Hines return filecmp.cmp(actual, expect, False) 4177b5baba8349b0741c36f4c0db86660bc0a96991Stephen Hines 4277b5baba8349b0741c36f4c0db86660bc0a96991Stephen Hines 43a4c54ee5ceb193d514618d341ec6967bff02cb2bStephen Hinesdef UpdateFiles(src, dst): 44a4c54ee5ceb193d514618d341ec6967bff02cb2bStephen Hines """Update dst if it is different from src.""" 45a4c54ee5ceb193d514618d341ec6967bff02cb2bStephen Hines if not CompareFiles(src, dst): 46a4c54ee5ceb193d514618d341ec6967bff02cb2bStephen Hines print 'Copying from %s to %s' % (src, dst) 47a4c54ee5ceb193d514618d341ec6967bff02cb2bStephen Hines shutil.copyfile(src, dst) 48a4c54ee5ceb193d514618d341ec6967bff02cb2bStephen Hines 49a4c54ee5ceb193d514618d341ec6967bff02cb2bStephen Hines 500b874dacc570d6ed42cad26e0907548d459dfde5Stephen Hinesdef GetCommandLineArgs(filename): 51a4c54ee5ceb193d514618d341ec6967bff02cb2bStephen Hines """Extracts command line arguments from first comment line in a file.""" 520b874dacc570d6ed42cad26e0907548d459dfde5Stephen Hines f = open(filename, 'r') 530b874dacc570d6ed42cad26e0907548d459dfde5Stephen Hines line = f.readline() 540b874dacc570d6ed42cad26e0907548d459dfde5Stephen Hines if line[0] == '/' and line [1] == '/': 55a4c54ee5ceb193d514618d341ec6967bff02cb2bStephen Hines return line[2:].strip() 560b874dacc570d6ed42cad26e0907548d459dfde5Stephen Hines else: 57a4c54ee5ceb193d514618d341ec6967bff02cb2bStephen Hines return '' 580b874dacc570d6ed42cad26e0907548d459dfde5Stephen Hines 590b874dacc570d6ed42cad26e0907548d459dfde5Stephen Hines 6077b5baba8349b0741c36f4c0db86660bc0a96991Stephen Hinesdef ExecTest(dirname): 6177b5baba8349b0741c36f4c0db86660bc0a96991Stephen Hines """Executes an llvm-rs-cc test from dirname.""" 6277b5baba8349b0741c36f4c0db86660bc0a96991Stephen Hines passed = True 6377b5baba8349b0741c36f4c0db86660bc0a96991Stephen Hines 6477b5baba8349b0741c36f4c0db86660bc0a96991Stephen Hines if Options.verbose != 0: 6577b5baba8349b0741c36f4c0db86660bc0a96991Stephen Hines print 'Testing %s' % dirname 6677b5baba8349b0741c36f4c0db86660bc0a96991Stephen Hines 6777b5baba8349b0741c36f4c0db86660bc0a96991Stephen Hines os.chdir(dirname) 68dd6206bb61bf8df2ed6b643abe8a29c48a315685Stephen Hines stdout_file = open('stdout.txt', 'w+') 69dd6206bb61bf8df2ed6b643abe8a29c48a315685Stephen Hines stderr_file = open('stderr.txt', 'w+') 7077b5baba8349b0741c36f4c0db86660bc0a96991Stephen Hines 7177b5baba8349b0741c36f4c0db86660bc0a96991Stephen Hines cmd_string = ('../../../../../out/host/linux-x86/bin/llvm-rs-cc ' 7277b5baba8349b0741c36f4c0db86660bc0a96991Stephen Hines '-o tmp/ -p tmp/ ' 73c632be206ac4fe49a5db05cfa54942d774329dbeStephen Hines '-MD ' 74ca13d7cc4c71ef5426e16bc997d4640facf7eda4Stephen Hines '-I ../../../../../frameworks/rs/scriptc/ ' 754470828373378510f1d02cae4e80cfe143577de6Stephen Hines '-I ../../../../../external/clang/lib/Headers/') 7677b5baba8349b0741c36f4c0db86660bc0a96991Stephen Hines base_args = cmd_string.split() 7777b5baba8349b0741c36f4c0db86660bc0a96991Stephen Hines rs_files = glob.glob('*.rs') 788dd522d2a6b96c79117bdc5d6b58d336846e4346Stephen Hines rs_files.sort() 790b874dacc570d6ed42cad26e0907548d459dfde5Stephen Hines 800b874dacc570d6ed42cad26e0907548d459dfde5Stephen Hines # Extra command line arguments can be placed as // comments at the start of 810b874dacc570d6ed42cad26e0907548d459dfde5Stephen Hines # any .rs file. We automatically bundle up all of these extra args and invoke 820b874dacc570d6ed42cad26e0907548d459dfde5Stephen Hines # llvm-rs-cc with them. 83a4c54ee5ceb193d514618d341ec6967bff02cb2bStephen Hines extra_args_str = '' 840b874dacc570d6ed42cad26e0907548d459dfde5Stephen Hines for rs_file in rs_files: 850b874dacc570d6ed42cad26e0907548d459dfde5Stephen Hines extra_args_str += GetCommandLineArgs(rs_file) 860b874dacc570d6ed42cad26e0907548d459dfde5Stephen Hines extra_args = extra_args_str.split() 870b874dacc570d6ed42cad26e0907548d459dfde5Stephen Hines 880b874dacc570d6ed42cad26e0907548d459dfde5Stephen Hines args = base_args + extra_args + rs_files 8977b5baba8349b0741c36f4c0db86660bc0a96991Stephen Hines 90e5e64432476a44b59c61ded233b1149109c7a7c3Stephen Hines if Options.verbose > 1: 91e5e64432476a44b59c61ded233b1149109c7a7c3Stephen Hines print 'Executing:', 92e5e64432476a44b59c61ded233b1149109c7a7c3Stephen Hines for arg in args: 93e5e64432476a44b59c61ded233b1149109c7a7c3Stephen Hines print arg, 94e5e64432476a44b59c61ded233b1149109c7a7c3Stephen Hines print 95e5e64432476a44b59c61ded233b1149109c7a7c3Stephen Hines 9677b5baba8349b0741c36f4c0db86660bc0a96991Stephen Hines # Execute the command and check the resulting shell return value. 9777b5baba8349b0741c36f4c0db86660bc0a96991Stephen Hines # All tests that are expected to FAIL have directory names that 9877b5baba8349b0741c36f4c0db86660bc0a96991Stephen Hines # start with 'F_'. Other tests that are expected to PASS have 9977b5baba8349b0741c36f4c0db86660bc0a96991Stephen Hines # directory names that start with 'P_'. 1006b201eb3306b9609a991728a52ce948974bd4aedStephen Hines ret = 0 1016b201eb3306b9609a991728a52ce948974bd4aedStephen Hines try: 1026b201eb3306b9609a991728a52ce948974bd4aedStephen Hines ret = subprocess.call(args, stdout=stdout_file, stderr=stderr_file) 1036b201eb3306b9609a991728a52ce948974bd4aedStephen Hines except: 1046b201eb3306b9609a991728a52ce948974bd4aedStephen Hines passed = False 1056b201eb3306b9609a991728a52ce948974bd4aedStephen Hines 106dd6206bb61bf8df2ed6b643abe8a29c48a315685Stephen Hines stdout_file.flush() 107dd6206bb61bf8df2ed6b643abe8a29c48a315685Stephen Hines stderr_file.flush() 108dd6206bb61bf8df2ed6b643abe8a29c48a315685Stephen Hines 109dd6206bb61bf8df2ed6b643abe8a29c48a315685Stephen Hines if Options.verbose > 1: 110dd6206bb61bf8df2ed6b643abe8a29c48a315685Stephen Hines stdout_file.seek(0) 111dd6206bb61bf8df2ed6b643abe8a29c48a315685Stephen Hines stderr_file.seek(0) 112dd6206bb61bf8df2ed6b643abe8a29c48a315685Stephen Hines for line in stdout_file: 113dd6206bb61bf8df2ed6b643abe8a29c48a315685Stephen Hines print 'STDOUT>', line, 114dd6206bb61bf8df2ed6b643abe8a29c48a315685Stephen Hines for line in stderr_file: 115dd6206bb61bf8df2ed6b643abe8a29c48a315685Stephen Hines print 'STDERR>', line, 116dd6206bb61bf8df2ed6b643abe8a29c48a315685Stephen Hines 117dd6206bb61bf8df2ed6b643abe8a29c48a315685Stephen Hines stdout_file.close() 118dd6206bb61bf8df2ed6b643abe8a29c48a315685Stephen Hines stderr_file.close() 119dd6206bb61bf8df2ed6b643abe8a29c48a315685Stephen Hines 12077b5baba8349b0741c36f4c0db86660bc0a96991Stephen Hines if dirname[0:2] == 'F_': 12177b5baba8349b0741c36f4c0db86660bc0a96991Stephen Hines if ret == 0: 12277b5baba8349b0741c36f4c0db86660bc0a96991Stephen Hines passed = False 12377b5baba8349b0741c36f4c0db86660bc0a96991Stephen Hines if Options.verbose: 12477b5baba8349b0741c36f4c0db86660bc0a96991Stephen Hines print 'Command passed on invalid input' 12577b5baba8349b0741c36f4c0db86660bc0a96991Stephen Hines elif dirname[0:2] == 'P_': 12677b5baba8349b0741c36f4c0db86660bc0a96991Stephen Hines if ret != 0: 12777b5baba8349b0741c36f4c0db86660bc0a96991Stephen Hines passed = False 12877b5baba8349b0741c36f4c0db86660bc0a96991Stephen Hines if Options.verbose: 12977b5baba8349b0741c36f4c0db86660bc0a96991Stephen Hines print 'Command failed on valid input' 13077b5baba8349b0741c36f4c0db86660bc0a96991Stephen Hines else: 131e5e64432476a44b59c61ded233b1149109c7a7c3Stephen Hines passed = (ret == 0) 13277b5baba8349b0741c36f4c0db86660bc0a96991Stephen Hines if Options.verbose: 13377b5baba8349b0741c36f4c0db86660bc0a96991Stephen Hines print 'Test Directory name should start with an F or a P' 13477b5baba8349b0741c36f4c0db86660bc0a96991Stephen Hines 135a4c54ee5ceb193d514618d341ec6967bff02cb2bStephen Hines if not CompareFiles('stdout.txt', 'stdout.txt.expect'): 13677b5baba8349b0741c36f4c0db86660bc0a96991Stephen Hines passed = False 13777b5baba8349b0741c36f4c0db86660bc0a96991Stephen Hines if Options.verbose: 13877b5baba8349b0741c36f4c0db86660bc0a96991Stephen Hines print 'stdout is different' 139a4c54ee5ceb193d514618d341ec6967bff02cb2bStephen Hines if not CompareFiles('stderr.txt', 'stderr.txt.expect'): 14077b5baba8349b0741c36f4c0db86660bc0a96991Stephen Hines passed = False 14177b5baba8349b0741c36f4c0db86660bc0a96991Stephen Hines if Options.verbose: 14277b5baba8349b0741c36f4c0db86660bc0a96991Stephen Hines print 'stderr is different' 14377b5baba8349b0741c36f4c0db86660bc0a96991Stephen Hines 144a4c54ee5ceb193d514618d341ec6967bff02cb2bStephen Hines if Options.updateCTS: 145a4c54ee5ceb193d514618d341ec6967bff02cb2bStephen Hines # Copy resulting files to appropriate CTS directory (if different). 146a4c54ee5ceb193d514618d341ec6967bff02cb2bStephen Hines if passed and glob.glob('IN_CTS'): 147a4c54ee5ceb193d514618d341ec6967bff02cb2bStephen Hines cts_path = '../../../../../cts/' 148a4c54ee5ceb193d514618d341ec6967bff02cb2bStephen Hines cts_res_raw_path = cts_path + 'tests/res/raw/' 149a4c54ee5ceb193d514618d341ec6967bff02cb2bStephen Hines cts_src_path = cts_path + 'tests/tests/renderscript/src/' 150a4c54ee5ceb193d514618d341ec6967bff02cb2bStephen Hines for bc_src in glob.glob('tmp/*.bc'): 151a4c54ee5ceb193d514618d341ec6967bff02cb2bStephen Hines bc_dst = re.sub('tmp\/', cts_res_raw_path, bc_src, 1) 152a4c54ee5ceb193d514618d341ec6967bff02cb2bStephen Hines UpdateFiles(bc_src, bc_dst) 153a4c54ee5ceb193d514618d341ec6967bff02cb2bStephen Hines for java_src in glob.glob('tmp/android/renderscript/cts/*.java'): 154a4c54ee5ceb193d514618d341ec6967bff02cb2bStephen Hines java_dst = re.sub('tmp\/', cts_src_path, java_src, 1) 155a4c54ee5ceb193d514618d341ec6967bff02cb2bStephen Hines UpdateFiles(java_src, java_dst) 156a4c54ee5ceb193d514618d341ec6967bff02cb2bStephen Hines 15788bb794545936b6915f4ce22fd4404b3cda3bbd7Stephen Hines if Options.cleanup: 1586b201eb3306b9609a991728a52ce948974bd4aedStephen Hines try: 1596b201eb3306b9609a991728a52ce948974bd4aedStephen Hines os.remove('stdout.txt') 1606b201eb3306b9609a991728a52ce948974bd4aedStephen Hines os.remove('stderr.txt') 1616b201eb3306b9609a991728a52ce948974bd4aedStephen Hines shutil.rmtree('tmp/') 1626b201eb3306b9609a991728a52ce948974bd4aedStephen Hines except: 1636b201eb3306b9609a991728a52ce948974bd4aedStephen Hines pass 16477b5baba8349b0741c36f4c0db86660bc0a96991Stephen Hines 16577b5baba8349b0741c36f4c0db86660bc0a96991Stephen Hines os.chdir('..') 16677b5baba8349b0741c36f4c0db86660bc0a96991Stephen Hines return passed 16777b5baba8349b0741c36f4c0db86660bc0a96991Stephen Hines 16877b5baba8349b0741c36f4c0db86660bc0a96991Stephen Hines 16988bb794545936b6915f4ce22fd4404b3cda3bbd7Stephen Hinesdef Usage(): 170a4c54ee5ceb193d514618d341ec6967bff02cb2bStephen Hines """Print out usage information.""" 17188bb794545936b6915f4ce22fd4404b3cda3bbd7Stephen Hines print ('Usage: %s [OPTION]... [TESTNAME]...' 172a4c54ee5ceb193d514618d341ec6967bff02cb2bStephen Hines 'Renderscript Compiler Test Harness\n' 17388bb794545936b6915f4ce22fd4404b3cda3bbd7Stephen Hines 'Runs TESTNAMEs (all tests by default)\n' 17488bb794545936b6915f4ce22fd4404b3cda3bbd7Stephen Hines 'Available Options:\n' 17588bb794545936b6915f4ce22fd4404b3cda3bbd7Stephen Hines ' -h, --help Help message\n' 17688bb794545936b6915f4ce22fd4404b3cda3bbd7Stephen Hines ' -n, --no-cleanup Don\'t clean up after running tests\n' 177a4c54ee5ceb193d514618d341ec6967bff02cb2bStephen Hines ' -u, --update-cts Update CTS test versions\n' 17888bb794545936b6915f4ce22fd4404b3cda3bbd7Stephen Hines ' -v, --verbose Verbose output\n' 17988bb794545936b6915f4ce22fd4404b3cda3bbd7Stephen Hines ) % (sys.argv[0]), 18088bb794545936b6915f4ce22fd4404b3cda3bbd7Stephen Hines return 18188bb794545936b6915f4ce22fd4404b3cda3bbd7Stephen Hines 18288bb794545936b6915f4ce22fd4404b3cda3bbd7Stephen Hines 18377b5baba8349b0741c36f4c0db86660bc0a96991Stephen Hinesdef main(): 18477b5baba8349b0741c36f4c0db86660bc0a96991Stephen Hines passed = 0 18577b5baba8349b0741c36f4c0db86660bc0a96991Stephen Hines failed = 0 18688bb794545936b6915f4ce22fd4404b3cda3bbd7Stephen Hines files = [] 187e5e64432476a44b59c61ded233b1149109c7a7c3Stephen Hines failed_tests = [] 18877b5baba8349b0741c36f4c0db86660bc0a96991Stephen Hines 18977b5baba8349b0741c36f4c0db86660bc0a96991Stephen Hines for arg in sys.argv[1:]: 19088bb794545936b6915f4ce22fd4404b3cda3bbd7Stephen Hines if arg in ('-h', '--help'): 19188bb794545936b6915f4ce22fd4404b3cda3bbd7Stephen Hines Usage() 19288bb794545936b6915f4ce22fd4404b3cda3bbd7Stephen Hines return 0 19388bb794545936b6915f4ce22fd4404b3cda3bbd7Stephen Hines elif arg in ('-n', '--no-cleanup'): 19488bb794545936b6915f4ce22fd4404b3cda3bbd7Stephen Hines Options.cleanup = 0 195a4c54ee5ceb193d514618d341ec6967bff02cb2bStephen Hines elif arg in ('-u', '--update-cts'): 196a4c54ee5ceb193d514618d341ec6967bff02cb2bStephen Hines Options.updateCTS = 1 19788bb794545936b6915f4ce22fd4404b3cda3bbd7Stephen Hines elif arg in ('-v', '--verbose'): 198e5e64432476a44b59c61ded233b1149109c7a7c3Stephen Hines Options.verbose += 1 19977b5baba8349b0741c36f4c0db86660bc0a96991Stephen Hines else: 20088bb794545936b6915f4ce22fd4404b3cda3bbd7Stephen Hines # Test list to run 20188bb794545936b6915f4ce22fd4404b3cda3bbd7Stephen Hines if os.path.isdir(arg): 20288bb794545936b6915f4ce22fd4404b3cda3bbd7Stephen Hines files.append(arg) 20388bb794545936b6915f4ce22fd4404b3cda3bbd7Stephen Hines else: 20488bb794545936b6915f4ce22fd4404b3cda3bbd7Stephen Hines print >> sys.stderr, 'Invalid test or option: %s' % arg 20588bb794545936b6915f4ce22fd4404b3cda3bbd7Stephen Hines return 1 20688bb794545936b6915f4ce22fd4404b3cda3bbd7Stephen Hines 20788bb794545936b6915f4ce22fd4404b3cda3bbd7Stephen Hines if not files: 208e5e64432476a44b59c61ded233b1149109c7a7c3Stephen Hines tmp_files = os.listdir('.') 209e5e64432476a44b59c61ded233b1149109c7a7c3Stephen Hines # Only run tests that are known to PASS or FAIL 210e5e64432476a44b59c61ded233b1149109c7a7c3Stephen Hines # Disabled tests can be marked D_ and invoked explicitly 211e5e64432476a44b59c61ded233b1149109c7a7c3Stephen Hines for f in tmp_files: 212e5e64432476a44b59c61ded233b1149109c7a7c3Stephen Hines if os.path.isdir(f) and (f[0:2] == 'F_' or f[0:2] == 'P_'): 213e5e64432476a44b59c61ded233b1149109c7a7c3Stephen Hines files.append(f) 21477b5baba8349b0741c36f4c0db86660bc0a96991Stephen Hines 21577b5baba8349b0741c36f4c0db86660bc0a96991Stephen Hines for f in files: 21677b5baba8349b0741c36f4c0db86660bc0a96991Stephen Hines if os.path.isdir(f): 21777b5baba8349b0741c36f4c0db86660bc0a96991Stephen Hines if ExecTest(f): 21877b5baba8349b0741c36f4c0db86660bc0a96991Stephen Hines passed += 1 21977b5baba8349b0741c36f4c0db86660bc0a96991Stephen Hines else: 22077b5baba8349b0741c36f4c0db86660bc0a96991Stephen Hines failed += 1 221e5e64432476a44b59c61ded233b1149109c7a7c3Stephen Hines failed_tests.append(f) 22277b5baba8349b0741c36f4c0db86660bc0a96991Stephen Hines 22377b5baba8349b0741c36f4c0db86660bc0a96991Stephen Hines print 'Tests Passed: %d\n' % passed, 22477b5baba8349b0741c36f4c0db86660bc0a96991Stephen Hines print 'Tests Failed: %d\n' % failed, 225e5e64432476a44b59c61ded233b1149109c7a7c3Stephen Hines if failed: 226e5e64432476a44b59c61ded233b1149109c7a7c3Stephen Hines print 'Failures:', 227e5e64432476a44b59c61ded233b1149109c7a7c3Stephen Hines for t in failed_tests: 228e5e64432476a44b59c61ded233b1149109c7a7c3Stephen Hines print t, 22977b5baba8349b0741c36f4c0db86660bc0a96991Stephen Hines 23077b5baba8349b0741c36f4c0db86660bc0a96991Stephen Hines return failed != 0 23177b5baba8349b0741c36f4c0db86660bc0a96991Stephen Hines 23277b5baba8349b0741c36f4c0db86660bc0a96991Stephen Hines 23377b5baba8349b0741c36f4c0db86660bc0a96991Stephen Hinesif __name__ == '__main__': 23477b5baba8349b0741c36f4c0db86660bc0a96991Stephen Hines sys.exit(main()) 235