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 6import cStringIO 7import functools 8import multiprocessing 9import optparse 10import os 11import re 12import shutil 13import subprocess 14import sys 15 16import common 17import pngdiffer 18import suppressor 19 20class 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 28def 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 56def 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 75def 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 85def 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 174if __name__ == '__main__': 175 sys.exit(main()) 176