• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# Copyright 2016 the V8 project authors. All rights reserved.
2# Use of this source code is governed by a BSD-style license that can be
3# found in the LICENSE file.
4
5# Requires python-coverage. Native python coverage version >= 3.7.1 should
6# be installed to get the best speed.
7
8import copy
9import coverage
10import logging
11import json
12import os
13import shutil
14import sys
15import tempfile
16import unittest
17
18
19# Directory of this file.
20LOCATION = os.path.dirname(os.path.abspath(__file__))
21
22# V8 checkout directory.
23BASE_DIR = os.path.dirname(os.path.dirname(LOCATION))
24
25# Executable location.
26BUILD_DIR = os.path.join(BASE_DIR, 'out', 'Release')
27
28def abs_line(line):
29  """Absolute paths as output by the llvm symbolizer."""
30  return '%s/%s' % (BUILD_DIR, line)
31
32
33#------------------------------------------------------------------------------
34
35# Data for test_process_symbolizer_output. This simulates output from the
36# llvm symbolizer. The paths are not normlized.
37SYMBOLIZER_OUTPUT = (
38  abs_line('../../src/foo.cc:87:7\n') +
39  abs_line('../../src/foo.cc:92:0\n') + # Test sorting.
40  abs_line('../../src/baz/bar.h:1234567:0\n') + # Test large line numbers.
41  abs_line('../../src/foo.cc:92:0\n') + # Test duplicates.
42  abs_line('../../src/baz/bar.h:0:0\n') + # Test subdirs.
43  '/usr/include/cool_stuff.h:14:2\n' + # Test dropping absolute paths.
44  abs_line('../../src/foo.cc:87:10\n') + # Test dropping character indexes.
45  abs_line('../../third_party/icu.cc:0:0\n') + # Test dropping excluded dirs.
46  abs_line('../../src/baz/bar.h:11:0\n')
47)
48
49# The expected post-processed output maps relative file names to line numbers.
50# The numbers are sorted and unique.
51EXPECTED_PROCESSED_OUTPUT = {
52  'src/baz/bar.h': [0, 11, 1234567],
53  'src/foo.cc': [87, 92],
54}
55
56
57#------------------------------------------------------------------------------
58
59# Data for test_merge_instrumented_line_results. A list of absolute paths to
60# all executables.
61EXE_LIST = [
62  '/path/to/d8',
63  '/path/to/cctest',
64  '/path/to/unittests',
65]
66
67# Post-processed llvm symbolizer output as returned by
68# process_symbolizer_output. These are lists of this output for merging.
69INSTRUMENTED_LINE_RESULTS = [
70  {
71    'src/baz/bar.h': [0, 3, 7],
72    'src/foo.cc': [11],
73  },
74  {
75    'src/baz/bar.h': [3, 7, 8],
76    'src/baz.cc': [2],
77    'src/foo.cc': [1, 92],
78  },
79  {
80    'src/baz.cc': [1],
81    'src/foo.cc': [92, 93],
82  },
83]
84
85# This shows initial instrumentation. No lines are covered, hence,
86# the coverage mask is 0 for all lines. The line tuples remain sorted by
87# line number and contain no duplicates.
88EXPECTED_INSTRUMENTED_LINES_DATA = {
89  'version': 1,
90  'tests': ['cctest', 'd8', 'unittests'],
91  'files': {
92    'src/baz/bar.h': [[0, 0], [3, 0], [7, 0], [8, 0]],
93    'src/baz.cc': [[1, 0], [2, 0]],
94    'src/foo.cc': [[1, 0], [11, 0], [92, 0], [93, 0]],
95  },
96}
97
98
99#------------------------------------------------------------------------------
100
101# Data for test_merge_covered_line_results. List of post-processed
102# llvm-symbolizer output as a tuple including the executable name of each data
103# set.
104COVERED_LINE_RESULTS = [
105  ({
106     'src/baz/bar.h': [3, 7],
107     'src/foo.cc': [11],
108   }, 'd8'),
109  ({
110     'src/baz/bar.h': [3, 7],
111     'src/baz.cc': [2],
112     'src/foo.cc': [1],
113   }, 'cctest'),
114  ({
115     'src/foo.cc': [92],
116     'src/baz.cc': [2],
117   }, 'unittests'),
118]
119
120# This shows initial instrumentation + coverage. The mask bits are:
121# cctest: 1, d8: 2, unittests:4. So a line covered by cctest and unittests
122# has a coverage mask of 0b101, e.g. line 2 in src/baz.cc.
123EXPECTED_COVERED_LINES_DATA = {
124  'version': 1,
125  'tests': ['cctest', 'd8', 'unittests'],
126  'files': {
127    'src/baz/bar.h': [[0, 0b0], [3, 0b11], [7, 0b11], [8, 0b0]],
128    'src/baz.cc': [[1, 0b0], [2, 0b101]],
129    'src/foo.cc': [[1, 0b1], [11, 0b10], [92, 0b100], [93, 0b0]],
130  },
131}
132
133
134#------------------------------------------------------------------------------
135
136# Data for test_split.
137
138EXPECTED_SPLIT_FILES = [
139  (
140    os.path.join('src', 'baz', 'bar.h.json'),
141    {
142      'version': 1,
143      'tests': ['cctest', 'd8', 'unittests'],
144      'files': {
145        'src/baz/bar.h': [[0, 0b0], [3, 0b11], [7, 0b11], [8, 0b0]],
146      },
147    },
148  ),
149  (
150    os.path.join('src', 'baz.cc.json'),
151    {
152      'version': 1,
153      'tests': ['cctest', 'd8', 'unittests'],
154      'files': {
155        'src/baz.cc': [[1, 0b0], [2, 0b101]],
156      },
157    },
158  ),
159  (
160    os.path.join('src', 'foo.cc.json'),
161    {
162      'version': 1,
163      'tests': ['cctest', 'd8', 'unittests'],
164      'files': {
165        'src/foo.cc': [[1, 0b1], [11, 0b10], [92, 0b100], [93, 0b0]],
166      },
167    },
168  ),
169]
170
171
172class FormatterTests(unittest.TestCase):
173  @classmethod
174  def setUpClass(cls):
175    sys.path.append(LOCATION)
176    cls._cov = coverage.coverage(
177        include=([os.path.join(LOCATION, 'sancov_formatter.py')]))
178    cls._cov.start()
179    import sancov_formatter
180    global sancov_formatter
181
182  @classmethod
183  def tearDownClass(cls):
184    cls._cov.stop()
185    cls._cov.report()
186
187  def test_process_symbolizer_output(self):
188    result = sancov_formatter.process_symbolizer_output(SYMBOLIZER_OUTPUT)
189    self.assertEquals(EXPECTED_PROCESSED_OUTPUT, result)
190
191  def test_merge_instrumented_line_results(self):
192    result = sancov_formatter.merge_instrumented_line_results(
193      EXE_LIST, INSTRUMENTED_LINE_RESULTS)
194    self.assertEquals(EXPECTED_INSTRUMENTED_LINES_DATA, result)
195
196  def test_merge_covered_line_results(self):
197    data = copy.deepcopy(EXPECTED_INSTRUMENTED_LINES_DATA)
198    sancov_formatter.merge_covered_line_results(
199      data, COVERED_LINE_RESULTS)
200    self.assertEquals(EXPECTED_COVERED_LINES_DATA, data)
201
202  def test_split(self):
203    _, json_input = tempfile.mkstemp(prefix='tmp_coverage_test_split')
204    with open(json_input, 'w') as f:
205      json.dump(EXPECTED_COVERED_LINES_DATA, f)
206    output_dir = tempfile.mkdtemp(prefix='tmp_coverage_test_split')
207
208    try:
209      sancov_formatter.main([
210        'split',
211        '--json-input', json_input,
212        '--output-dir', output_dir,
213      ])
214
215      for file_name, expected_data in EXPECTED_SPLIT_FILES:
216        full_path = os.path.join(output_dir, file_name)
217        self.assertTrue(os.path.exists(full_path))
218        with open(full_path) as f:
219          self.assertEquals(expected_data, json.load(f))
220    finally:
221      os.remove(json_input)
222      shutil.rmtree(output_dir)
223