Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(450)

Side by Side Diff: third_party/coverage-3.6/coverage/results.py

Issue 14988009: First cut of testing infrastructure for recipes. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/tools/build
Patch Set: nitfixen Created 7 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
(Empty)
1 """Results of coverage measurement."""
2
3 import os
4
5 from coverage.backward import iitems, set, sorted # pylint: disable=W0622
6 from coverage.misc import format_lines, join_regex, NoSource
7 from coverage.parser import CodeParser
8
9
10 class Analysis(object):
11 """The results of analyzing a code unit."""
12
13 def __init__(self, cov, code_unit):
14 self.coverage = cov
15 self.code_unit = code_unit
16
17 self.filename = self.code_unit.filename
18 ext = os.path.splitext(self.filename)[1]
19 source = None
20 if ext == '.py':
21 if not os.path.exists(self.filename):
22 source = self.coverage.file_locator.get_zip_data(self.filename)
23 if not source:
24 raise NoSource("No source for code: '%s'" % self.filename)
25
26 self.parser = CodeParser(
27 text=source, filename=self.filename,
28 exclude=self.coverage._exclude_regex('exclude')
29 )
30 self.statements, self.excluded = self.parser.parse_source()
31
32 # Identify missing statements.
33 executed = self.coverage.data.executed_lines(self.filename)
34 exec1 = self.parser.first_lines(executed)
35 self.missing = sorted(set(self.statements) - set(exec1))
36
37 if self.coverage.data.has_arcs():
38 self.no_branch = self.parser.lines_matching(
39 join_regex(self.coverage.config.partial_list),
40 join_regex(self.coverage.config.partial_always_list)
41 )
42 n_branches = self.total_branches()
43 mba = self.missing_branch_arcs()
44 n_partial_branches = sum(
45 [len(v) for k,v in iitems(mba) if k not in self.missing]
46 )
47 n_missing_branches = sum([len(v) for k,v in iitems(mba)])
48 else:
49 n_branches = n_partial_branches = n_missing_branches = 0
50 self.no_branch = set()
51
52 self.numbers = Numbers(
53 n_files=1,
54 n_statements=len(self.statements),
55 n_excluded=len(self.excluded),
56 n_missing=len(self.missing),
57 n_branches=n_branches,
58 n_partial_branches=n_partial_branches,
59 n_missing_branches=n_missing_branches,
60 )
61
62 def missing_formatted(self):
63 """The missing line numbers, formatted nicely.
64
65 Returns a string like "1-2, 5-11, 13-14".
66
67 """
68 return format_lines(self.statements, self.missing)
69
70 def has_arcs(self):
71 """Were arcs measured in this result?"""
72 return self.coverage.data.has_arcs()
73
74 def arc_possibilities(self):
75 """Returns a sorted list of the arcs in the code."""
76 arcs = self.parser.arcs()
77 return arcs
78
79 def arcs_executed(self):
80 """Returns a sorted list of the arcs actually executed in the code."""
81 executed = self.coverage.data.executed_arcs(self.filename)
82 m2fl = self.parser.first_line
83 executed = [(m2fl(l1), m2fl(l2)) for (l1,l2) in executed]
84 return sorted(executed)
85
86 def arcs_missing(self):
87 """Returns a sorted list of the arcs in the code not executed."""
88 possible = self.arc_possibilities()
89 executed = self.arcs_executed()
90 missing = [
91 p for p in possible
92 if p not in executed
93 and p[0] not in self.no_branch
94 ]
95 return sorted(missing)
96
97 def arcs_unpredicted(self):
98 """Returns a sorted list of the executed arcs missing from the code."""
99 possible = self.arc_possibilities()
100 executed = self.arcs_executed()
101 # Exclude arcs here which connect a line to itself. They can occur
102 # in executed data in some cases. This is where they can cause
103 # trouble, and here is where it's the least burden to remove them.
104 unpredicted = [
105 e for e in executed
106 if e not in possible
107 and e[0] != e[1]
108 ]
109 return sorted(unpredicted)
110
111 def branch_lines(self):
112 """Returns a list of line numbers that have more than one exit."""
113 exit_counts = self.parser.exit_counts()
114 return [l1 for l1,count in iitems(exit_counts) if count > 1]
115
116 def total_branches(self):
117 """How many total branches are there?"""
118 exit_counts = self.parser.exit_counts()
119 return sum([count for count in exit_counts.values() if count > 1])
120
121 def missing_branch_arcs(self):
122 """Return arcs that weren't executed from branch lines.
123
124 Returns {l1:[l2a,l2b,...], ...}
125
126 """
127 missing = self.arcs_missing()
128 branch_lines = set(self.branch_lines())
129 mba = {}
130 for l1, l2 in missing:
131 if l1 in branch_lines:
132 if l1 not in mba:
133 mba[l1] = []
134 mba[l1].append(l2)
135 return mba
136
137 def branch_stats(self):
138 """Get stats about branches.
139
140 Returns a dict mapping line numbers to a tuple:
141 (total_exits, taken_exits).
142 """
143
144 exit_counts = self.parser.exit_counts()
145 missing_arcs = self.missing_branch_arcs()
146 stats = {}
147 for lnum in self.branch_lines():
148 exits = exit_counts[lnum]
149 try:
150 missing = len(missing_arcs[lnum])
151 except KeyError:
152 missing = 0
153 stats[lnum] = (exits, exits - missing)
154 return stats
155
156
157 class Numbers(object):
158 """The numerical results of measuring coverage.
159
160 This holds the basic statistics from `Analysis`, and is used to roll
161 up statistics across files.
162
163 """
164 # A global to determine the precision on coverage percentages, the number
165 # of decimal places.
166 _precision = 0
167 _near0 = 1.0 # These will change when _precision is changed.
168 _near100 = 99.0
169
170 def __init__(self, n_files=0, n_statements=0, n_excluded=0, n_missing=0,
171 n_branches=0, n_partial_branches=0, n_missing_branches=0
172 ):
173 self.n_files = n_files
174 self.n_statements = n_statements
175 self.n_excluded = n_excluded
176 self.n_missing = n_missing
177 self.n_branches = n_branches
178 self.n_partial_branches = n_partial_branches
179 self.n_missing_branches = n_missing_branches
180
181 def set_precision(cls, precision):
182 """Set the number of decimal places used to report percentages."""
183 assert 0 <= precision < 10
184 cls._precision = precision
185 cls._near0 = 1.0 / 10**precision
186 cls._near100 = 100.0 - cls._near0
187 set_precision = classmethod(set_precision)
188
189 def _get_n_executed(self):
190 """Returns the number of executed statements."""
191 return self.n_statements - self.n_missing
192 n_executed = property(_get_n_executed)
193
194 def _get_n_executed_branches(self):
195 """Returns the number of executed branches."""
196 return self.n_branches - self.n_missing_branches
197 n_executed_branches = property(_get_n_executed_branches)
198
199 def _get_pc_covered(self):
200 """Returns a single percentage value for coverage."""
201 if self.n_statements > 0:
202 pc_cov = (100.0 * (self.n_executed + self.n_executed_branches) /
203 (self.n_statements + self.n_branches))
204 else:
205 pc_cov = 100.0
206 return pc_cov
207 pc_covered = property(_get_pc_covered)
208
209 def _get_pc_covered_str(self):
210 """Returns the percent covered, as a string, without a percent sign.
211
212 Note that "0" is only returned when the value is truly zero, and "100"
213 is only returned when the value is truly 100. Rounding can never
214 result in either "0" or "100".
215
216 """
217 pc = self.pc_covered
218 if 0 < pc < self._near0:
219 pc = self._near0
220 elif self._near100 < pc < 100:
221 pc = self._near100
222 else:
223 pc = round(pc, self._precision)
224 return "%.*f" % (self._precision, pc)
225 pc_covered_str = property(_get_pc_covered_str)
226
227 def pc_str_width(cls):
228 """How many characters wide can pc_covered_str be?"""
229 width = 3 # "100"
230 if cls._precision > 0:
231 width += 1 + cls._precision
232 return width
233 pc_str_width = classmethod(pc_str_width)
234
235 def __add__(self, other):
236 nums = Numbers()
237 nums.n_files = self.n_files + other.n_files
238 nums.n_statements = self.n_statements + other.n_statements
239 nums.n_excluded = self.n_excluded + other.n_excluded
240 nums.n_missing = self.n_missing + other.n_missing
241 nums.n_branches = self.n_branches + other.n_branches
242 nums.n_partial_branches = (
243 self.n_partial_branches + other.n_partial_branches
244 )
245 nums.n_missing_branches = (
246 self.n_missing_branches + other.n_missing_branches
247 )
248 return nums
249
250 def __radd__(self, other):
251 # Implementing 0+Numbers allows us to sum() a list of Numbers.
252 if other == 0:
253 return self
254 return NotImplemented
OLDNEW
« no previous file with comments | « third_party/coverage-3.6/coverage/report.py ('k') | third_party/coverage-3.6/coverage/summary.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698