blob: 9ff710f4a1673dbc020083d5c02775188a542de4 [file] [log] [blame]
commit-bot@chromium.org11f15622014-01-07 17:03:40 +00001#!/usr/bin/python
2
3"""
4Copyright 2014 Google Inc.
5
6Use of this source code is governed by a BSD-style license that can be
7found in the LICENSE file.
8
9A wrapper around the standard Python unittest library, adding features we need
10for various unittests within this directory.
epoger66ed8dc2014-07-17 12:54:16 -070011
12TODO(epoger): Move this into the common repo for broader use? Or at least in
13a more common place within the Skia repo?
commit-bot@chromium.org11f15622014-01-07 17:03:40 +000014"""
15
commit-bot@chromium.org3f045172014-05-15 15:10:48 +000016import errno
epoger66ed8dc2014-07-17 12:54:16 -070017import filecmp
commit-bot@chromium.org11f15622014-01-07 17:03:40 +000018import os
commit-bot@chromium.org3f045172014-05-15 15:10:48 +000019import shutil
epoger66ed8dc2014-07-17 12:54:16 -070020import tempfile
commit-bot@chromium.org11f15622014-01-07 17:03:40 +000021import unittest
22
epoger66ed8dc2014-07-17 12:54:16 -070023TRUNK_DIR = os.path.abspath(os.path.join(
24 os.path.dirname(__file__), os.pardir, os.pardir))
commit-bot@chromium.org164052e2014-02-07 18:41:49 +000025
commit-bot@chromium.org11f15622014-01-07 17:03:40 +000026
27class TestCase(unittest.TestCase):
28
epoger66ed8dc2014-07-17 12:54:16 -070029 def __init__(self, *args, **kwargs):
30 super(TestCase, self).__init__(*args, **kwargs)
31 # Subclasses should override this default value if they want their output
32 # to be automatically compared against expectations (see setUp and tearDown)
33 self._testdata_dir = None
34
35 def setUp(self):
36 """Called before each test."""
37 # Get the name of this test, in such a way that it will be consistent
38 # regardless of the directory it is run from (throw away package names,
39 # if any).
40 self._test_name = '.'.join(self.id().split('.')[-3:])
41
42 self._temp_dir = tempfile.mkdtemp()
43 if self._testdata_dir:
44 self.create_empty_dir(self.output_dir_actual)
45
46 def tearDown(self):
47 """Called after each test."""
48 shutil.rmtree(self._temp_dir)
49 if self._testdata_dir and os.path.exists(self.output_dir_expected):
50 different_files = _find_different_files(self.output_dir_actual,
51 self.output_dir_expected)
52 # Don't add any cleanup code below this assert!
53 # Then if tests fail, the artifacts will not be cleaned up.
54 assert (not different_files), \
55 ('found differing files:\n' +
56 '\n'.join(['tkdiff %s %s &' % (
57 os.path.join(self.output_dir_actual, basename),
58 os.path.join(self.output_dir_expected, basename))
59 for basename in different_files]))
60
61 @property
62 def temp_dir(self):
63 return self._temp_dir
64
65 @property
66 def input_dir(self):
67 assert self._testdata_dir, 'self._testdata_dir must be set'
68 return os.path.join(self._testdata_dir, 'inputs')
69
70 @property
71 def output_dir_actual(self):
72 assert self._testdata_dir, 'self._testdata_dir must be set'
73 return os.path.join(
74 self._testdata_dir, 'outputs', 'actual', self._test_name)
75
76 @property
77 def output_dir_expected(self):
78 assert self._testdata_dir, 'self._testdata_dir must be set'
79 return os.path.join(
80 self._testdata_dir, 'outputs', 'expected', self._test_name)
81
commit-bot@chromium.org11f15622014-01-07 17:03:40 +000082 def shortDescription(self):
83 """Tell unittest framework to not print docstrings for test cases."""
84 return None
85
commit-bot@chromium.org3f045172014-05-15 15:10:48 +000086 def create_empty_dir(self, path):
87 """Creates an empty directory at path and returns path.
88
89 Args:
90 path: path on local disk
91 """
epoger66ed8dc2014-07-17 12:54:16 -070092 # Delete the old one, if any.
93 if os.path.isdir(path):
94 shutil.rmtree(path=path, ignore_errors=True)
95 elif os.path.lexists(path):
96 os.remove(path)
97
98 # Create the new one.
commit-bot@chromium.org3f045172014-05-15 15:10:48 +000099 try:
100 os.makedirs(path)
101 except OSError as exc:
epoger66ed8dc2014-07-17 12:54:16 -0700102 # Guard against race condition (somebody else is creating the same dir)
commit-bot@chromium.org3f045172014-05-15 15:10:48 +0000103 if exc.errno != errno.EEXIST:
104 raise
105 return path
106
commit-bot@chromium.org11f15622014-01-07 17:03:40 +0000107
epoger66ed8dc2014-07-17 12:54:16 -0700108def _find_different_files(dir1, dir2, ignore_subtree_names=None):
109 """Returns a list of any files that differ between the directory trees rooted
110 at dir1 and dir2.
commit-bot@chromium.org11f15622014-01-07 17:03:40 +0000111
epoger66ed8dc2014-07-17 12:54:16 -0700112 Args:
113 dir1: root of a directory tree; if nonexistent, will raise OSError
114 dir2: root of another directory tree; if nonexistent, will raise OSError
115 ignore_subtree_names: list of subtree directory names to ignore;
116 defaults to ['.svn'], so all SVN files are ignores
commit-bot@chromium.org11f15622014-01-07 17:03:40 +0000117
epoger66ed8dc2014-07-17 12:54:16 -0700118 TODO(epoger): include the dirname within each filename (not just the
119 basename), to make it easier to locate any differences
120 """
121 differing_files = []
122 if ignore_subtree_names is None:
123 ignore_subtree_names = ['.svn']
124 dircmp = filecmp.dircmp(dir1, dir2, ignore=ignore_subtree_names)
125 differing_files.extend(dircmp.left_only)
126 differing_files.extend(dircmp.right_only)
127 differing_files.extend(dircmp.common_funny)
128 differing_files.extend(dircmp.diff_files)
129 differing_files.extend(dircmp.funny_files)
130 for common_dir in dircmp.common_dirs:
131 differing_files.extend(_find_different_files(
132 os.path.join(dir1, common_dir), os.path.join(dir2, common_dir)))
133 return differing_files
commit-bot@chromium.org11f15622014-01-07 17:03:40 +0000134
135
136def main(test_case_class):
137 """Run the unit tests within the given class.
138
139 Raises an Exception if any of those tests fail (in case we are running in the
140 context of run_all.py, which depends on that Exception to signal failures).
commit-bot@chromium.org11f15622014-01-07 17:03:40 +0000141 """
142 suite = unittest.TestLoader().loadTestsFromTestCase(test_case_class)
143 results = unittest.TextTestRunner(verbosity=2).run(suite)
144 if not results.wasSuccessful():
145 raise Exception('failed unittest %s' % test_case_class)