Home | History | Annotate | Download | only in tests
      1 #!/usr/bin/python
      2 
      3 """
      4 Copyright 2014 Google Inc.
      5 
      6 Use of this source code is governed by a BSD-style license that can be
      7 found in the LICENSE file.
      8 
      9 A wrapper around the standard Python unittest library, adding features we need
     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?
     14 """
     15 
     16 import errno
     17 import filecmp
     18 import os
     19 import shutil
     20 import tempfile
     21 import unittest
     22 
     23 TRUNK_DIR = os.path.abspath(os.path.join(
     24     os.path.dirname(__file__), os.pardir, os.pardir))
     25 
     26 
     27 class TestCase(unittest.TestCase):
     28 
     29   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 
     82   def shortDescription(self):
     83     """Tell unittest framework to not print docstrings for test cases."""
     84     return None
     85 
     86   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     """
     92     # 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.
     99     try:
    100       os.makedirs(path)
    101     except OSError as exc:
    102       # Guard against race condition (somebody else is creating the same dir)
    103       if exc.errno != errno.EEXIST:
    104         raise
    105     return path
    106 
    107 
    108 def _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.
    111 
    112   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
    117 
    118   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
    134 
    135 
    136 def 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).
    141   """
    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)
    146