Home | History | Annotate | Download | only in tools
      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