10 |
10 |
11 import coverage |
11 import coverage |
12 from coverage import env |
12 from coverage import env |
13 from coverage.backward import iitems |
13 from coverage.backward import iitems |
14 from coverage.files import flat_rootname |
14 from coverage.files import flat_rootname |
15 from coverage.misc import CoverageException, Hasher, isolate_module |
15 from coverage.misc import CoverageException, file_be_gone, Hasher, isolate_module |
16 from coverage.report import Reporter |
16 from coverage.report import Reporter |
17 from coverage.results import Numbers |
17 from coverage.results import Numbers |
18 from coverage.templite import Templite |
18 from coverage.templite import Templite |
19 |
19 |
20 os = isolate_module(os) |
20 os = isolate_module(os) |
76 # These files will be copied from the htmlfiles directory to the output |
76 # These files will be copied from the htmlfiles directory to the output |
77 # directory. |
77 # directory. |
78 STATIC_FILES = [ |
78 STATIC_FILES = [ |
79 ("style.css", ""), |
79 ("style.css", ""), |
80 ("jquery.min.js", "jquery"), |
80 ("jquery.min.js", "jquery"), |
81 ("jquery.debounce.min.js", "jquery-debounce"), |
81 ("jquery.ba-throttle-debounce.min.js", "jquery-throttle-debounce"), |
82 ("jquery.hotkeys.js", "jquery-hotkeys"), |
82 ("jquery.hotkeys.js", "jquery-hotkeys"), |
83 ("jquery.isonscreen.js", "jquery-isonscreen"), |
83 ("jquery.isonscreen.js", "jquery-isonscreen"), |
84 ("jquery.tablesorter.min.js", "jquery-tablesorter"), |
84 ("jquery.tablesorter.min.js", "jquery-tablesorter"), |
85 ("coverage_html.js", ""), |
85 ("coverage_html.js", ""), |
86 ("keybd_closed.png", ""), |
86 ("keybd_closed.png", ""), |
103 self.source_tmpl = Templite(read_data("pyfile.html"), self.template_globals) |
103 self.source_tmpl = Templite(read_data("pyfile.html"), self.template_globals) |
104 |
104 |
105 self.coverage = cov |
105 self.coverage = cov |
106 |
106 |
107 self.files = [] |
107 self.files = [] |
|
108 self.all_files_nums = [] |
108 self.has_arcs = self.coverage.data.has_arcs() |
109 self.has_arcs = self.coverage.data.has_arcs() |
109 self.status = HtmlStatus() |
110 self.status = HtmlStatus() |
110 self.extra_css = None |
111 self.extra_css = None |
111 self.totals = Numbers() |
112 self.totals = Numbers() |
112 self.time_stamp = datetime.datetime.now().strftime('%Y-%m-%d %H:%M') |
113 self.time_stamp = datetime.datetime.now().strftime('%Y-%m-%d %H:%M') |
135 self.extra_css = os.path.basename(self.config.extra_css) |
136 self.extra_css = os.path.basename(self.config.extra_css) |
136 |
137 |
137 # Process all the files. |
138 # Process all the files. |
138 self.report_files(self.html_file, morfs, self.config.html_dir) |
139 self.report_files(self.html_file, morfs, self.config.html_dir) |
139 |
140 |
140 if not self.files: |
141 if not self.all_files_nums: |
141 raise CoverageException("No data to report.") |
142 raise CoverageException("No data to report.") |
142 |
143 |
143 # Write the index file. |
144 # Write the index file. |
144 self.index_file() |
145 self.index_file() |
145 |
146 |
169 self.coverage.data.add_to_hash(fr.filename, m) |
170 self.coverage.data.add_to_hash(fr.filename, m) |
170 return m.hexdigest() |
171 return m.hexdigest() |
171 |
172 |
172 def html_file(self, fr, analysis): |
173 def html_file(self, fr, analysis): |
173 """Generate an HTML file for one source file.""" |
174 """Generate an HTML file for one source file.""" |
|
175 rootname = flat_rootname(fr.relative_filename()) |
|
176 html_filename = rootname + ".html" |
|
177 html_path = os.path.join(self.directory, html_filename) |
|
178 |
|
179 # Get the numbers for this file. |
|
180 nums = analysis.numbers |
|
181 self.all_files_nums.append(nums) |
|
182 |
|
183 if self.config.skip_covered: |
|
184 # Don't report on 100% files. |
|
185 no_missing_lines = (nums.n_missing == 0) |
|
186 no_missing_branches = (nums.n_partial_branches == 0) |
|
187 if no_missing_lines and no_missing_branches: |
|
188 # If there's an existing file, remove it. |
|
189 file_be_gone(html_path) |
|
190 return |
|
191 |
174 source = fr.source() |
192 source = fr.source() |
175 |
193 |
176 # Find out if the file on disk is already correct. |
194 # Find out if the file on disk is already correct. |
177 rootname = flat_rootname(fr.relative_filename()) |
|
178 this_hash = self.file_hash(source.encode('utf-8'), fr) |
195 this_hash = self.file_hash(source.encode('utf-8'), fr) |
179 that_hash = self.status.file_hash(rootname) |
196 that_hash = self.status.file_hash(rootname) |
180 if this_hash == that_hash: |
197 if this_hash == that_hash: |
181 # Nothing has changed to require the file to be reported again. |
198 # Nothing has changed to require the file to be reported again. |
182 self.files.append(self.status.index_info(rootname)) |
199 self.files.append(self.status.index_info(rootname)) |
183 return |
200 return |
184 |
201 |
185 self.status.set_file_hash(rootname, this_hash) |
202 self.status.set_file_hash(rootname, this_hash) |
186 |
|
187 # Get the numbers for this file. |
|
188 nums = analysis.numbers |
|
189 |
203 |
190 if self.has_arcs: |
204 if self.has_arcs: |
191 missing_branch_arcs = analysis.missing_branch_arcs() |
205 missing_branch_arcs = analysis.missing_branch_arcs() |
192 arcs_executed = analysis.arcs_executed() |
206 arcs_executed = analysis.arcs_executed() |
193 |
207 |
267 'nums': nums, |
281 'nums': nums, |
268 'lines': lines, |
282 'lines': lines, |
269 'time_stamp': self.time_stamp, |
283 'time_stamp': self.time_stamp, |
270 }) |
284 }) |
271 |
285 |
272 html_filename = rootname + ".html" |
|
273 html_path = os.path.join(self.directory, html_filename) |
|
274 write_html(html_path, html) |
286 write_html(html_path, html) |
275 |
287 |
276 # Save this file's information for the index file. |
288 # Save this file's information for the index file. |
277 index_info = { |
289 index_info = { |
278 'nums': nums, |
290 'nums': nums, |
284 |
296 |
285 def index_file(self): |
297 def index_file(self): |
286 """Write the index.html file for this report.""" |
298 """Write the index.html file for this report.""" |
287 index_tmpl = Templite(read_data("index.html"), self.template_globals) |
299 index_tmpl = Templite(read_data("index.html"), self.template_globals) |
288 |
300 |
289 self.totals = sum(f['nums'] for f in self.files) |
301 self.totals = sum(self.all_files_nums) |
290 |
302 |
291 html = index_tmpl.render({ |
303 html = index_tmpl.render({ |
292 'has_arcs': self.has_arcs, |
304 'has_arcs': self.has_arcs, |
293 'extra_css': self.extra_css, |
305 'extra_css': self.extra_css, |
294 'files': self.files, |
306 'files': self.files, |
382 'version': coverage.__version__, |
394 'version': coverage.__version__, |
383 'settings': self.settings, |
395 'settings': self.settings, |
384 'files': files, |
396 'files': files, |
385 } |
397 } |
386 with open(status_file, "w") as fout: |
398 with open(status_file, "w") as fout: |
387 json.dump(status, fout) |
399 json.dump(status, fout, separators=(',', ':')) |
388 |
400 |
389 # Older versions of ShiningPanda look for the old name, status.dat. |
401 # Older versions of ShiningPanda look for the old name, status.dat. |
390 # Accomodate them if we are running under Jenkins. |
402 # Accommodate them if we are running under Jenkins. |
391 # https://issues.jenkins-ci.org/browse/JENKINS-28428 |
403 # https://issues.jenkins-ci.org/browse/JENKINS-28428 |
392 if "JENKINS_URL" in os.environ: |
404 if "JENKINS_URL" in os.environ: |
393 with open(os.path.join(directory, "status.dat"), "w") as dat: |
405 with open(os.path.join(directory, "status.dat"), "w") as dat: |
394 dat.write("https://issues.jenkins-ci.org/browse/JENKINS-28428\n") |
406 dat.write("https://issues.jenkins-ci.org/browse/JENKINS-28428\n") |
395 |
407 |