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

Unified Diff: client/tools/coverage.py

Issue 9837113: Move update.py to samples/swarm, remove a bunch of deprecated files for (Closed) Base URL: http://dart.googlecode.com/svn/branches/bleeding_edge/dart/
Patch Set: Created 8 years, 9 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 side-by-side diff with in-line comments
Download patch
« no previous file with comments | « client/tools/coverage.css ('k') | client/tools/jscoverage_wrapper.py » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: client/tools/coverage.py
===================================================================
--- client/tools/coverage.py (revision 5923)
+++ client/tools/coverage.py (working copy)
@@ -1,450 +0,0 @@
-# Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file
-# for details. All rights reserved. Use of this source code is governed by a
-# BSD-style license that can be found in the LICENSE file.
-
-#!/usr/bin/python2.4
-#
-
-"""Uses jscoverage to instrument a web unit test and runs it.
-
-Uses jscoverage to instrument a web unit test and extracts coverage statistics
-from running it in a headless webkit browser.
-"""
-
-import codecs
-import json
-import optparse
-import os
-import re
-import sourcemap # located in this same directory
-import subprocess
-import sys
-
-def InstrumentScript(script_name, src_dir, dst_dir, jscoveragecmd, verbose):
- """ Instruments a test using jscoverage.
-
- Args:
- script_name: the original javascript file containing the test.
- src_dir: the directory where the test is found.
- dst_dir: the directory where to place the instrumented test.
- jscoveragecmd: the fully-qualified command to run jscoverage.
-
- Returns:
- 0 if no errors where found.
- 1 if jscoverage gave errors.
- """
- SafeMakeDirs(dst_dir)
-
- PrintNewStep("instrumenting")
- src_file = os.path.join(src_dir, script_name)
- dst_file = os.path.join(dst_dir, script_name)
- if not os.path.exists(src_file):
- print "Script not found: " + src_file
- return 1
-
- if os.path.exists(dst_file):
- if os.path.getmtime(src_file) < os.path.getmtime(dst_file):
- # Skip running jscoverage if the input hasn't been updated
- return 0
-
- # Create the instrumented code using jscoverage:
- status, output, err = ExecuteCommand([sys.executable,
- 'tools/jscoverage_wrapper.py', jscoveragecmd, src_file, dst_dir], verbose)
- if status:
- print "ERROR: jscoverage had errors: "
- print output
- print err
- return 1
- return 0
-
-def SafeMakeDirs(dirname):
- """ Creates a directory and, if necessary its parent directories.
-
- This function will safely return if other concurrent jobs try to create the
- same directory.
- """
- if not os.path.exists(dirname):
- try:
- os.makedirs(dirname)
- except Exception:
- # this check allows invoking this script concurrently in many jobs
- if not os.path.exists(dirname):
- raise
-
-COVERAGE_TEST_TEMPLATE = '''
-<html>
-<head>
- <title> Coverage for %s </title>
-</head>
-<body>
- <h1> Running %s </h1>
- <script type="text/javascript" src="%s"></script>
- <script type="text/javascript" src="%s"></script>
-</body>
-</html>
-'''
-
-def CollectCoverageForTest(test, controller, script, drt, src_dir,
- dst_dir, sourcemapfile, result, verbose):
- """ Collect test coverage information from an instrumented test.
- Internally this function generates an html page for collecting the
- coverage results. This page is just like the HTML that runs a test, but
- instead of using 'test_controller.js' (which indicates whether tests pass
- or fail), we use 'coverage_controller.js' (which prints out coverage
- information).
-
- Args:
- test: the name of the test
- controller: path to coverage_controller.js
- script: actual javascript test (previously instrumented with jscoverage)
- drt: path to DumpRenderTree
- src_dir: directory where original 'script' is found.
- dst_dir: directory where the instrumented 'script' is found and where to
- generate the html file.
- sourcemapfile: sourcemap file associated with the script test. This is
- typically the source map for the original
- (non-instrumented code) since the jscoverage results are
- reported with respect to the original line numbers.
- result: a dictionary that wlll hold the results of this invocation. The
- dictionary keys are original dart file names, the value is a
- pair containing two sets of numbers: covered lines, and
- non-covered but executable lines. Note, some lines are not
- mentioned because they could be dropped during compilation (e.g.
- empty lines, comments, etc).
- This dictionary doesn't need to be empty, in which case this
- function will combine the results of this test with what
- previously was recoded in 'result'.
- verbose: show verbose progress mesages
- """
- html_contents = COVERAGE_TEST_TEMPLATE % (test, test, controller, script)
- html_output_file = os.path.join(dst_dir, test + '_coverage.html')
- with open(html_output_file, 'w') as f:
- f.write(html_contents)
-
- PrintNewStep("running")
- status, output, err = ExecuteCommand([drt, html_output_file], verbose)
- lines = output.split('\n')
- if len(lines) <= 2 or status:
- print "ERROR: can't obtain coverage for %s%s%s" % (
- RED_COLOR, html_output_file, NO_COLOR)
- return 1 if not status else status
-
- PrintNewStep("processing")
- # output is json, possibly split in multiple lines. We skip the first line
- # (DumpRenderTree shows a content-type line).
- coverage_res = ''.join(lines[1: lines.index("#EOF")])
- try:
- json_obj = json.loads(coverage_res)
- except Exception:
- print "ERROR: can't obtain coverage for %s%s%s" % (
- RED_COLOR, html_output_file, NO_COLOR)
- return 1
- _processResult(json_obj, script, src_dir, sourcemapfile, result)
-
-
-# Patterns used to detect declaration sites in the generated js code
-HOIST_FUNCTION_PATTERN = re.compile("^function ")
-MEMBER_PATTERN = re.compile(
- "[^ ]*prototype\.[^ ]*\$member = function\([^\)]*\){$")
-GETTER_PATTERN = re.compile(
- "[^ ]*prototype\.[^ ]*\$getter = function\([^\)]*\){$")
-SETTER_PATTERN = re.compile(
- "[^ ]*prototype\.[^ ]*\$setter = function\([^\)]*\){$")
-
-def _processResult(json_obj, js_file, src_dir, sourcemap_file, result):
- """ Process the result of a single test run. See 'CollectCoverageForTest' for
- more details.
-
- Args:
- json_obj: the json object that was exported by coverage_controller.js
- js_file: name of the script file
- src_dir: directory where the generated jsfile is located of the script
- sourcemap_file: sourcemap file associated with the script test.
- result: a dictionary that will hold the results of this invocation.
- """
- if js_file not in json_obj or len(json_obj) != 1:
- raise Exception("broken format in coverage result")
-
- lines_covered = json_obj[js_file]
- smap = sourcemap.parse(sourcemap_file)
- with open(os.path.join(src_dir, js_file), 'r') as f:
- js_code = f.readlines()
-
- for line in range(len(lines_covered)):
- original = smap.get_source_location(line, 1)
- if original:
- filename, srcline, srccolumn, srcid = original
- if filename not in result:
- result[filename] = (set(), set())
- if lines_covered[line] is not None:
- if lines_covered[line] > 0:
- # exclude the line if it looks like a generated class declaration or
- # a method declaration
- srccode = js_code[line - 1]
- if (HOIST_FUNCTION_PATTERN.match(srccode) is None
- and GETTER_PATTERN.match(srccode) is None
- and SETTER_PATTERN.match(srccode) is None
- and MEMBER_PATTERN.match(srccode) is None):
- result[filename][0].add(srcline)
- elif lines_covered[line] == 0:
- result[filename][1].add(srcline)
-
-# Global color constants
-GREEN_COLOR = "\033[32m"
-YELLOW_COLOR = "\033[33m"
-RED_COLOR = "\033[31m"
-NO_COLOR = "\033[0m"
-
-# Templates for the HTML output
-SUMMARY_TEMPLATE = '''<html>
- <head>
- <style>
- %s
- </style>
- </head>
- <body>
- <script type="application/javascript">
- %s
- </script>
- <script type="application/javascript">
- var files = []
- var code = {}
- var summary = {}
- %s
-
- render(files, code, summary);
- </script>
- </body>
-</html>
-'''
-
-# Template for exporting the coverage information data for each file
-FILE_TEMPLATE = '''
- // file %s
- files.push("%s");
- code["%s"] = [%s];
- summary["%s"] = [%s];'''
-
-# TODO(sigmund): increase these thresholds to 95 and 70
-GOOD_THRESHOLD = 80
-OK_THRESHOLD = 50
-
-def ReportSummary(outdir, result):
- """ Analyzes the results and produces an ASCII and an HTML summary report.
- Args:
- outdir: directory where to generate the HTML report
- result: a dictionary containing results of the coverage analysis. The
- dictionary maps file names to a pair of 2 sets of numbers. The
- first set contains executable covered lines, the second set
- contains executable non-covered lines. Any missing line is
- likely not translated into code (e.g. comments, an interface
- body) or translated into declarations (e.g. a getter
- declaration).
-
- """
- basepath = os.path.join(os.path.dirname(sys.argv[0]), "../")
- html_summary_lines = []
- print_summary_lines = []
- for key in result.keys():
- filepath = os.path.relpath(os.path.join(basepath, key))
- if (os.path.exists(filepath) and
- # exclude library directories under client/
- not "dom/generated" in filepath and
- not "json/" in filepath):
- linenum = 0
- realcode = 0
- total_covered = 0
- escaped_lines = []
- file_summary = []
- with codecs.open(filepath, 'r', 'utf-8') as f:
- lines = f.read()
- for line in lines.split('\n'):
- linenum += 1
- stripline = line.strip()
- if linenum in result[key][0]:
- total_covered += 1
- file_summary.append("1")
- realcode += 1
- elif linenum in result[key][1]:
- file_summary.append("0")
- realcode += 1
- else:
- file_summary.append("2")
-
- escaped_lines.append(
- '"' + line.replace('"','\\"').replace('<', '<" + "') + '"')
-
- percent = total_covered * 100 / realcode
-
- # append a pair, the first component is only used for sorting
- print_summary_lines.append((filepath, "%s%3d%% (%4d of %4d) - %s%s"
- % (GREEN_COLOR if (percent >= GOOD_THRESHOLD) else
- YELLOW_COLOR if (percent >= OK_THRESHOLD) else
- RED_COLOR,
- percent, total_covered, realcode,
- filepath,
- NO_COLOR)))
-
- html_summary_lines.append(FILE_TEMPLATE % (
- filepath, filepath,
- filepath, ",".join(escaped_lines),
- filepath, ",".join(file_summary)))
-
- print_summary_lines.sort()
- print "\n".join([s for (percent, s) in print_summary_lines])
-
- outfile = os.path.join(outdir, 'coverage_summary.html')
- resource_dir = os.path.abspath(os.path.dirname(sys.argv[0]))
-
- # Inject css and js code within the coverage page:
- with open(os.path.join(resource_dir, 'coverage.css'), 'r') as f:
- style = f.read()
- with open(os.path.join(resource_dir, 'show_coverage.js'), 'r') as f:
- jscode = f.read()
- with codecs.open(outfile, 'w', 'utf-8') as f:
- f.write(SUMMARY_TEMPLATE % (style, jscode, "\n".join(html_summary_lines)))
-
- print ("Detailed summary available at: %s" % outfile)
- return 0
-
-def ExecuteCommand(cmd, verbose):
- """Execute a command in a subprocess.
- """
- if verbose: print '\nExecuting: ' + ' '.join(cmd)
- pipe = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
- (output, err) = pipe.communicate()
- if pipe.returncode != 0 and verbose:
- print 'Execution failed: ' + output + '\n' + err
- print output
- print err
- return pipe.returncode, output, err
-
-def main():
- global total_tests
- parser = Flags()
- (options, args) = parser.parse_args()
- if not AreOptionsValid(options):
- parser.print_help()
- return 1
-
- # Create dir for instrumented code
- instrumented_dir = os.path.join(os.path.dirname(options.dir),
- os.path.basename(options.dir) + "_instrumented")
-
- # Determine the set of tests to execute
- prefix = options.tests_prefix
- tests = []
- if os.path.exists(prefix) and os.path.isdir(prefix):
- test_dir = prefix
- test_file_prefix = None
- else:
- test_dir = os.path.dirname(prefix)
- test_file_prefix = os.path.basename(prefix)
-
- for root, dirs, files in os.walk(test_dir):
- for f in files:
- if f.endswith('.app'):
- path = os.path.join(root, f)
- if test_file_prefix is None or path.startswith(prefix):
- tests.append(path)
-
- # Run tests and collect results
- result = dict()
- total_tests = len(tests)
- for test in tests:
- testname = test.replace("/", "_")
- script = testname + ".js"
- PrintNewTest(test)
- status = InstrumentScript(script, options.dir, instrumented_dir,
- options.jscoverage, options.verbose)
- if status:
- print "skipped %s " % script
- global last_line_length
- last_line_length = 0
- else:
- sourcemapfile = os.path.join(
- os.path.join(options.sourcemapdir, test), test + ".js.map")
- CollectCoverageForTest(
- testname, options.controller, script, options.drt,
- options.dir, instrumented_dir, sourcemapfile, result, options.verbose)
-
- print "" # end compact progress indication
- ReportSummary(options.dir, result)
- return 0
-
-# special functions and variables to print progress compactly
-current_test = 0
-current_test_name = ''
-total_tests = 0 # to be filled in later after parsing options
-last_line_length = 0
-
-def PrintLine(s):
- global last_line_length
- if last_line_length > 0:
- print "\r" + (" " * last_line_length) + "\r",
- last_line_length = len(s)
- print s,
- sys.stdout.flush()
-
-def PrintNewTest(test):
- global current_test
- global current_test_name
- global total_tests
- global GREEN_COLOR
- global NO_COLOR
- current_test += 1
- current_test_name = test
- PrintLine("%d of %d (%d%%): %s (%sstart%s)" % (current_test, total_tests,
- (100 * current_test / total_tests), test, GREEN_COLOR, NO_COLOR))
-
-def PrintNewStep(message):
- global current_test
- global current_test_name
- global total_tests
- global GREEN_COLOR
- global NO_COLOR
- PrintLine("%d of %d (%d%%): %s (%s%s%s)" % (current_test, total_tests,
- (100 * current_test / total_tests), current_test_name,
- GREEN_COLOR, message, NO_COLOR))
-
-
-def Flags():
- result = optparse.OptionParser()
- result.add_option("-d", "--dir",
- help="Directory where the compiled tests can be found.",
- default=None)
- result.add_option("--controller",
- help="Path to the coverage controller js file.",
- default=None)
- result.add_option("-v", "--verbose",
- help="Print a messages and progress",
- default=False,
- action="store_true")
- result.add_option("-t", "--tests_prefix",
- help="Prefix (typically a directory) where to crawl for test app files",
- type="string",
- action="store",
- default=None)
- result.add_option("--drt",
- help="The location for DumpRenderTree in the local file system",
- default=None)
- result.add_option("--jscoverage",
- help="The location for jscoverage in the local file system",
- default=None)
- result.add_option("--sourcemapdir",
- help="The location for sourcemap files in the local file system",
- default=None)
- result.set_usage(
- "coverage.py -d <cdart-output-dir> -t <test-dir> "
- "--drt=<path-to-DumpRenderTree> "
- "--jscoverage=<path-to-jscoverage> "
- "--sourcemapdir=<path-to-dir>"
- "[options]")
- return result
-
-def AreOptionsValid(options):
- return (options.dir and options.tests_prefix and options.drt
- and options.jscoverage and options.sourcemapdir)
-
-if __name__ == '__main__':
- sys.exit(main())
« no previous file with comments | « client/tools/coverage.css ('k') | client/tools/jscoverage_wrapper.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698