Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(1)

Side by Side Diff: testing/tools/test_runner.py

Issue 2578893004: Gold support in PDFium (Closed)
Patch Set: Cleanup and added to DEPS Created 4 years ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« samples/pdfium_test.cc ('K') | « testing/tools/gold.py ('k') | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 #!/usr/bin/env python 1 #!/usr/bin/env python
2 # Copyright 2016 The PDFium Authors. All rights reserved. 2 # Copyright 2016 The PDFium Authors. All rights reserved.
3 # Use of this source code is governed by a BSD-style license that can be 3 # Use of this source code is governed by a BSD-style license that can be
4 # found in the LICENSE file. 4 # found in the LICENSE file.
5 5
6 import cStringIO 6 import cStringIO
7 import functools 7 import functools
8 import multiprocessing 8 import multiprocessing
9 import optparse 9 import optparse
10 import os 10 import os
11 import re 11 import re
12 import shutil 12 import shutil
13 import subprocess 13 import subprocess
14 import sys 14 import sys
15 15
16 import common 16 import common
17 import gold
17 import pngdiffer 18 import pngdiffer
18 import suppressor 19 import suppressor
19 20
20 class KeyboardInterruptError(Exception): pass 21 class KeyboardInterruptError(Exception): pass
21 22
22 # Nomenclature: 23 # Nomenclature:
23 # x_root - "x" 24 # x_root - "x"
24 # x_filename - "x.ext" 25 # x_filename - "x.ext"
25 # x_path - "path/to/a/b/c/x.ext" 26 # x_path - "path/to/a/b/c/x.ext"
26 # c_dir - "path/to/a/b/c" 27 # c_dir - "path/to/a/b/c"
27 28
28 def TestOneFileParallel(this, test_case): 29 def TestOneFileParallel(this, test_case):
29 """Wrapper to call GenerateAndTest() and redirect output to stdout.""" 30 """Wrapper to call GenerateAndTest() and redirect output to stdout."""
30 try: 31 try:
31 input_filename, source_dir = test_case 32 input_filename, source_dir = test_case
32 result = this.GenerateAndTest(input_filename, source_dir); 33 result = this.GenerateAndTest(input_filename, source_dir);
33 return (result, input_filename, source_dir) 34 return (result, input_filename, source_dir)
34 except KeyboardInterrupt: 35 except KeyboardInterrupt:
35 raise KeyboardInterruptError() 36 raise KeyboardInterruptError()
36 37
37 38
38 class TestRunner: 39 class TestRunner:
39 def __init__(self, dirname): 40 def __init__(self, dirname):
40 self.test_dir = dirname 41 self.test_dir = dirname
41 42
43 # GenerateAndTest returns a tuple <success, outputfiles> where
44 # success is a boolean indicating whether the tests passed comparison
45 # tests and outputfiles is a list tuples:
46 # (path_to_image, md5_hash_of_pixelbuffer)
42 def GenerateAndTest(self, input_filename, source_dir): 47 def GenerateAndTest(self, input_filename, source_dir):
43 input_root, _ = os.path.splitext(input_filename) 48 input_root, _ = os.path.splitext(input_filename)
44 expected_txt_path = os.path.join(source_dir, input_root + '_expected.txt') 49 expected_txt_path = os.path.join(source_dir, input_root + '_expected.txt')
45 50
46 pdf_path = os.path.join(self.working_dir, input_root + '.pdf') 51 pdf_path = os.path.join(self.working_dir, input_root + '.pdf')
47 52
48 # Remove any existing generated images from previous runs. 53 # Remove any existing generated images from previous runs.
49 actual_images = self.image_differ.GetActualFiles(input_filename, source_dir, 54 actual_images = self.image_differ.GetActualFiles(input_filename, source_dir,
50 self.working_dir) 55 self.working_dir)
51 for image in actual_images: 56 for image in actual_images:
52 if os.path.exists(image): 57 if os.path.exists(image):
53 os.remove(image) 58 os.remove(image)
54 59
55 sys.stdout.flush() 60 sys.stdout.flush()
56 61
57 raised_exception = self.Generate(source_dir, input_filename, input_root, 62 raised_exception = self.Generate(source_dir, input_filename, input_root,
58 pdf_path) 63 pdf_path)
59 64
60 if raised_exception != None: 65 if raised_exception != None:
61 print "FAILURE: " + input_filename + "; " + str(raised_exception) 66 print "FAILURE: " + input_filename + "; " + str(raised_exception)
62 return False 67 return False, []
63 68
69 results = []
64 if os.path.exists(expected_txt_path): 70 if os.path.exists(expected_txt_path):
65 raised_exception = self.TestText(input_root, expected_txt_path, pdf_path) 71 raised_exception = self.TestText(input_root, expected_txt_path, pdf_path)
66 else: 72 else:
67 raised_exception = self.TestPixel(input_root, pdf_path) 73 raised_exception, results = self.TestPixel(input_root, pdf_path)
68 74
69 if raised_exception != None: 75 if raised_exception != None:
70 print "FAILURE: " + input_filename + "; " + str(raised_exception) 76 print "FAILURE: " + input_filename + "; " + str(raised_exception)
71 return False 77 return False, results
72 78
73 if len(actual_images): 79 if len(actual_images):
74 if self.image_differ.HasDifferences(input_filename, source_dir, 80 if self.image_differ.HasDifferences(input_filename, source_dir,
75 self.working_dir): 81 self.working_dir):
76 return False 82 return False, results
77 83 return True, results
78 return True
79 84
80 def Generate(self, source_dir, input_filename, input_root, pdf_path): 85 def Generate(self, source_dir, input_filename, input_root, pdf_path):
81 original_path = os.path.join(source_dir, input_filename) 86 original_path = os.path.join(source_dir, input_filename)
82 input_path = os.path.join(source_dir, input_root + '.in') 87 input_path = os.path.join(source_dir, input_root + '.in')
83 88
84 input_event_path = os.path.join(source_dir, input_root + ".evt") 89 input_event_path = os.path.join(source_dir, input_root + ".evt")
85 if os.path.exists(input_event_path): 90 if os.path.exists(input_event_path):
86 output_event_path = os.path.splitext(pdf_path)[0] + ".evt" 91 output_event_path = os.path.splitext(pdf_path)[0] + ".evt"
87 shutil.copyfile(input_event_path, output_event_path) 92 shutil.copyfile(input_event_path, output_event_path)
88 93
(...skipping 19 matching lines...) Expand all
108 subprocess.check_call(cmd_to_run, stdout=outfile) 113 subprocess.check_call(cmd_to_run, stdout=outfile)
109 114
110 cmd = [sys.executable, self.text_diff_path, expected_txt_path, txt_path] 115 cmd = [sys.executable, self.text_diff_path, expected_txt_path, txt_path]
111 return common.RunCommand(cmd) 116 return common.RunCommand(cmd)
112 117
113 118
114 def TestPixel(self, input_root, pdf_path): 119 def TestPixel(self, input_root, pdf_path):
115 cmd_to_run = common.DrMemoryWrapper(self.drmem_wrapper, input_root) 120 cmd_to_run = common.DrMemoryWrapper(self.drmem_wrapper, input_root)
116 cmd_to_run.extend([self.pdfium_test_path, '--send-events', '--png', 121 cmd_to_run.extend([self.pdfium_test_path, '--send-events', '--png',
117 pdf_path]) 122 pdf_path])
118 return common.RunCommand(cmd_to_run) 123 return common.RunCommandExtractHashedFiles(cmd_to_run)
119
120 124
121 def HandleResult(self, input_filename, input_path, result): 125 def HandleResult(self, input_filename, input_path, result):
126 if self.gold_results:
127 success, image_paths = result
128 for img_path, md5_hash in image_paths:
129 # the output filename (without extension becomes the test name)
130 test_name = os.path.splitext(os.path.split(img_path)[1])[0]
131 self.gold_results.AddTestResult(test_name, md5_hash, img_path)
132
122 if self.test_suppressor.IsResultSuppressed(input_filename): 133 if self.test_suppressor.IsResultSuppressed(input_filename):
123 if result: 134 if result:
124 self.surprises.append(input_path) 135 self.surprises.append(input_path)
125 else: 136 else:
126 if not result: 137 if not result:
127 self.failures.append(input_path) 138 self.failures.append(input_path)
128 139
129 140
130 def Run(self): 141 def Run(self):
131 parser = optparse.OptionParser() 142 parser = optparse.OptionParser()
132 parser.add_option('--build-dir', default=os.path.join('out', 'Debug'), 143 parser.add_option('--build-dir', default=os.path.join('out', 'Debug'),
133 help='relative path from the base source directory') 144 help='relative path from the base source directory')
134 parser.add_option('-j', default=multiprocessing.cpu_count(), 145 parser.add_option('-j', default=multiprocessing.cpu_count(),
135 dest='num_workers', type='int', 146 dest='num_workers', type='int',
136 help='run NUM_WORKERS jobs in parallel') 147 help='run NUM_WORKERS jobs in parallel')
137 parser.add_option('--wrapper', default='', dest="wrapper", 148 parser.add_option('--wrapper', default='', dest="wrapper",
138 help='wrapper for running test under Dr. Memory') 149 help='wrapper for running test under Dr. Memory')
150 parser.add_option('--gold_properties', default='', dest="gold_properties",
151 help='Key value pairs that are written to the top level of the JSON file that is ingested by Gold.')
152 parser.add_option('--gold_key', default='', dest="gold_key",
153 help='Key value pairs that are added to the "key" field of the JSON file that is ingested by Gold.')
154 parser.add_option('--gold_output_dir', default='', dest="gold_output_dir",
155 help='Path of where to write the JSON output to be uploade d to Gold.')
156 parser.add_option('--ignore_errors', action="store_true", dest="ignore_error s",
157 help='Prevents the return value from being non-zero when i mage comparison fails.')
139 options, args = parser.parse_args() 158 options, args = parser.parse_args()
140 159
141 finder = common.DirectoryFinder(options.build_dir) 160 finder = common.DirectoryFinder(options.build_dir)
142 self.fixup_path = finder.ScriptPath('fixup_pdf_template.py') 161 self.fixup_path = finder.ScriptPath('fixup_pdf_template.py')
143 self.text_diff_path = finder.ScriptPath('text_diff.py') 162 self.text_diff_path = finder.ScriptPath('text_diff.py')
144 163
145 self.drmem_wrapper = options.wrapper 164 self.drmem_wrapper = options.wrapper
146 165
147 self.source_dir = finder.TestingDir() 166 self.source_dir = finder.TestingDir()
148 if self.test_dir != 'corpus': 167 if self.test_dir != 'corpus':
(...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after
184 for input_filename in filename_list: 203 for input_filename in filename_list:
185 if input_file_re.match(input_filename): 204 if input_file_re.match(input_filename):
186 input_path = os.path.join(file_dir, input_filename) 205 input_path = os.path.join(file_dir, input_filename)
187 if not self.test_suppressor.IsExecutionSuppressed(input_path): 206 if not self.test_suppressor.IsExecutionSuppressed(input_path):
188 if os.path.isfile(input_path): 207 if os.path.isfile(input_path):
189 test_cases.append((input_filename, file_dir)) 208 test_cases.append((input_filename, file_dir))
190 209
191 self.failures = [] 210 self.failures = []
192 self.surprises = [] 211 self.surprises = []
193 212
213 # Collect Gold results if an output directory was named.
214 self.gold_results = None
215 if options.gold_output_dir:
216 self.gold_results = gold.GoldResults("pdfium",
217 options.gold_output_dir,
218 options.gold_properties,
219 options.gold_key)
220
194 if options.num_workers > 1 and len(test_cases) > 1: 221 if options.num_workers > 1 and len(test_cases) > 1:
195 try: 222 try:
196 pool = multiprocessing.Pool(options.num_workers) 223 pool = multiprocessing.Pool(options.num_workers)
197 worker_func = functools.partial(TestOneFileParallel, self) 224 worker_func = functools.partial(TestOneFileParallel, self)
198 225
199 worker_results = pool.imap(worker_func, test_cases) 226 worker_results = pool.imap(worker_func, test_cases)
200 for worker_result in worker_results: 227 for worker_result in worker_results:
201 result, input_filename, source_dir = worker_result 228 result, input_filename, source_dir = worker_result
202 input_path = os.path.join(source_dir, input_filename) 229 input_path = os.path.join(source_dir, input_filename)
203 230
204 self.HandleResult(input_filename, input_path, result) 231 self.HandleResult(input_filename, input_path, result)
205 232
206 except KeyboardInterrupt: 233 except KeyboardInterrupt:
207 pool.terminate() 234 pool.terminate()
208 finally: 235 finally:
209 pool.close() 236 pool.close()
210 pool.join() 237 pool.join()
211 else: 238 else:
212 for test_case in test_cases: 239 for test_case in test_cases:
213 input_filename, input_file_dir = test_case 240 input_filename, input_file_dir = test_case
214 result = self.GenerateAndTest(input_filename, input_file_dir) 241 result = self.GenerateAndTest(input_filename, input_file_dir)
215 self.HandleResult(input_filename, 242 self.HandleResult(input_filename,
216 os.path.join(input_file_dir, input_filename), result) 243 os.path.join(input_file_dir, input_filename), result)
217 244
245 if self.gold_results:
246 self.gold_results.WriteResults()
247
218 if self.surprises: 248 if self.surprises:
219 self.surprises.sort() 249 self.surprises.sort()
220 print '\n\nUnexpected Successes:' 250 print '\n\nUnexpected Successes:'
221 for surprise in self.surprises: 251 for surprise in self.surprises:
222 print surprise; 252 print surprise;
223 253
224 if self.failures: 254 if self.failures:
225 self.failures.sort() 255 self.failures.sort()
226 print '\n\nSummary of Failures:' 256 print '\n\nSummary of Failures:'
227 for failure in self.failures: 257 for failure in self.failures:
228 print failure 258 print failure
229 return 1 259 if not options.ignore_errors:
230 260 return 1
231 return 0 261 return 0
OLDNEW
« samples/pdfium_test.cc ('K') | « testing/tools/gold.py ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698