2016-03-08 10:47:58 +00:00
|
|
|
# Copyright 2016 the V8 project authors. All rights reserved.
|
|
|
|
# Use of this source code is governed by a BSD-style license that can be
|
|
|
|
# found in the LICENSE file.
|
|
|
|
|
|
|
|
# Requires python-coverage. Native python coverage version >= 3.7.1 should
|
|
|
|
# be installed to get the best speed.
|
|
|
|
|
|
|
|
import copy
|
|
|
|
import coverage
|
|
|
|
import logging
|
2016-03-21 09:23:21 +00:00
|
|
|
import json
|
2016-03-08 10:47:58 +00:00
|
|
|
import os
|
2016-03-21 09:23:21 +00:00
|
|
|
import shutil
|
2016-03-08 10:47:58 +00:00
|
|
|
import sys
|
2016-03-21 09:23:21 +00:00
|
|
|
import tempfile
|
2016-03-08 10:47:58 +00:00
|
|
|
import unittest
|
|
|
|
|
|
|
|
|
|
|
|
# Directory of this file.
|
|
|
|
LOCATION = os.path.dirname(os.path.abspath(__file__))
|
|
|
|
|
|
|
|
# V8 checkout directory.
|
|
|
|
BASE_DIR = os.path.dirname(os.path.dirname(LOCATION))
|
|
|
|
|
|
|
|
# Executable location.
|
|
|
|
BUILD_DIR = os.path.join(BASE_DIR, 'out', 'Release')
|
|
|
|
|
|
|
|
def abs_line(line):
|
|
|
|
"""Absolute paths as output by the llvm symbolizer."""
|
|
|
|
return '%s/%s' % (BUILD_DIR, line)
|
|
|
|
|
|
|
|
|
|
|
|
#------------------------------------------------------------------------------
|
|
|
|
|
|
|
|
# Data for test_process_symbolizer_output. This simulates output from the
|
|
|
|
# llvm symbolizer. The paths are not normlized.
|
|
|
|
SYMBOLIZER_OUTPUT = (
|
|
|
|
abs_line('../../src/foo.cc:87:7\n') +
|
|
|
|
abs_line('../../src/foo.cc:92:0\n') + # Test sorting.
|
|
|
|
abs_line('../../src/baz/bar.h:1234567:0\n') + # Test large line numbers.
|
|
|
|
abs_line('../../src/foo.cc:92:0\n') + # Test duplicates.
|
|
|
|
abs_line('../../src/baz/bar.h:0:0\n') + # Test subdirs.
|
|
|
|
'/usr/include/cool_stuff.h:14:2\n' + # Test dropping absolute paths.
|
|
|
|
abs_line('../../src/foo.cc:87:10\n') + # Test dropping character indexes.
|
|
|
|
abs_line('../../third_party/icu.cc:0:0\n') + # Test dropping excluded dirs.
|
|
|
|
abs_line('../../src/baz/bar.h:11:0\n')
|
|
|
|
)
|
|
|
|
|
|
|
|
# The expected post-processed output maps relative file names to line numbers.
|
|
|
|
# The numbers are sorted and unique.
|
|
|
|
EXPECTED_PROCESSED_OUTPUT = {
|
|
|
|
'src/baz/bar.h': [0, 11, 1234567],
|
|
|
|
'src/foo.cc': [87, 92],
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
#------------------------------------------------------------------------------
|
|
|
|
|
|
|
|
# Data for test_merge_instrumented_line_results. A list of absolute paths to
|
|
|
|
# all executables.
|
|
|
|
EXE_LIST = [
|
|
|
|
'/path/to/d8',
|
|
|
|
'/path/to/cctest',
|
|
|
|
'/path/to/unittests',
|
|
|
|
]
|
|
|
|
|
|
|
|
# Post-processed llvm symbolizer output as returned by
|
|
|
|
# process_symbolizer_output. These are lists of this output for merging.
|
|
|
|
INSTRUMENTED_LINE_RESULTS = [
|
|
|
|
{
|
|
|
|
'src/baz/bar.h': [0, 3, 7],
|
|
|
|
'src/foo.cc': [11],
|
|
|
|
},
|
|
|
|
{
|
|
|
|
'src/baz/bar.h': [3, 7, 8],
|
|
|
|
'src/baz.cc': [2],
|
|
|
|
'src/foo.cc': [1, 92],
|
|
|
|
},
|
|
|
|
{
|
|
|
|
'src/baz.cc': [1],
|
|
|
|
'src/foo.cc': [92, 93],
|
|
|
|
},
|
|
|
|
]
|
|
|
|
|
|
|
|
# This shows initial instrumentation. No lines are covered, hence,
|
|
|
|
# the coverage mask is 0 for all lines. The line tuples remain sorted by
|
|
|
|
# line number and contain no duplicates.
|
|
|
|
EXPECTED_INSTRUMENTED_LINES_DATA = {
|
|
|
|
'version': 1,
|
|
|
|
'tests': ['cctest', 'd8', 'unittests'],
|
|
|
|
'files': {
|
|
|
|
'src/baz/bar.h': [[0, 0], [3, 0], [7, 0], [8, 0]],
|
|
|
|
'src/baz.cc': [[1, 0], [2, 0]],
|
|
|
|
'src/foo.cc': [[1, 0], [11, 0], [92, 0], [93, 0]],
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
#------------------------------------------------------------------------------
|
|
|
|
|
|
|
|
# Data for test_merge_covered_line_results. List of post-processed
|
|
|
|
# llvm-symbolizer output as a tuple including the executable name of each data
|
|
|
|
# set.
|
|
|
|
COVERED_LINE_RESULTS = [
|
|
|
|
({
|
|
|
|
'src/baz/bar.h': [3, 7],
|
|
|
|
'src/foo.cc': [11],
|
|
|
|
}, 'd8'),
|
|
|
|
({
|
|
|
|
'src/baz/bar.h': [3, 7],
|
|
|
|
'src/baz.cc': [2],
|
|
|
|
'src/foo.cc': [1],
|
|
|
|
}, 'cctest'),
|
|
|
|
({
|
|
|
|
'src/foo.cc': [92],
|
|
|
|
'src/baz.cc': [2],
|
|
|
|
}, 'unittests'),
|
|
|
|
]
|
|
|
|
|
|
|
|
# This shows initial instrumentation + coverage. The mask bits are:
|
|
|
|
# cctest: 1, d8: 2, unittests:4. So a line covered by cctest and unittests
|
|
|
|
# has a coverage mask of 0b101, e.g. line 2 in src/baz.cc.
|
|
|
|
EXPECTED_COVERED_LINES_DATA = {
|
|
|
|
'version': 1,
|
|
|
|
'tests': ['cctest', 'd8', 'unittests'],
|
|
|
|
'files': {
|
|
|
|
'src/baz/bar.h': [[0, 0b0], [3, 0b11], [7, 0b11], [8, 0b0]],
|
|
|
|
'src/baz.cc': [[1, 0b0], [2, 0b101]],
|
|
|
|
'src/foo.cc': [[1, 0b1], [11, 0b10], [92, 0b100], [93, 0b0]],
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2016-03-21 09:23:21 +00:00
|
|
|
#------------------------------------------------------------------------------
|
|
|
|
|
|
|
|
# Data for test_split.
|
|
|
|
|
|
|
|
EXPECTED_SPLIT_FILES = [
|
|
|
|
(
|
|
|
|
os.path.join('src', 'baz', 'bar.h.json'),
|
|
|
|
{
|
|
|
|
'version': 1,
|
|
|
|
'tests': ['cctest', 'd8', 'unittests'],
|
|
|
|
'files': {
|
|
|
|
'src/baz/bar.h': [[0, 0b0], [3, 0b11], [7, 0b11], [8, 0b0]],
|
|
|
|
},
|
|
|
|
},
|
|
|
|
),
|
|
|
|
(
|
|
|
|
os.path.join('src', 'baz.cc.json'),
|
|
|
|
{
|
|
|
|
'version': 1,
|
|
|
|
'tests': ['cctest', 'd8', 'unittests'],
|
|
|
|
'files': {
|
|
|
|
'src/baz.cc': [[1, 0b0], [2, 0b101]],
|
|
|
|
},
|
|
|
|
},
|
|
|
|
),
|
|
|
|
(
|
|
|
|
os.path.join('src', 'foo.cc.json'),
|
|
|
|
{
|
|
|
|
'version': 1,
|
|
|
|
'tests': ['cctest', 'd8', 'unittests'],
|
|
|
|
'files': {
|
|
|
|
'src/foo.cc': [[1, 0b1], [11, 0b10], [92, 0b100], [93, 0b0]],
|
|
|
|
},
|
|
|
|
},
|
|
|
|
),
|
|
|
|
]
|
|
|
|
|
|
|
|
|
2016-03-08 10:47:58 +00:00
|
|
|
class FormatterTests(unittest.TestCase):
|
|
|
|
@classmethod
|
|
|
|
def setUpClass(cls):
|
|
|
|
sys.path.append(LOCATION)
|
|
|
|
cls._cov = coverage.coverage(
|
|
|
|
include=([os.path.join(LOCATION, 'sancov_formatter.py')]))
|
|
|
|
cls._cov.start()
|
|
|
|
import sancov_formatter
|
|
|
|
global sancov_formatter
|
|
|
|
|
|
|
|
@classmethod
|
|
|
|
def tearDownClass(cls):
|
|
|
|
cls._cov.stop()
|
|
|
|
cls._cov.report()
|
|
|
|
|
|
|
|
def test_process_symbolizer_output(self):
|
|
|
|
result = sancov_formatter.process_symbolizer_output(SYMBOLIZER_OUTPUT)
|
|
|
|
self.assertEquals(EXPECTED_PROCESSED_OUTPUT, result)
|
|
|
|
|
|
|
|
def test_merge_instrumented_line_results(self):
|
|
|
|
result = sancov_formatter.merge_instrumented_line_results(
|
|
|
|
EXE_LIST, INSTRUMENTED_LINE_RESULTS)
|
|
|
|
self.assertEquals(EXPECTED_INSTRUMENTED_LINES_DATA, result)
|
|
|
|
|
|
|
|
def test_merge_covered_line_results(self):
|
|
|
|
data = copy.deepcopy(EXPECTED_INSTRUMENTED_LINES_DATA)
|
|
|
|
sancov_formatter.merge_covered_line_results(
|
|
|
|
data, COVERED_LINE_RESULTS)
|
|
|
|
self.assertEquals(EXPECTED_COVERED_LINES_DATA, data)
|
2016-03-21 09:23:21 +00:00
|
|
|
|
|
|
|
def test_split(self):
|
|
|
|
_, json_input = tempfile.mkstemp(prefix='tmp_coverage_test_split')
|
|
|
|
with open(json_input, 'w') as f:
|
|
|
|
json.dump(EXPECTED_COVERED_LINES_DATA, f)
|
|
|
|
output_dir = tempfile.mkdtemp(prefix='tmp_coverage_test_split')
|
|
|
|
|
|
|
|
try:
|
|
|
|
sancov_formatter.main([
|
|
|
|
'split',
|
|
|
|
'--json-input', json_input,
|
|
|
|
'--output-dir', output_dir,
|
|
|
|
])
|
|
|
|
|
|
|
|
for file_name, expected_data in EXPECTED_SPLIT_FILES:
|
|
|
|
full_path = os.path.join(output_dir, file_name)
|
|
|
|
self.assertTrue(os.path.exists(full_path))
|
|
|
|
with open(full_path) as f:
|
|
|
|
self.assertEquals(expected_data, json.load(f))
|
|
|
|
finally:
|
|
|
|
os.remove(json_input)
|
|
|
|
shutil.rmtree(output_dir)
|