| #!/usr/bin/python2.4 |
| # |
| # Copyright 2009, Google Inc. |
| # All rights reserved. |
| # |
| # Redistribution and use in source and binary forms, with or without |
| # modification, are permitted provided that the following conditions are |
| # met: |
| # |
| # * Redistributions of source code must retain the above copyright |
| # notice, this list of conditions and the following disclaimer. |
| # * Redistributions in binary form must reproduce the above |
| # copyright notice, this list of conditions and the following disclaimer |
| # in the documentation and/or other materials provided with the |
| # distribution. |
| # * Neither the name of Google Inc. nor the names of its |
| # contributors may be used to endorse or promote products derived from |
| # this software without specific prior written permission. |
| # |
| # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
| # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
| # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
| # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
| # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
| # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
| # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
| # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
| # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
| # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| |
| """Unit tests for Crocodile.""" |
| |
| import os |
| import StringIO |
| import unittest |
| import croc |
| |
| #------------------------------------------------------------------------------ |
| |
| |
| class TestCoverageStats(unittest.TestCase): |
| """Tests for croc.CoverageStats.""" |
| |
| def testAdd(self): |
| """Test Add().""" |
| c = croc.CoverageStats() |
| |
| # Initially empty |
| self.assertEqual(c, {}) |
| |
| # Add items |
| c['a'] = 1 |
| c['b'] = 0 |
| self.assertEqual(c, {'a': 1, 'b': 0}) |
| |
| # Add dict with non-overlapping items |
| c.Add({'c': 5}) |
| self.assertEqual(c, {'a': 1, 'b': 0, 'c': 5}) |
| |
| # Add dict with overlapping items |
| c.Add({'a': 4, 'd': 3}) |
| self.assertEqual(c, {'a': 5, 'b': 0, 'c': 5, 'd': 3}) |
| |
| #------------------------------------------------------------------------------ |
| |
| |
| class TestCoveredFile(unittest.TestCase): |
| """Tests for croc.CoveredFile.""" |
| |
| def setUp(self): |
| self.cov_file = croc.CoveredFile('bob.cc', group='source', language='C++') |
| |
| def testInit(self): |
| """Test init.""" |
| f = self.cov_file |
| |
| # Check initial values |
| self.assertEqual(f.filename, 'bob.cc') |
| self.assertEqual(f.attrs, {'group': 'source', 'language': 'C++'}) |
| self.assertEqual(f.lines, {}) |
| self.assertEqual(f.stats, {}) |
| self.assertEqual(f.local_path, None) |
| self.assertEqual(f.in_lcov, False) |
| |
| def testUpdateCoverageEmpty(self): |
| """Test updating coverage when empty.""" |
| f = self.cov_file |
| f.UpdateCoverage() |
| self.assertEqual(f.stats, { |
| 'lines_executable': 0, |
| 'lines_instrumented': 0, |
| 'lines_covered': 0, |
| 'files_executable': 1, |
| }) |
| |
| def testUpdateCoverageExeOnly(self): |
| """Test updating coverage when no lines are instrumented.""" |
| f = self.cov_file |
| f.lines = {1: None, 2: None, 4: None} |
| f.UpdateCoverage() |
| self.assertEqual(f.stats, { |
| 'lines_executable': 3, |
| 'lines_instrumented': 0, |
| 'lines_covered': 0, |
| 'files_executable': 1, |
| }) |
| |
| # Now mark the file instrumented via in_lcov |
| f.in_lcov = True |
| f.UpdateCoverage() |
| self.assertEqual(f.stats, { |
| 'lines_executable': 3, |
| 'lines_instrumented': 0, |
| 'lines_covered': 0, |
| 'files_executable': 1, |
| 'files_instrumented': 1, |
| }) |
| |
| def testUpdateCoverageExeAndInstr(self): |
| """Test updating coverage when no lines are covered.""" |
| f = self.cov_file |
| f.lines = {1: None, 2: None, 4: 0, 5: 0, 7: None} |
| f.UpdateCoverage() |
| self.assertEqual(f.stats, { |
| 'lines_executable': 5, |
| 'lines_instrumented': 2, |
| 'lines_covered': 0, |
| 'files_executable': 1, |
| 'files_instrumented': 1, |
| }) |
| |
| def testUpdateCoverageWhenCovered(self): |
| """Test updating coverage when lines are covered.""" |
| f = self.cov_file |
| f.lines = {1: None, 2: None, 3: 1, 4: 0, 5: 0, 6: 1, 7: None} |
| f.UpdateCoverage() |
| self.assertEqual(f.stats, { |
| 'lines_executable': 7, |
| 'lines_instrumented': 4, |
| 'lines_covered': 2, |
| 'files_executable': 1, |
| 'files_instrumented': 1, |
| 'files_covered': 1, |
| }) |
| |
| #------------------------------------------------------------------------------ |
| |
| |
| class TestCoveredDir(unittest.TestCase): |
| """Tests for croc.CoveredDir.""" |
| |
| def setUp(self): |
| self.cov_dir = croc.CoveredDir('/a/b/c') |
| |
| def testInit(self): |
| """Test init.""" |
| d = self.cov_dir |
| |
| # Check initial values |
| self.assertEqual(d.dirpath, '/a/b/c') |
| self.assertEqual(d.files, {}) |
| self.assertEqual(d.subdirs, {}) |
| self.assertEqual(d.stats_by_group, {'all': {}}) |
| |
| def testGetTreeEmpty(self): |
| """Test getting empty tree.""" |
| d = self.cov_dir |
| self.assertEqual(d.GetTree(), 'c/') |
| |
| def testGetTreeStats(self): |
| """Test getting tree with stats.""" |
| d = self.cov_dir |
| d.stats_by_group['all'] = croc.CoverageStats( |
| lines_executable=50, lines_instrumented=30, lines_covered=20) |
| d.stats_by_group['bar'] = croc.CoverageStats( |
| lines_executable=0, lines_instrumented=0, lines_covered=0) |
| d.stats_by_group['foo'] = croc.CoverageStats( |
| lines_executable=33, lines_instrumented=22, lines_covered=11) |
| # 'bar' group is skipped because it has no executable lines |
| self.assertEqual( |
| d.GetTree(), |
| 'c/ all:20/30/50 foo:11/22/33') |
| |
| def testGetTreeSubdir(self): |
| """Test getting tree with subdirs.""" |
| d1 = self.cov_dir = croc.CoveredDir('/a') |
| d2 = self.cov_dir = croc.CoveredDir('/a/b') |
| d3 = self.cov_dir = croc.CoveredDir('/a/c') |
| d4 = self.cov_dir = croc.CoveredDir('/a/b/d') |
| d5 = self.cov_dir = croc.CoveredDir('/a/b/e') |
| d1.subdirs = {'/a/b': d2, '/a/c': d3} |
| d2.subdirs = {'/a/b/d': d4, '/a/b/e': d5} |
| self.assertEqual(d1.GetTree(), 'a/\n b/\n d/\n e/\n c/') |
| |
| #------------------------------------------------------------------------------ |
| |
| |
| class TestCoverage(unittest.TestCase): |
| """Tests for croc.Coverage.""" |
| |
| def MockWalk(self, src_dir): |
| """Mock for os.walk(). |
| |
| Args: |
| src_dir: Source directory to walk. |
| |
| Returns: |
| A list of (dirpath, dirnames, filenames) tuples. |
| """ |
| self.mock_walk_calls.append(src_dir) |
| return self.mock_walk_return |
| |
| def MockScanFile(self, filename, language): |
| """Mock for croc_scan.ScanFile(). |
| |
| Args: |
| filename: Path to file to scan. |
| language: Language for file. |
| |
| Returns: |
| A list of executable lines. |
| """ |
| self.mock_scan_calls.append([filename, language]) |
| if filename in self.mock_scan_return: |
| return self.mock_scan_return[filename] |
| else: |
| return self.mock_scan_return['default'] |
| |
| def setUp(self): |
| """Per-test setup.""" |
| |
| # Empty coverage object |
| self.cov = croc.Coverage() |
| |
| # Coverage object with minimal setup |
| self.cov_minimal = croc.Coverage() |
| self.cov_minimal.AddRoot('/src') |
| self.cov_minimal.AddRoot('c:\\source') |
| self.cov_minimal.AddRule('^_/', include=1, group='my') |
| self.cov_minimal.AddRule('.*\\.c$', language='C') |
| self.cov_minimal.AddRule('.*\\.c##$', language='C##') # sharper than thou |
| |
| # Data for MockWalk() |
| self.mock_walk_calls = [] |
| self.mock_walk_return = [] |
| |
| # Data for MockScanFile() |
| self.mock_scan_calls = [] |
| self.mock_scan_return = {'default': [1]} |
| |
| def testInit(self): |
| """Test init.""" |
| c = self.cov |
| self.assertEqual(c.files, {}) |
| self.assertEqual(c.root_dirs, []) |
| self.assertEqual(c.print_stats, []) |
| self.assertEqual(c.rules, []) |
| |
| def testAddRoot(self): |
| """Test AddRoot() and CleanupFilename().""" |
| c = self.cov |
| |
| # Check for identity on already-clean filenames |
| self.assertEqual(c.CleanupFilename(''), '') |
| self.assertEqual(c.CleanupFilename('a'), 'a') |
| self.assertEqual(c.CleanupFilename('.a'), '.a') |
| self.assertEqual(c.CleanupFilename('..a'), '..a') |
| self.assertEqual(c.CleanupFilename('a.b'), 'a.b') |
| self.assertEqual(c.CleanupFilename('a/b/c'), 'a/b/c') |
| self.assertEqual(c.CleanupFilename('a/b/c/'), 'a/b/c/') |
| |
| # Backslash to forward slash |
| self.assertEqual(c.CleanupFilename('a\\b\\c'), 'a/b/c') |
| |
| # Handle relative paths |
| self.assertEqual(c.CleanupFilename('.'), |
| c.CleanupFilename(os.path.abspath('.'))) |
| self.assertEqual(c.CleanupFilename('..'), |
| c.CleanupFilename(os.path.abspath('..'))) |
| self.assertEqual(c.CleanupFilename('./foo/bar'), |
| c.CleanupFilename(os.path.abspath('./foo/bar'))) |
| self.assertEqual(c.CleanupFilename('../../a/b/c'), |
| c.CleanupFilename(os.path.abspath('../../a/b/c'))) |
| |
| # Replace alt roots |
| c.AddRoot('foo') |
| self.assertEqual(c.CleanupFilename('foo'), '_') |
| self.assertEqual(c.CleanupFilename('foo/bar/baz'), '_/bar/baz') |
| self.assertEqual(c.CleanupFilename('aaa/foo'), 'aaa/foo') |
| |
| # Alt root replacement is applied for all roots |
| c.AddRoot('foo/bar', '_B') |
| self.assertEqual(c.CleanupFilename('foo/bar/baz'), '_B/baz') |
| |
| # Can use previously defined roots in cleanup |
| c.AddRoot('_/nom/nom/nom', '_CANHAS') |
| self.assertEqual(c.CleanupFilename('foo/nom/nom/nom/cheezburger'), |
| '_CANHAS/cheezburger') |
| |
| # Verify roots starting with UNC paths or drive letters work, and that |
| # more than one root can point to the same alt_name |
| c.AddRoot('/usr/local/foo', '_FOO') |
| c.AddRoot('D:\\my\\foo', '_FOO') |
| self.assertEqual(c.CleanupFilename('/usr/local/foo/a/b'), '_FOO/a/b') |
| self.assertEqual(c.CleanupFilename('D:\\my\\foo\\c\\d'), '_FOO/c/d') |
| |
| # Cannot specify a blank alt_name |
| self.assertRaises(ValueError, c.AddRoot, 'some_dir', '') |
| |
| def testAddRule(self): |
| """Test AddRule() and ClassifyFile().""" |
| c = self.cov |
| |
| # With only the default rule, nothing gets kept |
| self.assertEqual(c.ClassifyFile('_/src/'), {}) |
| self.assertEqual(c.ClassifyFile('_/src/a.c'), {}) |
| |
| # Add rules to include a tree and set a default group |
| c.AddRule('^_/src/', include=1, group='source') |
| self.assertEqual(c.ClassifyFile('_/src/'), |
| {'include': 1, 'group': 'source'}) |
| self.assertEqual(c.ClassifyFile('_/notsrc/'), {}) |
| self.assertEqual(c.ClassifyFile('_/src/a.c'), |
| {'include': 1, 'group': 'source'}) |
| |
| # Define some languages and groups |
| c.AddRule('.*\\.(c|h)$', language='C') |
| c.AddRule('.*\\.py$', language='Python') |
| c.AddRule('.*_test\\.', group='test') |
| self.assertEqual(c.ClassifyFile('_/src/a.c'), |
| {'include': 1, 'group': 'source', 'language': 'C'}) |
| self.assertEqual(c.ClassifyFile('_/src/a.h'), |
| {'include': 1, 'group': 'source', 'language': 'C'}) |
| self.assertEqual(c.ClassifyFile('_/src/a.cpp'), |
| {'include': 1, 'group': 'source'}) |
| self.assertEqual(c.ClassifyFile('_/src/a_test.c'), |
| {'include': 1, 'group': 'test', 'language': 'C'}) |
| self.assertEqual(c.ClassifyFile('_/src/test_a.c'), |
| {'include': 1, 'group': 'source', 'language': 'C'}) |
| self.assertEqual(c.ClassifyFile('_/src/foo/bar.py'), |
| {'include': 1, 'group': 'source', 'language': 'Python'}) |
| self.assertEqual(c.ClassifyFile('_/src/test.py'), |
| {'include': 1, 'group': 'source', 'language': 'Python'}) |
| |
| # Exclude a path (for example, anything in a build output dir) |
| c.AddRule('.*/build/', include=0) |
| # But add back in a dir which matched the above rule but isn't a build |
| # output dir |
| c.AddRule('_/src/tools/build/', include=1) |
| self.assertEqual(c.ClassifyFile('_/src/build.c').get('include'), 1) |
| self.assertEqual(c.ClassifyFile('_/src/build/').get('include'), 0) |
| self.assertEqual(c.ClassifyFile('_/src/build/a.c').get('include'), 0) |
| self.assertEqual(c.ClassifyFile('_/src/tools/build/').get('include'), 1) |
| self.assertEqual(c.ClassifyFile('_/src/tools/build/t.c').get('include'), 1) |
| |
| def testGetCoveredFile(self): |
| """Test GetCoveredFile().""" |
| c = self.cov_minimal |
| |
| # Not currently any covered files |
| self.assertEqual(c.GetCoveredFile('_/a.c'), None) |
| |
| # Add some files |
| a_c = c.GetCoveredFile('_/a.c', add=True) |
| b_c = c.GetCoveredFile('_/b.c##', add=True) |
| self.assertEqual(a_c.filename, '_/a.c') |
| self.assertEqual(a_c.attrs, {'include': 1, 'group': 'my', 'language': 'C'}) |
| self.assertEqual(b_c.filename, '_/b.c##') |
| self.assertEqual(b_c.attrs, |
| {'include': 1, 'group': 'my', 'language': 'C##'}) |
| |
| # Specifying the same filename should return the existing object |
| self.assertEqual(c.GetCoveredFile('_/a.c'), a_c) |
| self.assertEqual(c.GetCoveredFile('_/a.c', add=True), a_c) |
| |
| # Filenames get cleaned on the way in, as do root paths |
| self.assertEqual(c.GetCoveredFile('/src/a.c'), a_c) |
| self.assertEqual(c.GetCoveredFile('c:\\source\\a.c'), a_c) |
| |
| # TODO: Make sure that covered files require language, group, and include |
| # (since that checking is now done in GetCoveredFile() rather than |
| # ClassifyFile()) |
| |
| def testRemoveCoveredFile(self): |
| """Test RemoveCoveredFile().""" |
| # TODO: TEST ME! |
| |
| def testParseLcov(self): |
| """Test ParseLcovData().""" |
| c = self.cov_minimal |
| |
| c.ParseLcovData([ |
| '# Ignore unknown lines', |
| # File we should include' |
| 'SF:/src/a.c', |
| 'DA:10,1', |
| 'DA:11,0', |
| 'DA:12,1 \n', # Trailing whitespace should get stripped |
| 'end_of_record', |
| # File we should ignore |
| 'SF:/not_src/a.c', |
| 'DA:20,1', |
| 'end_of_record', |
| # Same as first source file, but alternate root |
| 'SF:c:\\source\\a.c', |
| 'DA:30,1', |
| 'end_of_record', |
| # Ignore extra end of record |
| 'end_of_record', |
| # Ignore data points after end of record |
| 'DA:40,1', |
| # Instrumented but uncovered file |
| 'SF:/src/b.c', |
| 'DA:50,0', |
| 'end_of_record', |
| # Empty file (instrumented but no executable lines) |
| 'SF:c:\\source\\c.c', |
| 'end_of_record', |
| ]) |
| |
| # We should know about three files |
| self.assertEqual(sorted(c.files), ['_/a.c', '_/b.c', '_/c.c']) |
| |
| # Check expected contents |
| a_c = c.GetCoveredFile('_/a.c') |
| self.assertEqual(a_c.lines, {10: 1, 11: 0, 12: 1, 30: 1}) |
| self.assertEqual(a_c.stats, { |
| 'files_executable': 1, |
| 'files_instrumented': 1, |
| 'files_covered': 1, |
| 'lines_instrumented': 4, |
| 'lines_executable': 4, |
| 'lines_covered': 3, |
| }) |
| self.assertEqual(a_c.in_lcov, True) |
| |
| b_c = c.GetCoveredFile('_/b.c') |
| self.assertEqual(b_c.lines, {50: 0}) |
| self.assertEqual(b_c.stats, { |
| 'files_executable': 1, |
| 'files_instrumented': 1, |
| 'lines_instrumented': 1, |
| 'lines_executable': 1, |
| 'lines_covered': 0, |
| }) |
| self.assertEqual(b_c.in_lcov, True) |
| |
| c_c = c.GetCoveredFile('_/c.c') |
| self.assertEqual(c_c.lines, {}) |
| self.assertEqual(c_c.stats, { |
| 'files_executable': 1, |
| 'files_instrumented': 1, |
| 'lines_instrumented': 0, |
| 'lines_executable': 0, |
| 'lines_covered': 0, |
| }) |
| self.assertEqual(c_c.in_lcov, True) |
| |
| # TODO: Test that files are marked as instrumented if they come from lcov, |
| # even if they don't have any instrumented lines. (and that in_lcov is set |
| # for those files - probably should set that via some method rather than |
| # directly...) |
| |
| def testGetStat(self): |
| """Test GetStat() and PrintStat().""" |
| c = self.cov |
| |
| # Add some stats, so there's something to report |
| c.tree.stats_by_group = { |
| 'all': { |
| 'count_a': 10, |
| 'count_b': 4, |
| 'foo': 'bar', |
| }, |
| 'tests': { |
| 'count_a': 2, |
| 'count_b': 5, |
| 'baz': 'bob', |
| }, |
| } |
| |
| # Test missing stats and groups |
| self.assertRaises(croc.CrocStatError, c.GetStat, 'nosuch') |
| self.assertRaises(croc.CrocStatError, c.GetStat, 'baz') |
| self.assertRaises(croc.CrocStatError, c.GetStat, 'foo', group='tests') |
| self.assertRaises(croc.CrocStatError, c.GetStat, 'foo', group='nosuch') |
| |
| # Test returning defaults |
| self.assertEqual(c.GetStat('nosuch', default=13), 13) |
| self.assertEqual(c.GetStat('baz', default='aaa'), 'aaa') |
| self.assertEqual(c.GetStat('foo', group='tests', default=0), 0) |
| self.assertEqual(c.GetStat('foo', group='nosuch', default=''), '') |
| |
| # Test getting stats |
| self.assertEqual(c.GetStat('count_a'), 10) |
| self.assertEqual(c.GetStat('count_a', group='tests'), 2) |
| self.assertEqual(c.GetStat('foo', default='baz'), 'bar') |
| |
| # Test stat math (eval) |
| self.assertEqual(c.GetStat('count_a - count_b'), 6) |
| self.assertEqual(c.GetStat('100.0 * count_a / count_b', group='tests'), |
| 40.0) |
| # Should catch eval errors |
| self.assertRaises(croc.CrocStatError, c.GetStat, '100 / 0') |
| self.assertRaises(croc.CrocStatError, c.GetStat, 'count_a -') |
| |
| # Test nested stats via S() |
| self.assertEqual(c.GetStat('count_a - S("count_a", group="tests")'), 8) |
| self.assertRaises(croc.CrocStatError, c.GetStat, 'S()') |
| self.assertRaises(croc.CrocStatError, c.GetStat, 'S("nosuch")') |
| |
| # Test PrintStat() |
| # We won't see the first print, but at least verify it doesn't assert |
| c.PrintStat('count_a', format='(test to stdout: %s)') |
| # Send subsequent prints to a file |
| f = StringIO.StringIO() |
| c.PrintStat('count_b', outfile=f) |
| # Test specifying output format |
| c.PrintStat('count_a', format='Count A = %05d', outfile=f) |
| # Test specifing additional keyword args |
| c.PrintStat('count_a', group='tests', outfile=f) |
| c.PrintStat('nosuch', default=42, outfile=f) |
| self.assertEqual(f.getvalue(), ("""\ |
| GetStat('count_b') = 4 |
| Count A = 00010 |
| GetStat('count_a') = 2 |
| GetStat('nosuch') = 42 |
| """)) |
| f.close() |
| |
| def testAddConfigEmpty(self): |
| """Test AddConfig() with empty config.""" |
| c = self.cov |
| # Most minimal config is an empty dict; should do nothing |
| c.AddConfig('{} # And we ignore comments') |
| |
| def testAddConfig(self): |
| """Test AddConfig().""" |
| c = self.cov |
| lcov_queue = [] |
| addfiles_queue = [] |
| |
| c.AddConfig("""{ |
| 'roots' : [ |
| {'root' : '/foo'}, |
| {'root' : '/bar', 'altname' : 'BAR'}, |
| ], |
| 'rules' : [ |
| {'regexp' : '^_/', 'group' : 'apple'}, |
| {'regexp' : 're2', 'include' : 1, 'language' : 'elvish'}, |
| ], |
| 'lcov_files' : ['a.lcov', 'b.lcov'], |
| 'add_files' : ['/src', 'BAR/doo'], |
| 'print_stats' : [ |
| {'stat' : 'count_a'}, |
| {'stat' : 'count_b', 'group' : 'tests'}, |
| ], |
| 'extra_key' : 'is ignored', |
| }""", lcov_queue=lcov_queue, addfiles_queue=addfiles_queue) |
| |
| self.assertEqual(lcov_queue, ['a.lcov', 'b.lcov']) |
| self.assertEqual(addfiles_queue, ['/src', 'BAR/doo']) |
| self.assertEqual(c.root_dirs, [['/foo', '_'], ['/bar', 'BAR']]) |
| self.assertEqual(c.print_stats, [ |
| {'stat': 'count_a'}, |
| {'stat': 'count_b', 'group': 'tests'}, |
| ]) |
| # Convert compiled re's back to patterns for comparison |
| rules = [[r[0].pattern] + r[1:] for r in c.rules] |
| self.assertEqual(rules, [ |
| ['^_/', {'group': 'apple'}], |
| ['re2', {'include': 1, 'language': 'elvish'}], |
| ]) |
| |
| def testAddFilesSimple(self): |
| """Test AddFiles() simple call.""" |
| c = self.cov_minimal |
| c.add_files_walk = self.MockWalk |
| c.scan_file = self.MockScanFile |
| |
| c.AddFiles('/a/b/c') |
| self.assertEqual(self.mock_walk_calls, ['/a/b/c']) |
| self.assertEqual(self.mock_scan_calls, []) |
| self.assertEqual(c.files, {}) |
| |
| def testAddFilesRootMap(self): |
| """Test AddFiles() with root mappings.""" |
| c = self.cov_minimal |
| c.add_files_walk = self.MockWalk |
| c.scan_file = self.MockScanFile |
| |
| c.AddRoot('_/subdir', 'SUBDIR') |
| |
| # AddFiles() should replace the 'SUBDIR' alt_name, then match both |
| # possible roots for the '_' alt_name. |
| c.AddFiles('SUBDIR/foo') |
| self.assertEqual(self.mock_walk_calls, |
| ['/src/subdir/foo', 'c:/source/subdir/foo']) |
| self.assertEqual(self.mock_scan_calls, []) |
| self.assertEqual(c.files, {}) |
| |
| def testAddFilesNonEmpty(self): |
| """Test AddFiles() where files are returned.""" |
| |
| c = self.cov_minimal |
| c.add_files_walk = self.MockWalk |
| c.scan_file = self.MockScanFile |
| |
| # Add a rule to exclude a subdir |
| c.AddRule('^_/proj1/excluded/', include=0) |
| |
| # Add a rule to exclude adding some fiels |
| c.AddRule('.*noscan.c$', add_if_missing=0) |
| |
| # Set data for mock walk and scan |
| self.mock_walk_return = [ |
| [ |
| '/src/proj1', |
| ['excluded', 'subdir'], |
| ['a.c', 'no.f', 'yes.c', 'noexe.c', 'bob_noscan.c'], |
| ], |
| [ |
| '/src/proj1/subdir', |
| [], |
| ['cherry.c'], |
| ], |
| ] |
| |
| # Add a file with no executable lines; it should be scanned but not added |
| self.mock_scan_return['/src/proj1/noexe.c'] = [] |
| |
| c.AddFiles('/src/proj1') |
| |
| self.assertEqual(self.mock_walk_calls, ['/src/proj1']) |
| self.assertEqual(self.mock_scan_calls, [ |
| ['/src/proj1/a.c', 'C'], |
| ['/src/proj1/yes.c', 'C'], |
| ['/src/proj1/noexe.c', 'C'], |
| ['/src/proj1/subdir/cherry.c', 'C'], |
| ]) |
| |
| # Include files from the main dir and subdir |
| self.assertEqual(sorted(c.files), [ |
| '_/proj1/a.c', |
| '_/proj1/subdir/cherry.c', |
| '_/proj1/yes.c']) |
| |
| # Excluded dir should have been pruned from the mock walk data dirnames. |
| # In the real os.walk() call this prunes the walk. |
| self.assertEqual(self.mock_walk_return[0][1], ['subdir']) |
| |
| def testUpdateTreeStats(self): |
| """Test UpdateTreeStats().""" |
| |
| c = self.cov_minimal |
| c.AddRule('.*_test', group='test') |
| |
| # Fill the files list |
| c.ParseLcovData([ |
| 'SF:/src/a.c', |
| 'DA:10,1', 'DA:11,1', 'DA:20,0', |
| 'end_of_record', |
| 'SF:/src/a_test.c', |
| 'DA:10,1', 'DA:11,1', 'DA:12,1', |
| 'end_of_record', |
| 'SF:/src/foo/b.c', |
| 'DA:10,1', 'DA:11,1', 'DA:20,0', 'DA:21,0', 'DA:30,0', |
| 'end_of_record', |
| 'SF:/src/foo/b_test.c', |
| 'DA:20,0', 'DA:21,0', 'DA:22,0', |
| 'end_of_record', |
| ]) |
| c.UpdateTreeStats() |
| |
| t = c.tree |
| self.assertEqual(t.dirpath, '') |
| self.assertEqual(sorted(t.files), []) |
| self.assertEqual(sorted(t.subdirs), ['_']) |
| self.assertEqual(t.stats_by_group, { |
| 'all': { |
| 'files_covered': 3, |
| 'files_executable': 4, |
| 'lines_executable': 14, |
| 'lines_covered': 7, |
| 'lines_instrumented': 14, |
| 'files_instrumented': 4, |
| }, |
| 'my': { |
| 'files_covered': 2, |
| 'files_executable': 2, |
| 'lines_executable': 8, |
| 'lines_covered': 4, |
| 'lines_instrumented': 8, |
| 'files_instrumented': 2, |
| }, |
| 'test': { |
| 'files_covered': 1, |
| 'files_executable': 2, |
| 'lines_executable': 6, |
| 'lines_covered': 3, |
| 'lines_instrumented': 6, |
| 'files_instrumented': 2, |
| }, |
| }) |
| |
| t = t.subdirs['_'] |
| self.assertEqual(t.dirpath, '_') |
| self.assertEqual(sorted(t.files), ['a.c', 'a_test.c']) |
| self.assertEqual(sorted(t.subdirs), ['foo']) |
| self.assertEqual(t.stats_by_group, { |
| 'all': { |
| 'files_covered': 3, |
| 'files_executable': 4, |
| 'lines_executable': 14, |
| 'lines_covered': 7, |
| 'lines_instrumented': 14, |
| 'files_instrumented': 4, |
| }, |
| 'my': { |
| 'files_covered': 2, |
| 'files_executable': 2, |
| 'lines_executable': 8, |
| 'lines_covered': 4, |
| 'lines_instrumented': 8, |
| 'files_instrumented': 2, |
| }, |
| 'test': { |
| 'files_covered': 1, |
| 'files_executable': 2, |
| 'lines_executable': 6, |
| 'lines_covered': 3, |
| 'lines_instrumented': 6, |
| 'files_instrumented': 2, |
| }, |
| }) |
| |
| t = t.subdirs['foo'] |
| self.assertEqual(t.dirpath, '_/foo') |
| self.assertEqual(sorted(t.files), ['b.c', 'b_test.c']) |
| self.assertEqual(sorted(t.subdirs), []) |
| self.assertEqual(t.stats_by_group, { |
| 'test': { |
| 'files_executable': 1, |
| 'files_instrumented': 1, |
| 'lines_executable': 3, |
| 'lines_instrumented': 3, |
| 'lines_covered': 0, |
| }, |
| 'all': { |
| 'files_covered': 1, |
| 'files_executable': 2, |
| 'lines_executable': 8, |
| 'lines_covered': 2, |
| 'lines_instrumented': 8, |
| 'files_instrumented': 2, |
| }, |
| 'my': { |
| 'files_covered': 1, |
| 'files_executable': 1, |
| 'lines_executable': 5, |
| 'lines_covered': 2, |
| 'lines_instrumented': 5, |
| 'files_instrumented': 1, |
| } |
| }) |
| |
| # TODO: test: less important, since these are thin wrappers around other |
| # tested methods. |
| # ParseConfig() |
| # ParseLcovFile() |
| # PrintTree() |
| |
| #------------------------------------------------------------------------------ |
| |
| if __name__ == '__main__': |
| unittest.main() |