759 lines
24 KiB
Python
Executable File
759 lines
24 KiB
Python
Executable File
#!/usr/bin/env python
|
|
# Copyright (c) 2011 The Chromium Authors. All rights reserved.
|
|
# Use of this source code is governed by a BSD-style license that can be
|
|
# found in the LICENSE file.
|
|
|
|
"""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 testEmptyTreeStats(self):
|
|
"""Make sure we don't choke when absolutely nothing happened.
|
|
|
|
How we might hit this: bot compile error."""
|
|
c = self.cov_minimal
|
|
t = c.tree
|
|
t.stats_by_group['all'].AddDefaults()
|
|
self.assertEqual(t.stats_by_group, {
|
|
'all': { 'files_covered': 0,
|
|
'files_instrumented': 0,
|
|
'files_executable': 0,
|
|
'lines_covered': 0,
|
|
'lines_instrumented': 0,
|
|
'lines_executable': 0 }})
|
|
|
|
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()
|