results_test.py revision 2a78e82f5eb82c3832231e36957110044b0783b3
151b2906e11752df6c18351cf520e30522d3b53a1Torne (Richard Coles)#!/usr/bin/python
251b2906e11752df6c18351cf520e30522d3b53a1Torne (Richard Coles)
351b2906e11752df6c18351cf520e30522d3b53a1Torne (Richard Coles)"""
451b2906e11752df6c18351cf520e30522d3b53a1Torne (Richard Coles)Copyright 2013 Google Inc.
551b2906e11752df6c18351cf520e30522d3b53a1Torne (Richard Coles)
651b2906e11752df6c18351cf520e30522d3b53a1Torne (Richard Coles)Use of this source code is governed by a BSD-style license that can be
751b2906e11752df6c18351cf520e30522d3b53a1Torne (Richard Coles)found in the LICENSE file.
851b2906e11752df6c18351cf520e30522d3b53a1Torne (Richard Coles)
951b2906e11752df6c18351cf520e30522d3b53a1Torne (Richard Coles)Test results.py
1051b2906e11752df6c18351cf520e30522d3b53a1Torne (Richard Coles)
1151b2906e11752df6c18351cf520e30522d3b53a1Torne (Richard Coles)TODO(epoger): Create a command to update the expected results (in
1251b2906e11752df6c18351cf520e30522d3b53a1Torne (Richard Coles)OUTPUT_DIR_EXPECTED) when appropriate.  For now, you should:
1351b2906e11752df6c18351cf520e30522d3b53a1Torne (Richard Coles)1. examine the results in OUTPUT_DIR_ACTUAL and make sure they are ok
1451b2906e11752df6c18351cf520e30522d3b53a1Torne (Richard Coles)2. rm -rf OUTPUT_DIR_EXPECTED
1551b2906e11752df6c18351cf520e30522d3b53a1Torne (Richard Coles)3. mv OUTPUT_DIR_ACTUAL OUTPUT_DIR_EXPECTED
1651b2906e11752df6c18351cf520e30522d3b53a1Torne (Richard Coles)Although, if you're using an SVN checkout, this will blow away .svn directories
1751b2906e11752df6c18351cf520e30522d3b53a1Torne (Richard Coles)within OUTPUT_DIR_EXPECTED, which wouldn't be good...
1851b2906e11752df6c18351cf520e30522d3b53a1Torne (Richard Coles)
1951b2906e11752df6c18351cf520e30522d3b53a1Torne (Richard Coles)"""
2051b2906e11752df6c18351cf520e30522d3b53a1Torne (Richard Coles)
2151b2906e11752df6c18351cf520e30522d3b53a1Torne (Richard Coles)import filecmp
2251b2906e11752df6c18351cf520e30522d3b53a1Torne (Richard Coles)import os
2351b2906e11752df6c18351cf520e30522d3b53a1Torne (Richard Coles)import shutil
2451b2906e11752df6c18351cf520e30522d3b53a1Torne (Richard Coles)import sys
2551b2906e11752df6c18351cf520e30522d3b53a1Torne (Richard Coles)import tempfile
2651b2906e11752df6c18351cf520e30522d3b53a1Torne (Richard Coles)import unittest
2751b2906e11752df6c18351cf520e30522d3b53a1Torne (Richard Coles)
2851b2906e11752df6c18351cf520e30522d3b53a1Torne (Richard Coles)# Imports from within Skia
2951b2906e11752df6c18351cf520e30522d3b53a1Torne (Richard Coles)import results
3051b2906e11752df6c18351cf520e30522d3b53a1Torne (Richard Coles)import gm_json  # must import results first, so that gm_json will be in sys.path
3151b2906e11752df6c18351cf520e30522d3b53a1Torne (Richard Coles)
3251b2906e11752df6c18351cf520e30522d3b53a1Torne (Richard Coles)PARENT_DIR = os.path.dirname(os.path.realpath(__file__))
33197021e6b966cfb06891637935ef33fff06433d1Ben MurdochINPUT_DIR = os.path.join(PARENT_DIR, 'tests', 'inputs')
34197021e6b966cfb06891637935ef33fff06433d1Ben MurdochOUTPUT_DIR_ACTUAL   = os.path.join(PARENT_DIR, 'tests', 'outputs', 'actual')
355d92fedcae5e801a8b224de090094f2d9df0b54aTorne (Richard Coles)OUTPUT_DIR_EXPECTED = os.path.join(PARENT_DIR, 'tests', 'outputs', 'expected')
365d92fedcae5e801a8b224de090094f2d9df0b54aTorne (Richard Coles)
3751b2906e11752df6c18351cf520e30522d3b53a1Torne (Richard Coles)
3851b2906e11752df6c18351cf520e30522d3b53a1Torne (Richard Coles)class ResultsTest(unittest.TestCase):
3951b2906e11752df6c18351cf520e30522d3b53a1Torne (Richard Coles)
40323480423219ecd77329f8326dc5e0e3b50926d4Torne (Richard Coles)  def setUp(self):
4151b2906e11752df6c18351cf520e30522d3b53a1Torne (Richard Coles)    self._temp_dir = tempfile.mkdtemp()
4251b2906e11752df6c18351cf520e30522d3b53a1Torne (Richard Coles)    self._output_dir_actual   = os.path.join(OUTPUT_DIR_ACTUAL, self.id())
4351b2906e11752df6c18351cf520e30522d3b53a1Torne (Richard Coles)    self._output_dir_expected = os.path.join(OUTPUT_DIR_EXPECTED, self.id())
4451b2906e11752df6c18351cf520e30522d3b53a1Torne (Richard Coles)    create_empty_dir(self._output_dir_actual)
4551b2906e11752df6c18351cf520e30522d3b53a1Torne (Richard Coles)
4651b2906e11752df6c18351cf520e30522d3b53a1Torne (Richard Coles)  def tearDown(self):
4709380295ba73501a205346becac22c6978e4671dTorne (Richard Coles)    shutil.rmtree(self._temp_dir)
4851b2906e11752df6c18351cf520e30522d3b53a1Torne (Richard Coles)    different_files = find_different_files(self._output_dir_actual,
495d92fedcae5e801a8b224de090094f2d9df0b54aTorne (Richard Coles)                                           self._output_dir_expected)
50a854de003a23bf3c7f95ec0f8154ada64092ff5cTorne (Richard Coles)    # Maybe we should move this assert elsewhere?  It's unusual to see an
51a854de003a23bf3c7f95ec0f8154ada64092ff5cTorne (Richard Coles)    # assert within tearDown(), but my thinking was:
5251b2906e11752df6c18351cf520e30522d3b53a1Torne (Richard Coles)    # 1. Every test case will have some collection of output files that need to
5351b2906e11752df6c18351cf520e30522d3b53a1Torne (Richard Coles)    #    be validated.
5451b2906e11752df6c18351cf520e30522d3b53a1Torne (Richard Coles)    # 2. So put that validation within tearDown(), which will be called after
55c1847b1379d12d0e05df27436bf19a9b1bf12deaTorne (Richard Coles)    #    every test case!
5651b2906e11752df6c18351cf520e30522d3b53a1Torne (Richard Coles)    #
5751b2906e11752df6c18351cf520e30522d3b53a1Torne (Richard Coles)    # I have confirmed that the test really does fail if this assert is
5809380295ba73501a205346becac22c6978e4671dTorne (Richard Coles)    # triggered.
5951b2906e11752df6c18351cf520e30522d3b53a1Torne (Richard Coles)    #
6051b2906e11752df6c18351cf520e30522d3b53a1Torne (Richard Coles)    # Ravi notes: if somebody later comes along and adds cleanup code below the
6151b2906e11752df6c18351cf520e30522d3b53a1Torne (Richard Coles)    # assert, then if tests fail, the artifacts will not be cleaned up.
6251b2906e11752df6c18351cf520e30522d3b53a1Torne (Richard Coles)    assert (not different_files), \
6351b2906e11752df6c18351cf520e30522d3b53a1Torne (Richard Coles)      ('found differing files between actual dir %s and expected dir %s: %s' %
6451b2906e11752df6c18351cf520e30522d3b53a1Torne (Richard Coles)       (self._output_dir_actual, self._output_dir_expected, different_files))
6551b2906e11752df6c18351cf520e30522d3b53a1Torne (Richard Coles)
6651b2906e11752df6c18351cf520e30522d3b53a1Torne (Richard Coles)  def shortDescription(self):
6751b2906e11752df6c18351cf520e30522d3b53a1Torne (Richard Coles)    """Tell unittest framework to not print docstrings for test cases."""
6851b2906e11752df6c18351cf520e30522d3b53a1Torne (Richard Coles)    return None
6951b2906e11752df6c18351cf520e30522d3b53a1Torne (Richard Coles)
7051b2906e11752df6c18351cf520e30522d3b53a1Torne (Richard Coles)  def test_gm(self):
7151b2906e11752df6c18351cf520e30522d3b53a1Torne (Richard Coles)    """Process results of a GM run with the Results object."""
7251b2906e11752df6c18351cf520e30522d3b53a1Torne (Richard Coles)    results_obj = results.Results(
7351b2906e11752df6c18351cf520e30522d3b53a1Torne (Richard Coles)        actuals_root=os.path.join(INPUT_DIR, 'gm-actuals'),
7451b2906e11752df6c18351cf520e30522d3b53a1Torne (Richard Coles)        expected_root=os.path.join(INPUT_DIR, 'gm-expectations'),
7551b2906e11752df6c18351cf520e30522d3b53a1Torne (Richard Coles)        generated_images_root=self._temp_dir)
7651b2906e11752df6c18351cf520e30522d3b53a1Torne (Richard Coles)    gm_json.WriteToFile(results_obj.get_results_of_type(results.RESULTS_ALL),
7751b2906e11752df6c18351cf520e30522d3b53a1Torne (Richard Coles)                        os.path.join(self._output_dir_actual, 'gm.json'))
7851b2906e11752df6c18351cf520e30522d3b53a1Torne (Richard Coles)
7951b2906e11752df6c18351cf520e30522d3b53a1Torne (Richard Coles)
8051b2906e11752df6c18351cf520e30522d3b53a1Torne (Richard Coles)def create_empty_dir(path):
8151b2906e11752df6c18351cf520e30522d3b53a1Torne (Richard Coles)  """Create an empty directory at the given path."""
8251b2906e11752df6c18351cf520e30522d3b53a1Torne (Richard Coles)  if os.path.isdir(path):
8351b2906e11752df6c18351cf520e30522d3b53a1Torne (Richard Coles)    shutil.rmtree(path)
8451b2906e11752df6c18351cf520e30522d3b53a1Torne (Richard Coles)  elif os.path.lexists(path):
8551b2906e11752df6c18351cf520e30522d3b53a1Torne (Richard Coles)    os.remove(path)
8651b2906e11752df6c18351cf520e30522d3b53a1Torne (Richard Coles)  os.makedirs(path)
8751b2906e11752df6c18351cf520e30522d3b53a1Torne (Richard Coles)
8851b2906e11752df6c18351cf520e30522d3b53a1Torne (Richard Coles)
8951b2906e11752df6c18351cf520e30522d3b53a1Torne (Richard Coles)def find_different_files(dir1, dir2, ignore_subtree_names=None):
9051b2906e11752df6c18351cf520e30522d3b53a1Torne (Richard Coles)  """Returns a list of any files that differ between the directory trees rooted
9151b2906e11752df6c18351cf520e30522d3b53a1Torne (Richard Coles)  at dir1 and dir2.
9251b2906e11752df6c18351cf520e30522d3b53a1Torne (Richard Coles)
9351b2906e11752df6c18351cf520e30522d3b53a1Torne (Richard Coles)  Args:
9451b2906e11752df6c18351cf520e30522d3b53a1Torne (Richard Coles)    dir1: root of a directory tree; if nonexistent, will raise OSError
9551b2906e11752df6c18351cf520e30522d3b53a1Torne (Richard Coles)    dir2: root of another directory tree; if nonexistent, will raise OSError
9651b2906e11752df6c18351cf520e30522d3b53a1Torne (Richard Coles)    ignore_subtree_names: list of subtree directory names to ignore;
9751b2906e11752df6c18351cf520e30522d3b53a1Torne (Richard Coles)          defaults to ['.svn'], so all SVN files are ignores
9851b2906e11752df6c18351cf520e30522d3b53a1Torne (Richard Coles)
9951b2906e11752df6c18351cf520e30522d3b53a1Torne (Richard Coles)  TODO(epoger): include the dirname within each filename (not just the
10051b2906e11752df6c18351cf520e30522d3b53a1Torne (Richard Coles)  basename), to make it easier to locate any differences
10151b2906e11752df6c18351cf520e30522d3b53a1Torne (Richard Coles)  """
10251b2906e11752df6c18351cf520e30522d3b53a1Torne (Richard Coles)  differing_files = []
10351b2906e11752df6c18351cf520e30522d3b53a1Torne (Richard Coles)  if ignore_subtree_names is None:
10451b2906e11752df6c18351cf520e30522d3b53a1Torne (Richard Coles)    ignore_subtree_names = ['.svn']
10551b2906e11752df6c18351cf520e30522d3b53a1Torne (Richard Coles)  dircmp = filecmp.dircmp(dir1, dir2, ignore=ignore_subtree_names)
10651b2906e11752df6c18351cf520e30522d3b53a1Torne (Richard Coles)  differing_files.extend(dircmp.left_only)
10751b2906e11752df6c18351cf520e30522d3b53a1Torne (Richard Coles)  differing_files.extend(dircmp.right_only)
10851b2906e11752df6c18351cf520e30522d3b53a1Torne (Richard Coles)  differing_files.extend(dircmp.common_funny)
10951b2906e11752df6c18351cf520e30522d3b53a1Torne (Richard Coles)  differing_files.extend(dircmp.diff_files)
11051b2906e11752df6c18351cf520e30522d3b53a1Torne (Richard Coles)  differing_files.extend(dircmp.funny_files)
11151b2906e11752df6c18351cf520e30522d3b53a1Torne (Richard Coles)  for common_dir in dircmp.common_dirs:
11251b2906e11752df6c18351cf520e30522d3b53a1Torne (Richard Coles)    differing_files.extend(find_different_files(
11351b2906e11752df6c18351cf520e30522d3b53a1Torne (Richard Coles)        os.path.join(dir1, common_dir), os.path.join(dir2, common_dir)))
11451b2906e11752df6c18351cf520e30522d3b53a1Torne (Richard Coles)  return differing_files
11551b2906e11752df6c18351cf520e30522d3b53a1Torne (Richard Coles)
11651b2906e11752df6c18351cf520e30522d3b53a1Torne (Richard Coles)
11751b2906e11752df6c18351cf520e30522d3b53a1Torne (Richard Coles)def main():
11851b2906e11752df6c18351cf520e30522d3b53a1Torne (Richard Coles)  suite = unittest.TestLoader().loadTestsFromTestCase(ResultsTest)
119323480423219ecd77329f8326dc5e0e3b50926d4Torne (Richard Coles)  unittest.TextTestRunner(verbosity=2).run(suite)
12051b2906e11752df6c18351cf520e30522d3b53a1Torne (Richard Coles)
12151b2906e11752df6c18351cf520e30522d3b53a1Torne (Richard Coles)
122323480423219ecd77329f8326dc5e0e3b50926d4Torne (Richard Coles)if __name__ == '__main__':
12351b2906e11752df6c18351cf520e30522d3b53a1Torne (Richard Coles)  main()
12451b2906e11752df6c18351cf520e30522d3b53a1Torne (Richard Coles)