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

Side by Side Diff: tools/find_bad_images_in_skps.py

Issue 24449003: Make Jpeg decoding more fault resistant. (Closed) Base URL: https://skia.googlecode.com/svn/trunk
Patch Set: final rebase Created 7 years, 2 months 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 | Annotate | Revision Log
« no previous file with comments | « tests/JpegTest.cpp ('k') | tools/test_image_decoder.cpp » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 #!/usr/bin/env python
2
3 # Copyright 2013 Google Inc. All rights reserved.
4 # Use of this source code is governed by a BSD-style license that can be
5 # found in the LICENSE file.
6
7 """
8 This script will take as an argument either a list of skp files or a
9 set of directories that contains skp files. It will then test each
10 skp file with the `render_pictures` program. If that program either
11 spits out any unexpected output or doesn't return 0, I will flag that
12 skp file as problematic. We then extract all of the embedded images
13 inside the skp and test each one of them against the
14 SkImageDecoder::DecodeFile function. Again, we consider any
15 extraneous output or a bad return value an error. In the event of an
16 error, we retain the image and print out information about the error.
17 The output (on stdout) is formatted as a csv document.
18
19 A copy of each bad image is left in a directory created by
20 tempfile.mkdtemp().
21 """
22
23 import glob
24 import os
25 import re
26 import shutil
27 import subprocess
28 import sys
29 import tempfile
30 import threading
31
32 import test_rendering # skia/trunk/tools. reuse FindPathToProgram()
33
34 USAGE = """
35 Usage:
36 {command} SKP_FILE [SKP_FILES]
37 {command} SKP_DIR [SKP_DIRS]\n
38 Environment variables:
39 To run multiple worker threads, set NUM_THREADS.
40 To use a different temporary storage location, set TMPDIR.
41
42 """
43
44 def execute_program(args, ignores=None):
45 """
46 Execute a process and waits for it to complete. Returns all
47 output (stderr and stdout) after (optional) filtering.
48
49 @param args is passed into subprocess.Popen().
50
51 @param ignores (optional) is a list of regular expression strings
52 that will be ignored in the output.
53
54 @returns a tuple (returncode, output)
55 """
56 if ignores is None:
57 ignores = []
58 else:
59 ignores = [re.compile(ignore) for ignore in ignores]
60 proc = subprocess.Popen(
61 args,
62 stdout=subprocess.PIPE,
63 stderr=subprocess.STDOUT)
64 output = ''.join(
65 line for line in proc.stdout
66 if not any(bool(ignore.match(line)) for ignore in ignores))
67 returncode = proc.wait()
68 return (returncode, output)
69
70
71 def list_files(paths):
72 """
73 Accepts a list of directories or filenames on the command line.
74 We do not choose to recurse into directories beyond one level.
75 """
76 class NotAFileException(Exception):
77 pass
78 for path in paths:
79 for globbedpath in glob.iglob(path): # useful on win32
80 if os.path.isdir(globbedpath):
81 for filename in os.listdir(globbedpath):
82 newpath = os.path.join(globbedpath, filename)
83 if os.path.isfile(newpath):
84 yield newpath
85 elif os.path.isfile(globbedpath):
86 yield globbedpath
87 else:
88 raise NotAFileException('{} is not a file'.format(globbedpath))
89
90
91 class BadImageFinder(object):
92
93 def __init__(self, directory=None):
94 self.render_pictures = test_rendering.FindPathToProgram(
95 'render_pictures')
96 self.test_image_decoder = test_rendering.FindPathToProgram(
97 'test_image_decoder')
98 assert os.path.isfile(self.render_pictures)
99 assert os.path.isfile(self.test_image_decoder)
100 if directory is None:
101 self.saved_image_dir = tempfile.mkdtemp(prefix='skia_skp_test_')
102 else:
103 assert os.path.isdir(directory)
104 self.saved_image_dir = directory
105 self.bad_image_count = 0
106
107 def process_files(self, skp_files):
108 for path in skp_files:
109 self.process_file(path)
110
111 def process_file(self, skp_file):
112 assert self.saved_image_dir is not None
113 assert os.path.isfile(skp_file)
114 args = [self.render_pictures, '--readPath', skp_file]
115 ignores = ['^process_in', '^deserializ', '^drawing...', '^Non-defaul']
116 returncode, output = execute_program(args, ignores)
117 if (returncode == 0) and not output:
118 return
119 temp_image_dir = tempfile.mkdtemp(prefix='skia_skp_test___')
120 args = [ self.render_pictures, '--readPath', skp_file,
121 '--writePath', temp_image_dir, '--writeEncodedImages']
122 subprocess.call(args, stderr=open(os.devnull,'w'),
123 stdout=open(os.devnull,'w'))
124 for image_name in os.listdir(temp_image_dir):
125 image_path = os.path.join(temp_image_dir, image_name)
126 assert(os.path.isfile(image_path))
127 args = [self.test_image_decoder, image_path]
128 returncode, output = execute_program(args, [])
129 if (returncode == 0) and not output:
130 os.remove(image_path)
131 continue
132 try:
133 shutil.move(image_path, self.saved_image_dir)
134 except (shutil.Error,):
135 # If this happens, don't stop the entire process,
136 # just warn the user.
137 os.remove(image_path)
138 sys.stderr.write('{0} is a repeat.\n'.format(image_name))
139 self.bad_image_count += 1
140 if returncode == 2:
141 returncode = 'SkImageDecoder::DecodeFile returns false'
142 elif returncode == 0:
143 returncode = 'extra verbosity'
144 assert output
145 elif returncode == -11:
146 returncode = 'segmentation violation'
147 else:
148 returncode = 'returncode: {}'.format(returncode)
149 output = output.strip().replace('\n',' ').replace('"','\'')
150 suffix = image_name[-3:]
151 output_line = '"{0}","{1}","{2}","{3}","{4}"\n'.format(
152 returncode, suffix, skp_file, image_name, output)
153 sys.stdout.write(output_line)
154 sys.stdout.flush()
155 os.rmdir(temp_image_dir)
156 return
157
158 def main(main_argv):
159 if not main_argv or main_argv[0] in ['-h', '-?', '-help', '--help']:
160 sys.stderr.write(USAGE.format(command=__file__))
161 return 1
162 if 'NUM_THREADS' in os.environ:
163 number_of_threads = int(os.environ['NUM_THREADS'])
164 if number_of_threads < 1:
165 number_of_threads = 1
166 else:
167 number_of_threads = 1
168 os.environ['skia_images_png_suppressDecoderWarnings'] = 'true'
169 os.environ['skia_images_jpeg_suppressDecoderWarnings'] = 'true'
170
171 temp_dir = tempfile.mkdtemp(prefix='skia_skp_test_')
172 sys.stderr.write('Directory for bad images: {}\n'.format(temp_dir))
173 sys.stdout.write('"Error","Filetype","SKP File","Image File","Output"\n')
174 sys.stdout.flush()
175
176 finders = [
177 BadImageFinder(temp_dir) for index in xrange(number_of_threads)]
178 arguments = [[] for index in xrange(number_of_threads)]
179 for index, item in enumerate(list_files(main_argv)):
180 ## split up the given targets among the worker threads
181 arguments[index % number_of_threads].append(item)
182 threads = [
183 threading.Thread(
184 target=BadImageFinder.process_files, args=(finder,argument))
185 for finder, argument in zip(finders, arguments)]
186 for thread in threads:
187 thread.start()
188 for thread in threads:
189 thread.join()
190 number = sum(finder.bad_image_count for finder in finders)
191 sys.stderr.write('Number of bad images found: {}\n'.format(number))
192 return 0
193
194 if __name__ == '__main__':
195 exit(main(sys.argv[1:]))
196
197 # LocalWords: skp stdout csv
OLDNEW
« no previous file with comments | « tests/JpegTest.cpp ('k') | tools/test_image_decoder.cpp » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698