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

Side by Side Diff: tools/tests/base_unittest.py

Issue 397103003: combine base_unittest.py modules from gm and tools (Closed) Base URL: https://skia.googlesource.com/skia.git@master
Patch Set: everything but pylint cleanup Created 6 years, 5 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
OLDNEW
1 #!/usr/bin/python 1 #!/usr/bin/python
2 2
3 """ 3 """
4 Copyright 2014 Google Inc. 4 Copyright 2014 Google Inc.
5 5
6 Use of this source code is governed by a BSD-style license that can be 6 Use of this source code is governed by a BSD-style license that can be
7 found in the LICENSE file. 7 found in the LICENSE file.
8 8
9 A wrapper around the standard Python unittest library, adding features we need 9 A wrapper around the standard Python unittest library, adding features we need
10 for various unittests within this directory. 10 for various unittests within this directory.
11
12 TODO(epoger): Move this into the common repo for broader use? Or at least in
13 a more common place within the Skia repo?
11 """ 14 """
12 15
13 import errno 16 import errno
17 import filecmp
14 import os 18 import os
15 import shutil 19 import shutil
20 import subprocess
16 import sys 21 import sys
22 import tempfile
17 import unittest 23 import unittest
18 24
19 # Set the PYTHONPATH to include the tools directory. 25 TRUNK_DIR = os.path.abspath(os.path.join(
20 sys.path.append( 26 os.path.dirname(__file__), os.pardir, os.pardir))
21 os.path.join(os.path.dirname(os.path.realpath(__file__)), os.pardir))
22 import find_run_binary
23 27
24 28
25 class TestCase(unittest.TestCase): 29 class TestCase(unittest.TestCase):
26 30
31 def __init__(self, *args, **kwargs):
32 super(TestCase, self).__init__(*args, **kwargs)
33 # Subclasses should override this default value if they want their output
34 # to be automatically compared against expectations (see setUp and tearDown)
35 self._testdata_dir = None
36
37 def setUp(self):
38 """Called before each test."""
39 # Get the name of this test, in such a way that it will be consistent
40 # regardless of the directory it is run from (throw away package names,
41 # if any).
42 self._test_name = '.'.join(self.id().split('.')[-3:])
43
44 self._temp_dir = tempfile.mkdtemp()
45 if self._testdata_dir:
46 self.create_empty_dir(self.output_dir_actual)
47
48 def tearDown(self):
49 """Called after each test."""
50 shutil.rmtree(self._temp_dir)
51 if self._testdata_dir and os.path.exists(self.output_dir_expected):
52 different_files = _find_different_files(self.output_dir_actual,
53 self.output_dir_expected)
54 # Don't add any cleanup code below this assert!
55 # Then if tests fail, the artifacts will not be cleaned up.
56 assert (not different_files), \
57 ('found differing files:\n' +
58 '\n'.join(['tkdiff %s %s &' % (
59 os.path.join(self.output_dir_actual, basename),
60 os.path.join(self.output_dir_expected, basename))
61 for basename in different_files]))
62
63 @property
64 def temp_dir(self):
65 return self._temp_dir
66
67 @property
68 def input_dir(self):
69 assert self._testdata_dir, 'self._testdata_dir must be set'
70 return os.path.join(self._testdata_dir, 'inputs')
71
72 @property
73 def output_dir_actual(self):
74 assert self._testdata_dir, 'self._testdata_dir must be set'
75 return os.path.join(
76 self._testdata_dir, 'outputs', 'actual', self._test_name)
77
78 @property
79 def output_dir_expected(self):
80 assert self._testdata_dir, 'self._testdata_dir must be set'
81 return os.path.join(
82 self._testdata_dir, 'outputs', 'expected', self._test_name)
83
27 def shortDescription(self): 84 def shortDescription(self):
28 """Tell unittest framework to not print docstrings for test cases.""" 85 """Tell unittest framework to not print docstrings for test cases."""
29 return None 86 return None
30 87
31 def create_empty_dir(self, path): 88 def create_empty_dir(self, path):
32 """Creates an empty directory at path and returns path. 89 """Creates an empty directory at path and returns path.
33 90
34 Args: 91 Args:
35 path: path on local disk 92 path: path on local disk
36 """ 93 """
37 shutil.rmtree(path=path, ignore_errors=True) 94 # Delete the old one, if any.
95 if os.path.isdir(path):
96 shutil.rmtree(path=path, ignore_errors=True)
97 elif os.path.lexists(path):
98 os.remove(path)
99
100 # Create the new one.
38 try: 101 try:
39 os.makedirs(path) 102 os.makedirs(path)
40 except OSError as exc: 103 except OSError as exc:
104 # Guard against race condition (somebody else is creating the same dir)
41 if exc.errno != errno.EEXIST: 105 if exc.errno != errno.EEXIST:
42 raise 106 raise
43 return path 107 return path
44 108
45 def run_command(self, args):
46 """Runs a program from the command line and returns stdout.
47 109
48 Args: 110 def _find_different_files(dir1, dir2, ignore_subtree_names=None):
49 args: Command line to run, as a list of string parameters. args[0] is the 111 """Returns a list of any files that differ between the directory trees rooted
50 binary to run. 112 at dir1 and dir2.
51 113
52 Returns: 114 Args:
53 stdout from the program, as a single string. 115 dir1: root of a directory tree; if nonexistent, will raise OSError
116 dir2: root of another directory tree; if nonexistent, will raise OSError
117 ignore_subtree_names: list of subtree directory names to ignore;
118 defaults to ['.svn'], so all SVN files are ignores
54 119
55 Raises: 120 TODO(epoger): include the dirname within each filename (not just the
56 Exception: the program exited with a nonzero return code. 121 basename), to make it easier to locate any differences
57 """ 122 """
58 return find_run_binary.run_command(args) 123 differing_files = []
59 124 if ignore_subtree_names is None:
60 def find_path_to_program(self, program): 125 ignore_subtree_names = ['.svn']
61 """Returns path to an existing program binary. 126 dircmp = filecmp.dircmp(dir1, dir2, ignore=ignore_subtree_names)
62 127 differing_files.extend(dircmp.left_only)
63 Args: 128 differing_files.extend(dircmp.right_only)
64 program: Basename of the program to find (e.g., 'render_pictures'). 129 differing_files.extend(dircmp.common_funny)
65 130 differing_files.extend(dircmp.diff_files)
66 Returns: 131 differing_files.extend(dircmp.funny_files)
67 Absolute path to the program binary, as a string. 132 for common_dir in dircmp.common_dirs:
68 133 differing_files.extend(_find_different_files(
69 Raises: 134 os.path.join(dir1, common_dir), os.path.join(dir2, common_dir)))
70 Exception: unable to find the program binary. 135 return differing_files
71 """
72 return find_run_binary.find_path_to_program(program)
73 136
74 137
75 def main(test_case_class): 138 def main(test_case_class):
76 """Run the unit tests within the given class. 139 """Run the unit tests within the given class.
77 140
78 Raises an Exception if any of those tests fail (in case we are running in the 141 Raises an Exception if any of those tests fail (in case we are running in the
79 context of run_all.py, which depends on that Exception to signal failures). 142 context of run_all.py, which depends on that Exception to signal failures).
80
81 TODO(epoger): Make all of our unit tests use the Python unittest framework,
82 so we can leverage its ability to run *all* the tests and report failures at
83 the end.
84 """ 143 """
85 suite = unittest.TestLoader().loadTestsFromTestCase(test_case_class) 144 suite = unittest.TestLoader().loadTestsFromTestCase(test_case_class)
86 results = unittest.TextTestRunner(verbosity=2).run(suite) 145 results = unittest.TextTestRunner(verbosity=2).run(suite)
87 if not results.wasSuccessful(): 146 if not results.wasSuccessful():
88 raise Exception('failed unittest %s' % test_case_class) 147 raise Exception('failed unittest %s' % test_case_class)
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698