| Index: tools/find_bad_images_in_skps.py
|
| diff --git a/tools/find_bad_images_in_skps.py b/tools/find_bad_images_in_skps.py
|
| new file mode 100755
|
| index 0000000000000000000000000000000000000000..5dcf6a477e3ba5cbaa29ec9896ea152fcf79b31c
|
| --- /dev/null
|
| +++ b/tools/find_bad_images_in_skps.py
|
| @@ -0,0 +1,197 @@
|
| +#!/usr/bin/env python
|
| +
|
| +# Copyright 2013 Google Inc. All rights reserved.
|
| +# Use of this source code is governed by a BSD-style license that can be
|
| +# found in the LICENSE file.
|
| +
|
| +"""
|
| +This script will take as an argument either a list of skp files or a
|
| +set of directories that contains skp files. It will then test each
|
| +skp file with the `render_pictures` program. If that program either
|
| +spits out any unexpected output or doesn't return 0, I will flag that
|
| +skp file as problematic. We then extract all of the embedded images
|
| +inside the skp and test each one of them against the
|
| +SkImageDecoder::DecodeFile function. Again, we consider any
|
| +extraneous output or a bad return value an error. In the event of an
|
| +error, we retain the image and print out information about the error.
|
| +The output (on stdout) is formatted as a csv document.
|
| +
|
| +A copy of each bad image is left in a directory created by
|
| +tempfile.mkdtemp().
|
| +"""
|
| +
|
| +import glob
|
| +import os
|
| +import re
|
| +import shutil
|
| +import subprocess
|
| +import sys
|
| +import tempfile
|
| +import threading
|
| +
|
| +import test_rendering # skia/trunk/tools. reuse FindPathToProgram()
|
| +
|
| +USAGE = """
|
| +Usage:
|
| + {command} SKP_FILE [SKP_FILES]
|
| + {command} SKP_DIR [SKP_DIRS]\n
|
| +Environment variables:
|
| + To run multiple worker threads, set NUM_THREADS.
|
| + To use a different temporary storage location, set TMPDIR.
|
| +
|
| +"""
|
| +
|
| +def execute_program(args, ignores=None):
|
| + """
|
| + Execute a process and waits for it to complete. Returns all
|
| + output (stderr and stdout) after (optional) filtering.
|
| +
|
| + @param args is passed into subprocess.Popen().
|
| +
|
| + @param ignores (optional) is a list of regular expression strings
|
| + that will be ignored in the output.
|
| +
|
| + @returns a tuple (returncode, output)
|
| + """
|
| + if ignores is None:
|
| + ignores = []
|
| + else:
|
| + ignores = [re.compile(ignore) for ignore in ignores]
|
| + proc = subprocess.Popen(
|
| + args,
|
| + stdout=subprocess.PIPE,
|
| + stderr=subprocess.STDOUT)
|
| + output = ''.join(
|
| + line for line in proc.stdout
|
| + if not any(bool(ignore.match(line)) for ignore in ignores))
|
| + returncode = proc.wait()
|
| + return (returncode, output)
|
| +
|
| +
|
| +def list_files(paths):
|
| + """
|
| + Accepts a list of directories or filenames on the command line.
|
| + We do not choose to recurse into directories beyond one level.
|
| + """
|
| + class NotAFileException(Exception):
|
| + pass
|
| + for path in paths:
|
| + for globbedpath in glob.iglob(path): # useful on win32
|
| + if os.path.isdir(globbedpath):
|
| + for filename in os.listdir(globbedpath):
|
| + newpath = os.path.join(globbedpath, filename)
|
| + if os.path.isfile(newpath):
|
| + yield newpath
|
| + elif os.path.isfile(globbedpath):
|
| + yield globbedpath
|
| + else:
|
| + raise NotAFileException('{} is not a file'.format(globbedpath))
|
| +
|
| +
|
| +class BadImageFinder(object):
|
| +
|
| + def __init__(self, directory=None):
|
| + self.render_pictures = test_rendering.FindPathToProgram(
|
| + 'render_pictures')
|
| + self.test_image_decoder = test_rendering.FindPathToProgram(
|
| + 'test_image_decoder')
|
| + assert os.path.isfile(self.render_pictures)
|
| + assert os.path.isfile(self.test_image_decoder)
|
| + if directory is None:
|
| + self.saved_image_dir = tempfile.mkdtemp(prefix='skia_skp_test_')
|
| + else:
|
| + assert os.path.isdir(directory)
|
| + self.saved_image_dir = directory
|
| + self.bad_image_count = 0
|
| +
|
| + def process_files(self, skp_files):
|
| + for path in skp_files:
|
| + self.process_file(path)
|
| +
|
| + def process_file(self, skp_file):
|
| + assert self.saved_image_dir is not None
|
| + assert os.path.isfile(skp_file)
|
| + args = [self.render_pictures, '--readPath', skp_file]
|
| + ignores = ['^process_in', '^deserializ', '^drawing...', '^Non-defaul']
|
| + returncode, output = execute_program(args, ignores)
|
| + if (returncode == 0) and not output:
|
| + return
|
| + temp_image_dir = tempfile.mkdtemp(prefix='skia_skp_test___')
|
| + args = [ self.render_pictures, '--readPath', skp_file,
|
| + '--writePath', temp_image_dir, '--writeEncodedImages']
|
| + subprocess.call(args, stderr=open(os.devnull,'w'),
|
| + stdout=open(os.devnull,'w'))
|
| + for image_name in os.listdir(temp_image_dir):
|
| + image_path = os.path.join(temp_image_dir, image_name)
|
| + assert(os.path.isfile(image_path))
|
| + args = [self.test_image_decoder, image_path]
|
| + returncode, output = execute_program(args, [])
|
| + if (returncode == 0) and not output:
|
| + os.remove(image_path)
|
| + continue
|
| + try:
|
| + shutil.move(image_path, self.saved_image_dir)
|
| + except (shutil.Error,):
|
| + # If this happens, don't stop the entire process,
|
| + # just warn the user.
|
| + os.remove(image_path)
|
| + sys.stderr.write('{0} is a repeat.\n'.format(image_name))
|
| + self.bad_image_count += 1
|
| + if returncode == 2:
|
| + returncode = 'SkImageDecoder::DecodeFile returns false'
|
| + elif returncode == 0:
|
| + returncode = 'extra verbosity'
|
| + assert output
|
| + elif returncode == -11:
|
| + returncode = 'segmentation violation'
|
| + else:
|
| + returncode = 'returncode: {}'.format(returncode)
|
| + output = output.strip().replace('\n',' ').replace('"','\'')
|
| + suffix = image_name[-3:]
|
| + output_line = '"{0}","{1}","{2}","{3}","{4}"\n'.format(
|
| + returncode, suffix, skp_file, image_name, output)
|
| + sys.stdout.write(output_line)
|
| + sys.stdout.flush()
|
| + os.rmdir(temp_image_dir)
|
| + return
|
| +
|
| +def main(main_argv):
|
| + if not main_argv or main_argv[0] in ['-h', '-?', '-help', '--help']:
|
| + sys.stderr.write(USAGE.format(command=__file__))
|
| + return 1
|
| + if 'NUM_THREADS' in os.environ:
|
| + number_of_threads = int(os.environ['NUM_THREADS'])
|
| + if number_of_threads < 1:
|
| + number_of_threads = 1
|
| + else:
|
| + number_of_threads = 1
|
| + os.environ['skia_images_png_suppressDecoderWarnings'] = 'true'
|
| + os.environ['skia_images_jpeg_suppressDecoderWarnings'] = 'true'
|
| +
|
| + temp_dir = tempfile.mkdtemp(prefix='skia_skp_test_')
|
| + sys.stderr.write('Directory for bad images: {}\n'.format(temp_dir))
|
| + sys.stdout.write('"Error","Filetype","SKP File","Image File","Output"\n')
|
| + sys.stdout.flush()
|
| +
|
| + finders = [
|
| + BadImageFinder(temp_dir) for index in xrange(number_of_threads)]
|
| + arguments = [[] for index in xrange(number_of_threads)]
|
| + for index, item in enumerate(list_files(main_argv)):
|
| + ## split up the given targets among the worker threads
|
| + arguments[index % number_of_threads].append(item)
|
| + threads = [
|
| + threading.Thread(
|
| + target=BadImageFinder.process_files, args=(finder,argument))
|
| + for finder, argument in zip(finders, arguments)]
|
| + for thread in threads:
|
| + thread.start()
|
| + for thread in threads:
|
| + thread.join()
|
| + number = sum(finder.bad_image_count for finder in finders)
|
| + sys.stderr.write('Number of bad images found: {}\n'.format(number))
|
| + return 0
|
| +
|
| +if __name__ == '__main__':
|
| + exit(main(sys.argv[1:]))
|
| +
|
| +# LocalWords: skp stdout csv
|
|
|