1 #!/usr/bin/env python 2 # Copyright 2015 The PDFium Authors. All rights reserved. 3 # Use of this source code is governed by a BSD-style license that can be 4 # found in the LICENSE file. 5 6 import cStringIO 7 import functools 8 import multiprocessing 9 import optparse 10 import os 11 import re 12 import shutil 13 import subprocess 14 import sys 15 16 import common 17 import pngdiffer 18 import suppressor 19 20 class KeyboardInterruptError(Exception): pass 21 22 # Nomenclature: 23 # x_root - "x" 24 # x_filename - "x.ext" 25 # x_path - "path/to/a/b/c/x.ext" 26 # c_dir - "path/to/a/b/c" 27 28 def test_one_file(input_filename, source_dir, working_dir, 29 pdfium_test_path, image_differ, drmem_wrapper, 30 redirect_output=False): 31 input_path = os.path.join(source_dir, input_filename) 32 pdf_path = os.path.join(working_dir, input_filename) 33 # Remove any existing generated images from previous runs. 34 actual_images = image_differ.GetActualFiles( 35 input_filename, source_dir, working_dir) 36 for image in actual_images: 37 if os.path.exists(image): 38 os.remove(image) 39 40 shutil.copyfile(input_path, pdf_path) 41 sys.stdout.flush() 42 # add Dr. Memory wrapper if exist 43 # remove .pdf suffix 44 cmd_to_run = common.DrMemoryWrapper(drmem_wrapper, 45 os.path.splitext(input_filename)[0]) 46 cmd_to_run.extend([pdfium_test_path, '--png', pdf_path]) 47 # run test 48 error = common.RunCommand(cmd_to_run, redirect_output) 49 if error: 50 print "FAILURE: " + input_filename + "; " + str(error) 51 return False 52 return not image_differ.HasDifferences(input_filename, source_dir, 53 working_dir, redirect_output) 54 55 56 def test_one_file_parallel(working_dir, pdfium_test_path, image_differ, 57 test_case): 58 """Wrapper function to call test_one_file() and redirect output to stdout.""" 59 try: 60 old_stdout = sys.stdout 61 old_stderr = sys.stderr 62 sys.stdout = cStringIO.StringIO() 63 sys.stderr = sys.stdout 64 input_filename, source_dir = test_case 65 result = test_one_file(input_filename, source_dir, working_dir, 66 pdfium_test_path, image_differ, "", True); 67 output = sys.stdout 68 sys.stdout = old_stdout 69 sys.stderr = old_stderr 70 return (result, output.getvalue(), input_filename, source_dir) 71 except KeyboardInterrupt: 72 raise KeyboardInterruptError() 73 74 75 def handle_result(test_suppressor, input_filename, input_path, result, 76 surprises, failures): 77 if test_suppressor.IsSuppressed(input_filename): 78 if result: 79 surprises.append(input_path) 80 else: 81 if not result: 82 failures.append(input_path) 83 84 85 def main(): 86 parser = optparse.OptionParser() 87 parser.add_option('--build-dir', default=os.path.join('out', 'Debug'), 88 help='relative path from the base source directory') 89 parser.add_option('-j', default=multiprocessing.cpu_count(), 90 dest='num_workers', type='int', 91 help='run NUM_WORKERS jobs in parallel') 92 parser.add_option('--wrapper', default='', dest="wrapper", 93 help='Dr. Memory wrapper for running test under Dr. Memory') 94 options, args = parser.parse_args() 95 finder = common.DirectoryFinder(options.build_dir) 96 pdfium_test_path = finder.ExecutablePath('pdfium_test') 97 if not os.path.exists(pdfium_test_path): 98 print "FAILURE: Can't find test executable '%s'" % pdfium_test_path 99 print "Use --build-dir to specify its location." 100 return 1 101 working_dir = finder.WorkingDir(os.path.join('testing', 'corpus')) 102 if not os.path.exists(working_dir): 103 os.makedirs(working_dir) 104 105 test_suppressor = suppressor.Suppressor(finder) 106 image_differ = pngdiffer.PNGDiffer(finder) 107 108 # test files are under .../pdfium/testing/corpus. 109 failures = [] 110 surprises = [] 111 walk_from_dir = finder.TestingDir('corpus'); 112 input_file_re = re.compile('^[a-zA-Z0-9_.]+[.]pdf$') 113 test_cases = [] 114 115 if len(args): 116 for file_name in args: 117 input_path = os.path.join(walk_from_dir, file_name) 118 if not os.path.isfile(input_path): 119 print "Can't find test file '%s'" % file_name 120 return 1 121 122 test_cases.append((os.path.basename(input_path), 123 os.path.dirname(input_path))) 124 else: 125 for source_dir, _, filename_list in os.walk(walk_from_dir): 126 for input_filename in filename_list: 127 if input_file_re.match(input_filename): 128 input_path = os.path.join(source_dir, input_filename) 129 if os.path.isfile(input_path): 130 test_cases.append((input_filename, source_dir)) 131 132 if options.num_workers > 1 and len(test_cases) > 1: 133 try: 134 pool = multiprocessing.Pool(options.num_workers) 135 worker_func = functools.partial(test_one_file_parallel, working_dir, 136 pdfium_test_path, image_differ) 137 worker_results = pool.imap(worker_func, test_cases) 138 for worker_result in worker_results: 139 result, output, input_filename, source_dir = worker_result 140 input_path = os.path.join(source_dir, input_filename) 141 sys.stdout.write(output) 142 handle_result(test_suppressor, input_filename, input_path, result, 143 surprises, failures) 144 pool.close() 145 except KeyboardInterrupt: 146 pool.terminate() 147 finally: 148 pool.join() 149 else: 150 for test_case in test_cases: 151 input_filename, source_dir = test_case 152 result = test_one_file(input_filename, source_dir, working_dir, 153 pdfium_test_path, image_differ, 154 options.wrapper) 155 handle_result(test_suppressor, input_filename, input_path, result, 156 surprises, failures) 157 158 if surprises: 159 surprises.sort() 160 print '\n\nUnexpected Successes:' 161 for surprise in surprises: 162 print surprise; 163 164 if failures: 165 failures.sort() 166 print '\n\nSummary of Failures:' 167 for failure in failures: 168 print failure 169 return 1 170 171 return 0 172 173 174 if __name__ == '__main__': 175 sys.exit(main()) 176