istanbul-0.4.5/000077500000000000000000000000001275640326200133505ustar00rootroot00000000000000istanbul-0.4.5/.jshintignore000066400000000000000000000001451275640326200160540ustar00rootroot00000000000000lib/assets/vendor test/cli/sample-project test/cli/sample-project-link test/browser/support/vendor istanbul-0.4.5/.jshintrc000066400000000000000000000017721275640326200152040ustar00rootroot00000000000000{ "bitwise": true, "camelcase": false, "curly": true, "eqeqeq": true, "forin": true, "freeze": true, "immed": true, "latedef": true, "maxlen": 150, "newcap": true, "noarg": true, "nonbsp": true, "nonew": true, "plusplus": true, "trailing": true, "undef": true, "unused": true, "strict": false, "asi": false, "boss": false, "debug": false, "eqnull": false, "esnext": false, "evil": false, "expr": false, "funcscope": false, "globalstrict": false, "iterator": false, "lastsemic": false, "laxbreak": true, "laxcomma": false, "loopfunc": false, "multistr": false, "notypeof": false, "proto": false, "scripturl": false, "smarttabs": false, "shadow": false, "sub": false, "supernew": false, "validthis": false, "noyield": false, "browser": true, "node": true, "nomen": false, "onevar": true, "passfail": false, "white": false } istanbul-0.4.5/.travis.yml000066400000000000000000000004161275640326200154620ustar00rootroot00000000000000language: node_js node_js: - "0.10" - "0.12" sudo: false branches: except: - gh-pages script: - npm test --cover after_script: - if [[ `node --version` == *v0.12* ]]; then cat ./build/coverage/lcov.info | ./node_modules/coveralls/bin/coveralls.js; fi istanbul-0.4.5/CHANGELOG.md000066400000000000000000000345401275640326200151670ustar00rootroot00000000000000Changelog ---------
0.4.5
  • log filename when file fails to parse using esprima, thanks to @djorg83
  • swap fileset for glob (security fix), thanks to @popomore and @graingert
0.4.4
  • Handle ExportNamedDeclaration, thanks to @VictoryStick
  • Use tmpdir setting in temp store, thanks to @inversion
  • Set "medium" coverage CSS color scheme to yellow, thanks to @JamesMGreene
  • use os.tmpdir() instead of os.tmpDir(), thanks to @ChALkeR
0.4.3
  • Create new handlebars instance for the HTML report, thanks to @doowb
  • MetaProperty support thanks to @steve-gray
  • Use ansi colors from 16-color palette for better console support, thanks to @jtangelder
  • Misc doc/ css fixes thanks to @pra85, @abejfehr
0.4.2 Fix confusing error message on check-coverage failures, thanks to @isaacs/td>
v0.4.1
  • Update esprima to 2.7.x, thanks to @ariya
  • Make table header clickable in HTML report, thanks to @iphands
  • Fix strict mode issues thanks to @kpdecker
  • update ignore code example for UMD, thanks to @pgurnee
  • misc build fixes, no user visible changes, thanks to @ariya
v0.4.0
  • HTML report design, thanks a bunch to @tmcw
  • "loading config file" message on the console is now tied to the verbose state, thanks @asa-git
  • Add the `l` property to documentation, thanks @kitsonk
v0.3.21
  • Updated dependencies to the latest
v0.3.20
  • Fix broken es6 `super` support, thanks @sterlinghw
  • Improve readability via better lineHeight, thanks @dhoko
  • Adding ability to set custom block name in teamcity report, thanks @aryelu
  • Replaced deprecated util.puts with console.log, thanks @arty-name
v0.3.19 Fix instrumenter for multiple blank array positions, thanks @alexdunphy
v0.3.18 Upgrade esprima, get support for more ES6 features
v0.3.17 Upgrade esprima, get correct for-of support
v0.3.16
  • upgrades to filset and async modules, thanks to @roderickhsiao, @popomore
  • updated text reporter so that it displays a list of the lines missing coverage, thanks @bcoe
v0.3.15
  • Fix #375: add nodir option to exclude directory for *.js matcher thanks to @yurenju
  • Fix #362: When setting up the `reportDir` add it to `reporter.dir`
  • Fixes #238 (added a poorman's clone)
  • Incrementing hits on ignored statements implemented
  • `a:visited color: #777` (a nice gray color)
v0.3.14 Add text-lcov report format to emit lcov to console, thanks to @bcoe
v0.3.13 Fix #339
v0.3.12 Allow other-than-dot-js files to be hooked, thanks to @sethpollack
v0.3.11 Avoid modification of global objects, thanks to @dominykas
v0.3.10 Update escodegen to 1.6.x and add browser download script
v0.3.9
  • Merge harmony branch and start adding ES6 features to istanbul
  • Arrow functions are the only feature of interest now
  • `for-of` and `yield` support exist but not present in mainline esprima yet
v0.3.8
  • Fail check coverage command when no coverage files found, thanks to @nexus-uw
  • handle relative paths in check-coverage, thanks to @dragn
  • support explicit includes for cover, thanks to @tonylukasavage
v0.3.7 Fix asset paths on windows, thanks to @juangabreil
v0.3.6
  • Update to Esprima 2.0
  • Remove YUI dependency and provide custom sort code. No network access needed for HTML report view
  • use supports-color module to colorize output, thanks to @gustavnikolaj
  • Fix tests to work on Windows, thanks to @dougwilson
  • Docs: "Instrument code" API example correction thanks to @robatron
  • Extracted embedded CSS and JavaScript and made them external files, thanks to @booleangate
v0.3.5

Merge #275 - `--include-all-sources` option. Thanks @gustavnikolaj

The `--preload-sources` option is now deprecated and superseded by the `--include-all-sources` option instead. This provides a better coverage representation of the code that has not been included for testing.

v0.3.4 Merge #219 - Support reporting within symlink/junction. Thanks to @dougwilson
v0.3.3 Merge #268 - per file coverage enforcement. Thanks to @ryan-roemer
v0.3.2 Republish 0.3.1 because of bad shasum
v0.3.1 Fixes #249
v0.3.0 The *reports* release. **Potentially backwards-incompatible** if you are using undocumented features or custom report implementations.
  • Change report command line to support multiple reports, add back-compat processing with warnings
  • Enable `report` command to read report list from config, thanks to @piuccio
  • Support multiple reports for `cover` and `report` commands
  • Support per-report config options in configuration file
  • Turn reports into event emitters so they can signal `done`
  • Add `Reporter` class to be able to generate multiple reports
  • Add a bunch of API docs, refactor README
v0.2.16Make YUI links https-always since relative links break local filesystem use-case
v0.2.15make link protocols relative so they don't break on https connections (thanks to @yasyf)
v0.2.14Fix hook to deal with non-string/ missing filenames (thanks to @jason0x43), update dependencies
v0.2.13Add `--preload-sources` option to `cover` command to make code not required by tests to appear in the coverage report.
v0.2.12Text summary as valid markdown, thanks to @smikes
v0.2.11Allow source map generation, thanks to @jason0x43
v0.2.10Add flag to handle sigints and dump coverage, thanks to @samccone
v0.2.9Fix #202
v0.2.8Upgrade esprima
v0.2.7
  • Upgrade esprima
  • Misc jshint fixes
v0.2.6
  • Revert bad commit for tree summarizer
v0.2.5
  • Add clover report, thanks to @bixdeng, @mpderbec
  • Fix cobertura report bug for relative paths, thanks to @jxiaodev
  • Run self-coverage on tests always
  • Fix tree summarizer when relative paths are involved, thanks to @Swatinem
v0.2.4
  • Fix line-split algo to handle Mac lin separators, thanks to @asifrc
  • Update README for quick intro to ignoring code for coverage, thanks to @gergelyke
v0.2.3
  • Add YAML config file. `istanbul help config` has more details
  • Support custom reporting thresholds using the `watermarks` section of the config file
v0.2.2update escodegen, handlebars and resolve dependency versions
v0.2.1
  • Add ability to skip branches and other hard-to-test code using comments. See the doc for more details
  • Turn `util.error` into `console.error` for node 0.11 compatibility, thanks to @pornel
v0.2.0
  • Add --preserve-comments to instrumenter options, thanks to @arikon
  • Support 'use strict;' in file scope, thanks to @pornel
Up minor version due to the new way in which the global object is accessed. This _should_ be backwards-compatible but has not been tested in the wild.
v0.1.46Fix #114
v0.1.45Add teamcity reporter, thanks to @chrisgladd
v0.1.44Fix inconsistency in processing empty switch with latest esprima, up deps
v0.1.43Add colors to text report thanks to @runk
v0.1.42fix #78: embed source regression introduced in v0.1.38. Fix broken test for this
v0.1.41add json report to dump coverage object for certain use cases
v0.1.40forward sourceStore from lcov to html report, pull request by @vojtajina
v0.1.39add tag to cobertura report, pull request by @jhansche
v0.1.38
  • factor out AST instrumentation into own instrumentASTSync method
  • always set function declaration coverage stats to 1 since every such declaration is "executed" exactly one time by the compiler
v0.1.37--complete-copy flag contrib from @kami, correct strict mode semantics for instrumented functions
v0.1.36real quiet when --print=none specified, add repo URL to package.json, add contributors
v0.1.35accept cobertura contrib from @nbrownus, fix #52
v0.1.34fix async reporting, update dependencies, accept html cleanup contrib from @mathiasbynens
v0.1.33initialize global coverage object before running tests to workaround mocha leak detection
v0.1.32Fix for null nodes in array expressions, add @unindented as contributor
v0.1.31Misc internal fixes and test changes
v0.1.30Write standard blurbs ("writing coverage object..." etc.) to stderr rather than stdout
v0.1.29Allow --post-require-hook to be a module that can be `require`-d
v0.1.28Add --post-require-hook switch to support use-cases similar to the YUI loader
v0.1.27Add --hook-run-in-context switch to support RequireJS modules. Thanks to @millermedeiros for the pull request
v0.1.26Add support for minimum uncovered unit for check-coverage. Fixes #25
v0.1.25Allow for relative paths in the YUI loader hook
v0.1.24Add lcov summaries. Fixes issue #20
v0.1.23Add ability to save a baseline coverage file for the instrument command. Fixes issue #19
v0.1.22Add signature attribute to cobertura method tags to fix NPE by the Hudson publisher
v0.1.21Add cobertura XML report format; exprimental for now
v0.1.20Fix HTML/ lcov report interface to be more customizable for middleware needs
v0.1.19make all hooking non-destructive in that already loaded modules are never reloaded. Add self-test mode so that already loaded istanbul modules can be unloaded prior to hooking.
v0.1.18Add option to hook in non-destructive mode; i.e. the require cache is not unloaded when hooking
v0.1.17Export some more objects; undocumented for now
v0.1.16Fix npm keywords for istanbul which expects an array of strings but was being fed a single string with keywords instead
v0.1.15Add the 'check-coverage' command so that Istanbul can be used as a posttest script to enforce minimum coverage
v0.1.14Expose the experimental YUI load hook in the interface
v0.1.13Internal jshint cleanup, no features or fixes
v0.1.12Give npm the README that was getting inadvertently excluded
v0.1.11Merge pull request #14 for HTML tweaks. Thanks @davglass. Add @davglass and @nowamasa as contributors in `package.json`
v0.1.10Fix to issue #12. Do not install `uncaughtException` handler and pass input error back to CLI using a callback as opposed to throwing.
v0.1.9Attempt to create reporting directory again just before writing coverage in addition to initial creation
v0.1.8Fix issue #11.
v0.1.7Add text summary and detailed reporting available as --print [summary|detail|both|none]. summary is the default if nothing specified.
v0.1.6Handle backslashes in the file path correctly in emitted code. Fixes #9. Thanks to @nowamasa for bug report and fix
v0.1.5make object-utils.js work on a browser as-is
v0.1.4partial fix for issue #4; add titles to missing coverage spans, remove negative margin for missing if/else indicators
v0.1.3Set the environment variable running_under_istanbul to 1 when that is the case. This allows test runners that use istanbul as a library to back off on using it when set.
v0.1.2HTML reporting cosmetics. Reports now show syntax-colored JS using `prettify`. Summary tables no longer wrap in awkward places.
v0.1.1Fixes issue #1. HTML reports use sources embedded inside the file coverage objects if found rather than reading from the filesystem
v0.1.0Initial version
istanbul-0.4.5/LICENSE000066400000000000000000000027131275640326200143600ustar00rootroot00000000000000Copyright 2012 Yahoo! Inc. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the Yahoo! Inc. nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL YAHOO! INC. BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. istanbul-0.4.5/README.md000066400000000000000000000265531275640326200146420ustar00rootroot00000000000000## Istanbul - a JS code coverage tool written in JS [![Build Status](https://secure.travis-ci.org/gotwarlost/istanbul.svg?branch=master)](http://travis-ci.org/gotwarlost/istanbul) [![Dependency Status](https://gemnasium.com/gotwarlost/istanbul.svg)](https://gemnasium.com/gotwarlost/istanbul) [![Coverage Status](https://img.shields.io/coveralls/gotwarlost/istanbul.svg)](https://coveralls.io/r/gotwarlost/istanbul?branch=master) [![bitHound Score](https://www.bithound.io/github/gotwarlost/istanbul/badges/score.svg)](https://www.bithound.io/github/gotwarlost/istanbul) [![NPM](https://nodei.co/npm/istanbul.png?downloads=true)](https://nodei.co/npm/istanbul/) **New** `v0.4.0` now has beautiful HTML reports. Props to Tom MacWright @tmcw for a fantastic job! * [Features and use cases](#features) * [Getting started and configuration](#getting-started) * [Usage on Windows](#usage-on-windows) * [The command line](#the-command-line) * [Ignoring code for coverage](#ignoring-code-for-coverage) * [API](#api) * [Changelog](https://github.com/gotwarlost/istanbul/blob/master/CHANGELOG.md) * [License and credits](#license) ### Features * All-javascript instrumentation library that tracks **statement, branch, and function coverage**. * **Module loader hooks** to instrument code on the fly * **Command line tools** to run node unit tests "with coverage turned on" and no cooperation whatsoever from the test runner * Multiple report formats: **HTML**, **LCOV**, **Cobertura** and more. * Ability to use as [middleware](https://github.com/gotwarlost/istanbul-middleware) when serving JS files that need to be tested on the browser. * Can be used on the **command line** as well as a **library** * Based on the awesome `esprima` parser and the equally awesome `escodegen` code generator * Well-tested on node (prev, current and next versions) and the browser (instrumentation library only) ### Use cases Supports the following use cases and more * transparent coverage of nodejs unit tests * instrumentation/ reporting of files in batch mode for browser tests * Server side code coverage for nodejs by embedding it as [custom middleware](https://github.com/gotwarlost/istanbul-middleware) ### Getting started $ npm install -g istanbul The best way to see it in action is to run node unit tests. Say you have a test script `test.js` that runs all tests for your node project without coverage. Simply: $ cd /path/to/your/source/root $ istanbul cover test.js and this should produce a `coverage.json`, `lcov.info` and `lcov-report/*html` under `./coverage` Sample of code coverage reports produced by this tool (for this tool!): [HTML reports](http://gotwarlost.github.com/istanbul/public/coverage/lcov-report/index.html) ### Usage on Windows Istanbul assumes that the `command` passed to it is a JS file (e.g. Jasmine, vows etc.), this is however not true on Windows where npm wrap bin files in a `.cmd` file. Since Istanbul can not parse `.cmd` files you need to reference the bin file manually. Here is an example using Jasmine 2: istanbul cover node_modules\jasmine\bin\jasmine.js In order to use this cross platform (e.i. Linux, Mac and Windows), you can insert the above line into the script object in your package.json file but with normal slash. "scripts": { "test": "istanbul cover node_modules/jasmine/bin/jasmine.js" } ### Configuring Drop a `.istanbul.yml` file at the top of the source tree to configure istanbul. `istanbul help config` tells you more about the config file format. ### The command line $ istanbul help gives you detailed help on all commands. ``` Usage: istanbul help config | `config` provides help with istanbul configuration Available commands are: check-coverage checks overall/per-file coverage against thresholds from coverage JSON files. Exits 1 if thresholds are not met, 0 otherwise cover transparently adds coverage information to a node command. Saves coverage.json and reports at the end of execution help shows help instrument instruments a file or a directory tree and writes the instrumented code to the desired output location report writes reports for coverage JSON objects produced in a previous run test cover a node command only when npm_config_coverage is set. Use in an `npm test` script for conditional coverage Command names can be abbreviated as long as the abbreviation is unambiguous ``` To get detailed help for a command and what command-line options it supports, run: istanbul help (Most of the command line options are not covered in this document.) #### The `cover` command $ istanbul cover my-test-script.js -- my test args # note the -- between the command name and the arguments to be passed The `cover` command can be used to get a coverage object and reports for any arbitrary node script. By default, coverage information is written under `./coverage` - this can be changed using command-line options. The `cover` command can also be passed an optional `--handle-sigint` flag to enable writing reports when a user triggers a manual SIGINT of the process that is being covered. This can be useful when you are generating coverage for a long lived process. #### The `test` command The `test` command has almost the same behavior as the `cover` command, except that it skips coverage unless the `npm_config_coverage` environment variable is set. **This command is deprecated** since the latest versions of npm do not seem to set the `npm_config_coverage` variable. #### The `instrument` command Instruments a single JS file or an entire directory tree and produces an output directory tree with instrumented code. This should not be required for running node unit tests but is useful for tests to be run on the browser. #### The `report` command Writes reports using `coverage*.json` files as the source of coverage information. Reports are available in multiple formats and can be individually configured using the istanbul config file. See `istanbul help report` for more details. #### The `check-coverage` command Checks the coverage of statements, functions, branches, and lines against the provided thresholds. Positive thresholds are taken to be the minimum percentage required and negative numbers are taken to be the number of uncovered entities allowed. ### Ignoring code for coverage * Skip an `if` or `else` path with `/* istanbul ignore if */` or `/* istanbul ignore else */` respectively. * For all other cases, skip the next 'thing' in the source with: `/* istanbul ignore next */` See [ignoring-code-for-coverage.md](ignoring-code-for-coverage.md) for the spec. ### API All the features of istanbul can be accessed as a library. #### Instrument code ```javascript var istanbul = require('istanbul'); var instrumenter = new istanbul.Instrumenter(); var generatedCode = instrumenter.instrumentSync('function meaningOfLife() { return 42; }', 'filename.js'); ``` #### Generate reports given a bunch of coverage JSON objects ```javascript var istanbul = require('istanbul'), collector = new istanbul.Collector(), reporter = new istanbul.Reporter(), sync = false; collector.add(obj1); collector.add(obj2); //etc. reporter.add('text'); reporter.addAll([ 'lcov', 'clover' ]); reporter.write(collector, sync, function () { console.log('All reports generated'); }); ``` For the gory details consult the [public API](http://gotwarlost.github.com/istanbul/public/apidocs/index.html) ### Multiple Process Usage Istanbul can be used in a multiple process environment by running each process with Istanbul, writing a unique coverage file for each process, and combining the results when generating reports. The method used to perform this will depend on the process forking API used. For example when using the [cluster module](http://nodejs.org/api/cluster.html) you must setup the master to start child processes with Istanbul coverage, disable reporting, and output coverage files that include the PID in the filename. Before each run you may need to clear out the coverage data directory. ```javascript if(cluster.isMaster) { // setup cluster if running with istanbul coverage if(process.env.running_under_istanbul) { // use coverage for forked process // disabled reporting and output for child process // enable pid in child process coverage filename cluster.setupMaster({ exec: './node_modules/.bin/istanbul', args: [ 'cover', '--report', 'none', '--print', 'none', '--include-pid', process.argv[1], '--'].concat(process.argv.slice(2)) }); } // ... // ... cluster.fork(); // ... } else { // ... worker code } ``` ### Coverage.json For details on the format of the coverage.json object, [see here](./coverage.json.md). ### License istanbul is licensed under the [BSD License](http://github.com/gotwarlost/istanbul/raw/master/LICENSE). ### Third-party libraries The following third-party libraries are used by this module: * abbrev: https://github.com/isaacs/abbrev-js - to handle command abbreviations * async: https://github.com/caolan/async - for parallel instrumentation of files * escodegen: https://github.com/Constellation/escodegen - for JS code generation * esprima: https://github.com/ariya/esprima - for JS parsing * glob: https://github.com/isaacs/node-glob - for loading and matching path expressions * handlebars: https://github.com/wycats/handlebars.js/ - for report template expansion * js-yaml: https://github.com/nodeca/js-yaml - for YAML config file load * mkdirp: https://github.com/substack/node-mkdirp - to create output directories * nodeunit: https://github.com/caolan/nodeunit - dev dependency for unit tests * nopt: https://github.com/isaacs/nopt - for option parsing * once: https://github.com/isaacs/once - to ensure callbacks are called once * resolve: https://github.com/substack/node-resolve - for resolving a post-require hook module name into its main file. * rimraf - https://github.com/isaacs/rimraf - dev dependency for unit tests * which: https://github.com/isaacs/node-which - to resolve a node command to a file for the `cover` command * wordwrap: https://github.com/substack/node-wordwrap - for prettier help * prettify: http://code.google.com/p/google-code-prettify/ - for syntax colored HTML reports. Files checked in under `lib/vendor/` ### Inspired by * YUI test coverage - https://github.com/yui/yuitest - the grand-daddy of JS coverage tools. Istanbul has been specifically designed to offer an alternative to this library with an easy migration path. * cover: https://github.com/itay/node-cover - the inspiration for the `cover` command, modeled after the `run` command in that tool. The coverage methodology used by istanbul is quite different, however ### Shout out to * [mfncooper](https://github.com/mfncooper) - for great brainstorming discussions * [reid](https://github.com/reid), [davglass](https://github.com/davglass), the YUI dudes, for interesting conversations, encouragement, support and gentle pressure to get it done :) ### Why the funky name? Since all the good ones are taken. Comes from the loose association of ideas across coverage, carpet-area coverage, the country that makes good carpets and so on... istanbul-0.4.5/coverage.json.md000066400000000000000000000057231275640326200164440ustar00rootroot00000000000000# Format of coverage.json `coverage.json` contains a report object, which is a hash where keys are file names (absolute paths), and values are coverage data for that file (the result of `json.stringify(collector.fileCoverageFor(filename))`) Each entry consists of: * `path` - The path to the file. This is an absolute path, and should be the same as the key in the report object. * `s` - Hash of statement counts, where keys as statement IDs. * `b` - Hash of branch counts, where keys are branch IDs and values are arrays of counts. For an if statement, the value would have two counts; one for the if, and one for the else. Switch statements would have an array of values for each case. * `f` - Hash of function counts, where keys are function IDs. * `fnMap` - Hash of functions where keys are function IDs, and values are `{name, line, loc, skip}`, where `name` is the name of the function, `line` is the line the function is declared on, and `loc` is the `Location` of the function declaration (just the declaration, not the entire function body - see 'Location Objects' below.) If `skip` is present and true, then this indicates that this function was ignored by a `### instabul ignore ... ###` pragma. Note that if a function is not ignored the `skip` field will be missing entirely. * `statementMap` - Hash where keys are statement IDs, and values are `Location` objects for each statement. The `Location` for a function definition is really an assignment, and should include the entire function. In addition to the normal location object fields, a `statementMap` entry can also have an optional `skip` field. * `branchMap` - Hash where keys are branch IDs, and values are `{line, type, locations}` objects. `line` is the line the branch starts on. `type` is the type of the branch (e.g. "if", "switch"). `locations` is an array of `Location` objects, one for each possible outcome of the branch. Note for an `if` statement where there is no `else` clause, there will still be two `locations` generated. Istanbul does *not* generate coverage for the `default` case of a switch statement if `default` is not explicitly present in the source code. * `l` - Hash of line counts, where keys are the line number. `locations` for an if statement are always 0-length and located at the start of the `if` (even the location for the "else"). For a `switch` statement, `locations` start at the start of the `case` statement and go to the end of the line before the next case statement (note Istanbul does nothing clever here if a `case` is missing a `break`.) Each location in `locations` can also optionally have a `skip: true` field to indicate that this branch was ignored. IDs used in the fnMap, statementMap, and branchMap are sequential integers, starting at 1. ## Location Objects Location objects are a `{start: {line, column}, end: {line, column}}` object that describes the start and end of a piece of code. Note that `line` is 1-based, but `column` is 0-based. istanbul-0.4.5/download-escodegen-browser.sh000077500000000000000000000011441275640326200211310ustar00rootroot00000000000000#!/bin/sh ESCG_DIR=node_modules/escodegen ESCG_VERSION=`grep '"version"' ${ESCG_DIR}/package.json | awk '{print $2}' | sed 's/[",]//g'` OUT_FILE=${ESCG_DIR}/escodegen.browser.min.js if [ ! -f ${OUT_FILE} ] then set -v rm -rf __escodegen_clone__ git clone --branch ${ESCG_VERSION} https://github.com/estools/escodegen.git __escodegen_clone__ cd __escodegen_clone__ # Temporarily ignore missing package, see #489 perl -i -ne '/esprima\-moz/ or print' package.json npm i && npm run build-min mv escodegen.browser.min.js ../${OUT_FILE} cd - rm -rf __escodegen_clone__ fi istanbul-0.4.5/generate-pages.sh000077500000000000000000000003251275640326200165760ustar00rootroot00000000000000set -ex export PAGES_DIR=../istanbul-pages npm test --coverage mkdir -p public/apidocs yuidoc . rsync -rvt ./public/apidocs/ ${PAGES_DIR}/public/apidocs/ rsync -rvt ./build/coverage/ ${PAGES_DIR}/public/coverage istanbul-0.4.5/ignoring-code-for-coverage.md000066400000000000000000000105331275640326200207750ustar00rootroot00000000000000## Ignoring code for coverage purposes Some branches in JS code are typically hard, if not impossible to test. Examples are a `hasOwnProperty` check, [UMD wrappers](https://github.com/umdjs/umd) and so on. Istanbul now has a facility by which coverage can be excluded for certain sections of code. ### The interface 1. Coverage can be explicitly skipped using comments. There is no automatic pattern match of expressions to determine if they should be skipped for coverage. 2. A coverage skip hint looks like `/* istanbul ignore [non-word] [optional-docs] */` 3. For `if` conditions you can say `/* istanbul ignore if */` or `/* istanbul ignore else */` and that will end up ignoring whichever path was required to be ignored. 4. For all other cases, the Swiss army knife `/* istanbul ignore next */` may be used which skips the "next thing" in the source code 5. The "next" thing may be, among other things: * A JS statement (including assignments, ifs, loops, switches, functions) in which case all of the statement is ignored for all forms of coverage. * A switch case statement, in which case the particular case is ignored for branch coverage and its contents ignored for all forms * A conditional inside a ternary expression in which case the branch is ignored * A part of a logical expression in which case that part of the expression is ignored for branch coverage 6. It is up to the caller to scope this as narrowly as possible. For example, if you have a source file that is wrapped in a function expression, adding `/* istanbul ignore next */` at the top of the file will ignore the whole file! ### How it works When some part of the JS is considered skipped, nothing actually happens in terms of changes to the instrumentation. Everything is calculated as though nothing was skipped - all that changes is that there is a `skip` attribute added to the metadata of the statement, function or branch as applicable. Coverage reporting however takes the `skip` attribute into account and artificially increments counts, when 0 and skipped to pretend that the thing in question was covered. The HTML report shows the coverage after taking skips into account but at the same time colors the skipped statements with a gray color for easy visual scan. This design makes it possible to report on either of the coverage numbers ("raw" v/s "processed"), show a count of statements/ functions/ branches skipped etc. The HTML and text summary reports display counts of how many statements, branches and functions were ignored. ### Some practical examples #### Ignore an else path ```javascript /* istanbul ignore else */ if (foo.hasOwnProperty('bar')) { // do something } ``` Usually istanbul would complain about missing coverage for the `else` branch but it won't do so because of the comment. #### Ignore an if path ```javascript /* istanbul ignore if */ if (hardToReproduceError)) { return callback(hardToReproduceError); } ``` In this case, you do not have to produce the error to have full branch coverage. #### Ignore specific switch cases ```javascript switch (foo) { case 1: /* some code */; break; /* istanbul ignore next */ case 2: // really difficult to enter in a unit test for some reason someCode(); } ``` In the above example, the `case 2` branch is treated as covered. #### Ignore default assignments ```javascript var object = parameter || /* istanbul ignore next: tired of writing tests */ {}; ``` In the above example, the entire line will be treated a covered even if you don't have a test for a falsy `parameter` value. In this example the trailing `: tired of writing tests` string is an explanatory comment for your future self. It can be anything. #### Ignore specific conditions in an expression ```javascript if (simpleError || /* istanbul ignore next */ reallyDifficultToProduceError) { } ``` You get the idea by now. #### Ignore a UMD wrapper ```javascript (function (root, factory) { 'use strict'; /* istanbul ignore next */ if (typeof exports === 'object') { // CommonJS module.exports = factory(); } else if (typeof define === 'function' && define.amd) { // AMD. Register as an anonymous module. define(factory); } else { // Browser globals root.module = factory(); } })(this, fn); ``` This will cause the entire function expression to be skipped for coverage. istanbul-0.4.5/index.js000066400000000000000000000103611275640326200150160ustar00rootroot00000000000000/* Copyright (c) 2012, Yahoo! Inc. All rights reserved. Copyrights licensed under the New BSD License. See the accompanying LICENSE file for terms. */ /*jslint nomen: true */ var path = require('path'), Store = require('./lib/store'), Report = require('./lib/report'), meta = require('./lib/util/meta'); //register our standard plugins require('./lib/register-plugins'); /** * the top-level API for `istanbul`. provides access to the key libraries in * istanbul so you can write your own tools using `istanbul` as a library. * * Usage * ----- * * var istanbul = require('istanbul'); * * * @class Istanbul * @static * @module main * @main main */ module.exports = { /** * the Instrumenter class. * @property Instrumenter * @type Instrumenter * @static */ Instrumenter: require('./lib/instrumenter'), /** * the Store class. * @property Store * @type Store * @static */ Store: Store, /** * the Collector class * @property Collector * @type Collector * @static */ Collector: require('./lib/collector'), /** * the hook module * @property hook * @type Hook * @static */ hook: require('./lib/hook'), /** * the Report class * @property Report * @type Report * @static */ Report: Report, /** * the config module * @property config * @type Config * @static */ config: require('./lib/config'), /** * the Reporter class * @property Reporter * @type Reporter * @static */ Reporter: require('./lib/reporter'), /** * utility for processing coverage objects * @property utils * @type ObjectUtils * @static */ utils: require('./lib/object-utils'), /** * asynchronously returns a function that can match filesystem paths. * The function returned in the callback may be passed directly as a `matcher` * to the functions in the `hook` module. * * When no options are passed, the match function is one that matches all JS * files under the current working directory except ones under `node_modules` * * Match patterns are `ant`-style patterns processed using the `glob` library. * Examples not provided due to limitations in putting asterisks inside * jsdoc comments. Please refer to tests under `test/other/test-matcher.js` * for examples. * * @method matcherFor * @static * @param {Object} options Optional. Lookup options. * @param {String} [options.root] the root of the filesystem tree under * which to match files. Defaults to `process.cwd()` * @param {Array} [options.includes] an array of include patterns to match. * Defaults to all JS files under the root. * @param {Array} [options.excludes] and array of exclude patterns. File paths * matching these patterns will be excluded by the returned matcher. * Defaults to files under `node_modules` found anywhere under root. * @param {Function(err, matchFunction)} callback The callback that is * called with two arguments. The first is an `Error` object in case * of errors or a falsy value if there were no errors. The second * is a function that may be use as a matcher. */ matcherFor: require('./lib/util/file-matcher').matcherFor, /** * the version of the library * @property VERSION * @type String * @static */ VERSION: meta.VERSION, /** * the abstract Writer class * @property Writer * @type Writer * @static */ Writer: require('./lib/util/writer').Writer, /** * the abstract ContentWriter class * @property ContentWriter * @type ContentWriter * @static */ ContentWriter: require('./lib/util/writer').ContentWriter, /** * the concrete FileWriter class * @property FileWriter * @type FileWriter * @static */ FileWriter: require('./lib/util/file-writer'), //undocumented _yuiLoadHook: require('./lib/util/yui-load-hook'), //undocumented TreeSummarizer: require('./lib/util/tree-summarizer'), //undocumented assetsDir: path.resolve(__dirname, 'lib', 'assets') }; istanbul-0.4.5/lib/000077500000000000000000000000001275640326200141165ustar00rootroot00000000000000istanbul-0.4.5/lib/assets/000077500000000000000000000000001275640326200154205ustar00rootroot00000000000000istanbul-0.4.5/lib/assets/base.css000066400000000000000000000120271275640326200170460ustar00rootroot00000000000000body, html { margin:0; padding: 0; height: 100%; } body { font-family: Helvetica Neue, Helvetica, Arial; font-size: 14px; color:#333; } .small { font-size: 12px; } *, *:after, *:before { -webkit-box-sizing:border-box; -moz-box-sizing:border-box; box-sizing:border-box; } h1 { font-size: 20px; margin: 0;} h2 { font-size: 14px; } pre { font: 12px/1.4 Consolas, "Liberation Mono", Menlo, Courier, monospace; margin: 0; padding: 0; -moz-tab-size: 2; -o-tab-size: 2; tab-size: 2; } a { color:#0074D9; text-decoration:none; } a:hover { text-decoration:underline; } .strong { font-weight: bold; } .space-top1 { padding: 10px 0 0 0; } .pad2y { padding: 20px 0; } .pad1y { padding: 10px 0; } .pad2x { padding: 0 20px; } .pad2 { padding: 20px; } .pad1 { padding: 10px; } .space-left2 { padding-left:55px; } .space-right2 { padding-right:20px; } .center { text-align:center; } .clearfix { display:block; } .clearfix:after { content:''; display:block; height:0; clear:both; visibility:hidden; } .fl { float: left; } @media only screen and (max-width:640px) { .col3 { width:100%; max-width:100%; } .hide-mobile { display:none!important; } } .quiet { color: #7f7f7f; color: rgba(0,0,0,0.5); } .quiet a { opacity: 0.7; } .fraction { font-family: Consolas, 'Liberation Mono', Menlo, Courier, monospace; font-size: 10px; color: #555; background: #E8E8E8; padding: 4px 5px; border-radius: 3px; vertical-align: middle; } div.path a:link, div.path a:visited { color: #333; } table.coverage { border-collapse: collapse; margin: 10px 0 0 0; padding: 0; } table.coverage td { margin: 0; padding: 0; vertical-align: top; } table.coverage td.line-count { text-align: right; padding: 0 5px 0 20px; } table.coverage td.line-coverage { text-align: right; padding-right: 10px; min-width:20px; } table.coverage td span.cline-any { display: inline-block; padding: 0 5px; width: 100%; } .missing-if-branch { display: inline-block; margin-right: 5px; border-radius: 3px; position: relative; padding: 0 4px; background: #333; color: yellow; } .skip-if-branch { display: none; margin-right: 10px; position: relative; padding: 0 4px; background: #ccc; color: white; } .missing-if-branch .typ, .skip-if-branch .typ { color: inherit !important; } .coverage-summary { border-collapse: collapse; width: 100%; } .coverage-summary tr { border-bottom: 1px solid #bbb; } .keyline-all { border: 1px solid #ddd; } .coverage-summary td, .coverage-summary th { padding: 10px; } .coverage-summary tbody { border: 1px solid #bbb; } .coverage-summary td { border-right: 1px solid #bbb; } .coverage-summary td:last-child { border-right: none; } .coverage-summary th { text-align: left; font-weight: normal; white-space: nowrap; } .coverage-summary th.file { border-right: none !important; } .coverage-summary th.pct { } .coverage-summary th.pic, .coverage-summary th.abs, .coverage-summary td.pct, .coverage-summary td.abs { text-align: right; } .coverage-summary td.file { white-space: nowrap; } .coverage-summary td.pic { min-width: 120px !important; } .coverage-summary tfoot td { } .coverage-summary .sorter { height: 10px; width: 7px; display: inline-block; margin-left: 0.5em; background: url(sort-arrow-sprite.png) no-repeat scroll 0 0 transparent; } .coverage-summary .sorted .sorter { background-position: 0 -20px; } .coverage-summary .sorted-desc .sorter { background-position: 0 -10px; } .status-line { height: 10px; } /* dark red */ .red.solid, .status-line.low, .low .cover-fill { background:#C21F39 } .low .chart { border:1px solid #C21F39 } /* medium red */ .cstat-no, .fstat-no, .cbranch-no, .cbranch-no { background:#F6C6CE } /* light red */ .low, .cline-no { background:#FCE1E5 } /* light green */ .high, .cline-yes { background:rgb(230,245,208) } /* medium green */ .cstat-yes { background:rgb(161,215,106) } /* dark green */ .status-line.high, .high .cover-fill { background:rgb(77,146,33) } .high .chart { border:1px solid rgb(77,146,33) } /* dark yellow (gold) */ .medium .chart { border:1px solid #f9cd0b; } .status-line.medium, .medium .cover-fill { background: #f9cd0b; } /* light yellow */ .medium { background: #fff4c2; } /* light gray */ span.cline-neutral { background: #eaeaea; } .cbranch-no { background: yellow !important; color: #111; } .cstat-skip { background: #ddd; color: #111; } .fstat-skip { background: #ddd; color: #111 !important; } .cbranch-skip { background: #ddd !important; color: #111; } .cover-fill, .cover-empty { display:inline-block; height: 12px; } .chart { line-height: 0; } .cover-empty { background: white; } .cover-full { border-right: none !important; } pre.prettyprint { border: none !important; padding: 0 !important; margin: 0 !important; } .com { color: #999 !important; } .ignore-none { color: #999; font-weight: normal; } .wrapper { min-height: 100%; height: auto !important; height: 100%; margin: 0 auto -48px; } .footer, .push { height: 48px; } istanbul-0.4.5/lib/assets/sort-arrow-sprite.png000066400000000000000000000003211275640326200215450ustar00rootroot00000000000000‰PNG  IHDR°F•3tEXtSoftwareAdobe ImageReadyqÉe<sIDATxÚ¼RAÀ ¾ÄÿŸà›D»°Üe[ Ò ÔHfFX g¡fÖªÇ3ŠDd F©w±-˘°ˆ Á nÓgÙM+OVÚºâ=¹""/îÄ¥'Yø‘•?HÿhV’ ²@ª§p÷ð‡Yø8Bwc,ˆIEND®B`‚istanbul-0.4.5/lib/assets/sorter.js000066400000000000000000000120661275640326200173010ustar00rootroot00000000000000var addSorting = (function () { "use strict"; var cols, currentSort = { index: 0, desc: false }; // returns the summary table element function getTable() { return document.querySelector('.coverage-summary'); } // returns the thead element of the summary table function getTableHeader() { return getTable().querySelector('thead tr'); } // returns the tbody element of the summary table function getTableBody() { return getTable().querySelector('tbody'); } // returns the th element for nth column function getNthColumn(n) { return getTableHeader().querySelectorAll('th')[n]; } // loads all columns function loadColumns() { var colNodes = getTableHeader().querySelectorAll('th'), colNode, cols = [], col, i; for (i = 0; i < colNodes.length; i += 1) { colNode = colNodes[i]; col = { key: colNode.getAttribute('data-col'), sortable: !colNode.getAttribute('data-nosort'), type: colNode.getAttribute('data-type') || 'string' }; cols.push(col); if (col.sortable) { col.defaultDescSort = col.type === 'number'; colNode.innerHTML = colNode.innerHTML + ''; } } return cols; } // attaches a data attribute to every tr element with an object // of data values keyed by column name function loadRowData(tableRow) { var tableCols = tableRow.querySelectorAll('td'), colNode, col, data = {}, i, val; for (i = 0; i < tableCols.length; i += 1) { colNode = tableCols[i]; col = cols[i]; val = colNode.getAttribute('data-value'); if (col.type === 'number') { val = Number(val); } data[col.key] = val; } return data; } // loads all row data function loadData() { var rows = getTableBody().querySelectorAll('tr'), i; for (i = 0; i < rows.length; i += 1) { rows[i].data = loadRowData(rows[i]); } } // sorts the table using the data for the ith column function sortByIndex(index, desc) { var key = cols[index].key, sorter = function (a, b) { a = a.data[key]; b = b.data[key]; return a < b ? -1 : a > b ? 1 : 0; }, finalSorter = sorter, tableBody = document.querySelector('.coverage-summary tbody'), rowNodes = tableBody.querySelectorAll('tr'), rows = [], i; if (desc) { finalSorter = function (a, b) { return -1 * sorter(a, b); }; } for (i = 0; i < rowNodes.length; i += 1) { rows.push(rowNodes[i]); tableBody.removeChild(rowNodes[i]); } rows.sort(finalSorter); for (i = 0; i < rows.length; i += 1) { tableBody.appendChild(rows[i]); } } // removes sort indicators for current column being sorted function removeSortIndicators() { var col = getNthColumn(currentSort.index), cls = col.className; cls = cls.replace(/ sorted$/, '').replace(/ sorted-desc$/, ''); col.className = cls; } // adds sort indicators for current column being sorted function addSortIndicators() { getNthColumn(currentSort.index).className += currentSort.desc ? ' sorted-desc' : ' sorted'; } // adds event listeners for all sorter widgets function enableUI() { var i, el, ithSorter = function ithSorter(i) { var col = cols[i]; return function () { var desc = col.defaultDescSort; if (currentSort.index === i) { desc = !currentSort.desc; } sortByIndex(i, desc); removeSortIndicators(); currentSort.index = i; currentSort.desc = desc; addSortIndicators(); }; }; for (i =0 ; i < cols.length; i += 1) { if (cols[i].sortable) { // add the click event handler on the th so users // dont have to click on those tiny arrows el = getNthColumn(i).querySelector('.sorter').parentElement; if (el.addEventListener) { el.addEventListener('click', ithSorter(i)); } else { el.attachEvent('onclick', ithSorter(i)); } } } } // adds sorting functionality to the UI return function () { if (!getTable()) { return; } cols = loadColumns(); loadData(cols); addSortIndicators(); enableUI(); }; })(); window.addEventListener('load', addSorting); istanbul-0.4.5/lib/cli.js000077500000000000000000000047121275640326200152320ustar00rootroot00000000000000#!/usr/bin/env node /* Copyright (c) 2012, Yahoo! Inc. All rights reserved. Copyrights licensed under the New BSD License. See the accompanying LICENSE file for terms. */ var async = require('async'), Command = require('./command'), inputError = require('./util/input-error'), exitProcess = process.exit; //hold a reference to original process.exit so that we are not affected even when a test changes it require('./register-plugins'); function findCommandPosition(args) { var i; for (i = 0; i < args.length; i += 1) { if (args[i].charAt(0) !== '-') { return i; } } return -1; } function exit(ex, code) { // flush output for Node.js Windows pipe bug // https://github.com/joyent/node/issues/6247 is just one bug example // https://github.com/visionmedia/mocha/issues/333 has a good discussion var streams = [process.stdout, process.stderr]; async.forEach(streams, function (stream, done) { // submit a write request and wait until it's written stream.write('', done); }, function () { if (ex) { if (typeof ex === 'string') { console.error(ex); exitProcess(1); } else { throw ex; // turn it into an uncaught exception } } else { exitProcess(code); } }); } function errHandler (ex) { if (!ex) { return; } if (!ex.inputError) { // exit with exception stack trace exit(ex); } else { //don't print nasty traces but still exit(1) console.error(ex.message); console.error('Try "istanbul help" for usage'); exit(null, 1); } } function runCommand(args, callback) { var pos = findCommandPosition(args), command, commandArgs, commandObject; if (pos < 0) { return callback(inputError.create('Need a command to run')); } commandArgs = args.slice(0, pos); command = args[pos]; commandArgs.push.apply(commandArgs, args.slice(pos + 1)); try { commandObject = Command.create(command); } catch (ex) { errHandler(inputError.create(ex.message)); return; } commandObject.run(commandArgs, errHandler); } function runToCompletion(args) { runCommand(args, errHandler); } /* istanbul ignore if: untestable */ if (require.main === module) { var args = Array.prototype.slice.call(process.argv, 2); runToCompletion(args); } module.exports = { runToCompletion: runToCompletion }; istanbul-0.4.5/lib/collector.js000066400000000000000000000110741275640326200164450ustar00rootroot00000000000000/* Copyright (c) 2012, Yahoo! Inc. All rights reserved. Copyrights licensed under the New BSD License. See the accompanying LICENSE file for terms. */ "use strict"; var MemoryStore = require('./store/memory'), utils = require('./object-utils'); /** * a mechanism to merge multiple coverage objects into one. Handles the use case * of overlapping coverage information for the same files in multiple coverage * objects and does not double-count in this situation. For example, if * you pass the same coverage object multiple times, the final merged object will be * no different that any of the objects passed in (except for execution counts). * * The `Collector` is built for scale to handle thousands of coverage objects. * By default, all processing is done in memory since the common use-case is of * one or a few coverage objects. You can work around memory * issues by passing in a `Store` implementation that stores temporary computations * on disk (the `tmp` store, for example). * * The `getFinalCoverage` method returns an object with merged coverage information * and is provided as a convenience for implementors working with coverage information * that can fit into memory. Reporters, in the interest of generality, should *not* use this method for * creating reports. * * Usage * ----- * * var collector = new require('istanbul').Collector(); * * files.forEach(function (f) { * //each coverage object can have overlapping information about multiple files * collector.add(JSON.parse(fs.readFileSync(f, 'utf8'))); * }); * * collector.files().forEach(function(file) { * var fileCoverage = collector.fileCoverageFor(file); * console.log('Coverage for ' + file + ' is:' + JSON.stringify(fileCoverage)); * }); * * // convenience method: do not use this when dealing with a large number of files * var finalCoverage = collector.getFinalCoverage(); * * @class Collector * @module main * @constructor * @param {Object} options Optional. Configuration options. * @param {Store} options.store - an implementation of `Store` to use for temporary * calculations. */ function Collector(options) { options = options || {}; this.store = options.store || new MemoryStore(); } Collector.prototype = { /** * adds a coverage object to the collector. * * @method add * @param {Object} coverage the coverage object. * @param {String} testName Optional. The name of the test used to produce the object. * This is currently not used. */ add: function (coverage /*, testName */) { var store = this.store; Object.keys(coverage).forEach(function (key) { var fileCoverage = coverage[key]; if (store.hasKey(key)) { store.setObject(key, utils.mergeFileCoverage(fileCoverage, store.getObject(key))); } else { store.setObject(key, fileCoverage); } }); }, /** * returns a list of unique file paths for which coverage information has been added. * @method files * @return {Array} an array of file paths for which coverage information is present. */ files: function () { return this.store.keys(); }, /** * return file coverage information for a single file * @method fileCoverageFor * @param {String} fileName the path for the file for which coverage information is * required. Must be one of the values returned in the `files()` method. * @return {Object} the coverage information for the specified file. */ fileCoverageFor: function (fileName) { var ret = this.store.getObject(fileName); utils.addDerivedInfoForFile(ret); return ret; }, /** * returns file coverage information for all files. This has the same format as * any of the objects passed in to the `add` method. The number of keys in this * object will be a superset of all keys found in the objects passed to `add()` * @method getFinalCoverage * @return {Object} the merged coverage information */ getFinalCoverage: function () { var ret = {}, that = this; this.files().forEach(function (file) { ret[file] = that.fileCoverageFor(file); }); return ret; }, /** * disposes this collector and reclaims temporary resources used in the * computation. Calls `dispose()` on the underlying store. * @method dispose */ dispose: function () { this.store.dispose(); } }; module.exports = Collector;istanbul-0.4.5/lib/command/000077500000000000000000000000001275640326200155345ustar00rootroot00000000000000istanbul-0.4.5/lib/command/check-coverage.js000066400000000000000000000167651275640326200207570ustar00rootroot00000000000000/* Copyright (c) 2012, Yahoo! Inc. All rights reserved. Copyrights licensed under the New BSD License. See the accompanying LICENSE file for terms. */ var nopt = require('nopt'), path = require('path'), fs = require('fs'), Collector = require('../collector'), formatOption = require('../util/help-formatter').formatOption, util = require('util'), utils = require('../object-utils'), filesFor = require('../util/file-matcher').filesFor, Command = require('./index'), configuration = require('../config'); function isAbsolute(file) { if (path.isAbsolute) { return path.isAbsolute(file); } return path.resolve(file) === path.normalize(file); } function CheckCoverageCommand() { Command.call(this); } function removeFiles(covObj, root, files) { var filesObj = {}, obj = {}; // Create lookup table. files.forEach(function (file) { filesObj[file] = true; }); Object.keys(covObj).forEach(function (key) { // Exclude keys will always be relative, but covObj keys can be absolute or relative var excludeKey = isAbsolute(key) ? path.relative(root, key) : key; // Also normalize for files that start with `./`, etc. excludeKey = path.normalize(excludeKey); if (filesObj[excludeKey] !== true) { obj[key] = covObj[key]; } }); return obj; } CheckCoverageCommand.TYPE = 'check-coverage'; util.inherits(CheckCoverageCommand, Command); Command.mix(CheckCoverageCommand, { synopsis: function () { return "checks overall/per-file coverage against thresholds from coverage JSON files. Exits 1 if thresholds are not met, 0 otherwise"; }, usage: function () { console.error('\nUsage: ' + this.toolName() + ' ' + this.type() + ' []\n\nOptions are:\n\n' + [ formatOption('--statements ', 'global statement coverage threshold'), formatOption('--functions ', 'global function coverage threshold'), formatOption('--branches ', 'global branch coverage threshold'), formatOption('--lines ', 'global line coverage threshold') ].join('\n\n') + '\n'); console.error('\n\n'); console.error('Thresholds, when specified as a positive number are taken to be the minimum percentage required.'); console.error('When a threshold is specified as a negative number it represents the maximum number of uncovered entities allowed.\n'); console.error('For example, --statements 90 implies minimum statement coverage is 90%.'); console.error(' --statements -10 implies that no more than 10 uncovered statements are allowed\n'); console.error('Per-file thresholds can be specified via a configuration file.\n'); console.error(' is a glob pattern that can be used to select one or more coverage files ' + 'for merge. This defaults to "**/coverage*.json"'); console.error('\n'); }, run: function (args, callback) { var template = { config: path, root: path, statements: Number, lines: Number, branches: Number, functions: Number, verbose: Boolean }, opts = nopt(template, { v : '--verbose' }, args, 0), // Translate to config opts. config = configuration.loadFile(opts.config, { verbose: opts.verbose, check: { global: { statements: opts.statements, lines: opts.lines, branches: opts.branches, functions: opts.functions } } }), includePattern = '**/coverage*.json', root, collector = new Collector(), errors = []; if (opts.argv.remain.length > 0) { includePattern = opts.argv.remain[0]; } root = opts.root || process.cwd(); filesFor({ root: root, includes: [ includePattern ] }, function (err, files) { if (err) { throw err; } if (files.length === 0) { return callback('ERROR: No coverage files found.'); } files.forEach(function (file) { var coverageObject = JSON.parse(fs.readFileSync(file, 'utf8')); collector.add(coverageObject); }); var thresholds = { global: { statements: config.check.global.statements || 0, branches: config.check.global.branches || 0, lines: config.check.global.lines || 0, functions: config.check.global.functions || 0, excludes: config.check.global.excludes || [] }, each: { statements: config.check.each.statements || 0, branches: config.check.each.branches || 0, lines: config.check.each.lines || 0, functions: config.check.each.functions || 0, excludes: config.check.each.excludes || [] } }, rawCoverage = collector.getFinalCoverage(), globalResults = utils.summarizeCoverage(removeFiles(rawCoverage, root, thresholds.global.excludes)), eachResults = removeFiles(rawCoverage, root, thresholds.each.excludes); // Summarize per-file results and mutate original results. Object.keys(eachResults).forEach(function (key) { eachResults[key] = utils.summarizeFileCoverage(eachResults[key]); }); if (config.verbose) { console.log('Compare actuals against thresholds'); console.log(JSON.stringify({ global: globalResults, each: eachResults, thresholds: thresholds }, undefined, 4)); } function check(name, thresholds, actuals) { [ "statements", "branches", "lines", "functions" ].forEach(function (key) { var actual = actuals[key].pct, actualUncovered = actuals[key].total - actuals[key].covered, threshold = thresholds[key]; if (threshold < 0) { if (threshold * -1 < actualUncovered) { errors.push('ERROR: Uncovered count for ' + key + ' (' + actualUncovered + ') exceeds ' + name + ' threshold (' + -1 * threshold + ')'); } } else { if (actual < threshold) { errors.push('ERROR: Coverage for ' + key + ' (' + actual + '%) does not meet ' + name + ' threshold (' + threshold + '%)'); } } }); } check("global", thresholds.global, globalResults); Object.keys(eachResults).forEach(function (key) { check("per-file" + " (" + key + ") ", thresholds.each, eachResults[key]); }); return callback(errors.length === 0 ? null : errors.join("\n")); }); } }); module.exports = CheckCoverageCommand; istanbul-0.4.5/lib/command/common/000077500000000000000000000000001275640326200170245ustar00rootroot00000000000000istanbul-0.4.5/lib/command/common/run-with-cover.js000066400000000000000000000274341275640326200222650ustar00rootroot00000000000000/* Copyright (c) 2012, Yahoo! Inc. All rights reserved. Copyrights licensed under the New BSD License. See the accompanying LICENSE file for terms. */ var Module = require('module'), path = require('path'), fs = require('fs'), nopt = require('nopt'), which = require('which'), mkdirp = require('mkdirp'), existsSync = fs.existsSync || path.existsSync, inputError = require('../../util/input-error'), matcherFor = require('../../util/file-matcher').matcherFor, Instrumenter = require('../../instrumenter'), Collector = require('../../collector'), formatOption = require('../../util/help-formatter').formatOption, hook = require('../../hook'), Reporter = require('../../reporter'), resolve = require('resolve'), configuration = require('../../config'); function usage(arg0, command) { console.error('\nUsage: ' + arg0 + ' ' + command + ' [] [-- ]\n\nOptions are:\n\n' + [ formatOption('--config ', 'the configuration file to use, defaults to .istanbul.yml'), formatOption('--root ', 'the root path to look for files to instrument, defaults to .'), formatOption('-x [-x ]', 'one or more glob patterns e.g. "**/vendor/**"'), formatOption('-i [-i ]', 'one or more glob patterns e.g. "**/*.js"'), formatOption('--[no-]default-excludes', 'apply default excludes [ **/node_modules/**, **/test/**, **/tests/** ], defaults to true'), formatOption('--hook-run-in-context', 'hook vm.runInThisContext in addition to require (supports RequireJS), defaults to false'), formatOption('--post-require-hook | ', 'JS module that exports a function for post-require processing'), formatOption('--report [--report ] ', 'report format, defaults to lcov (= lcov.info + HTML)'), formatOption('--dir ', 'report directory, defaults to ./coverage'), formatOption('--print ', 'type of report to print to console, one of summary (default), detail, both or none'), formatOption('--verbose, -v', 'verbose mode'), formatOption('--[no-]preserve-comments', 'remove / preserve comments in the output, defaults to false'), formatOption('--include-all-sources', 'instrument all unused sources after running tests, defaults to false'), formatOption('--[no-]include-pid', 'include PID in output coverage filename') ].join('\n\n') + '\n'); console.error('\n'); } function run(args, commandName, enableHooks, callback) { var template = { config: path, root: path, x: [ Array, String ], report: [Array, String ], dir: path, verbose: Boolean, yui: Boolean, 'default-excludes': Boolean, print: String, 'self-test': Boolean, 'hook-run-in-context': Boolean, 'post-require-hook': String, 'preserve-comments': Boolean, 'include-all-sources': Boolean, 'preload-sources': Boolean, i: [ Array, String ], 'include-pid': Boolean }, opts = nopt(template, { v : '--verbose' }, args, 0), overrides = { verbose: opts.verbose, instrumentation: { root: opts.root, 'default-excludes': opts['default-excludes'], excludes: opts.x, 'include-all-sources': opts['include-all-sources'], 'preload-sources': opts['preload-sources'], 'include-pid': opts['include-pid'] }, reporting: { reports: opts.report, print: opts.print, dir: opts.dir }, hooks: { 'hook-run-in-context': opts['hook-run-in-context'], 'post-require-hook': opts['post-require-hook'], 'handle-sigint': opts['handle-sigint'] } }, config = configuration.loadFile(opts.config, overrides), verbose = config.verbose, cmdAndArgs = opts.argv.remain, preserveComments = opts['preserve-comments'], includePid = opts['include-pid'], cmd, cmdArgs, reportingDir, reporter = new Reporter(config), runFn, excludes; if (cmdAndArgs.length === 0) { return callback(inputError.create('Need a filename argument for the ' + commandName + ' command!')); } cmd = cmdAndArgs.shift(); cmdArgs = cmdAndArgs; if (!existsSync(cmd)) { try { cmd = which.sync(cmd); } catch (ex) { return callback(inputError.create('Unable to resolve file [' + cmd + ']')); } } else { cmd = path.resolve(cmd); } runFn = function () { process.argv = ["node", cmd].concat(cmdArgs); if (verbose) { console.log('Running: ' + process.argv.join(' ')); } process.env.running_under_istanbul=1; Module.runMain(cmd, null, true); }; excludes = config.instrumentation.excludes(true); if (enableHooks) { reportingDir = path.resolve(config.reporting.dir()); mkdirp.sync(reportingDir); //ensure we fail early if we cannot do this reporter.dir = reportingDir; reporter.addAll(config.reporting.reports()); if (config.reporting.print() !== 'none') { switch (config.reporting.print()) { case 'detail': reporter.add('text'); break; case 'both': reporter.add('text'); reporter.add('text-summary'); break; default: reporter.add('text-summary'); break; } } excludes.push(path.relative(process.cwd(), path.join(reportingDir, '**', '*'))); matcherFor({ root: config.instrumentation.root() || process.cwd(), includes: opts.i || config.instrumentation.extensions().map(function (ext) { return '**/*' + ext; }), excludes: excludes }, function (err, matchFn) { if (err) { return callback(err); } var coverageVar = '$$cov_' + new Date().getTime() + '$$', instrumenter = new Instrumenter({ coverageVariable: coverageVar , preserveComments: preserveComments}), transformer = instrumenter.instrumentSync.bind(instrumenter), hookOpts = { verbose: verbose, extensions: config.instrumentation.extensions() }, postRequireHook = config.hooks.postRequireHook(), postLoadHookFile; if (postRequireHook) { postLoadHookFile = path.resolve(postRequireHook); } else if (opts.yui) { //EXPERIMENTAL code: do not rely on this in anyway until the docs say it is allowed postLoadHookFile = path.resolve(__dirname, '../../util/yui-load-hook'); } if (postRequireHook) { if (!existsSync(postLoadHookFile)) { //assume it is a module name and resolve it try { postLoadHookFile = resolve.sync(postRequireHook, { basedir: process.cwd() }); } catch (ex) { if (verbose) { console.error('Unable to resolve [' + postRequireHook + '] as a node module'); } callback(ex); return; } } } if (postLoadHookFile) { if (verbose) { console.error('Use post-load-hook: ' + postLoadHookFile); } hookOpts.postLoadHook = require(postLoadHookFile)(matchFn, transformer, verbose); } if (opts['self-test']) { hook.unloadRequireCache(matchFn); } // runInThisContext is used by RequireJS [issue #23] if (config.hooks.hookRunInContext()) { hook.hookRunInThisContext(matchFn, transformer, hookOpts); } hook.hookRequire(matchFn, transformer, hookOpts); //initialize the global variable to stop mocha from complaining about leaks global[coverageVar] = {}; // enable passing --handle-sigint to write reports on SIGINT. // This allows a user to manually kill a process while // still getting the istanbul report. if (config.hooks.handleSigint()) { process.once('SIGINT', process.exit); } process.once('exit', function () { var pidExt = includePid ? ('-' + process.pid) : '', file = path.resolve(reportingDir, 'coverage' + pidExt + '.json'), collector, cov; if (typeof global[coverageVar] === 'undefined' || Object.keys(global[coverageVar]).length === 0) { console.error('No coverage information was collected, exit without writing coverage information'); return; } else { cov = global[coverageVar]; } //important: there is no event loop at this point //everything that happens in this exit handler MUST be synchronous if (config.instrumentation.includeAllSources()) { // Files that are not touched by code ran by the test runner is manually instrumented, to // illustrate the missing coverage. matchFn.files.forEach(function (file) { if (!cov[file]) { transformer(fs.readFileSync(file, 'utf-8'), file); // When instrumenting the code, istanbul will give each FunctionDeclaration a value of 1 in coverState.s, // presumably to compensate for function hoisting. We need to reset this, as the function was not hoisted, // as it was never loaded. Object.keys(instrumenter.coverState.s).forEach(function (key) { instrumenter.coverState.s[key] = 0; }); cov[file] = instrumenter.coverState; } }); } mkdirp.sync(reportingDir); //yes, do this again since some test runners could clean the dir initially created if (config.reporting.print() !== 'none') { console.error('============================================================================='); console.error('Writing coverage object [' + file + ']'); } fs.writeFileSync(file, JSON.stringify(cov), 'utf8'); collector = new Collector(); collector.add(cov); if (config.reporting.print() !== 'none') { console.error('Writing coverage reports at [' + reportingDir + ']'); console.error('============================================================================='); } reporter.write(collector, true, callback); }); runFn(); }); } else { runFn(); } } module.exports = { run: run, usage: usage }; istanbul-0.4.5/lib/command/cover.js000066400000000000000000000014711275640326200172130ustar00rootroot00000000000000/* Copyright (c) 2012, Yahoo! Inc. All rights reserved. Copyrights licensed under the New BSD License. See the accompanying LICENSE file for terms. */ var runWithCover = require('./common/run-with-cover'), util = require('util'), Command = require('./index'); function CoverCommand() { Command.call(this); } CoverCommand.TYPE = 'cover'; util.inherits(CoverCommand, Command); Command.mix(CoverCommand, { synopsis: function () { return "transparently adds coverage information to a node command. Saves coverage.json and reports at the end of execution"; }, usage: function () { runWithCover.usage(this.toolName(), this.type()); }, run: function (args, callback) { runWithCover.run(args, this.type(), true, callback); } }); module.exports = CoverCommand; istanbul-0.4.5/lib/command/help.js000066400000000000000000000103301275640326200170170ustar00rootroot00000000000000/* Copyright (c) 2012, Yahoo! Inc. All rights reserved. Copyrights licensed under the New BSD License. See the accompanying LICENSE file for terms. */ var Command = require('./index.js'), util = require('util'), formatOption = require('../util/help-formatter').formatOption, VERSION = require('../../index').VERSION, configuration = require('../config'), yaml = require('js-yaml'), formatPara = require('../util/help-formatter').formatPara; function showConfigHelp(toolName) { console.error('\nConfiguring ' + toolName); console.error('===================='); console.error('\n' + formatPara(toolName + ' can be configured globally using a .istanbul.yml YAML file ' + 'at the root of your source tree. Every command also accepts a --config= argument to ' + 'customize its location per command. The alternate config file can be in YAML, JSON or node.js ' + '(exporting the config object).')); console.error('\n' + formatPara('The config file currently has four sections for instrumentation, reporting, hooks, ' + 'and checking. Note that certain commands (like `cover`) use information from multiple sections.')); console.error('\n' + formatPara('Keys in the config file usually correspond to command line parameters with the same name. ' + 'The verbose option for every command shows you the exact configuration used. See the api ' + 'docs for an explanation of each key.')); console.error('\n' + formatPara('You only need to specify the keys that you want to override. Your overrides will be merged ' + 'with the default config.')); console.error('\nThe default configuration is as follows:\n'); console.error(yaml.safeDump(configuration.defaultConfig(), { indent: 4, flowLevel: 3 })); console.error('\n' + formatPara('The `watermarks` section does not have a command line equivalent. It allows you to set up ' + 'low and high watermark percentages for reporting. These are honored by all reporters that colorize ' + 'their output based on low/ medium/ high coverage.')); console.error('\n' + formatPara('The `reportConfig` section allows you to configure each report format independently ' + 'and has no command-line equivalent either.')); console.error('\n' + formatPara('The `check` section configures minimum threshold enforcement for coverage results. ' + '`global` applies to all files together and `each` on a per-file basis. A list of files can ' + 'be excluded from enforcement relative to root via the `exclude` property.')); console.error(''); } function HelpCommand() { Command.call(this); } HelpCommand.TYPE = 'help'; util.inherits(HelpCommand, Command); Command.mix(HelpCommand, { synopsis: function () { return "shows help"; }, usage: function () { console.error('\nUsage: ' + this.toolName() + ' ' + this.type() + ' config | \n'); console.error('`config` provides help with istanbul configuration\n'); console.error('Available commands are:\n'); var commandObj; Command.getCommandList().forEach(function (cmd) { commandObj = Command.create(cmd); console.error(formatOption(cmd, commandObj.synopsis())); console.error("\n"); }); console.error("Command names can be abbreviated as long as the abbreviation is unambiguous"); console.error(this.toolName() + ' version:' + VERSION); console.error("\n"); }, run: function (args, callback) { var command; if (args.length === 0) { this.usage(); } else { if (args[0] === 'config') { showConfigHelp(this.toolName()); } else { try { command = Command.create(args[0]); command.usage('istanbul', Command.resolveCommandName(args[0])); } catch (ex) { console.error('Invalid command: ' + args[0]); this.usage(); } } } return callback(); } }); module.exports = HelpCommand; istanbul-0.4.5/lib/command/index.js000066400000000000000000000021731275640326200172040ustar00rootroot00000000000000/* Copyright (c) 2012, Yahoo! Inc. All rights reserved. Copyrights licensed under the New BSD License. See the accompanying LICENSE file for terms. */ var Factory = require('../util/factory'), factory = new Factory('command', __dirname, true); function Command() {} // add register, create, mix, loadAll, getCommandList, resolveCommandName to the Command object factory.bindClassMethods(Command); Command.prototype = { toolName: function () { return require('../util/meta').NAME; }, type: function () { return this.constructor.TYPE; }, synopsis: /* istanbul ignore next: base method */ function () { return "the developer has not written a one-line summary of the " + this.type() + " command"; }, usage: /* istanbul ignore next: base method */ function () { console.error("the developer has not provided a usage for the " + this.type() + " command"); }, run: /* istanbul ignore next: abstract method */ function (args, callback) { return callback(new Error("run: must be overridden for the " + this.type() + " command")); } }; module.exports = Command; istanbul-0.4.5/lib/command/instrument.js000066400000000000000000000246761275640326200203210ustar00rootroot00000000000000/* Copyright (c) 2012, Yahoo! Inc. All rights reserved. Copyrights licensed under the New BSD License. See the accompanying LICENSE file for terms. */ var path = require('path'), mkdirp = require('mkdirp'), once = require('once'), async = require('async'), fs = require('fs'), filesFor = require('../util/file-matcher').filesFor, nopt = require('nopt'), Instrumenter = require('../instrumenter'), inputError = require('../util/input-error'), formatOption = require('../util/help-formatter').formatOption, util = require('util'), Command = require('./index'), Collector = require('../collector'), configuration = require('../config'), verbose; /* * Chunk file size to use when reading non JavaScript files in memory * and copying them over when using complete-copy flag. */ var READ_FILE_CHUNK_SIZE = 64 * 1024; function BaselineCollector(instrumenter) { this.instrumenter = instrumenter; this.collector = new Collector(); this.instrument = instrumenter.instrument.bind(this.instrumenter); var origInstrumentSync = instrumenter.instrumentSync; this.instrumentSync = function () { var args = Array.prototype.slice.call(arguments), ret = origInstrumentSync.apply(this.instrumenter, args), baseline = this.instrumenter.lastFileCoverage(), coverage = {}; coverage[baseline.path] = baseline; this.collector.add(coverage); return ret; }; //monkey patch the instrumenter to call our version instead instrumenter.instrumentSync = this.instrumentSync.bind(this); } BaselineCollector.prototype = { getCoverage: function () { return this.collector.getFinalCoverage(); } }; function processFiles(instrumenter, inputDir, outputDir, relativeNames, extensions) { var processor = function (name, callback) { var inputFile = path.resolve(inputDir, name), outputFile = path.resolve(outputDir, name), inputFileExtenstion = path.extname(inputFile), isJavaScriptFile = extensions.indexOf(inputFileExtenstion) > -1, oDir = path.dirname(outputFile), readStream, writeStream; callback = once(callback); mkdirp.sync(oDir); if (fs.statSync(inputFile).isDirectory()) { return callback(null, name); } if (isJavaScriptFile) { fs.readFile(inputFile, 'utf8', function (err, data) { if (err) { return callback(err, name); } instrumenter.instrument(data, inputFile, function (iErr, instrumented) { if (iErr) { return callback(iErr, name); } fs.writeFile(outputFile, instrumented, 'utf8', function (err) { return callback(err, name); }); }); }); } else { // non JavaScript file, copy it as is readStream = fs.createReadStream(inputFile, {'bufferSize': READ_FILE_CHUNK_SIZE}); writeStream = fs.createWriteStream(outputFile); readStream.on('error', callback); writeStream.on('error', callback); readStream.pipe(writeStream); readStream.on('end', function() { callback(null, name); }); } }, q = async.queue(processor, 10), errors = [], count = 0, startTime = new Date().getTime(); q.push(relativeNames, function (err, name) { var inputFile, outputFile; if (err) { errors.push({ file: name, error: err.message || err.toString() }); inputFile = path.resolve(inputDir, name); outputFile = path.resolve(outputDir, name); fs.writeFileSync(outputFile, fs.readFileSync(inputFile)); } if (verbose) { console.log('Processed: ' + name); } else { if (count % 100 === 0) { process.stdout.write('.'); } } count += 1; }); q.drain = function () { var endTime = new Date().getTime(); console.log('\nProcessed [' + count + '] files in ' + Math.floor((endTime - startTime) / 1000) + ' secs'); if (errors.length > 0) { console.log('The following ' + errors.length + ' file(s) had errors and were copied as-is'); console.log(errors); } }; } function InstrumentCommand() { Command.call(this); } InstrumentCommand.TYPE = 'instrument'; util.inherits(InstrumentCommand, Command); Command.mix(InstrumentCommand, { synopsis: function synopsis() { return "instruments a file or a directory tree and writes the instrumented code to the desired output location"; }, usage: function () { console.error('\nUsage: ' + this.toolName() + ' ' + this.type() + ' \n\nOptions are:\n\n' + [ formatOption('--config ', 'the configuration file to use, defaults to .istanbul.yml'), formatOption('--output ', 'The output file or directory. This is required when the input is a directory, ' + 'defaults to standard output when input is a file'), formatOption('-x [-x ]', 'one or more glob patterns (e.g. "**/vendor/**" to ignore all files ' + 'under a vendor directory). Also see the --default-excludes option'), formatOption('--variable ', 'change the variable name of the global coverage variable from the ' + 'default value of `__coverage__` to something else'), formatOption('--embed-source', 'embed source code into the coverage object, defaults to false'), formatOption('--[no-]compact', 'produce [non]compact output, defaults to compact'), formatOption('--[no-]preserve-comments', 'remove / preserve comments in the output, defaults to false'), formatOption('--[no-]complete-copy', 'also copy non-javascript files to the ouput directory as is, defaults to false'), formatOption('--save-baseline', 'produce a baseline coverage.json file out of all files instrumented'), formatOption('--baseline-file ', 'filename of baseline file, defaults to coverage/coverage-baseline.json'), formatOption('--es-modules', 'source code uses es import/export module syntax') ].join('\n\n') + '\n'); console.error('\n'); }, run: function (args, callback) { var template = { config: path, output: path, x: [Array, String], variable: String, compact: Boolean, 'complete-copy': Boolean, verbose: Boolean, 'save-baseline': Boolean, 'baseline-file': path, 'embed-source': Boolean, 'preserve-comments': Boolean, 'es-modules': Boolean }, opts = nopt(template, { v : '--verbose' }, args, 0), overrides = { verbose: opts.verbose, instrumentation: { variable: opts.variable, compact: opts.compact, 'embed-source': opts['embed-source'], 'preserve-comments': opts['preserve-comments'], excludes: opts.x, 'complete-copy': opts['complete-copy'], 'save-baseline': opts['save-baseline'], 'baseline-file': opts['baseline-file'], 'es-modules': opts['es-modules'] } }, config = configuration.loadFile(opts.config, overrides), iOpts = config.instrumentation, cmdArgs = opts.argv.remain, file, stats, stream, includes, instrumenter, needBaseline = iOpts.saveBaseline(), baselineFile = path.resolve(iOpts.baselineFile()), output = opts.output; verbose = config.verbose; if (cmdArgs.length !== 1) { return callback(inputError.create('Need exactly one filename/ dirname argument for the instrument command!')); } if (iOpts.completeCopy()) { includes = ['**/*']; } else { includes = iOpts.extensions().map(function(ext) { return '**/*' + ext; }); } instrumenter = new Instrumenter({ coverageVariable: iOpts.variable(), embedSource: iOpts.embedSource(), noCompact: !iOpts.compact(), preserveComments: iOpts.preserveComments(), esModules: iOpts.esModules() }); if (needBaseline) { mkdirp.sync(path.dirname(baselineFile)); instrumenter = new BaselineCollector(instrumenter); process.on('exit', function () { console.log('Saving baseline coverage at: ' + baselineFile); fs.writeFileSync(baselineFile, JSON.stringify(instrumenter.getCoverage()), 'utf8'); }); } file = path.resolve(cmdArgs[0]); stats = fs.statSync(file); if (stats.isDirectory()) { if (!output) { return callback(inputError.create('Need an output directory [-o ] when input is a directory!')); } if (output === file) { return callback(inputError.create('Cannot instrument into the same directory/ file as input!')); } mkdirp.sync(output); filesFor({ root: file, includes: includes, excludes: opts.x || iOpts.excludes(false), // backwards-compat, *sigh* relative: true }, function (err, files) { if (err) { return callback(err); } processFiles(instrumenter, file, output, files, iOpts.extensions()); }); } else { if (output) { stream = fs.createWriteStream(output); } else { stream = process.stdout; } stream.write(instrumenter.instrumentSync(fs.readFileSync(file, 'utf8'), file)); if (stream !== process.stdout) { stream.end(); } } } }); module.exports = InstrumentCommand; istanbul-0.4.5/lib/command/report.js000066400000000000000000000111201275640326200174000ustar00rootroot00000000000000/* Copyright (c) 2012, Yahoo! Inc. All rights reserved. Copyrights licensed under the New BSD License. See the accompanying LICENSE file for terms. */ var nopt = require('nopt'), Report = require('../report'), Reporter = require('../reporter'), path = require('path'), fs = require('fs'), Collector = require('../collector'), helpFormatter = require('../util/help-formatter'), formatOption = helpFormatter.formatOption, formatPara = helpFormatter.formatPara, filesFor = require('../util/file-matcher').filesFor, util = require('util'), Command = require('./index'), configuration = require('../config'); function ReportCommand() { Command.call(this); } ReportCommand.TYPE = 'report'; util.inherits(ReportCommand, Command); function printDeprecationMessage(pat, fmt) { console.error('**********************************************************************'); console.error('DEPRECATION WARNING! You are probably using the old format of the report command'); console.error('This will stop working soon, see `istanbul help report` for the new command format'); console.error('Assuming you meant: istanbul report --include=' + pat + ' ' + fmt); console.error('**********************************************************************'); } Command.mix(ReportCommand, { synopsis: function () { return "writes reports for coverage JSON objects produced in a previous run"; }, usage: function () { console.error('\nUsage: ' + this.toolName() + ' ' + this.type() + ' [ ... ]\n\nOptions are:\n\n' + [ formatOption('--config ', 'the configuration file to use, defaults to .istanbul.yml'), formatOption('--root ', 'The input root directory for finding coverage files'), formatOption('--dir ', 'The output directory where files will be written. This defaults to ./coverage/'), formatOption('--include ', 'The glob pattern to select one or more coverage files, defaults to **/coverage*.json'), formatOption('--verbose, -v', 'verbose mode') ].join('\n\n')); console.error('\n'); console.error(' is one of '); Report.getReportList().forEach(function (name) { console.error(formatOption(name, Report.create(name).synopsis())); }); console.error(""); console.error(formatPara([ 'Default format is lcov unless otherwise specified in the config file.', 'In addition you can tweak the file names for various reports using the config file.', 'Type `istanbul help config` to see what can be tweaked.' ].join(' '))); console.error('\n'); }, run: function (args, callback) { var template = { config: path, root: path, dir: path, include: String, verbose: Boolean }, opts = nopt(template, { v : '--verbose' }, args, 0), includePattern = opts.include || '**/coverage*.json', root, collector = new Collector(), config = configuration.loadFile(opts.config, { verbose: opts.verbose, reporting: { dir: opts.dir } }), formats = opts.argv.remain, reporter = new Reporter(config); // Start: backward compatible processing if (formats.length === 2 && Report.getReportList().indexOf(formats[1]) < 0) { includePattern = formats[1]; formats = [ formats[0] ]; printDeprecationMessage(includePattern, formats[0]); } // End: backward compatible processing if (formats.length === 0) { formats = config.reporting.reports(); } if (formats.length === 0) { formats = [ 'lcov' ]; } reporter.addAll(formats); root = opts.root || process.cwd(); filesFor({ root: root, includes: [ includePattern ] }, function (err, files) { if (err) { throw err; } files.forEach(function (file) { var coverageObject = JSON.parse(fs.readFileSync(file, 'utf8')); collector.add(coverageObject); }); reporter.write(collector, false, function (err) { console.log('Done'); return callback(err); }); }); } }); module.exports = ReportCommand; istanbul-0.4.5/lib/command/test.js000066400000000000000000000015131275640326200170510ustar00rootroot00000000000000/* Copyright (c) 2012, Yahoo! Inc. All rights reserved. Copyrights licensed under the New BSD License. See the accompanying LICENSE file for terms. */ var runWithCover = require('./common/run-with-cover'), util = require('util'), Command = require('./index'); function TestCommand() { Command.call(this); } TestCommand.TYPE = 'test'; util.inherits(TestCommand, Command); Command.mix(TestCommand, { synopsis: function () { return "cover a node command only when npm_config_coverage is set. Use in an `npm test` script for conditional coverage"; }, usage: function () { runWithCover.usage(this.toolName(), this.type()); }, run: function (args, callback) { runWithCover.run(args, this.type(), !!process.env.npm_config_coverage, callback); } }); module.exports = TestCommand; istanbul-0.4.5/lib/config.js000066400000000000000000000361471275640326200157340ustar00rootroot00000000000000/* Copyright (c) 2013, Yahoo! Inc. All rights reserved. Copyrights licensed under the New BSD License. See the accompanying LICENSE file for terms. */ var path = require('path'), fs = require('fs'), existsSync = fs.existsSync || path.existsSync, CAMEL_PATTERN = /([a-z])([A-Z])/g, YML_PATTERN = /\.ya?ml$/, yaml = require('js-yaml'), defaults = require('./report/common/defaults'); function defaultConfig(includeBackCompatAttrs) { var ret = { verbose: false, instrumentation: { root: '.', extensions: ['.js'], 'default-excludes': true, excludes: [], 'embed-source': false, variable: '__coverage__', compact: true, 'preserve-comments': false, 'complete-copy': false, 'save-baseline': false, 'baseline-file': './coverage/coverage-baseline.json', 'include-all-sources': false, 'include-pid': false, 'es-modules': false }, reporting: { print: 'summary', reports: [ 'lcov' ], dir: './coverage' }, hooks: { 'hook-run-in-context': false, 'post-require-hook': null, 'handle-sigint': false }, check: { global: { statements: 0, lines: 0, branches: 0, functions: 0, excludes: [] // Currently list of files (root + path). For future, extend to patterns. }, each: { statements: 0, lines: 0, branches: 0, functions: 0, excludes: [] } } }; ret.reporting.watermarks = defaults.watermarks(); ret.reporting['report-config'] = defaults.defaultReportConfig(); if (includeBackCompatAttrs) { ret.instrumentation['preload-sources'] = false; } return ret; } function dasherize(word) { return word.replace(CAMEL_PATTERN, function (match, lch, uch) { return lch + '-' + uch.toLowerCase(); }); } function isScalar(v) { if (v === null) { return true; } return v !== undefined && !Array.isArray(v) && typeof v !== 'object'; } function isObject(v) { return typeof v === 'object' && v !== null && !Array.isArray(v); } function mergeObjects(explicit, template) { var ret = {}; Object.keys(template).forEach(function (k) { var v1 = template[k], v2 = explicit[k]; if (Array.isArray(v1)) { ret[k] = Array.isArray(v2) && v2.length > 0 ? v2 : v1; } else if (isObject(v1)) { v2 = isObject(v2) ? v2 : {}; ret[k] = mergeObjects(v2, v1); } else { ret[k] = isScalar(v2) ? v2 : v1; } }); return ret; } function mergeDefaults(explicit, implicit) { return mergeObjects(explicit || {}, implicit); } function addMethods() { var args = Array.prototype.slice.call(arguments), cons = args.shift(); args.forEach(function (arg) { var method = arg, property = dasherize(arg); cons.prototype[method] = function () { return this.config[property]; }; }); } /** * Object that returns instrumentation options * @class InstrumentOptions * @module config * @constructor * @param config the instrumentation part of the config object */ function InstrumentOptions(config) { if (config['preload-sources']) { console.error('The preload-sources option is deprecated, please use include-all-sources instead.'); config['include-all-sources'] = config['preload-sources']; } this.config = config; } /** * returns if default excludes should be turned on. Used by the `cover` command. * @method defaultExcludes * @return {Boolean} true if default excludes should be turned on */ /** * returns if non-JS files should be copied during instrumentation. Used by the * `instrument` command. * @method completeCopy * @return {Boolean} true if non-JS files should be copied */ /** * returns if the source should be embedded in the instrumented code. Used by the * `instrument` command. * @method embedSource * @return {Boolean} true if the source should be embedded in the instrumented code */ /** * the coverage variable name to use. Used by the `instrument` command. * @method variable * @return {String} the coverage variable name to use */ /** * returns if the output should be compact JS. Used by the `instrument` command. * @method compact * @return {Boolean} true if the output should be compact */ /** * returns if comments should be preserved in the generated JS. Used by the * `cover` and `instrument` commands. * @method preserveComments * @return {Boolean} true if comments should be preserved in the generated JS */ /** * returns if a zero-coverage baseline file should be written as part of * instrumentation. This allows reporting to display numbers for files that have * no tests. Used by the `instrument` command. * @method saveBaseline * @return {Boolean} true if a baseline coverage file should be written. */ /** * Sets the baseline coverage filename. Used by the `instrument` command. * @method baselineFile * @return {String} the name of the baseline coverage file. */ /** * returns if comments the JS to instrument contains es6 Module syntax. * @method esModules * @return {Boolean} true if code contains es6 import/export statements. */ /** * returns if the coverage filename should include the PID. Used by the `instrument` command. * @method includePid * @return {Boolean} true to include pid in coverage filename. */ addMethods(InstrumentOptions, 'extensions', 'defaultExcludes', 'completeCopy', 'embedSource', 'variable', 'compact', 'preserveComments', 'saveBaseline', 'baselineFile', 'esModules', 'includeAllSources', 'includePid'); /** * returns the root directory used by istanbul which is typically the root of the * source tree. Used by the `cover` and `report` commands. * @method root * @return {String} the root directory used by istanbul. */ InstrumentOptions.prototype.root = function () { return path.resolve(this.config.root); }; /** * returns an array of glob patterns that should be excluded for instrumentation. * Used by the `instrument` and `cover` commands. * @method excludes * @return {Array} an array of glob patterns that should be excluded for * instrumentation. */ InstrumentOptions.prototype.excludes = function (excludeTests) { var defs; if (this.defaultExcludes()) { defs = [ '**/node_modules/**' ]; if (excludeTests) { defs = defs.concat(['**/test/**', '**/tests/**']); } return defs.concat(this.config.excludes); } return this.config.excludes; }; /** * Object that returns reporting options * @class ReportingOptions * @module config * @constructor * @param config the reporting part of the config object */ function ReportingOptions(config) { this.config = config; } /** * returns the kind of information to be printed on the console. May be one * of `summary`, `detail`, `both` or `none`. Used by the * `cover` command. * @method print * @return {String} the kind of information to print to the console at the end * of the `cover` command execution. */ /** * returns a list of reports that should be generated at the end of a run. Used * by the `cover` and `report` commands. * @method reports * @return {Array} an array of reports that should be produced */ /** * returns the directory under which reports should be generated. Used by the * `cover` and `report` commands. * * @method dir * @return {String} the directory under which reports should be generated. */ /** * returns an object that has keys that are report format names and values that are objects * containing detailed configuration for each format. Running `istanbul help config` * will give you all the keys per report format that can be overridden. * Used by the `cover` and `report` commands. * @method reportConfig * @return {Object} detailed report configuration per report format. */ addMethods(ReportingOptions, 'print', 'reports', 'dir', 'reportConfig'); function isInvalidMark(v, key) { var prefix = 'Watermark for [' + key + '] :'; if (v.length !== 2) { return prefix + 'must be an array of length 2'; } v[0] = Number(v[0]); v[1] = Number(v[1]); if (isNaN(v[0]) || isNaN(v[1])) { return prefix + 'must have valid numbers'; } if (v[0] < 0 || v[1] < 0) { return prefix + 'must be positive numbers'; } if (v[1] > 100) { return prefix + 'cannot exceed 100'; } if (v[1] <= v[0]) { return prefix + 'low must be less than high'; } return null; } /** * returns the low and high watermarks to be used to designate whether coverage * is `low`, `medium` or `high`. Statements, functions, branches and lines can * have independent watermarks. These are respected by all reports * that color for low, medium and high coverage. See the default configuration for exact syntax * using `istanbul help config`. Used by the `cover` and `report` commands. * * @method watermarks * @return {Object} an object containing low and high watermarks for statements, * branches, functions and lines. */ ReportingOptions.prototype.watermarks = function () { var v = this.config.watermarks, defs = defaults.watermarks(), ret = {}; Object.keys(defs).forEach(function (k) { var mark = v[k], //it will already be a non-zero length array because of the way the merge works message = isInvalidMark(mark, k); if (message) { console.error(message); ret[k] = defs[k]; } else { ret[k] = mark; } }); return ret; }; /** * Object that returns hook options. Note that istanbul does not provide an * option to hook `require`. This is always done by the `cover` command. * @class HookOptions * @module config * @constructor * @param config the hooks part of the config object */ function HookOptions(config) { this.config = config; } /** * returns if `vm.runInThisContext` needs to be hooked, in addition to the standard * `require` hooks added by istanbul. This should be true for code that uses * RequireJS for example. Used by the `cover` command. * @method hookRunInContext * @return {Boolean} true if `vm.runInThisContext` needs to be hooked for coverage */ /** * returns a path to JS file or a dependent module that should be used for * post-processing files after they have been required. See the `yui-istanbul` module for * an example of a post-require hook. This particular hook modifies the yui loader when * that file is required to add istanbul interceptors. Use by the `cover` command * * @method postRequireHook * @return {String} a path to a JS file or the name of a node module that needs * to be used as a `require` post-processor */ /** * returns if istanbul needs to add a SIGINT (control-c, usually) handler to * save coverage information. Useful for getting code coverage out of processes * that run forever and need a SIGINT to terminate. * @method handleSigint * @return {Boolean} true if SIGINT needs to be hooked to write coverage information */ addMethods(HookOptions, 'hookRunInContext', 'postRequireHook', 'handleSigint'); /** * represents the istanbul configuration and provides sub-objects that can * return instrumentation, reporting and hook options respectively. * Usage * ----- * * var configObj = require('istanbul').config.loadFile(); * * console.log(configObj.reporting.reports()); * * @class Configuration * @module config * @param {Object} obj the base object to use as the configuration * @param {Object} overrides optional - override attributes that are merged into * the base config * @constructor */ function Configuration(obj, overrides) { var config = mergeDefaults(obj, defaultConfig(true)); if (isObject(overrides)) { config = mergeDefaults(overrides, config); } if (config.verbose) { console.error('Using configuration'); console.error('-------------------'); console.error(yaml.safeDump(config, { indent: 4, flowLevel: 3 })); console.error('-------------------\n'); } this.verbose = config.verbose; this.instrumentation = new InstrumentOptions(config.instrumentation); this.reporting = new ReportingOptions(config.reporting); this.hooks = new HookOptions(config.hooks); this.check = config.check; // Pass raw config sub-object. } /** * true if verbose logging is required * @property verbose * @type Boolean */ /** * instrumentation options * @property instrumentation * @type InstrumentOptions */ /** * reporting options * @property reporting * @type ReportingOptions */ /** * hook options * @property hooks * @type HookOptions */ function loadFile(file, overrides) { var defaultConfigFile = path.resolve('.istanbul.yml'), configObject; if (file) { if (!existsSync(file)) { throw new Error('Invalid configuration file specified:' + file); } } else { if (existsSync(defaultConfigFile)) { file = defaultConfigFile; } } if (file) { if (overrides && overrides.verbose === true) { console.error('Loading config: ' + file); } configObject = file.match(YML_PATTERN) ? yaml.safeLoad(fs.readFileSync(file, 'utf8'), { filename: file }) : require(path.resolve(file)); } return new Configuration(configObject, overrides); } function loadObject(obj, overrides) { return new Configuration(obj, overrides); } /** * methods to load the configuration object. * Usage * ----- * * var config = require('istanbul').config, * configObj = config.loadFile(); * * console.log(configObj.reporting.reports()); * * @class Config * @module main * @static */ module.exports = { /** * loads the specified configuration file with optional overrides. Throws * when a file is specified and it is not found. * @method loadFile * @static * @param {String} file the file to load. If falsy, the default config file, if present, is loaded. * If not a default config is used. * @param {Object} overrides - an object with override keys that are merged into the * config object loaded * @return {Configuration} the config object with overrides applied */ loadFile: loadFile, /** * loads the specified configuration object with optional overrides. * @method loadObject * @static * @param {Object} obj the object to use as the base configuration. * @param {Object} overrides - an object with override keys that are merged into the * config object * @return {Configuration} the config object with overrides applied */ loadObject: loadObject, /** * returns the default configuration object. Note that this is a plain object * and not a `Configuration` instance. * @method defaultConfig * @static * @return {Object} an object that represents the default config */ defaultConfig: defaultConfig }; istanbul-0.4.5/lib/hook.js000066400000000000000000000170121275640326200154150ustar00rootroot00000000000000/* Copyright (c) 2012, Yahoo! Inc. All rights reserved. Copyrights licensed under the New BSD License. See the accompanying LICENSE file for terms. */ /** * provides a mechanism to transform code in the scope of `require` or `vm.createScript`. * This mechanism is general and relies on a user-supplied `matcher` function that determines when transformations should be * performed and a user-supplied `transformer` function that performs the actual transform. * Instrumenting code for coverage is one specific example of useful hooking. * * Note that both the `matcher` and `transformer` must execute synchronously. * * For the common case of matching filesystem paths based on inclusion/ exclusion patterns, use the `matcherFor` * function in the istanbul API to get a matcher. * * It is up to the transformer to perform processing with side-effects, such as caching, storing the original * source code to disk in case of dynamically generated scripts etc. The `Store` class can help you with this. * * Usage * ----- * * var hook = require('istanbul').hook, * myMatcher = function (file) { return file.match(/foo/); }, * myTransformer = function (code, file) { return 'console.log("' + file + '");' + code; }; * * hook.hookRequire(myMatcher, myTransformer); * * var foo = require('foo'); //will now print foo's module path to console * * @class Hook * @module main */ var path = require('path'), fs = require('fs'), Module = require('module'), vm = require('vm'), originalLoaders = {}, originalCreateScript = vm.createScript, originalRunInThisContext = vm.runInThisContext; function transformFn(matcher, transformer, verbose) { return function (code, filename) { var shouldHook = typeof filename === 'string' && matcher(path.resolve(filename)), transformed, changed = false; if (shouldHook) { if (verbose) { console.error('Module load hook: transform [' + filename + ']'); } try { transformed = transformer(code, filename); changed = true; } catch (ex) { console.error('Transformation error; return original code'); console.error(ex); transformed = code; } } else { transformed = code; } return { code: transformed, changed: changed }; }; } function unloadRequireCache(matcher) { if (matcher && typeof require !== 'undefined' && require && require.cache) { Object.keys(require.cache).forEach(function (filename) { if (matcher(filename)) { delete require.cache[filename]; } }); } } /** * hooks `require` to return transformed code to the node module loader. * Exceptions in the transform result in the original code being used instead. * @method hookRequire * @static * @param matcher {Function(filePath)} a function that is called with the absolute path to the file being * `require`-d. Should return a truthy value when transformations need to be applied to the code, a falsy value otherwise * @param transformer {Function(code, filePath)} a function called with the original code and the associated path of the file * from where the code was loaded. Should return the transformed code. * @param options {Object} options Optional. * @param {Boolean} [options.verbose] write a line to standard error every time the transformer is called * @param {Function} [options.postLoadHook] a function that is called with the name of the file being * required. This is called after the require is processed irrespective of whether it was transformed. */ function hookRequire(matcher, transformer, options) { options = options || {}; var extensions, fn = transformFn(matcher, transformer, options.verbose), postLoadHook = options.postLoadHook && typeof options.postLoadHook === 'function' ? options.postLoadHook : null; extensions = options.extensions || ['.js']; extensions.forEach(function(ext){ if (!(ext in originalLoaders)) { originalLoaders[ext] = Module._extensions[ext] || Module._extensions['.js']; } Module._extensions[ext] = function (module, filename) { var ret = fn(fs.readFileSync(filename, 'utf8'), filename); if (ret.changed) { module._compile(ret.code, filename); } else { originalLoaders[ext](module, filename); } if (postLoadHook) { postLoadHook(filename); } }; }); } /** * unhook `require` to restore it to its original state. * @method unhookRequire * @static */ function unhookRequire() { Object.keys(originalLoaders).forEach(function(ext) { Module._extensions[ext] = originalLoaders[ext]; }); } /** * hooks `vm.createScript` to return transformed code out of which a `Script` object will be created. * Exceptions in the transform result in the original code being used instead. * @method hookCreateScript * @static * @param matcher {Function(filePath)} a function that is called with the filename passed to `vm.createScript` * Should return a truthy value when transformations need to be applied to the code, a falsy value otherwise * @param transformer {Function(code, filePath)} a function called with the original code and the filename passed to * `vm.createScript`. Should return the transformed code. * @param options {Object} options Optional. * @param {Boolean} [options.verbose] write a line to standard error every time the transformer is called */ function hookCreateScript(matcher, transformer, opts) { opts = opts || {}; var fn = transformFn(matcher, transformer, opts.verbose); vm.createScript = function (code, file) { var ret = fn(code, file); return originalCreateScript(ret.code, file); }; } /** * unhooks vm.createScript, restoring it to its original state. * @method unhookCreateScript * @static */ function unhookCreateScript() { vm.createScript = originalCreateScript; } /** * hooks `vm.runInThisContext` to return transformed code. * @method hookRunInThisContext * @static * @param matcher {Function(filePath)} a function that is called with the filename passed to `vm.createScript` * Should return a truthy value when transformations need to be applied to the code, a falsy value otherwise * @param transformer {Function(code, filePath)} a function called with the original code and the filename passed to * `vm.createScript`. Should return the transformed code. * @param options {Object} options Optional. * @param {Boolean} [options.verbose] write a line to standard error every time the transformer is called */ function hookRunInThisContext(matcher, transformer, opts) { opts = opts || {}; var fn = transformFn(matcher, transformer, opts.verbose); vm.runInThisContext = function (code, file) { var ret = fn(code, file); return originalRunInThisContext(ret.code, file); }; } /** * unhooks vm.runInThisContext, restoring it to its original state. * @method unhookRunInThisContext * @static */ function unhookRunInThisContext() { vm.runInThisContext = originalRunInThisContext; } module.exports = { hookRequire: hookRequire, unhookRequire: unhookRequire, hookCreateScript: hookCreateScript, unhookCreateScript: unhookCreateScript, hookRunInThisContext : hookRunInThisContext, unhookRunInThisContext : unhookRunInThisContext, unloadRequireCache: unloadRequireCache }; istanbul-0.4.5/lib/instrumenter.js000066400000000000000000001301331275640326200172140ustar00rootroot00000000000000/* Copyright (c) 2012, Yahoo! Inc. All rights reserved. Copyrights licensed under the New BSD License. See the accompanying LICENSE file for terms. */ /*global esprima, escodegen, window */ (function (isNode) { "use strict"; var SYNTAX, nodeType, ESP = isNode ? require('esprima') : esprima, ESPGEN = isNode ? require('escodegen') : escodegen, //TODO - package as dependency crypto = isNode ? require('crypto') : null, LEADER_WRAP = '(function () { ', TRAILER_WRAP = '\n}());', COMMENT_RE = /^\s*istanbul\s+ignore\s+(if|else|next)(?=\W|$)/, astgen, preconditions, cond, isArray = Array.isArray; /* istanbul ignore if: untestable */ if (!isArray) { isArray = function (thing) { return thing && Object.prototype.toString.call(thing) === '[object Array]'; }; } if (!isNode) { preconditions = { 'Could not find esprima': ESP, 'Could not find escodegen': ESPGEN, 'JSON object not in scope': JSON, 'Array does not implement push': [].push, 'Array does not implement unshift': [].unshift }; /* istanbul ignore next: untestable */ for (cond in preconditions) { if (preconditions.hasOwnProperty(cond)) { if (!preconditions[cond]) { throw new Error(cond); } } } } function generateTrackerVar(filename, omitSuffix) { var hash, suffix; if (crypto !== null) { hash = crypto.createHash('md5'); hash.update(filename); suffix = hash.digest('base64'); //trim trailing equal signs, turn identifier unsafe chars to safe ones + => _ and / => $ suffix = suffix.replace(new RegExp('=', 'g'), '') .replace(new RegExp('\\+', 'g'), '_') .replace(new RegExp('/', 'g'), '$'); } else { window.__cov_seq = window.__cov_seq || 0; window.__cov_seq += 1; suffix = window.__cov_seq; } return '__cov_' + (omitSuffix ? '' : suffix); } function pushAll(ary, thing) { if (!isArray(thing)) { thing = [ thing ]; } Array.prototype.push.apply(ary, thing); } SYNTAX = { // keep in sync with estraverse's VisitorKeys AssignmentExpression: ['left', 'right'], AssignmentPattern: ['left', 'right'], ArrayExpression: ['elements'], ArrayPattern: ['elements'], ArrowFunctionExpression: ['params', 'body'], AwaitExpression: ['argument'], // CAUTION: It's deferred to ES7. BlockStatement: ['body'], BinaryExpression: ['left', 'right'], BreakStatement: ['label'], CallExpression: ['callee', 'arguments'], CatchClause: ['param', 'body'], ClassBody: ['body'], ClassDeclaration: ['id', 'superClass', 'body'], ClassExpression: ['id', 'superClass', 'body'], ComprehensionBlock: ['left', 'right'], // CAUTION: It's deferred to ES7. ComprehensionExpression: ['blocks', 'filter', 'body'], // CAUTION: It's deferred to ES7. ConditionalExpression: ['test', 'consequent', 'alternate'], ContinueStatement: ['label'], DebuggerStatement: [], DirectiveStatement: [], DoWhileStatement: ['body', 'test'], EmptyStatement: [], ExportAllDeclaration: ['source'], ExportDefaultDeclaration: ['declaration'], ExportNamedDeclaration: ['declaration', 'specifiers', 'source'], ExportSpecifier: ['exported', 'local'], ExpressionStatement: ['expression'], ForStatement: ['init', 'test', 'update', 'body'], ForInStatement: ['left', 'right', 'body'], ForOfStatement: ['left', 'right', 'body'], FunctionDeclaration: ['id', 'params', 'body'], FunctionExpression: ['id', 'params', 'body'], GeneratorExpression: ['blocks', 'filter', 'body'], // CAUTION: It's deferred to ES7. Identifier: [], IfStatement: ['test', 'consequent', 'alternate'], ImportDeclaration: ['specifiers', 'source'], ImportDefaultSpecifier: ['local'], ImportNamespaceSpecifier: ['local'], ImportSpecifier: ['imported', 'local'], Literal: [], LabeledStatement: ['label', 'body'], LogicalExpression: ['left', 'right'], MetaProperty: ['meta', 'property'], MemberExpression: ['object', 'property'], MethodDefinition: ['key', 'value'], ModuleSpecifier: [], NewExpression: ['callee', 'arguments'], ObjectExpression: ['properties'], ObjectPattern: ['properties'], Program: ['body'], Property: ['key', 'value'], RestElement: [ 'argument' ], ReturnStatement: ['argument'], SequenceExpression: ['expressions'], SpreadElement: ['argument'], Super: [], SwitchStatement: ['discriminant', 'cases'], SwitchCase: ['test', 'consequent'], TaggedTemplateExpression: ['tag', 'quasi'], TemplateElement: [], TemplateLiteral: ['quasis', 'expressions'], ThisExpression: [], ThrowStatement: ['argument'], TryStatement: ['block', 'handler', 'finalizer'], UnaryExpression: ['argument'], UpdateExpression: ['argument'], VariableDeclaration: ['declarations'], VariableDeclarator: ['id', 'init'], WhileStatement: ['test', 'body'], WithStatement: ['object', 'body'], YieldExpression: ['argument'] }; for (nodeType in SYNTAX) { /* istanbul ignore else: has own property */ if (SYNTAX.hasOwnProperty(nodeType)) { SYNTAX[nodeType] = { name: nodeType, children: SYNTAX[nodeType] }; } } astgen = { variable: function (name) { return { type: SYNTAX.Identifier.name, name: name }; }, stringLiteral: function (str) { return { type: SYNTAX.Literal.name, value: String(str) }; }, numericLiteral: function (num) { return { type: SYNTAX.Literal.name, value: Number(num) }; }, statement: function (contents) { return { type: SYNTAX.ExpressionStatement.name, expression: contents }; }, dot: function (obj, field) { return { type: SYNTAX.MemberExpression.name, computed: false, object: obj, property: field }; }, subscript: function (obj, sub) { return { type: SYNTAX.MemberExpression.name, computed: true, object: obj, property: sub }; }, postIncrement: function (obj) { return { type: SYNTAX.UpdateExpression.name, operator: '++', prefix: false, argument: obj }; }, sequence: function (one, two) { return { type: SYNTAX.SequenceExpression.name, expressions: [one, two] }; }, returnStatement: function (expr) { return { type: SYNTAX.ReturnStatement.name, argument: expr }; } }; function Walker(walkMap, preprocessor, scope, debug) { this.walkMap = walkMap; this.preprocessor = preprocessor; this.scope = scope; this.debug = debug; if (this.debug) { this.level = 0; this.seq = true; } } function defaultWalker(node, walker) { var type = node.type, preprocessor, postprocessor, children = SYNTAX[type], // don't run generated nodes thru custom walks otherwise we will attempt to instrument the instrumentation code :) applyCustomWalker = !!node.loc || node.type === SYNTAX.Program.name, walkerFn = applyCustomWalker ? walker.walkMap[type] : null, i, j, walkFnIndex, childType, childNode, ret, childArray, childElement, pathElement, assignNode, isLast; if (!SYNTAX[type]) { console.error(node); console.error('Unsupported node type:' + type); return; } children = SYNTAX[type].children; /* istanbul ignore if: guard */ if (node.walking) { throw new Error('Infinite regress: Custom walkers may NOT call walker.apply(node)'); } node.walking = true; ret = walker.apply(node, walker.preprocessor); preprocessor = ret.preprocessor; if (preprocessor) { delete ret.preprocessor; ret = walker.apply(node, preprocessor); } if (isArray(walkerFn)) { for (walkFnIndex = 0; walkFnIndex < walkerFn.length; walkFnIndex += 1) { isLast = walkFnIndex === walkerFn.length - 1; ret = walker.apply(ret, walkerFn[walkFnIndex]); /*istanbul ignore next: paranoid check */ if (ret.type !== type && !isLast) { throw new Error('Only the last walker is allowed to change the node type: [type was: ' + type + ' ]'); } } } else { if (walkerFn) { ret = walker.apply(node, walkerFn); } } if (node.skipSelf) { return; } for (i = 0; i < children.length; i += 1) { childType = children[i]; childNode = node[childType]; if (childNode && !childNode.skipWalk) { pathElement = { node: node, property: childType }; if (isArray(childNode)) { childArray = []; for (j = 0; j < childNode.length; j += 1) { childElement = childNode[j]; pathElement.index = j; if (childElement) { assignNode = walker.apply(childElement, null, pathElement); if (isArray(assignNode.prepend)) { pushAll(childArray, assignNode.prepend); delete assignNode.prepend; } } else { assignNode = undefined; } pushAll(childArray, assignNode); } node[childType] = childArray; } else { assignNode = walker.apply(childNode, null, pathElement); /*istanbul ignore if: paranoid check */ if (isArray(assignNode.prepend)) { throw new Error('Internal error: attempt to prepend statements in disallowed (non-array) context'); /* if this should be allowed, this is how to solve it tmpNode = { type: 'BlockStatement', body: [] }; pushAll(tmpNode.body, assignNode.prepend); pushAll(tmpNode.body, assignNode); node[childType] = tmpNode; delete assignNode.prepend; */ } else { node[childType] = assignNode; } } } } postprocessor = ret.postprocessor; if (postprocessor) { delete ret.postprocessor; ret = walker.apply(ret, postprocessor); } delete node.walking; return ret; } Walker.prototype = { startWalk: function (node) { this.path = []; this.apply(node); }, apply: function (node, walkFn, pathElement) { var ret, i, seq, prefix; walkFn = walkFn || defaultWalker; if (this.debug) { this.seq += 1; this.level += 1; seq = this.seq; prefix = ''; for (i = 0; i < this.level; i += 1) { prefix += ' '; } console.log(prefix + 'Enter (' + seq + '):' + node.type); } if (pathElement) { this.path.push(pathElement); } ret = walkFn.call(this.scope, node, this); if (pathElement) { this.path.pop(); } if (this.debug) { this.level -= 1; console.log(prefix + 'Return (' + seq + '):' + node.type); } return ret || node; }, startLineForNode: function (node) { return node && node.loc && node.loc.start ? node.loc.start.line : /* istanbul ignore next: guard */ null; }, ancestor: function (n) { return this.path.length > n - 1 ? this.path[this.path.length - n] : /* istanbul ignore next: guard */ null; }, parent: function () { return this.ancestor(1); }, isLabeled: function () { var el = this.parent(); return el && el.node.type === SYNTAX.LabeledStatement.name; } }; /** * mechanism to instrument code for coverage. It uses the `esprima` and * `escodegen` libraries for JS parsing and code generation respectively. * * Works on `node` as well as the browser. * * Usage on nodejs * --------------- * * var instrumenter = new require('istanbul').Instrumenter(), * changed = instrumenter.instrumentSync('function meaningOfLife() { return 42; }', 'filename.js'); * * Usage in a browser * ------------------ * * Load `esprima.js`, `escodegen.js` and `instrumenter.js` (this file) using `script` tags or other means. * * Create an instrumenter object as: * * var instrumenter = new Instrumenter(), * changed = instrumenter.instrumentSync('function meaningOfLife() { return 42; }', 'filename.js'); * * Aside from demonstration purposes, it is unclear why you would want to instrument code in a browser. * * @class Instrumenter * @constructor * @param {Object} options Optional. Configuration options. * @param {String} [options.coverageVariable] the global variable name to use for * tracking coverage. Defaults to `__coverage__` * @param {Boolean} [options.embedSource] whether to embed the source code of every * file as an array in the file coverage object for that file. Defaults to `false` * @param {Boolean} [options.preserveComments] whether comments should be preserved in the output. Defaults to `false` * @param {Boolean} [options.noCompact] emit readable code when set. Defaults to `false` * @param {Boolean} [options.esModules] whether the code to instrument contains uses es * imports or exports. * @param {Boolean} [options.noAutoWrap] do not automatically wrap the source in * an anonymous function before covering it. By default, code is wrapped in * an anonymous function before it is parsed. This is done because * some nodejs libraries have `return` statements outside of * a function which is technically invalid Javascript and causes the parser to fail. * This construct, however, works correctly in node since module loading * is done in the context of an anonymous function. * * Note that the semantics of the code *returned* by the instrumenter does not change in any way. * The function wrapper is "unwrapped" before the instrumented code is generated. * @param {Object} [options.codeGenerationOptions] an object that is directly passed to the `escodegen` * library as configuration for code generation. The `noCompact` setting is not honored when this * option is specified * @param {Boolean} [options.debug] assist in debugging. Currently, the only effect of * setting this option is a pretty-print of the coverage variable. Defaults to `false` * @param {Boolean} [options.walkDebug] assist in debugging of the AST walker used by this class. * */ function Instrumenter(options) { this.opts = options || { debug: false, walkDebug: false, coverageVariable: '__coverage__', codeGenerationOptions: undefined, noAutoWrap: false, noCompact: false, embedSource: false, preserveComments: false, esModules: false }; if (this.opts.esModules && !this.opts.noAutoWrap) { this.opts.noAutoWrap = true; if (this.opts.debug) { console.log('Setting noAutoWrap to true as required by esModules'); } } this.walker = new Walker({ ArrowFunctionExpression: [ this.arrowBlockConverter ], ExpressionStatement: this.coverStatement, ExportNamedDeclaration: this.coverExport, BreakStatement: this.coverStatement, ContinueStatement: this.coverStatement, DebuggerStatement: this.coverStatement, ReturnStatement: this.coverStatement, ThrowStatement: this.coverStatement, TryStatement: [ this.paranoidHandlerCheck, this.coverStatement], VariableDeclaration: this.coverStatement, IfStatement: [ this.ifBlockConverter, this.coverStatement, this.ifBranchInjector ], ForStatement: [ this.skipInit, this.loopBlockConverter, this.coverStatement ], ForInStatement: [ this.skipLeft, this.loopBlockConverter, this.coverStatement ], ForOfStatement: [ this.skipLeft, this.loopBlockConverter, this.coverStatement ], WhileStatement: [ this.loopBlockConverter, this.coverStatement ], DoWhileStatement: [ this.loopBlockConverter, this.coverStatement ], SwitchStatement: [ this.coverStatement, this.switchBranchInjector ], SwitchCase: [ this.switchCaseInjector ], WithStatement: [ this.withBlockConverter, this.coverStatement ], FunctionDeclaration: [ this.coverFunction, this.coverStatement ], FunctionExpression: this.coverFunction, LabeledStatement: this.coverStatement, ConditionalExpression: this.conditionalBranchInjector, LogicalExpression: this.logicalExpressionBranchInjector, ObjectExpression: this.maybeAddType, MetaProperty: this.coverMetaProperty, }, this.extractCurrentHint, this, this.opts.walkDebug); //unit testing purposes only if (this.opts.backdoor && this.opts.backdoor.omitTrackerSuffix) { this.omitTrackerSuffix = true; } } Instrumenter.prototype = { /** * synchronous instrumentation method. Throws when illegal code is passed to it * @method instrumentSync * @param {String} code the code to be instrumented as a String * @param {String} filename Optional. The name of the file from which * the code was read. A temporary filename is generated when not specified. * Not specifying a filename is only useful for unit tests and demonstrations * of this library. */ instrumentSync: function (code, filename) { var program; //protect from users accidentally passing in a Buffer object instead if (typeof code !== 'string') { throw new Error('Code must be string'); } if (code.charAt(0) === '#') { //shebang, 'comment' it out, won't affect syntax tree locations for things we care about code = '//' + code; } if (!this.opts.noAutoWrap) { code = LEADER_WRAP + code + TRAILER_WRAP; } try { program = ESP.parse(code, { loc: true, range: true, tokens: this.opts.preserveComments, comment: true, sourceType: this.opts.esModules ? 'module' : 'script' }); } catch (e) { console.log('Failed to parse file: ' + filename); throw e; } if (this.opts.preserveComments) { program = ESPGEN.attachComments(program, program.comments, program.tokens); } if (!this.opts.noAutoWrap) { program = { type: SYNTAX.Program.name, body: program.body[0].expression.callee.body.body, comments: program.comments }; } return this.instrumentASTSync(program, filename, code); }, filterHints: function (comments) { var ret = [], i, comment, groups; if (!(comments && isArray(comments))) { return ret; } for (i = 0; i < comments.length; i += 1) { comment = comments[i]; /* istanbul ignore else: paranoid check */ if (comment && comment.value && comment.range && isArray(comment.range)) { groups = String(comment.value).match(COMMENT_RE); if (groups) { ret.push({ type: groups[1], start: comment.range[0], end: comment.range[1] }); } } } return ret; }, extractCurrentHint: function (node) { if (!node.range) { return; } var i = this.currentState.lastHintPosition + 1, hints = this.currentState.hints, nodeStart = node.range[0], hint; this.currentState.currentHint = null; while (i < hints.length) { hint = hints[i]; if (hint.end < nodeStart) { this.currentState.currentHint = hint; this.currentState.lastHintPosition = i; i += 1; } else { break; } } }, /** * synchronous instrumentation method that instruments an AST instead. * @method instrumentASTSync * @param {String} program the AST to be instrumented * @param {String} filename Optional. The name of the file from which * the code was read. A temporary filename is generated when not specified. * Not specifying a filename is only useful for unit tests and demonstrations * of this library. * @param {String} originalCode the original code corresponding to the AST, * used for embedding the source into the coverage object */ instrumentASTSync: function (program, filename, originalCode) { var usingStrict = false, codegenOptions, generated, preamble, lineCount, i; filename = filename || String(new Date().getTime()) + '.js'; this.sourceMap = null; this.coverState = { path: filename, s: {}, b: {}, f: {}, fnMap: {}, statementMap: {}, branchMap: {} }; this.currentState = { trackerVar: generateTrackerVar(filename, this.omitTrackerSuffix), func: 0, branch: 0, variable: 0, statement: 0, hints: this.filterHints(program.comments), currentHint: null, lastHintPosition: -1, ignoring: 0 }; if (program.body && program.body.length > 0 && this.isUseStrictExpression(program.body[0])) { //nuke it program.body.shift(); //and add it back at code generation time usingStrict = true; } this.walker.startWalk(program); codegenOptions = this.opts.codeGenerationOptions || { format: { compact: !this.opts.noCompact }}; codegenOptions.comment = this.opts.preserveComments; //console.log(JSON.stringify(program, undefined, 2)); generated = ESPGEN.generate(program, codegenOptions); preamble = this.getPreamble(originalCode || '', usingStrict); if (generated.map && generated.code) { lineCount = preamble.split(/\r\n|\r|\n/).length; // offset all the generated line numbers by the number of lines in the preamble for (i = 0; i < generated.map._mappings._array.length; i += 1) { generated.map._mappings._array[i].generatedLine += lineCount; } this.sourceMap = generated.map; generated = generated.code; } return preamble + '\n' + generated + '\n'; }, /** * Callback based instrumentation. Note that this still executes synchronously in the same process tick * and calls back immediately. It only provides the options for callback style error handling as * opposed to a `try-catch` style and nothing more. Implemented as a wrapper over `instrumentSync` * * @method instrument * @param {String} code the code to be instrumented as a String * @param {String} filename Optional. The name of the file from which * the code was read. A temporary filename is generated when not specified. * Not specifying a filename is only useful for unit tests and demonstrations * of this library. * @param {Function(err, instrumentedCode)} callback - the callback function */ instrument: function (code, filename, callback) { if (!callback && typeof filename === 'function') { callback = filename; filename = null; } try { callback(null, this.instrumentSync(code, filename)); } catch (ex) { callback(ex); } }, /** * returns the file coverage object for the code that was instrumented * just before calling this method. Note that this represents a * "zero-coverage" object which is not even representative of the code * being loaded in node or a browser (which would increase the statement * counts for mainline code). * @method lastFileCoverage * @return {Object} a "zero-coverage" file coverage object for the code last instrumented * by this instrumenter */ lastFileCoverage: function () { return this.coverState; }, /** * returns the source map object for the code that was instrumented * just before calling this method. * @method lastSourceMap * @return {Object} a source map object for the code last instrumented * by this instrumenter */ lastSourceMap: function () { return this.sourceMap; }, fixColumnPositions: function (coverState) { var offset = LEADER_WRAP.length, fixer = function (loc) { if (loc.start.line === 1) { loc.start.column -= offset; } if (loc.end.line === 1) { loc.end.column -= offset; } }, k, obj, i, locations; obj = coverState.statementMap; for (k in obj) { /* istanbul ignore else: has own property */ if (obj.hasOwnProperty(k)) { fixer(obj[k]); } } obj = coverState.fnMap; for (k in obj) { /* istanbul ignore else: has own property */ if (obj.hasOwnProperty(k)) { fixer(obj[k].loc); } } obj = coverState.branchMap; for (k in obj) { /* istanbul ignore else: has own property */ if (obj.hasOwnProperty(k)) { locations = obj[k].locations; for (i = 0; i < locations.length; i += 1) { fixer(locations[i]); } } } }, getPreamble: function (sourceCode, emitUseStrict) { var varName = this.opts.coverageVariable || '__coverage__', file = this.coverState.path.replace(/\\/g, '\\\\'), tracker = this.currentState.trackerVar, coverState, strictLine = emitUseStrict ? '"use strict";' : '', // return replacements using the function to ensure that the replacement is // treated like a dumb string and not as a string with RE replacement patterns replacer = function (s) { return function () { return s; }; }, code; if (!this.opts.noAutoWrap) { this.fixColumnPositions(this.coverState); } if (this.opts.embedSource) { this.coverState.code = sourceCode.split(/(?:\r?\n)|\r/); } coverState = this.opts.debug ? JSON.stringify(this.coverState, undefined, 4) : JSON.stringify(this.coverState); code = [ "%STRICT%", "var %VAR% = (Function('return this'))();", "if (!%VAR%.%GLOBAL%) { %VAR%.%GLOBAL% = {}; }", "%VAR% = %VAR%.%GLOBAL%;", "if (!(%VAR%['%FILE%'])) {", " %VAR%['%FILE%'] = %OBJECT%;", "}", "%VAR% = %VAR%['%FILE%'];" ].join("\n") .replace(/%STRICT%/g, replacer(strictLine)) .replace(/%VAR%/g, replacer(tracker)) .replace(/%GLOBAL%/g, replacer(varName)) .replace(/%FILE%/g, replacer(file)) .replace(/%OBJECT%/g, replacer(coverState)); return code; }, startIgnore: function () { this.currentState.ignoring += 1; }, endIgnore: function () { this.currentState.ignoring -= 1; }, convertToBlock: function (node) { if (!node) { return { type: 'BlockStatement', body: [] }; } else if (node.type === 'BlockStatement') { return node; } else { return { type: 'BlockStatement', body: [ node ] }; } }, arrowBlockConverter: function (node) { var retStatement; if (node.expression) { // turn expression nodes into a block with a return statement retStatement = astgen.returnStatement(node.body); // ensure the generated return statement is covered retStatement.loc = node.body.loc; node.body = this.convertToBlock(retStatement); node.expression = false; } }, paranoidHandlerCheck: function (node) { // if someone is using an older esprima on the browser // convert handlers array to single handler attribute // containing its first element /* istanbul ignore next */ if (!node.handler && node.handlers) { node.handler = node.handlers[0]; } }, ifBlockConverter: function (node) { node.consequent = this.convertToBlock(node.consequent); node.alternate = this.convertToBlock(node.alternate); }, loopBlockConverter: function (node) { node.body = this.convertToBlock(node.body); }, withBlockConverter: function (node) { node.body = this.convertToBlock(node.body); }, statementName: function (location, initValue) { var sName, ignoring = !!this.currentState.ignoring; location.skip = ignoring || undefined; initValue = initValue || 0; this.currentState.statement += 1; sName = this.currentState.statement; this.coverState.statementMap[sName] = location; this.coverState.s[sName] = initValue; return sName; }, skipInit: function (node /*, walker */) { if (node.init) { node.init.skipWalk = true; } }, skipLeft: function (node /*, walker */) { node.left.skipWalk = true; }, isUseStrictExpression: function (node) { return node && node.type === SYNTAX.ExpressionStatement.name && node.expression && node.expression.type === SYNTAX.Literal.name && node.expression.value === 'use strict'; }, maybeSkipNode: function (node, type) { var alreadyIgnoring = !!this.currentState.ignoring, hint = this.currentState.currentHint, ignoreThis = !alreadyIgnoring && hint && hint.type === type; if (ignoreThis) { this.startIgnore(); node.postprocessor = this.endIgnore; return true; } return false; }, coverMetaProperty: function(node /* , walker */) { node.skipSelf = true; }, coverStatement: function (node, walker) { var sName, incrStatementCount, parent, grandParent; this.maybeSkipNode(node, 'next'); if (this.isUseStrictExpression(node)) { grandParent = walker.ancestor(2); /* istanbul ignore else: difficult to test */ if (grandParent) { if ((grandParent.node.type === SYNTAX.FunctionExpression.name || grandParent.node.type === SYNTAX.FunctionDeclaration.name) && walker.parent().node.body[0] === node) { return; } } } if (node.type === SYNTAX.FunctionDeclaration.name) { // Called for the side-effect of setting the function's statement count to 1. this.statementName(node.loc, 1); } else { // We let `coverExport` handle ExportNamedDeclarations. parent = walker.parent(); if (parent && parent.node.type === SYNTAX.ExportNamedDeclaration.name) { return; } sName = this.statementName(node.loc); incrStatementCount = astgen.statement( astgen.postIncrement( astgen.subscript( astgen.dot(astgen.variable(this.currentState.trackerVar), astgen.variable('s')), astgen.stringLiteral(sName) ) ) ); this.splice(incrStatementCount, node, walker); } }, coverExport: function (node, walker) { var sName, incrStatementCount; if ( !node.declaration || !node.declaration.declarations ) { return; } this.maybeSkipNode(node, 'next'); sName = this.statementName(node.declaration.loc); incrStatementCount = astgen.statement( astgen.postIncrement( astgen.subscript( astgen.dot(astgen.variable(this.currentState.trackerVar), astgen.variable('s')), astgen.stringLiteral(sName) ) ) ); this.splice(incrStatementCount, node, walker); }, splice: function (statements, node, walker) { var targetNode = walker.isLabeled() ? walker.parent().node : node; targetNode.prepend = targetNode.prepend || []; pushAll(targetNode.prepend, statements); }, functionName: function (node, line, location) { this.currentState.func += 1; var id = this.currentState.func, ignoring = !!this.currentState.ignoring, name = node.id ? node.id.name : '(anonymous_' + id + ')', clone = function (attr) { var obj = location[attr] || /* istanbul ignore next */ {}; return { line: obj.line, column: obj.column }; }; this.coverState.fnMap[id] = { name: name, line: line, loc: { start: clone('start'), end: clone('end') }, skip: ignoring || undefined }; this.coverState.f[id] = 0; return id; }, coverFunction: function (node, walker) { var id, body = node.body, blockBody = body.body, popped; this.maybeSkipNode(node, 'next'); id = this.functionName(node, walker.startLineForNode(node), { start: node.loc.start, end: { line: node.body.loc.start.line, column: node.body.loc.start.column } }); if (blockBody.length > 0 && this.isUseStrictExpression(blockBody[0])) { popped = blockBody.shift(); } blockBody.unshift( astgen.statement( astgen.postIncrement( astgen.subscript( astgen.dot(astgen.variable(this.currentState.trackerVar), astgen.variable('f')), astgen.stringLiteral(id) ) ) ) ); if (popped) { blockBody.unshift(popped); } }, branchName: function (type, startLine, pathLocations) { var bName, paths = [], locations = [], i, ignoring = !!this.currentState.ignoring; this.currentState.branch += 1; bName = this.currentState.branch; for (i = 0; i < pathLocations.length; i += 1) { pathLocations[i].skip = pathLocations[i].skip || ignoring || undefined; locations.push(pathLocations[i]); paths.push(0); } this.coverState.b[bName] = paths; this.coverState.branchMap[bName] = { line: startLine, type: type, locations: locations }; return bName; }, branchIncrementExprAst: function (varName, branchIndex, down) { var ret = astgen.postIncrement( astgen.subscript( astgen.subscript( astgen.dot(astgen.variable(this.currentState.trackerVar), astgen.variable('b')), astgen.stringLiteral(varName) ), astgen.numericLiteral(branchIndex) ), down ); return ret; }, locationsForNodes: function (nodes) { var ret = [], i; for (i = 0; i < nodes.length; i += 1) { ret.push(nodes[i].loc); } return ret; }, ifBranchInjector: function (node, walker) { var alreadyIgnoring = !!this.currentState.ignoring, hint = this.currentState.currentHint, ignoreThen = !alreadyIgnoring && hint && hint.type === 'if', ignoreElse = !alreadyIgnoring && hint && hint.type === 'else', line = node.loc.start.line, col = node.loc.start.column, makeLoc = function () { return { line: line, column: col }; }, bName = this.branchName('if', walker.startLineForNode(node), [ { start: makeLoc(), end: makeLoc(), skip: ignoreThen || undefined }, { start: makeLoc(), end: makeLoc(), skip: ignoreElse || undefined } ]), thenBody = node.consequent.body, elseBody = node.alternate.body, child; thenBody.unshift(astgen.statement(this.branchIncrementExprAst(bName, 0))); elseBody.unshift(astgen.statement(this.branchIncrementExprAst(bName, 1))); if (ignoreThen) { child = node.consequent; child.preprocessor = this.startIgnore; child.postprocessor = this.endIgnore; } if (ignoreElse) { child = node.alternate; child.preprocessor = this.startIgnore; child.postprocessor = this.endIgnore; } }, branchLocationFor: function (name, index) { return this.coverState.branchMap[name].locations[index]; }, switchBranchInjector: function (node, walker) { var cases = node.cases, bName, i; if (!(cases && cases.length > 0)) { return; } bName = this.branchName('switch', walker.startLineForNode(node), this.locationsForNodes(cases)); for (i = 0; i < cases.length; i += 1) { cases[i].branchLocation = this.branchLocationFor(bName, i); cases[i].consequent.unshift(astgen.statement(this.branchIncrementExprAst(bName, i))); } }, switchCaseInjector: function (node) { var location = node.branchLocation; delete node.branchLocation; if (this.maybeSkipNode(node, 'next')) { location.skip = true; } }, conditionalBranchInjector: function (node, walker) { var bName = this.branchName('cond-expr', walker.startLineForNode(node), this.locationsForNodes([ node.consequent, node.alternate ])), ast1 = this.branchIncrementExprAst(bName, 0), ast2 = this.branchIncrementExprAst(bName, 1); node.consequent.preprocessor = this.maybeAddSkip(this.branchLocationFor(bName, 0)); node.alternate.preprocessor = this.maybeAddSkip(this.branchLocationFor(bName, 1)); node.consequent = astgen.sequence(ast1, node.consequent); node.alternate = astgen.sequence(ast2, node.alternate); }, maybeAddSkip: function (branchLocation) { return function (node) { var alreadyIgnoring = !!this.currentState.ignoring, hint = this.currentState.currentHint, ignoreThis = !alreadyIgnoring && hint && hint.type === 'next'; if (ignoreThis) { this.startIgnore(); node.postprocessor = this.endIgnore; } if (ignoreThis || alreadyIgnoring) { branchLocation.skip = true; } }; }, logicalExpressionBranchInjector: function (node, walker) { var parent = walker.parent(), leaves = [], bName, tuple, i; this.maybeSkipNode(node, 'next'); if (parent && parent.node.type === SYNTAX.LogicalExpression.name) { //already covered return; } this.findLeaves(node, leaves); bName = this.branchName('binary-expr', walker.startLineForNode(node), this.locationsForNodes(leaves.map(function (item) { return item.node; })) ); for (i = 0; i < leaves.length; i += 1) { tuple = leaves[i]; tuple.parent[tuple.property] = astgen.sequence(this.branchIncrementExprAst(bName, i), tuple.node); tuple.node.preprocessor = this.maybeAddSkip(this.branchLocationFor(bName, i)); } }, findLeaves: function (node, accumulator, parent, property) { if (node.type === SYNTAX.LogicalExpression.name) { this.findLeaves(node.left, accumulator, node, 'left'); this.findLeaves(node.right, accumulator, node, 'right'); } else { accumulator.push({ node: node, parent: parent, property: property }); } }, maybeAddType: function (node /*, walker */) { var props = node.properties, i, child; for (i = 0; i < props.length; i += 1) { child = props[i]; if (!child.type) { child.type = SYNTAX.Property.name; } } }, }; if (isNode) { module.exports = Instrumenter; } else { window.Instrumenter = Instrumenter; } }(typeof module !== 'undefined' && typeof module.exports !== 'undefined' && typeof exports !== 'undefined')); istanbul-0.4.5/lib/object-utils.js000066400000000000000000000337451275640326200170740ustar00rootroot00000000000000/* Copyright (c) 2012, Yahoo! Inc. All rights reserved. Copyrights licensed under the New BSD License. See the accompanying LICENSE file for terms. */ /** * utility methods to process coverage objects. A coverage object has the following * format. * * { * "/path/to/file1.js": { file1 coverage }, * "/path/to/file2.js": { file2 coverage } * } * * The internals of the file coverage object are intentionally not documented since * it is not a public interface. * * *Note:* When a method of this module has the word `File` in it, it will accept * one of the sub-objects of the main coverage object as an argument. Other * methods accept the higher level coverage object with multiple keys. * * Works on `node` as well as the browser. * * Usage on nodejs * --------------- * * var objectUtils = require('istanbul').utils; * * Usage in a browser * ------------------ * * Load this file using a `script` tag or other means. This will set `window.coverageUtils` * to this module's exports. * * @class ObjectUtils * @module main * @static */ (function (isNode) { /** * adds line coverage information to a file coverage object, reverse-engineering * it from statement coverage. The object passed in is updated in place. * * Note that if line coverage information is already present in the object, * it is not recomputed. * * @method addDerivedInfoForFile * @static * @param {Object} fileCoverage the coverage object for a single file */ function addDerivedInfoForFile(fileCoverage) { var statementMap = fileCoverage.statementMap, statements = fileCoverage.s, lineMap; if (!fileCoverage.l) { fileCoverage.l = lineMap = {}; Object.keys(statements).forEach(function (st) { var line = statementMap[st].start.line, count = statements[st], prevVal = lineMap[line]; if (count === 0 && statementMap[st].skip) { count = 1; } if (typeof prevVal === 'undefined' || prevVal < count) { lineMap[line] = count; } }); } } /** * adds line coverage information to all file coverage objects. * * @method addDerivedInfo * @static * @param {Object} coverage the coverage object */ function addDerivedInfo(coverage) { Object.keys(coverage).forEach(function (k) { addDerivedInfoForFile(coverage[k]); }); } /** * removes line coverage information from all file coverage objects * @method removeDerivedInfo * @static * @param {Object} coverage the coverage object */ function removeDerivedInfo(coverage) { Object.keys(coverage).forEach(function (k) { delete coverage[k].l; }); } function percent(covered, total) { var tmp; if (total > 0) { tmp = 1000 * 100 * covered / total + 5; return Math.floor(tmp / 10) / 100; } else { return 100.00; } } function computeSimpleTotals(fileCoverage, property, mapProperty) { var stats = fileCoverage[property], map = mapProperty ? fileCoverage[mapProperty] : null, ret = { total: 0, covered: 0, skipped: 0 }; Object.keys(stats).forEach(function (key) { var covered = !!stats[key], skipped = map && map[key].skip; ret.total += 1; if (covered || skipped) { ret.covered += 1; } if (!covered && skipped) { ret.skipped += 1; } }); ret.pct = percent(ret.covered, ret.total); return ret; } function computeBranchTotals(fileCoverage) { var stats = fileCoverage.b, branchMap = fileCoverage.branchMap, ret = { total: 0, covered: 0, skipped: 0 }; Object.keys(stats).forEach(function (key) { var branches = stats[key], map = branchMap[key], covered, skipped, i; for (i = 0; i < branches.length; i += 1) { covered = branches[i] > 0; skipped = map.locations && map.locations[i] && map.locations[i].skip; if (covered || skipped) { ret.covered += 1; } if (!covered && skipped) { ret.skipped += 1; } } ret.total += branches.length; }); ret.pct = percent(ret.covered, ret.total); return ret; } /** * returns a blank summary metrics object. A metrics object has the following * format. * * { * lines: lineMetrics, * statements: statementMetrics, * functions: functionMetrics, * branches: branchMetrics * linesCovered: lineCoveredCount * } * * Each individual metric object looks as follows: * * { * total: n, * covered: m, * pct: percent * } * * @method blankSummary * @static * @return {Object} a blank metrics object */ function blankSummary() { return { lines: { total: 0, covered: 0, skipped: 0, pct: 'Unknown' }, statements: { total: 0, covered: 0, skipped: 0, pct: 'Unknown' }, functions: { total: 0, covered: 0, skipped: 0, pct: 'Unknown' }, branches: { total: 0, covered: 0, skipped: 0, pct: 'Unknown' }, linesCovered: {} }; } /** * returns the summary metrics given the coverage object for a single file. See `blankSummary()` * to understand the format of the returned object. * * @method summarizeFileCoverage * @static * @param {Object} fileCoverage the coverage object for a single file. * @return {Object} the summary metrics for the file */ function summarizeFileCoverage(fileCoverage) { var ret = blankSummary(); addDerivedInfoForFile(fileCoverage); ret.lines = computeSimpleTotals(fileCoverage, 'l'); ret.functions = computeSimpleTotals(fileCoverage, 'f', 'fnMap'); ret.statements = computeSimpleTotals(fileCoverage, 's', 'statementMap'); ret.branches = computeBranchTotals(fileCoverage); ret.linesCovered = fileCoverage.l; return ret; } /** * merges two instances of file coverage objects *for the same file* * such that the execution counts are correct. * * @method mergeFileCoverage * @static * @param {Object} first the first file coverage object for a given file * @param {Object} second the second file coverage object for the same file * @return {Object} an object that is a result of merging the two. Note that * the input objects are not changed in any way. */ function mergeFileCoverage(first, second) { var ret = JSON.parse(JSON.stringify(first)), i; delete ret.l; //remove derived info Object.keys(second.s).forEach(function (k) { ret.s[k] += second.s[k]; }); Object.keys(second.f).forEach(function (k) { ret.f[k] += second.f[k]; }); Object.keys(second.b).forEach(function (k) { var retArray = ret.b[k], secondArray = second.b[k]; for (i = 0; i < retArray.length; i += 1) { retArray[i] += secondArray[i]; } }); return ret; } /** * merges multiple summary metrics objects by summing up the `totals` and * `covered` fields and recomputing the percentages. This function is generic * and can accept any number of arguments. * * @method mergeSummaryObjects * @static * @param {Object} summary... multiple summary metrics objects * @return {Object} the merged summary metrics */ function mergeSummaryObjects() { var ret = blankSummary(), args = Array.prototype.slice.call(arguments), keys = ['lines', 'statements', 'branches', 'functions'], increment = function (obj) { if (obj) { keys.forEach(function (key) { ret[key].total += obj[key].total; ret[key].covered += obj[key].covered; ret[key].skipped += obj[key].skipped; }); // keep track of all lines we have coverage for. Object.keys(obj.linesCovered).forEach(function (key) { if (!ret.linesCovered[key]) { ret.linesCovered[key] = obj.linesCovered[key]; } else { ret.linesCovered[key] += obj.linesCovered[key]; } }); } }; args.forEach(function (arg) { increment(arg); }); keys.forEach(function (key) { ret[key].pct = percent(ret[key].covered, ret[key].total); }); return ret; } /** * returns the coverage summary for a single coverage object. This is * wrapper over `summarizeFileCoverage` and `mergeSummaryObjects` for * the common case of a single coverage object * @method summarizeCoverage * @static * @param {Object} coverage the coverage object * @return {Object} summary coverage metrics across all files in the coverage object */ function summarizeCoverage(coverage) { var fileSummary = []; Object.keys(coverage).forEach(function (key) { fileSummary.push(summarizeFileCoverage(coverage[key])); }); return mergeSummaryObjects.apply(null, fileSummary); } /** * makes the coverage object generated by this library yuitest_coverage compatible. * Note that this transformation is lossy since the returned object will not have * statement and branch coverage. * * @method toYUICoverage * @static * @param {Object} coverage The `istanbul` coverage object * @return {Object} a coverage object in `yuitest_coverage` format. */ function toYUICoverage(coverage) { var ret = {}; addDerivedInfo(coverage); Object.keys(coverage).forEach(function (k) { var fileCoverage = coverage[k], lines = fileCoverage.l, functions = fileCoverage.f, fnMap = fileCoverage.fnMap, o; o = ret[k] = { lines: {}, calledLines: 0, coveredLines: 0, functions: {}, calledFunctions: 0, coveredFunctions: 0 }; Object.keys(lines).forEach(function (k) { o.lines[k] = lines[k]; o.coveredLines += 1; if (lines[k] > 0) { o.calledLines += 1; } }); Object.keys(functions).forEach(function (k) { var name = fnMap[k].name + ':' + fnMap[k].line; o.functions[name] = functions[k]; o.coveredFunctions += 1; if (functions[k] > 0) { o.calledFunctions += 1; } }); }); return ret; } /** * Creates new file coverage object with incremented hits count * on skipped statements, branches and functions * * @method incrementIgnoredTotals * @static * @param {Object} cov File coverage object * @return {Object} New file coverage object */ function incrementIgnoredTotals(cov) { //TODO: This may be slow in the browser and may break in older browsers // Look into using a library that works in Node and the browser var fileCoverage = JSON.parse(JSON.stringify(cov)); [ {mapKey: 'statementMap', hitsKey: 's'}, {mapKey: 'branchMap', hitsKey: 'b'}, {mapKey: 'fnMap', hitsKey: 'f'} ].forEach(function (keys) { Object.keys(fileCoverage[keys.mapKey]) .forEach(function (key) { var map = fileCoverage[keys.mapKey][key]; var hits = fileCoverage[keys.hitsKey]; if (keys.mapKey === 'branchMap') { var locations = map.locations; locations.forEach(function (location, index) { if (hits[key][index] === 0 && location.skip) { hits[key][index] = 1; } }); return; } if (hits[key] === 0 && map.skip) { hits[key] = 1; } }); }); return fileCoverage; } var exportables = { addDerivedInfo: addDerivedInfo, addDerivedInfoForFile: addDerivedInfoForFile, removeDerivedInfo: removeDerivedInfo, blankSummary: blankSummary, summarizeFileCoverage: summarizeFileCoverage, summarizeCoverage: summarizeCoverage, mergeFileCoverage: mergeFileCoverage, mergeSummaryObjects: mergeSummaryObjects, toYUICoverage: toYUICoverage, incrementIgnoredTotals: incrementIgnoredTotals }; /* istanbul ignore else: windows */ if (isNode) { module.exports = exportables; } else { window.coverageUtils = exportables; } }(typeof module !== 'undefined' && typeof module.exports !== 'undefined' && typeof exports !== 'undefined')); istanbul-0.4.5/lib/register-plugins.js000066400000000000000000000004741275640326200177640ustar00rootroot00000000000000 /* Copyright (c) 2012, Yahoo! Inc. All rights reserved. Copyrights licensed under the New BSD License. See the accompanying LICENSE file for terms. */ var Store = require('./store'), Report = require('./report'), Command = require('./command'); Store.loadAll(); Report.loadAll(); Command.loadAll(); istanbul-0.4.5/lib/report/000077500000000000000000000000001275640326200154315ustar00rootroot00000000000000istanbul-0.4.5/lib/report/clover.js000066400000000000000000000171201275640326200172620ustar00rootroot00000000000000var path = require('path'), util = require('util'), Report = require('./index'), FileWriter = require('../util/file-writer'), TreeSummarizer = require('../util/tree-summarizer'), utils = require('../object-utils'); /** * a `Report` implementation that produces a clover-style XML file. * * Usage * ----- * * var report = require('istanbul').Report.create('clover'); * * @class CloverReport * @module report * @extends Report * @constructor * @param {Object} opts optional * @param {String} [opts.dir] the directory in which to the clover.xml will be written * @param {String} [opts.file] the file name, defaulted to config attribute or 'clover.xml' */ function CloverReport(opts) { Report.call(this); opts = opts || {}; this.projectRoot = process.cwd(); this.dir = opts.dir || this.projectRoot; this.file = opts.file || this.getDefaultConfig().file; this.opts = opts; } CloverReport.TYPE = 'clover'; util.inherits(CloverReport, Report); function asJavaPackage(node) { return node.displayShortName(). replace(/\//g, '.'). replace(/\\/g, '.'). replace(/\.$/, ''); } function asClassName(node) { /*jslint regexp: true */ return node.fullPath().replace(/.*[\\\/]/, ''); } function quote(thing) { return '"' + thing + '"'; } function attr(n, v) { return ' ' + n + '=' + quote(v) + ' '; } function branchCoverageByLine(fileCoverage) { var branchMap = fileCoverage.branchMap, branches = fileCoverage.b, ret = {}; Object.keys(branchMap).forEach(function (k) { var line = branchMap[k].line, branchData = branches[k]; ret[line] = ret[line] || []; ret[line].push.apply(ret[line], branchData); }); Object.keys(ret).forEach(function (k) { var dataArray = ret[k], covered = dataArray.filter(function (item) { return item > 0; }), coverage = covered.length / dataArray.length * 100; ret[k] = { covered: covered.length, total: dataArray.length, coverage: coverage }; }); return ret; } function addClassStats(node, fileCoverage, writer) { fileCoverage = utils.incrementIgnoredTotals(fileCoverage); var metrics = node.metrics, branchByLine = branchCoverageByLine(fileCoverage), fnMap, lines; writer.println('\t\t\t'); writer.println('\t\t\t\t'); fnMap = fileCoverage.fnMap; lines = fileCoverage.l; Object.keys(lines).forEach(function (k) { var str = '\t\t\t\t'); }); writer.println('\t\t\t'); } function walk(node, collector, writer, level, projectRoot) { var metrics, totalFiles = 0, totalPackages = 0, totalLines = 0, tempLines = 0; if (level === 0) { metrics = node.metrics; writer.println(''); writer.println(''); writer.println('\t'); node.children.filter(function (child) { return child.kind === 'dir'; }). forEach(function (child) { totalPackages += 1; child.children.filter(function (child) { return child.kind !== 'dir'; }). forEach(function (child) { Object.keys(collector.fileCoverageFor(child.fullPath()).l).forEach(function (k){ tempLines = k; }); totalLines += Number(tempLines); totalFiles += 1; }); }); writer.println('\t\t'); } if (node.packageMetrics) { metrics = node.packageMetrics; writer.println('\t\t'); writer.println('\t\t\t'); node.children.filter(function (child) { return child.kind !== 'dir'; }). forEach(function (child) { addClassStats(child, collector.fileCoverageFor(child.fullPath()), writer); }); writer.println('\t\t'); } node.children.filter(function (child) { return child.kind === 'dir'; }). forEach(function (child) { walk(child, collector, writer, level + 1, projectRoot); }); if (level === 0) { writer.println('\t'); writer.println(''); } } Report.mix(CloverReport, { synopsis: function () { return 'XML coverage report that can be consumed by the clover tool'; }, getDefaultConfig: function () { return { file: 'clover.xml' }; }, writeReport: function (collector, sync) { var summarizer = new TreeSummarizer(), outputFile = path.join(this.dir, this.file), writer = this.opts.writer || new FileWriter(sync), projectRoot = this.projectRoot, that = this, tree, root; collector.files().forEach(function (key) { summarizer.addFileCoverageSummary(key, utils.summarizeFileCoverage(collector.fileCoverageFor(key))); }); tree = summarizer.getTreeSummary(); root = tree.root; writer.on('done', function () { that.emit('done'); }); writer.writeFile(outputFile, function (contentWriter) { walk(root, collector, contentWriter, 0, projectRoot); writer.done(); }); } }); module.exports = CloverReport; istanbul-0.4.5/lib/report/cobertura.js000066400000000000000000000162251275640326200177630ustar00rootroot00000000000000/* Copyright (c) 2012, Yahoo! Inc. All rights reserved. Copyrights licensed under the New BSD License. See the accompanying LICENSE file for terms. */ var path = require('path'), util = require('util'), Report = require('./index'), FileWriter = require('../util/file-writer'), TreeSummarizer = require('../util/tree-summarizer'), utils = require('../object-utils'); /** * a `Report` implementation that produces a cobertura-style XML file that conforms to the * http://cobertura.sourceforge.net/xml/coverage-04.dtd DTD. * * Usage * ----- * * var report = require('istanbul').Report.create('cobertura'); * * @class CoberturaReport * @module report * @extends Report * @constructor * @param {Object} opts optional * @param {String} [opts.dir] the directory in which to the cobertura-coverage.xml will be written */ function CoberturaReport(opts) { Report.call(this); opts = opts || {}; this.projectRoot = process.cwd(); this.dir = opts.dir || this.projectRoot; this.file = opts.file || this.getDefaultConfig().file; this.opts = opts; } CoberturaReport.TYPE = 'cobertura'; util.inherits(CoberturaReport, Report); function asJavaPackage(node) { return node.displayShortName(). replace(/\//g, '.'). replace(/\\/g, '.'). replace(/\.$/, ''); } function asClassName(node) { /*jslint regexp: true */ return node.fullPath().replace(/.*[\\\/]/, ''); } function quote(thing) { return '"' + thing + '"'; } function attr(n, v) { return ' ' + n + '=' + quote(v) + ' '; } function branchCoverageByLine(fileCoverage) { var branchMap = fileCoverage.branchMap, branches = fileCoverage.b, ret = {}; Object.keys(branchMap).forEach(function (k) { var line = branchMap[k].line, branchData = branches[k]; ret[line] = ret[line] || []; ret[line].push.apply(ret[line], branchData); }); Object.keys(ret).forEach(function (k) { var dataArray = ret[k], covered = dataArray.filter(function (item) { return item > 0; }), coverage = covered.length / dataArray.length * 100; ret[k] = { covered: covered.length, total: dataArray.length, coverage: coverage }; }); return ret; } function addClassStats(node, fileCoverage, writer, projectRoot) { fileCoverage = utils.incrementIgnoredTotals(fileCoverage); var metrics = node.metrics, branchByLine = branchCoverageByLine(fileCoverage), fnMap, lines; writer.println('\t\t'); writer.println('\t\t'); fnMap = fileCoverage.fnMap; Object.keys(fnMap).forEach(function (k) { var name = fnMap[k].name, hits = fileCoverage.f[k]; writer.println( '\t\t\t' ); //Add the function definition line and hits so that jenkins cobertura plugin records method hits writer.println( '\t\t\t\t' + '' + '' ); writer.println('\t\t\t'); }); writer.println('\t\t'); writer.println('\t\t'); lines = fileCoverage.l; Object.keys(lines).forEach(function (k) { var str = '\t\t\t'); }); writer.println('\t\t'); writer.println('\t\t'); } function walk(node, collector, writer, level, projectRoot) { var metrics; if (level === 0) { metrics = node.metrics; writer.println(''); writer.println(''); writer.println(''); writer.println(''); writer.println('\t' + projectRoot + ''); writer.println(''); writer.println(''); } if (node.packageMetrics) { metrics = node.packageMetrics; writer.println('\t'); writer.println('\t'); node.children.filter(function (child) { return child.kind !== 'dir'; }). forEach(function (child) { addClassStats(child, collector.fileCoverageFor(child.fullPath()), writer, projectRoot); }); writer.println('\t'); writer.println('\t'); } node.children.filter(function (child) { return child.kind === 'dir'; }). forEach(function (child) { walk(child, collector, writer, level + 1, projectRoot); }); if (level === 0) { writer.println(''); writer.println(''); } } Report.mix(CoberturaReport, { synopsis: function () { return 'XML coverage report that can be consumed by the cobertura tool'; }, getDefaultConfig: function () { return { file: 'cobertura-coverage.xml' }; }, writeReport: function (collector, sync) { var summarizer = new TreeSummarizer(), outputFile = path.join(this.dir, this.file), writer = this.opts.writer || new FileWriter(sync), projectRoot = this.projectRoot, that = this, tree, root; collector.files().forEach(function (key) { summarizer.addFileCoverageSummary(key, utils.summarizeFileCoverage(collector.fileCoverageFor(key))); }); tree = summarizer.getTreeSummary(); root = tree.root; writer.on('done', function () { that.emit('done'); }); writer.writeFile(outputFile, function (contentWriter) { walk(root, collector, contentWriter, 0, projectRoot); writer.done(); }); } }); module.exports = CoberturaReport; istanbul-0.4.5/lib/report/common/000077500000000000000000000000001275640326200167215ustar00rootroot00000000000000istanbul-0.4.5/lib/report/common/defaults.js000066400000000000000000000025221275640326200210670ustar00rootroot00000000000000/* Copyright (c) 2013, Yahoo! Inc. All rights reserved. Copyrights licensed under the New BSD License. See the accompanying LICENSE file for terms. */ var Report = require('../index'); var supportsColor = require('supports-color'); module.exports = { watermarks: function () { return { statements: [ 50, 80 ], lines: [ 50, 80 ], functions: [ 50, 80], branches: [ 50, 80 ] }; }, classFor: function (type, metrics, watermarks) { var mark = watermarks[type], value = metrics[type].pct; return value >= mark[1] ? 'high' : value >= mark[0] ? 'medium' : 'low'; }, colorize: function (str, clazz) { /* istanbul ignore if: untestable in batch mode */ var colors = { low: '31;1', medium: '33;1', high: '32;1' }; if (supportsColor && colors[clazz]) { return '\u001b[' + colors[clazz] + 'm' + str + '\u001b[0m'; } return str; }, defaultReportConfig: function () { var cfg = {}; Report.getReportList().forEach(function (type) { var rpt = Report.create(type), c = rpt.getDefaultConfig(); if (c) { cfg[type] = c; } }); return cfg; } }; istanbul-0.4.5/lib/report/html.js000066400000000000000000000532621275640326200167430ustar00rootroot00000000000000/* Copyright (c) 2012, Yahoo! Inc. All rights reserved. Copyrights licensed under the New BSD License. See the accompanying LICENSE file for terms. */ /*jshint maxlen: 300 */ var handlebars = require('handlebars').create(), defaults = require('./common/defaults'), path = require('path'), fs = require('fs'), util = require('util'), FileWriter = require('../util/file-writer'), Report = require('./index'), Store = require('../store'), InsertionText = require('../util/insertion-text'), TreeSummarizer = require('../util/tree-summarizer'), utils = require('../object-utils'), templateFor = function (name) { return handlebars.compile(fs.readFileSync(path.resolve(__dirname, 'templates', name + '.txt'), 'utf8')); }, headerTemplate = templateFor('head'), footerTemplate = templateFor('foot'), detailTemplate = handlebars.compile([ '', '{{#show_lines}}{{maxLines}}{{/show_lines}}', '{{#show_line_execution_counts fileCoverage}}{{maxLines}}{{/show_line_execution_counts}}', '
{{#show_code structured}}{{/show_code}}
', '\n' ].join('')), summaryTableHeader = [ '
', '', '', '', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', '', '', '' ].join('\n'), summaryLineTemplate = handlebars.compile([ '', '', '', '', '', '', '', '', '', '', '', '\n' ].join('\n\t')), summaryTableFooter = [ '', '
FileStatementsBranchesFunctionsLines
{{file}}
{{#show_picture}}{{metrics.statements.pct}}{{/show_picture}}
{{metrics.statements.pct}}%{{metrics.statements.covered}}/{{metrics.statements.total}}{{metrics.branches.pct}}%{{metrics.branches.covered}}/{{metrics.branches.total}}{{metrics.functions.pct}}%{{metrics.functions.covered}}/{{metrics.functions.total}}{{metrics.lines.pct}}%{{metrics.lines.covered}}/{{metrics.lines.total}}
', '
' ].join('\n'), lt = '\u0001', gt = '\u0002', RE_LT = //g, RE_AMP = /&/g, RE_lt = /\u0001/g, RE_gt = /\u0002/g; handlebars.registerHelper('show_picture', function (opts) { var num = Number(opts.fn(this)), rest, cls = ''; if (isFinite(num)) { if (num === 100) { cls = ' cover-full'; } num = Math.floor(num); rest = 100 - num; return '
' + '
'; } else { return ''; } }); handlebars.registerHelper('if_has_ignores', function (metrics, opts) { return (metrics.statements.skipped + metrics.functions.skipped + metrics.branches.skipped) === 0 ? '' : opts.fn(this); }); handlebars.registerHelper('show_ignores', function (metrics) { var statements = metrics.statements.skipped, functions = metrics.functions.skipped, branches = metrics.branches.skipped, result; if (statements === 0 && functions === 0 && branches === 0) { return 'none'; } result = []; if (statements >0) { result.push(statements === 1 ? '1 statement': statements + ' statements'); } if (functions >0) { result.push(functions === 1 ? '1 function' : functions + ' functions'); } if (branches >0) { result.push(branches === 1 ? '1 branch' : branches + ' branches'); } return result.join(', '); }); handlebars.registerHelper('show_lines', function (opts) { var maxLines = Number(opts.fn(this)), i, array = []; for (i = 0; i < maxLines; i += 1) { array[i] = i + 1; } return array.join('\n'); }); handlebars.registerHelper('show_line_execution_counts', function (context, opts) { var lines = context.l, maxLines = Number(opts.fn(this)), i, lineNumber, array = [], covered, value = ''; for (i = 0; i < maxLines; i += 1) { lineNumber = i + 1; value = ' '; covered = 'neutral'; if (lines.hasOwnProperty(lineNumber)) { if (lines[lineNumber] > 0) { covered = 'yes'; value = lines[lineNumber] + '×'; } else { covered = 'no'; } } array.push('' + value + ''); } return array.join('\n'); }); function customEscape(text) { text = text.toString(); return text.replace(RE_AMP, '&') .replace(RE_LT, '<') .replace(RE_GT, '>') .replace(RE_lt, '<') .replace(RE_gt, '>'); } handlebars.registerHelper('show_code', function (context /*, opts */) { var array = []; context.forEach(function (item) { array.push(customEscape(item.text) || ' '); }); return array.join('\n'); }); function title(str) { return ' title="' + str + '" '; } function annotateLines(fileCoverage, structuredText) { var lineStats = fileCoverage.l; if (!lineStats) { return; } Object.keys(lineStats).forEach(function (lineNumber) { var count = lineStats[lineNumber]; if (structuredText[lineNumber]) { structuredText[lineNumber].covered = count > 0 ? 'yes' : 'no'; } }); structuredText.forEach(function (item) { if (item.covered === null) { item.covered = 'neutral'; } }); } function annotateStatements(fileCoverage, structuredText) { var statementStats = fileCoverage.s, statementMeta = fileCoverage.statementMap; Object.keys(statementStats).forEach(function (stName) { var count = statementStats[stName], meta = statementMeta[stName], type = count > 0 ? 'yes' : 'no', startCol = meta.start.column, endCol = meta.end.column + 1, startLine = meta.start.line, endLine = meta.end.line, openSpan = lt + 'span class="' + (meta.skip ? 'cstat-skip' : 'cstat-no') + '"' + title('statement not covered') + gt, closeSpan = lt + '/span' + gt, text; if (type === 'no') { if (endLine !== startLine) { endLine = startLine; endCol = structuredText[startLine].text.originalLength(); } text = structuredText[startLine].text; text.wrap(startCol, openSpan, startLine === endLine ? endCol : text.originalLength(), closeSpan); } }); } function annotateFunctions(fileCoverage, structuredText) { var fnStats = fileCoverage.f, fnMeta = fileCoverage.fnMap; if (!fnStats) { return; } Object.keys(fnStats).forEach(function (fName) { var count = fnStats[fName], meta = fnMeta[fName], type = count > 0 ? 'yes' : 'no', startCol = meta.loc.start.column, endCol = meta.loc.end.column + 1, startLine = meta.loc.start.line, endLine = meta.loc.end.line, openSpan = lt + 'span class="' + (meta.skip ? 'fstat-skip' : 'fstat-no') + '"' + title('function not covered') + gt, closeSpan = lt + '/span' + gt, text; if (type === 'no') { if (endLine !== startLine) { endLine = startLine; endCol = structuredText[startLine].text.originalLength(); } text = structuredText[startLine].text; text.wrap(startCol, openSpan, startLine === endLine ? endCol : text.originalLength(), closeSpan); } }); } function annotateBranches(fileCoverage, structuredText) { var branchStats = fileCoverage.b, branchMeta = fileCoverage.branchMap; if (!branchStats) { return; } Object.keys(branchStats).forEach(function (branchName) { var branchArray = branchStats[branchName], sumCount = branchArray.reduce(function (p, n) { return p + n; }, 0), metaArray = branchMeta[branchName].locations, i, count, meta, type, startCol, endCol, startLine, endLine, openSpan, closeSpan, text; if (sumCount > 0) { //only highlight if partial branches are missing for (i = 0; i < branchArray.length; i += 1) { count = branchArray[i]; meta = metaArray[i]; type = count > 0 ? 'yes' : 'no'; startCol = meta.start.column; endCol = meta.end.column + 1; startLine = meta.start.line; endLine = meta.end.line; openSpan = lt + 'span class="branch-' + i + ' ' + (meta.skip ? 'cbranch-skip' : 'cbranch-no') + '"' + title('branch not covered') + gt; closeSpan = lt + '/span' + gt; if (count === 0) { //skip branches taken if (endLine !== startLine) { endLine = startLine; endCol = structuredText[startLine].text.originalLength(); } text = structuredText[startLine].text; if (branchMeta[branchName].type === 'if') { // and 'if' is a special case since the else branch might not be visible, being non-existent text.insertAt(startCol, lt + 'span class="' + (meta.skip ? 'skip-if-branch' : 'missing-if-branch') + '"' + title((i === 0 ? 'if' : 'else') + ' path not taken') + gt + (i === 0 ? 'I' : 'E') + lt + '/span' + gt, true, false); } else { text.wrap(startCol, openSpan, startLine === endLine ? endCol : text.originalLength(), closeSpan); } } } } }); } function getReportClass(stats, watermark) { var coveragePct = stats.pct, identity = 1; if (coveragePct * identity === coveragePct) { return coveragePct >= watermark[1] ? 'high' : coveragePct >= watermark[0] ? 'medium' : 'low'; } else { return ''; } } function cleanPath(name) { var SEP = path.sep || '/'; return (SEP !== '/') ? name.split(SEP).join('/') : name; } function isEmptySourceStore(sourceStore) { if (!sourceStore) { return true; } var cache = sourceStore.sourceCache; return cache && !Object.keys(cache).length; } /** * a `Report` implementation that produces HTML coverage reports. * * Usage * ----- * * var report = require('istanbul').Report.create('html'); * * * @class HtmlReport * @extends Report * @module report * @constructor * @param {Object} opts optional * @param {String} [opts.dir] the directory in which to generate reports. Defaults to `./html-report` */ function HtmlReport(opts) { Report.call(this); this.opts = opts || {}; this.opts.dir = this.opts.dir || path.resolve(process.cwd(), 'html-report'); this.opts.sourceStore = isEmptySourceStore(this.opts.sourceStore) ? Store.create('fslookup') : this.opts.sourceStore; this.opts.linkMapper = this.opts.linkMapper || this.standardLinkMapper(); this.opts.writer = this.opts.writer || null; this.opts.templateData = { datetime: Date() }; this.opts.watermarks = this.opts.watermarks || defaults.watermarks(); } HtmlReport.TYPE = 'html'; util.inherits(HtmlReport, Report); Report.mix(HtmlReport, { synopsis: function () { return 'Navigable HTML coverage report for every file and directory'; }, getPathHtml: function (node, linkMapper) { var parent = node.parent, nodePath = [], linkPath = [], i; while (parent) { nodePath.push(parent); parent = parent.parent; } for (i = 0; i < nodePath.length; i += 1) { linkPath.push('' + (cleanPath(nodePath[i].relativeName) || 'all files') + ''); } linkPath.reverse(); return linkPath.length > 0 ? linkPath.join(' / ') + ' ' + cleanPath(node.displayShortName()) : '/'; }, fillTemplate: function (node, templateData) { var opts = this.opts, linkMapper = opts.linkMapper; templateData.entity = node.name || 'All files'; templateData.metrics = node.metrics; templateData.reportClass = getReportClass(node.metrics.statements, opts.watermarks.statements); templateData.pathHtml = this.getPathHtml(node, linkMapper); templateData.base = { css: linkMapper.asset(node, 'base.css') }; templateData.sorter = { js: linkMapper.asset(node, 'sorter.js'), image: linkMapper.asset(node, 'sort-arrow-sprite.png') }; templateData.prettify = { js: linkMapper.asset(node, 'prettify.js'), css: linkMapper.asset(node, 'prettify.css') }; }, writeDetailPage: function (writer, node, fileCoverage) { var opts = this.opts, sourceStore = opts.sourceStore, templateData = opts.templateData, sourceText = fileCoverage.code && Array.isArray(fileCoverage.code) ? fileCoverage.code.join('\n') + '\n' : sourceStore.get(fileCoverage.path), code = sourceText.split(/(?:\r?\n)|\r/), count = 0, structured = code.map(function (str) { count += 1; return { line: count, covered: null, text: new InsertionText(str, true) }; }), context; structured.unshift({ line: 0, covered: null, text: new InsertionText("") }); this.fillTemplate(node, templateData); writer.write(headerTemplate(templateData)); writer.write('
\n');

        annotateLines(fileCoverage, structured);
        //note: order is important, since statements typically result in spanning the whole line and doing branches late
        //causes mismatched tags
        annotateBranches(fileCoverage, structured);
        annotateFunctions(fileCoverage, structured);
        annotateStatements(fileCoverage, structured);

        structured.shift();
        context = {
            structured: structured,
            maxLines: structured.length,
            fileCoverage: fileCoverage
        };
        writer.write(detailTemplate(context));
        writer.write('
\n'); writer.write(footerTemplate(templateData)); }, writeIndexPage: function (writer, node) { var linkMapper = this.opts.linkMapper, templateData = this.opts.templateData, children = Array.prototype.slice.apply(node.children), watermarks = this.opts.watermarks; children.sort(function (a, b) { return a.name < b.name ? -1 : 1; }); this.fillTemplate(node, templateData); writer.write(headerTemplate(templateData)); writer.write(summaryTableHeader); children.forEach(function (child) { var metrics = child.metrics, reportClasses = { statements: getReportClass(metrics.statements, watermarks.statements), lines: getReportClass(metrics.lines, watermarks.lines), functions: getReportClass(metrics.functions, watermarks.functions), branches: getReportClass(metrics.branches, watermarks.branches) }, data = { metrics: metrics, reportClasses: reportClasses, file: cleanPath(child.displayShortName()), output: linkMapper.fromParent(child) }; writer.write(summaryLineTemplate(data) + '\n'); }); writer.write(summaryTableFooter); writer.write(footerTemplate(templateData)); }, writeFiles: function (writer, node, dir, collector) { var that = this, indexFile = path.resolve(dir, 'index.html'), childFile; if (this.opts.verbose) { console.error('Writing ' + indexFile); } writer.writeFile(indexFile, function (contentWriter) { that.writeIndexPage(contentWriter, node); }); node.children.forEach(function (child) { if (child.kind === 'dir') { that.writeFiles(writer, child, path.resolve(dir, child.relativeName), collector); } else { childFile = path.resolve(dir, child.relativeName + '.html'); if (that.opts.verbose) { console.error('Writing ' + childFile); } writer.writeFile(childFile, function (contentWriter) { that.writeDetailPage(contentWriter, child, collector.fileCoverageFor(child.fullPath())); }); } }); }, standardLinkMapper: function () { return { fromParent: function (node) { var relativeName = cleanPath(node.relativeName); return node.kind === 'dir' ? relativeName + 'index.html' : relativeName + '.html'; }, ancestorHref: function (node, num) { var href = '', notDot = function(part) { return part !== '.'; }, separated, levels, i, j; for (i = 0; i < num; i += 1) { separated = cleanPath(node.relativeName).split('/').filter(notDot); levels = separated.length - 1; for (j = 0; j < levels; j += 1) { href += '../'; } node = node.parent; } return href; }, ancestor: function (node, num) { return this.ancestorHref(node, num) + 'index.html'; }, asset: function (node, name) { var i = 0, parent = node.parent; while (parent) { i += 1; parent = parent.parent; } return this.ancestorHref(node, i) + name; } }; }, writeReport: function (collector, sync) { var opts = this.opts, dir = opts.dir, summarizer = new TreeSummarizer(), writer = opts.writer || new FileWriter(sync), that = this, tree, copyAssets = function (subdir) { var srcDir = path.resolve(__dirname, '..', 'assets', subdir); fs.readdirSync(srcDir).forEach(function (f) { var resolvedSource = path.resolve(srcDir, f), resolvedDestination = path.resolve(dir, f), stat = fs.statSync(resolvedSource); if (stat.isFile()) { if (opts.verbose) { console.log('Write asset: ' + resolvedDestination); } writer.copyFile(resolvedSource, resolvedDestination); } }); }; collector.files().forEach(function (key) { summarizer.addFileCoverageSummary(key, utils.summarizeFileCoverage(collector.fileCoverageFor(key))); }); tree = summarizer.getTreeSummary(); [ '.', 'vendor'].forEach(function (subdir) { copyAssets(subdir); }); writer.on('done', function () { that.emit('done'); }); //console.log(JSON.stringify(tree.root, undefined, 4)); this.writeFiles(writer, tree.root, dir, collector); writer.done(); } }); module.exports = HtmlReport; istanbul-0.4.5/lib/report/index.js000066400000000000000000000064561275640326200171110ustar00rootroot00000000000000/* Copyright (c) 2012, Yahoo! Inc. All rights reserved. Copyrights licensed under the New BSD License. See the accompanying LICENSE file for terms. */ var util = require('util'), EventEmitter = require('events').EventEmitter, Factory = require('../util/factory'), factory = new Factory('report', __dirname, false); /** * An abstraction for producing coverage reports. * This class is both the base class as well as a factory for `Report` implementations. * All reports are event emitters and are expected to emit a `done` event when * the report writing is complete. * * See also the `Reporter` class for easily producing multiple coverage reports * with a single call. * * Usage * ----- * * var Report = require('istanbul').Report, * report = Report.create('html'), * collector = new require('istanbul').Collector; * * collector.add(coverageObject); * report.on('done', function () { console.log('done'); }); * report.writeReport(collector); * * @class Report * @module report * @main report * @constructor * @protected * @param {Object} options Optional. The options supported by a specific store implementation. */ function Report(/* options */) { EventEmitter.call(this); } util.inherits(Report, EventEmitter); //add register, create, mix, loadAll, getReportList as class methods factory.bindClassMethods(Report); /** * registers a new report implementation. * @method register * @static * @param {Function} constructor the constructor function for the report. This function must have a * `TYPE` property of type String, that will be used in `Report.create()` */ /** * returns a report implementation of the specified type. * @method create * @static * @param {String} type the type of report to create * @param {Object} opts Optional. Options specific to the report implementation * @return {Report} a new store of the specified type */ /** * returns the list of available reports as an array of strings * @method getReportList * @static * @return an array of supported report formats */ var proto = { /** * returns a one-line summary of the report * @method synopsis * @return {String} a description of what the report is about */ synopsis: function () { throw new Error('synopsis must be overridden'); }, /** * returns a config object that has override-able keys settable via config * @method getDefaultConfig * @return {Object|null} an object representing keys that can be overridden via * the istanbul configuration where the values are the defaults used when * not specified. A null return implies no config attributes */ getDefaultConfig: function () { return null; }, /** * writes the report for a set of coverage objects added to a collector. * @method writeReport * @param {Collector} collector the collector for getting the set of files and coverage * @param {Boolean} sync true if reports must be written synchronously, false if they can be written using asynchronous means (e.g. stream.write) */ writeReport: function (/* collector, sync */) { throw new Error('writeReport: must be overridden'); } }; Object.keys(proto).forEach(function (k) { Report.prototype[k] = proto[k]; }); module.exports = Report; istanbul-0.4.5/lib/report/json-summary.js000066400000000000000000000050121275640326200204310ustar00rootroot00000000000000/* Copyright (c) 2012, Yahoo! Inc. All rights reserved. Copyrights licensed under the New BSD License. See the accompanying LICENSE file for terms. */ var path = require('path'), objectUtils = require('../object-utils'), Writer = require('../util/file-writer'), util = require('util'), Report = require('./index'); /** * a `Report` implementation that produces a coverage JSON object with summary info only. * * Usage * ----- * * var report = require('istanbul').Report.create('json-summary'); * * * @class JsonSummaryReport * @extends Report * @module report * @constructor * @param {Object} opts optional * @param {String} [opts.dir] the directory in which to write the `coverage-summary.json` file. Defaults to `process.cwd()` */ function JsonSummaryReport(opts) { this.opts = opts || {}; this.opts.dir = this.opts.dir || process.cwd(); this.opts.file = this.opts.file || this.getDefaultConfig().file; this.opts.writer = this.opts.writer || null; } JsonSummaryReport.TYPE = 'json-summary'; util.inherits(JsonSummaryReport, Report); Report.mix(JsonSummaryReport, { synopsis: function () { return 'prints a summary coverage object as JSON to a file'; }, getDefaultConfig: function () { return { file: 'coverage-summary.json' }; }, writeReport: function (collector, sync) { var outputFile = path.resolve(this.opts.dir, this.opts.file), writer = this.opts.writer || new Writer(sync), that = this; var summaries = [], finalSummary; collector.files().forEach(function (file) { summaries.push(objectUtils.summarizeFileCoverage(collector.fileCoverageFor(file))); }); finalSummary = objectUtils.mergeSummaryObjects.apply(null, summaries); writer.on('done', function () { that.emit('done'); }); writer.writeFile(outputFile, function (contentWriter) { contentWriter.println("{"); contentWriter.write('"total":'); contentWriter.write(JSON.stringify(finalSummary)); collector.files().forEach(function (key) { contentWriter.println(","); contentWriter.write(JSON.stringify(key)); contentWriter.write(":"); contentWriter.write(JSON.stringify(objectUtils.summarizeFileCoverage(collector.fileCoverageFor(key)))); }); contentWriter.println("}"); }); writer.done(); } }); module.exports = JsonSummaryReport; istanbul-0.4.5/lib/report/json.js000066400000000000000000000041301275640326200167360ustar00rootroot00000000000000/* Copyright (c) 2012, Yahoo! Inc. All rights reserved. Copyrights licensed under the New BSD License. See the accompanying LICENSE file for terms. */ var path = require('path'), Writer = require('../util/file-writer'), util = require('util'), Report = require('./index'); /** * a `Report` implementation that produces a coverage JSON object. * * Usage * ----- * * var report = require('istanbul').Report.create('json'); * * * @class JsonReport * @extends Report * @module report * @constructor * @param {Object} opts optional * @param {String} [opts.dir] the directory in which to write the `coverage-final.json` file. Defaults to `process.cwd()` */ function JsonReport(opts) { this.opts = opts || {}; this.opts.dir = this.opts.dir || process.cwd(); this.opts.file = this.opts.file || this.getDefaultConfig().file; this.opts.writer = this.opts.writer || null; } JsonReport.TYPE = 'json'; util.inherits(JsonReport, Report); Report.mix(JsonReport, { synopsis: function () { return 'prints the coverage object as JSON to a file'; }, getDefaultConfig: function () { return { file: 'coverage-final.json' }; }, writeReport: function (collector, sync) { var outputFile = path.resolve(this.opts.dir, this.opts.file), writer = this.opts.writer || new Writer(sync), that = this; writer.on('done', function () { that.emit('done'); }); writer.writeFile(outputFile, function (contentWriter) { var first = true; contentWriter.println("{"); collector.files().forEach(function (key) { if (first) { first = false; } else { contentWriter.println(","); } contentWriter.write(JSON.stringify(key)); contentWriter.write(":"); contentWriter.write(JSON.stringify(collector.fileCoverageFor(key))); }); contentWriter.println("}"); }); writer.done(); } }); module.exports = JsonReport; istanbul-0.4.5/lib/report/lcov.js000066400000000000000000000040611275640326200167330ustar00rootroot00000000000000/* Copyright (c) 2012, Yahoo! Inc. All rights reserved. Copyrights licensed under the New BSD License. See the accompanying LICENSE file for terms. */ var path = require('path'), util = require('util'), mkdirp = require('mkdirp'), Report = require('./index'), LcovOnlyReport = require('./lcovonly'), HtmlReport = require('./html'); /** * a `Report` implementation that produces an LCOV coverage file and an associated HTML report from coverage objects. * The name and behavior of this report is designed to ease migration for projects that currently use `yuitest_coverage` * * Usage * ----- * * var report = require('istanbul').Report.create('lcov'); * * * @class LcovReport * @extends Report * @module report * @constructor * @param {Object} opts optional * @param {String} [opts.dir] the directory in which to the `lcov.info` file. * HTML files are written in a subdirectory called `lcov-report`. Defaults to `process.cwd()` */ function LcovReport(opts) { Report.call(this); opts = opts || {}; var baseDir = path.resolve(opts.dir || process.cwd()), htmlDir = path.resolve(baseDir, 'lcov-report'); mkdirp.sync(baseDir); this.lcov = new LcovOnlyReport({ dir: baseDir, watermarks: opts.watermarks }); this.html = new HtmlReport({ dir: htmlDir, watermarks: opts.watermarks, sourceStore: opts.sourceStore}); } LcovReport.TYPE = 'lcov'; util.inherits(LcovReport, Report); Report.mix(LcovReport, { synopsis: function () { return 'combined lcovonly and html report that generates an lcov.info file as well as HTML'; }, writeReport: function (collector, sync) { var handler = this.handleDone.bind(this); this.inProgress = 2; this.lcov.on('done', handler); this.html.on('done', handler); this.lcov.writeReport(collector, sync); this.html.writeReport(collector, sync); }, handleDone: function () { this.inProgress -= 1; if (this.inProgress === 0) { this.emit('done'); } } }); module.exports = LcovReport; istanbul-0.4.5/lib/report/lcovonly.js000066400000000000000000000066571275640326200176520ustar00rootroot00000000000000/* Copyright (c) 2012, Yahoo! Inc. All rights reserved. Copyrights licensed under the New BSD License. See the accompanying LICENSE file for terms. */ var path = require('path'), Writer = require('../util/file-writer'), util = require('util'), Report = require('./index'), utils = require('../object-utils'); /** * a `Report` implementation that produces an LCOV coverage file from coverage objects. * * Usage * ----- * * var report = require('istanbul').Report.create('lcovonly'); * * * @class LcovOnlyReport * @extends Report * @module report * @constructor * @param {Object} opts optional * @param {String} [opts.dir] the directory in which to the `lcov.info` file. Defaults to `process.cwd()` */ function LcovOnlyReport(opts) { this.opts = opts || {}; this.opts.dir = this.opts.dir || process.cwd(); this.opts.file = this.opts.file || this.getDefaultConfig().file; this.opts.writer = this.opts.writer || null; } LcovOnlyReport.TYPE = 'lcovonly'; util.inherits(LcovOnlyReport, Report); Report.mix(LcovOnlyReport, { synopsis: function () { return 'lcov coverage report that can be consumed by the lcov tool'; }, getDefaultConfig: function () { return { file: 'lcov.info' }; }, writeFileCoverage: function (writer, fc) { var functions = fc.f, functionMap = fc.fnMap, lines = fc.l, branches = fc.b, branchMap = fc.branchMap, summary = utils.summarizeFileCoverage(fc); writer.println('TN:'); //no test name writer.println('SF:' + fc.path); Object.keys(functions).forEach(function (key) { var meta = functionMap[key]; writer.println('FN:' + [ meta.line, meta.name ].join(',')); }); writer.println('FNF:' + summary.functions.total); writer.println('FNH:' + summary.functions.covered); Object.keys(functions).forEach(function (key) { var stats = functions[key], meta = functionMap[key]; writer.println('FNDA:' + [ stats, meta.name ].join(',')); }); Object.keys(lines).forEach(function (key) { var stat = lines[key]; writer.println('DA:' + [ key, stat ].join(',')); }); writer.println('LF:' + summary.lines.total); writer.println('LH:' + summary.lines.covered); Object.keys(branches).forEach(function (key) { var branchArray = branches[key], meta = branchMap[key], line = meta.line, i = 0; branchArray.forEach(function (b) { writer.println('BRDA:' + [line, key, i, b].join(',')); i += 1; }); }); writer.println('BRF:' + summary.branches.total); writer.println('BRH:' + summary.branches.covered); writer.println('end_of_record'); }, writeReport: function (collector, sync) { var outputFile = path.resolve(this.opts.dir, this.opts.file), writer = this.opts.writer || new Writer(sync), that = this; writer.on('done', function () { that.emit('done'); }); writer.writeFile(outputFile, function (contentWriter) { collector.files().forEach(function (key) { that.writeFileCoverage(contentWriter, collector.fileCoverageFor(key)); }); }); writer.done(); } }); module.exports = LcovOnlyReport; istanbul-0.4.5/lib/report/none.js000066400000000000000000000015771275640326200167400ustar00rootroot00000000000000/* Copyright (c) 2012, Yahoo! Inc. All rights reserved. Copyrights licensed under the New BSD License. See the accompanying LICENSE file for terms. */ var util = require('util'), Report = require('./index'); /** * a `Report` implementation that does nothing. Use to specify that no reporting * is needed. * * Usage * ----- * * var report = require('istanbul').Report.create('none'); * * * @class NoneReport * @extends Report * @module report * @constructor */ function NoneReport() { Report.call(this); } NoneReport.TYPE = 'none'; util.inherits(NoneReport, Report); Report.mix(NoneReport, { synopsis: function () { return 'Does nothing. Useful to override default behavior and suppress reporting entirely'; }, writeReport: function (/* collector, sync */) { //noop this.emit('done'); } }); module.exports = NoneReport; istanbul-0.4.5/lib/report/teamcity.js000066400000000000000000000060131275640326200176060ustar00rootroot00000000000000/* Copyright (c) 2012, Yahoo! Inc. All rights reserved. Copyrights licensed under the New BSD License. See the accompanying LICENSE file for terms. */ var path = require('path'), util = require('util'), mkdirp = require('mkdirp'), fs = require('fs'), utils = require('../object-utils'), Report = require('./index'); /** * a `Report` implementation that produces system messages interpretable by TeamCity. * * Usage * ----- * * var report = require('istanbul').Report.create('teamcity'); * * @class TeamcityReport * @extends Report * @module report * @constructor * @param {Object} opts optional * @param {String} [opts.dir] the directory in which to the text coverage report will be written, when writing to a file * @param {String} [opts.file] the filename for the report. When omitted, the report is written to console */ function TeamcityReport(opts) { Report.call(this); opts = opts || {}; this.dir = opts.dir || process.cwd(); this.file = opts.file; this.blockName = opts.blockName || this.getDefaultConfig().blockName; } TeamcityReport.TYPE = 'teamcity'; util.inherits(TeamcityReport, Report); function lineForKey(value, teamcityVar) { return '##teamcity[buildStatisticValue key=\'' + teamcityVar + '\' value=\'' + value + '\']'; } Report.mix(TeamcityReport, { synopsis: function () { return 'report with system messages that can be interpreted with TeamCity'; }, getDefaultConfig: function () { return { file: null , blockName: 'Code Coverage Summary'}; }, writeReport: function (collector /*, sync */) { var summaries = [], finalSummary, lines = [], text; collector.files().forEach(function (file) { summaries.push(utils.summarizeFileCoverage(collector.fileCoverageFor(file))); }); finalSummary = utils.mergeSummaryObjects.apply(null, summaries); lines.push(''); lines.push('##teamcity[blockOpened name=\''+ this.blockName +'\']'); //Statements Covered lines.push(lineForKey(finalSummary.statements.pct, 'CodeCoverageB')); //Methods Covered lines.push(lineForKey(finalSummary.functions.covered, 'CodeCoverageAbsMCovered')); lines.push(lineForKey(finalSummary.functions.total, 'CodeCoverageAbsMTotal')); lines.push(lineForKey(finalSummary.functions.pct, 'CodeCoverageM')); //Lines Covered lines.push(lineForKey(finalSummary.lines.covered, 'CodeCoverageAbsLCovered')); lines.push(lineForKey(finalSummary.lines.total, 'CodeCoverageAbsLTotal')); lines.push(lineForKey(finalSummary.lines.pct, 'CodeCoverageL')); lines.push('##teamcity[blockClosed name=\''+ this.blockName +'\']'); text = lines.join('\n'); if (this.file) { mkdirp.sync(this.dir); fs.writeFileSync(path.join(this.dir, this.file), text, 'utf8'); } else { console.log(text); } this.emit('done'); } }); module.exports = TeamcityReport; istanbul-0.4.5/lib/report/templates/000077500000000000000000000000001275640326200174275ustar00rootroot00000000000000istanbul-0.4.5/lib/report/templates/foot.txt000066400000000000000000000010001275640326200211260ustar00rootroot00000000000000
{{#if prettify}} {{/if}} istanbul-0.4.5/lib/report/templates/head.txt000066400000000000000000000036071275640326200210770ustar00rootroot00000000000000 Code coverage report for {{entity}} {{#if prettify}} {{/if}}

{{{pathHtml}}}

{{#with metrics.statements}}
{{pct}}% Statements {{covered}}/{{total}}
{{/with}} {{#with metrics.branches}}
{{pct}}% Branches {{covered}}/{{total}}
{{/with}} {{#with metrics.functions}}
{{pct}}% Functions {{covered}}/{{total}}
{{/with}} {{#with metrics.lines}}
{{pct}}% Lines {{covered}}/{{total}}
{{/with}} {{#if_has_ignores metrics}}
{{#show_ignores metrics}}{{/show_ignores}} Ignored     
{{/if_has_ignores}}
istanbul-0.4.5/lib/report/text-lcov.js000066400000000000000000000021201275640326200177070ustar00rootroot00000000000000var LcovOnly = require('./lcovonly'), util = require('util'); /** * a `Report` implementation that produces an LCOV coverage and prints it * to standard out. * * Usage * ----- * * var report = require('istanbul').Report.create('text-lcov'); * * @class TextLcov * @module report * @extends LcovOnly * @constructor * @param {Object} opts optional * @param {String} [opts.log] the method used to log to console. */ function TextLcov(opts) { var that = this; LcovOnly.call(this); this.opts = opts || {}; this.opts.log = this.opts.log || console.log; this.opts.writer = { println: function (ln) { that.opts.log(ln); } }; } TextLcov.TYPE = 'text-lcov'; util.inherits(TextLcov, LcovOnly); LcovOnly.super_.mix(TextLcov, { writeReport: function (collector) { var that = this, writer = this.opts.writer; collector.files().forEach(function (key) { that.writeFileCoverage(writer, collector.fileCoverageFor(key)); }); this.emit('done'); } }); module.exports = TextLcov; istanbul-0.4.5/lib/report/text-summary.js000066400000000000000000000063471275640326200204600ustar00rootroot00000000000000/* Copyright (c) 2012, Yahoo! Inc. All rights reserved. Copyrights licensed under the New BSD License. See the accompanying LICENSE file for terms. */ var path = require('path'), util = require('util'), mkdirp = require('mkdirp'), defaults = require('./common/defaults'), fs = require('fs'), utils = require('../object-utils'), Report = require('./index'); /** * a `Report` implementation that produces text output for overall coverage in summary format. * * Usage * ----- * * var report = require('istanbul').Report.create('text-summary'); * * @class TextSummaryReport * @extends Report * @module report * @constructor * @param {Object} opts optional * @param {String} [opts.dir] the directory in which to the text coverage report will be written, when writing to a file * @param {String} [opts.file] the filename for the report. When omitted, the report is written to console */ function TextSummaryReport(opts) { Report.call(this); opts = opts || {}; this.dir = opts.dir || process.cwd(); this.file = opts.file; this.watermarks = opts.watermarks || defaults.watermarks(); } TextSummaryReport.TYPE = 'text-summary'; util.inherits(TextSummaryReport, Report); function lineForKey(summary, key, watermarks) { var metrics = summary[key], skipped, result, clazz = defaults.classFor(key, summary, watermarks); key = key.substring(0, 1).toUpperCase() + key.substring(1); if (key.length < 12) { key += ' '.substring(0, 12 - key.length); } result = [ key , ':', metrics.pct + '%', '(', metrics.covered + '/' + metrics.total, ')'].join(' '); skipped = metrics.skipped; if (skipped > 0) { result += ', ' + skipped + ' ignored'; } return defaults.colorize(result, clazz); } Report.mix(TextSummaryReport, { synopsis: function () { return 'text report that prints a coverage summary across all files, typically to console'; }, getDefaultConfig: function () { return { file: null }; }, writeReport: function (collector /*, sync */) { var summaries = [], finalSummary, lines = [], watermarks = this.watermarks, text; collector.files().forEach(function (file) { summaries.push(utils.summarizeFileCoverage(collector.fileCoverageFor(file))); }); finalSummary = utils.mergeSummaryObjects.apply(null, summaries); lines.push(''); lines.push('=============================== Coverage summary ==============================='); lines.push.apply(lines, [ lineForKey(finalSummary, 'statements', watermarks), lineForKey(finalSummary, 'branches', watermarks), lineForKey(finalSummary, 'functions', watermarks), lineForKey(finalSummary, 'lines', watermarks) ]); lines.push('================================================================================'); text = lines.join('\n'); if (this.file) { mkdirp.sync(this.dir); fs.writeFileSync(path.join(this.dir, this.file), text, 'utf8'); } else { console.log(text); } this.emit('done'); } }); module.exports = TextSummaryReport; istanbul-0.4.5/lib/report/text.js000066400000000000000000000156231275640326200167620ustar00rootroot00000000000000/* Copyright (c) 2012, Yahoo! Inc. All rights reserved. Copyrights licensed under the New BSD License. See the accompanying LICENSE file for terms. */ var path = require('path'), mkdirp = require('mkdirp'), util = require('util'), fs = require('fs'), defaults = require('./common/defaults'), Report = require('./index'), TreeSummarizer = require('../util/tree-summarizer'), utils = require('../object-utils'), PCT_COLS = 9, MISSING_COL = 15, TAB_SIZE = 1, DELIM = ' |', COL_DELIM = '-|'; /** * a `Report` implementation that produces text output in a detailed table. * * Usage * ----- * * var report = require('istanbul').Report.create('text'); * * @class TextReport * @extends Report * @module report * @constructor * @param {Object} opts optional * @param {String} [opts.dir] the directory in which to the text coverage report will be written, when writing to a file * @param {String} [opts.file] the filename for the report. When omitted, the report is written to console * @param {Number} [opts.maxCols] the max column width of the report. By default, the width of the report is adjusted based on the length of the paths * to be reported. */ function TextReport(opts) { Report.call(this); opts = opts || {}; this.dir = opts.dir || process.cwd(); this.file = opts.file; this.summary = opts.summary; this.maxCols = opts.maxCols || 0; this.watermarks = opts.watermarks || defaults.watermarks(); } TextReport.TYPE = 'text'; util.inherits(TextReport, Report); function padding(num, ch) { var str = '', i; ch = ch || ' '; for (i = 0; i < num; i += 1) { str += ch; } return str; } function fill(str, width, right, tabs, clazz) { tabs = tabs || 0; str = String(str); var leadingSpaces = tabs * TAB_SIZE, remaining = width - leadingSpaces, leader = padding(leadingSpaces), fmtStr = '', fillStr, strlen = str.length; if (remaining > 0) { if (remaining >= strlen) { fillStr = padding(remaining - strlen); fmtStr = right ? fillStr + str : str + fillStr; } else { fmtStr = str.substring(strlen - remaining); fmtStr = '... ' + fmtStr.substring(4); } } fmtStr = defaults.colorize(fmtStr, clazz); return leader + fmtStr; } function formatName(name, maxCols, level, clazz) { return fill(name, maxCols, false, level, clazz); } function formatPct(pct, clazz, width) { return fill(pct, width || PCT_COLS, true, 0, clazz); } function nodeName(node) { return node.displayShortName() || 'All files'; } function tableHeader(maxNameCols) { var elements = []; elements.push(formatName('File', maxNameCols, 0)); elements.push(formatPct('% Stmts')); elements.push(formatPct('% Branch')); elements.push(formatPct('% Funcs')); elements.push(formatPct('% Lines')); elements.push(formatPct('Uncovered Lines', undefined, MISSING_COL)); return elements.join(' |') + ' |'; } function collectMissingLines(kind, linesCovered) { var missingLines = []; if (kind !== 'file') { return []; } Object.keys(linesCovered).forEach(function (key) { if (!linesCovered[key]) { missingLines.push(key); } }); return missingLines; } function tableRow(node, maxNameCols, level, watermarks) { var name = nodeName(node), statements = node.metrics.statements.pct, branches = node.metrics.branches.pct, functions = node.metrics.functions.pct, lines = node.metrics.lines.pct, missingLines = collectMissingLines(node.kind, node.metrics.linesCovered), elements = []; elements.push(formatName(name, maxNameCols, level, defaults.classFor('statements', node.metrics, watermarks))); elements.push(formatPct(statements, defaults.classFor('statements', node.metrics, watermarks))); elements.push(formatPct(branches, defaults.classFor('branches', node.metrics, watermarks))); elements.push(formatPct(functions, defaults.classFor('functions', node.metrics, watermarks))); elements.push(formatPct(lines, defaults.classFor('lines', node.metrics, watermarks))); elements.push(formatPct(missingLines.join(','), 'low', MISSING_COL)); return elements.join(DELIM) + DELIM; } function findNameWidth(node, level, last) { last = last || 0; level = level || 0; var idealWidth = TAB_SIZE * level + nodeName(node).length; if (idealWidth > last) { last = idealWidth; } node.children.forEach(function (child) { last = findNameWidth(child, level + 1, last); }); return last; } function makeLine(nameWidth) { var name = padding(nameWidth, '-'), pct = padding(PCT_COLS, '-'), elements = []; elements.push(name); elements.push(pct); elements.push(pct); elements.push(pct); elements.push(pct); elements.push(padding(MISSING_COL, '-')); return elements.join(COL_DELIM) + COL_DELIM; } function walk(node, nameWidth, array, level, watermarks) { var line; if (level === 0) { line = makeLine(nameWidth); array.push(line); array.push(tableHeader(nameWidth)); array.push(line); } else { array.push(tableRow(node, nameWidth, level, watermarks)); } node.children.forEach(function (child) { walk(child, nameWidth, array, level + 1, watermarks); }); if (level === 0) { array.push(line); array.push(tableRow(node, nameWidth, level, watermarks)); array.push(line); } } Report.mix(TextReport, { synopsis: function () { return 'text report that prints a coverage line for every file, typically to console'; }, getDefaultConfig: function () { return { file: null, maxCols: 0 }; }, writeReport: function (collector /*, sync */) { var summarizer = new TreeSummarizer(), tree, root, nameWidth, statsWidth = 4 * (PCT_COLS + 2) + MISSING_COL, maxRemaining, strings = [], text; collector.files().forEach(function (key) { summarizer.addFileCoverageSummary(key, utils.summarizeFileCoverage( collector.fileCoverageFor(key) )); }); tree = summarizer.getTreeSummary(); root = tree.root; nameWidth = findNameWidth(root); if (this.maxCols > 0) { maxRemaining = this.maxCols - statsWidth - 2; if (nameWidth > maxRemaining) { nameWidth = maxRemaining; } } walk(root, nameWidth, strings, 0, this.watermarks); text = strings.join('\n') + '\n'; if (this.file) { mkdirp.sync(this.dir); fs.writeFileSync(path.join(this.dir, this.file), text, 'utf8'); } else { console.log(text); } this.emit('done'); } }); module.exports = TextReport; istanbul-0.4.5/lib/reporter.js000066400000000000000000000071351275640326200163240ustar00rootroot00000000000000/* Copyright (c) 2014, Yahoo! Inc. All rights reserved. Copyrights licensed under the New BSD License. See the accompanying LICENSE file for terms. */ var Report = require('./report'), configuration = require('./config'), inputError = require('./util/input-error'); /** * convenience mechanism to write one or more reports ensuring that config * options are respected. * Usage * ----- * * var fs = require('fs'), * reporter = new require('istanbul').Reporter(), * collector = new require('istanbul').Collector(), * sync = true; * * collector.add(JSON.parse(fs.readFileSync('coverage.json', 'utf8'))); * reporter.add('lcovonly'); * reporter.addAll(['clover', 'cobertura']); * reporter.write(collector, sync, function () { console.log('done'); }); * * @class Reporter * @param {Configuration} cfg the config object, a falsy value will load the * default configuration instead * @param {String} dir the directory in which to write the reports, may be falsy * to use config or global defaults * @constructor * @module main */ function Reporter(cfg, dir) { this.config = cfg || configuration.loadFile(); this.dir = dir || this.config.reporting.dir(); this.reports = {}; } Reporter.prototype = { /** * adds a report to be generated. Must be one of the entries returned * by `Report.getReportList()` * @method add * @param {String} fmt the format of the report to generate */ add: function (fmt) { if (this.reports[fmt]) { // already added return; } var config = this.config, rptConfig = config.reporting.reportConfig()[fmt] || {}; rptConfig.verbose = config.verbose; rptConfig.dir = this.dir; rptConfig.watermarks = config.reporting.watermarks(); try { this.reports[fmt] = Report.create(fmt, rptConfig); } catch (ex) { throw inputError.create('Invalid report format [' + fmt + ']'); } }, /** * adds an array of report formats to be generated * @method addAll * @param {Array} fmts an array of report formats */ addAll: function (fmts) { var that = this; fmts.forEach(function (f) { that.add(f); }); }, /** * writes all reports added and calls the callback when done * @method write * @param {Collector} collector the collector having the coverage data * @param {Boolean} sync true to write reports synchronously * @param {Function} callback the callback to call when done. When `sync` * is true, the callback will be called in the same process tick. */ write: function (collector, sync, callback) { var reports = this.reports, verbose = this.config.verbose, handler = this.handleDone.bind(this, callback); this.inProgress = Object.keys(reports).length; Object.keys(reports).forEach(function (name) { var report = reports[name]; if (verbose) { console.error('Write report: ' + name); } report.on('done', handler); report.writeReport(collector, sync); }); }, /* * handles listening on all reports to be completed before calling the callback * @method handleDone * @private * @param {Function} callback the callback to call when all reports are * written */ handleDone: function (callback) { this.inProgress -= 1; if (this.inProgress === 0) { return callback(); } } }; module.exports = Reporter; istanbul-0.4.5/lib/store/000077500000000000000000000000001275640326200152525ustar00rootroot00000000000000istanbul-0.4.5/lib/store/fslookup.js000066400000000000000000000027771275640326200174670ustar00rootroot00000000000000/* Copyright (c) 2012, Yahoo! Inc. All rights reserved. Copyrights licensed under the New BSD License. See the accompanying LICENSE file for terms. */ var util = require('util'), fs = require('fs'), Store = require('./index'); /** * a `Store` implementation that doesn't actually store anything. It assumes that keys * are absolute file paths, and contents are contents of those files. * Thus, `set` for this store is no-op, `get` returns the * contents of the filename that the key represents, `hasKey` returns true if the key * supplied is a valid file path and `keys` always returns an empty array. * * Usage * ----- * * var store = require('istanbul').Store.create('fslookup'); * * * @class LookupStore * @extends Store * @module store * @constructor */ function LookupStore(opts) { Store.call(this, opts); } LookupStore.TYPE = 'fslookup'; util.inherits(LookupStore, Store); Store.mix(LookupStore, { keys: function () { return []; }, get: function (key) { return fs.readFileSync(key, 'utf8'); }, hasKey: function (key) { var stats; try { stats = fs.statSync(key); return stats.isFile(); } catch (ex) { return false; } }, set: function (key /*, contents */) { if (!this.hasKey(key)) { throw new Error('Attempt to set contents for non-existent file [' + key + '] on a fslookup store'); } return key; } }); module.exports = LookupStore; istanbul-0.4.5/lib/store/index.js000066400000000000000000000077551275640326200167350ustar00rootroot00000000000000/* Copyright (c) 2012, Yahoo! Inc. All rights reserved. Copyrights licensed under the New BSD License. See the accompanying LICENSE file for terms. */ var Factory = require('../util/factory'), factory = new Factory('store', __dirname, false); /** * An abstraction for keeping track of content against some keys (e.g. * original source, instrumented source, coverage objects against file names). * This class is both the base class as well as a factory for `Store` implementations. * * Usage * ----- * * var Store = require('istanbul').Store, * store = Store.create('memory'); * * //basic use * store.set('foo', 'foo-content'); * var content = store.get('foo'); * * //keys and values * store.keys().forEach(function (key) { * console.log(key + ':\n' + store.get(key); * }); * if (store.hasKey('bar') { console.log(store.get('bar'); } * * * //syntactic sugar * store.setObject('foo', { foo: true }); * console.log(store.getObject('foo').foo); * * store.dispose(); * * @class Store * @constructor * @module store * @param {Object} options Optional. The options supported by a specific store implementation. * @main store */ function Store(/* options */) {} //add register, create, mix, loadAll, getStoreList as class methods factory.bindClassMethods(Store); /** * registers a new store implementation. * @method register * @static * @param {Function} constructor the constructor function for the store. This function must have a * `TYPE` property of type String, that will be used in `Store.create()` */ /** * returns a store implementation of the specified type. * @method create * @static * @param {String} type the type of store to create * @param {Object} opts Optional. Options specific to the store implementation * @return {Store} a new store of the specified type */ Store.prototype = { /** * sets some content associated with a specific key. The manner in which * duplicate keys are handled for multiple `set()` calls with the same * key is implementation-specific. * * @method set * @param {String} key the key for the content * @param {String} contents the contents for the key */ set: function (/* key, contents */) { throw new Error("set: must be overridden"); }, /** * returns the content associated to a specific key or throws if the key * was not `set` * @method get * @param {String} key the key for which to get the content * @return {String} the content for the specified key */ get: function (/* key */) { throw new Error("get: must be overridden"); }, /** * returns a list of all known keys * @method keys * @return {Array} an array of seen keys */ keys: function () { throw new Error("keys: must be overridden"); }, /** * returns true if the key is one for which a `get()` call would work. * @method hasKey * @param {String} key * @return true if the key is valid for this store, false otherwise */ hasKey: function (/* key */) { throw new Error("hasKey: must be overridden"); }, /** * lifecycle method to dispose temporary resources associated with the store * @method dispose */ dispose: function () {}, /** * sugar method to return an object associated with a specific key. Throws * if the content set against the key was not a valid JSON string. * @method getObject * @param {String} key the key for which to return the associated object * @return {Object} the object corresponding to the key */ getObject: function (key) { return JSON.parse(this.get(key)); }, /** * sugar method to set an object against a specific key. * @method setObject * @param {String} key the key for the object * @param {Object} object the object to be stored */ setObject: function (key, object) { return this.set(key, JSON.stringify(object)); } }; module.exports = Store; istanbul-0.4.5/lib/store/memory.js000066400000000000000000000021211275640326200171140ustar00rootroot00000000000000/* Copyright (c) 2012, Yahoo! Inc. All rights reserved. Copyrights licensed under the New BSD License. See the accompanying LICENSE file for terms. */ var util = require('util'), Store = require('./index'); /** * a `Store` implementation using an in-memory object. * * Usage * ----- * * var store = require('istanbul').Store.create('memory'); * * * @class MemoryStore * @extends Store * @module store * @constructor */ function MemoryStore() { Store.call(this); this.map = {}; } MemoryStore.TYPE = 'memory'; util.inherits(MemoryStore, Store); Store.mix(MemoryStore, { set: function (key, contents) { this.map[key] = contents; }, get: function (key) { if (!this.hasKey(key)) { throw new Error('Unable to find entry for [' + key + ']'); } return this.map[key]; }, hasKey: function (key) { return this.map.hasOwnProperty(key); }, keys: function () { return Object.keys(this.map); }, dispose: function () { this.map = {}; } }); module.exports = MemoryStore; istanbul-0.4.5/lib/store/tmp.js000066400000000000000000000040561275640326200164150ustar00rootroot00000000000000/* Copyright (c) 2012, Yahoo! Inc. All rights reserved. Copyrights licensed under the New BSD License. See the accompanying LICENSE file for terms. */ var util = require('util'), path = require('path'), os = require('os'), fs = require('fs'), mkdirp = require('mkdirp'), Store = require('./index'); function makeTempDir() { var dir = path.join(os.tmpdir ? os.tmpdir() : /* istanbul ignore next */ (process.env.TMPDIR || '/tmp'), 'ts' + new Date().getTime()); mkdirp.sync(dir); return dir; } /** * a `Store` implementation using temporary files. * * Usage * ----- * * var store = require('istanbul').Store.create('tmp'); * * * @class TmpStore * @extends Store * @module store * @param {Object} opts Optional. * @param {String} [opts.tmp] a pre-existing directory to use as the `tmp` directory. When not specified, a random directory * is created under `os.tmpdir()` * @constructor */ function TmpStore(opts) { opts = opts || {}; this.tmp = opts.tmp || makeTempDir(); this.map = {}; this.seq = 0; this.prefix = 't' + new Date().getTime() + '-'; } TmpStore.TYPE = 'tmp'; util.inherits(TmpStore, Store); Store.mix(TmpStore, { generateTmpFileName: function () { this.seq += 1; return path.join(this.tmp, this.prefix + this.seq + '.tmp'); }, set: function (key, contents) { var tmpFile = this.generateTmpFileName(); fs.writeFileSync(tmpFile, contents, 'utf8'); this.map[key] = tmpFile; }, get: function (key) { var tmpFile = this.map[key]; if (!tmpFile) { throw new Error('Unable to find tmp entry for [' + tmpFile + ']'); } return fs.readFileSync(tmpFile, 'utf8'); }, hasKey: function (key) { return !!this.map[key]; }, keys: function () { return Object.keys(this.map); }, dispose: function () { var map = this.map; Object.keys(map).forEach(function (key) { fs.unlinkSync(map[key]); }); this.map = {}; } }); module.exports = TmpStore; istanbul-0.4.5/lib/util/000077500000000000000000000000001275640326200150735ustar00rootroot00000000000000istanbul-0.4.5/lib/util/factory.js000066400000000000000000000054061275640326200171050ustar00rootroot00000000000000/* Copyright (c) 2012, Yahoo! Inc. All rights reserved. Copyrights licensed under the New BSD License. See the accompanying LICENSE file for terms. */ var util = require('util'), path = require('path'), fs = require('fs'), abbrev = require('abbrev'); function Factory(kind, dir, allowAbbreviations) { this.kind = kind; this.dir = dir; this.allowAbbreviations = allowAbbreviations; this.classMap = {}; this.abbreviations = null; } Factory.prototype = { knownTypes: function () { var keys = Object.keys(this.classMap); keys.sort(); return keys; }, resolve: function (abbreviatedType) { if (!this.abbreviations) { this.abbreviations = abbrev(this.knownTypes()); } return this.abbreviations[abbreviatedType]; }, register: function (constructor) { var type = constructor.TYPE; if (!type) { throw new Error('Could not register ' + this.kind + ' constructor [no TYPE property]: ' + util.inspect(constructor)); } this.classMap[type] = constructor; this.abbreviations = null; }, create: function (type, opts) { var allowAbbrev = this.allowAbbreviations, realType = allowAbbrev ? this.resolve(type) : type, Cons; Cons = realType ? this.classMap[realType] : null; if (!Cons) { throw new Error('Invalid ' + this.kind + ' [' + type + '], allowed values are ' + this.knownTypes().join(', ')); } return new Cons(opts); }, loadStandard: function (dir) { var that = this; fs.readdirSync(dir).forEach(function (file) { if (file !== 'index.js' && file.indexOf('.js') === file.length - 3) { try { that.register(require(path.resolve(dir, file))); } catch (ex) { console.error(ex.message); console.error(ex.stack); throw new Error('Could not register ' + that.kind + ' from file ' + file); } } }); }, bindClassMethods: function (Cons) { var tmpKind = this.kind.charAt(0).toUpperCase() + this.kind.substring(1), //ucfirst allowAbbrev = this.allowAbbreviations; Cons.mix = Factory.mix; Cons.register = this.register.bind(this); Cons.create = this.create.bind(this); Cons.loadAll = this.loadStandard.bind(this, this.dir); Cons['get' + tmpKind + 'List'] = this.knownTypes.bind(this); if (allowAbbrev) { Cons['resolve' + tmpKind + 'Name'] = this.resolve.bind(this); } } }; Factory.mix = function (cons, proto) { Object.keys(proto).forEach(function (key) { cons.prototype[key] = proto[key]; }); }; module.exports = Factory; istanbul-0.4.5/lib/util/file-matcher.js000066400000000000000000000044121275640326200177720ustar00rootroot00000000000000/* Copyright (c) 2012, Yahoo! Inc. All rights reserved. Copyrights licensed under the New BSD License. See the accompanying LICENSE file for terms. */ var async = require('async'), glob = require('glob'), fs = require('fs'), path = require('path'), seq = 0; function filesFor(options, callback) { if (!callback && typeof options === 'function') { callback = options; options = null; } options = options || {}; var root = options.root, includes = options.includes, excludes = options.excludes, realpath = options.realpath, relative = options.relative, opts; root = root || process.cwd(); includes = includes && Array.isArray(includes) ? includes : [ '**/*.js' ]; excludes = excludes && Array.isArray(excludes) ? excludes : [ '**/node_modules/**' ]; opts = { cwd: root, nodir: true, ignore: excludes }; seq += 1; opts['x' + seq + new Date().getTime()] = true; //cache buster for minimatch cache bug glob(includes.join(' '), opts, function (err, files) { if (err) { return callback(err); } if (relative) { return callback(err, files); } if (!realpath) { files = files.map(function (file) { return path.resolve(root, file); }); return callback(err, files); } var realPathCache = module.constructor._realpathCache || {}; async.map(files, function (file, done) { fs.realpath(path.resolve(root, file), realPathCache, done); }, callback); }); } function matcherFor(options, callback) { if (!callback && typeof options === 'function') { callback = options; options = null; } options = options || {}; options.relative = false; //force absolute paths options.realpath = true; //force real paths (to match Node.js module paths) filesFor(options, function (err, files) { var fileMap = {}, matchFn; if (err) { return callback(err); } files.forEach(function (file) { fileMap[file] = true; }); matchFn = function (file) { return fileMap[file]; }; matchFn.files = Object.keys(fileMap); return callback(null, matchFn); }); } module.exports = { filesFor: filesFor, matcherFor: matcherFor }; istanbul-0.4.5/lib/util/file-writer.js000066400000000000000000000101001275640326200176520ustar00rootroot00000000000000/* Copyright (c) 2012, Yahoo! Inc. All rights reserved. Copyrights licensed under the New BSD License. See the accompanying LICENSE file for terms. */ var path = require('path'), util = require('util'), fs = require('fs'), async = require('async'), mkdirp = require('mkdirp'), writer = require('./writer'), Writer = writer.Writer, ContentWriter = writer.ContentWriter; function extend(cons, proto) { Object.keys(proto).forEach(function (k) { cons.prototype[k] = proto[k]; }); } function BufferedContentWriter() { ContentWriter.call(this); this.content = ''; } util.inherits(BufferedContentWriter, ContentWriter); extend(BufferedContentWriter, { write: function (str) { this.content += str; }, getContent: function () { return this.content; } }); function StreamContentWriter(stream) { ContentWriter.call(this); this.stream = stream; } util.inherits(StreamContentWriter, ContentWriter); extend(StreamContentWriter, { write: function (str) { this.stream.write(str); } }); function SyncFileWriter() { Writer.call(this); } util.inherits(SyncFileWriter, Writer); extend(SyncFileWriter, { writeFile: function (file, callback) { mkdirp.sync(path.dirname(file)); var cw = new BufferedContentWriter(); callback(cw); fs.writeFileSync(file, cw.getContent(), 'utf8'); }, done: function () { this.emit('done'); //everything already done } }); function AsyncFileWriter() { this.queue = async.queue(this.processFile.bind(this), 20); this.openFileMap = {}; } util.inherits(AsyncFileWriter, Writer); extend(AsyncFileWriter, { writeFile: function (file, callback) { this.openFileMap[file] = true; this.queue.push({ file: file, callback: callback }); }, processFile: function (task, cb) { var file = task.file, userCallback = task.callback, that = this, stream, contentWriter; mkdirp.sync(path.dirname(file)); stream = fs.createWriteStream(file); stream.on('close', function () { delete that.openFileMap[file]; cb(); that.checkDone(); }); stream.on('error', function (err) { that.emit('error', err); }); contentWriter = new StreamContentWriter(stream); userCallback(contentWriter); stream.end(); }, done: function () { this.doneCalled = true; this.checkDone(); }, checkDone: function () { if (!this.doneCalled) { return; } if (Object.keys(this.openFileMap).length === 0) { this.emit('done'); } } }); /** * a concrete writer implementation that can write files synchronously or * asynchronously based on the constructor argument passed to it. * * Usage * ----- * * var sync = true, * fileWriter = new require('istanbul').FileWriter(sync); * * fileWriter.on('done', function () { console.log('done'); }); * fileWriter.copyFile('/foo/bar.jpg', '/baz/bar.jpg'); * fileWriter.writeFile('/foo/index.html', function (contentWriter) { * contentWriter.println(''); * contentWriter.println(''); * }); * fileWriter.done(); // will emit the `done` event when all files are written * * @class FileWriter * @extends Writer * @module io * @param sync * @constructor */ function FileWriter(sync) { Writer.call(this); var that = this; this.delegate = sync ? new SyncFileWriter() : new AsyncFileWriter(); this.delegate.on('error', function (err) { that.emit('error', err); }); this.delegate.on('done', function () { that.emit('done'); }); } util.inherits(FileWriter, Writer); extend(FileWriter, { copyFile: function (source, dest) { mkdirp.sync(path.dirname(dest)); fs.writeFileSync(dest, fs.readFileSync(source)); }, writeFile: function (file, callback) { this.delegate.writeFile(file, callback); }, done: function () { this.delegate.done(); } }); module.exports = FileWriter;istanbul-0.4.5/lib/util/help-formatter.js000066400000000000000000000014361275640326200203660ustar00rootroot00000000000000/* Copyright (c) 2012, Yahoo! Inc. All rights reserved. Copyrights licensed under the New BSD License. See the accompanying LICENSE file for terms. */ var OPT_PREFIX = " ", OPT_START = OPT_PREFIX.length, TEXT_START = 14, STOP = 80, wrap = require('wordwrap')(TEXT_START, STOP), paraWrap = require('wordwrap')(1, STOP); function formatPara(text) { return paraWrap(text); } function formatOption(option, helpText) { var formattedText = wrap(helpText); if (option.length > TEXT_START - OPT_START - 2) { return OPT_PREFIX + option + '\n' + formattedText; } else { return OPT_PREFIX + option + formattedText.substring((OPT_PREFIX + option).length); } } module.exports = { formatPara: formatPara, formatOption: formatOption };istanbul-0.4.5/lib/util/input-error.js000066400000000000000000000004331275640326200177170ustar00rootroot00000000000000/* Copyright (c) 2012, Yahoo! Inc. All rights reserved. Copyrights licensed under the New BSD License. See the accompanying LICENSE file for terms. */ module.exports.create = function (message) { var err = new Error(message); err.inputError = true; return err; }; istanbul-0.4.5/lib/util/insertion-text.js000066400000000000000000000060551275640326200204330ustar00rootroot00000000000000/* Copyright (c) 2012, Yahoo! Inc. All rights reserved. Copyrights licensed under the New BSD License. See the accompanying LICENSE file for terms. */ function InsertionText(text, consumeBlanks) { this.text = text; this.origLength = text.length; this.offsets = []; this.consumeBlanks = consumeBlanks; this.startPos = this.findFirstNonBlank(); this.endPos = this.findLastNonBlank(); } var WHITE_RE = /[ \f\n\r\t\v\u00A0\u2028\u2029]/; InsertionText.prototype = { findFirstNonBlank: function () { var pos = -1, text = this.text, len = text.length, i; for (i = 0; i < len; i += 1) { if (!text.charAt(i).match(WHITE_RE)) { pos = i; break; } } return pos; }, findLastNonBlank: function () { var text = this.text, len = text.length, pos = text.length + 1, i; for (i = len - 1; i >= 0; i -= 1) { if (!text.charAt(i).match(WHITE_RE)) { pos = i; break; } } return pos; }, originalLength: function () { return this.origLength; }, insertAt: function (col, str, insertBefore, consumeBlanks) { consumeBlanks = typeof consumeBlanks === 'undefined' ? this.consumeBlanks : consumeBlanks; col = col > this.originalLength() ? this.originalLength() : col; col = col < 0 ? 0 : col; if (consumeBlanks) { if (col <= this.startPos) { col = 0; } if (col > this.endPos) { col = this.origLength; } } var len = str.length, offset = this.findOffset(col, len, insertBefore), realPos = col + offset, text = this.text; this.text = text.substring(0, realPos) + str + text.substring(realPos); return this; }, findOffset: function (pos, len, insertBefore) { var offsets = this.offsets, offsetObj, cumulativeOffset = 0, i; for (i = 0; i < offsets.length; i += 1) { offsetObj = offsets[i]; if (offsetObj.pos < pos || (offsetObj.pos === pos && !insertBefore)) { cumulativeOffset += offsetObj.len; } if (offsetObj.pos >= pos) { break; } } if (offsetObj && offsetObj.pos === pos) { offsetObj.len += len; } else { offsets.splice(i, 0, { pos: pos, len: len }); } return cumulativeOffset; }, wrap: function (startPos, startText, endPos, endText, consumeBlanks) { this.insertAt(startPos, startText, true, consumeBlanks); this.insertAt(endPos, endText, false, consumeBlanks); return this; }, wrapLine: function (startText, endText) { this.wrap(0, startText, this.originalLength(), endText); }, toString: function () { return this.text; } }; module.exports = InsertionText;istanbul-0.4.5/lib/util/meta.js000066400000000000000000000005701275640326200163610ustar00rootroot00000000000000/* Copyright (c) 2012, Yahoo! Inc. All rights reserved. Copyrights licensed under the New BSD License. See the accompanying LICENSE file for terms. */ var path = require('path'), fs = require('fs'), pkg = JSON.parse(fs.readFileSync(path.resolve(__dirname, '..', '..', 'package.json'), 'utf8')); module.exports = { NAME: pkg.name, VERSION: pkg.version }; istanbul-0.4.5/lib/util/tree-summarizer.js000066400000000000000000000145111275640326200205660ustar00rootroot00000000000000/* Copyright (c) 2012, Yahoo! Inc. All rights reserved. Copyrights licensed under the New BSD License. See the accompanying LICENSE file for terms. */ var path = require('path'), SEP = path.sep || '/', utils = require('../object-utils'); function commonArrayPrefix(first, second) { var len = first.length < second.length ? first.length : second.length, i, ret = []; for (i = 0; i < len; i += 1) { if (first[i] === second[i]) { ret.push(first[i]); } else { break; } } return ret; } function findCommonArrayPrefix(args) { if (args.length === 0) { return []; } var separated = args.map(function (arg) { return arg.split(SEP); }), ret = separated.pop(); if (separated.length === 0) { return ret.slice(0, ret.length - 1); } else { return separated.reduce(commonArrayPrefix, ret); } } function Node(fullName, kind, metrics) { this.name = fullName; this.fullName = fullName; this.kind = kind; this.metrics = metrics || null; this.parent = null; this.children = []; } Node.prototype = { displayShortName: function () { return this.relativeName; }, fullPath: function () { return this.fullName; }, addChild: function (child) { this.children.push(child); child.parent = this; }, toJSON: function () { return { name: this.name, relativeName: this.relativeName, fullName: this.fullName, kind: this.kind, metrics: this.metrics, parent: this.parent === null ? null : this.parent.name, children: this.children.map(function (node) { return node.toJSON(); }) }; } }; function TreeSummary(summaryMap, commonPrefix) { this.prefix = commonPrefix; this.convertToTree(summaryMap, commonPrefix); } TreeSummary.prototype = { getNode: function (shortName) { return this.map[shortName]; }, convertToTree: function (summaryMap, arrayPrefix) { var nodes = [], rootPath = arrayPrefix.join(SEP) + SEP, root = new Node(rootPath, 'dir'), tmp, tmpChildren, seen = {}, filesUnderRoot = false; seen[rootPath] = root; Object.keys(summaryMap).forEach(function (key) { var metrics = summaryMap[key], node, parentPath, parent; node = new Node(key, 'file', metrics); seen[key] = node; nodes.push(node); parentPath = path.dirname(key) + SEP; if (parentPath === SEP + SEP || parentPath === '.' + SEP) { parentPath = SEP + '__root__' + SEP; } parent = seen[parentPath]; if (!parent) { parent = new Node(parentPath, 'dir'); root.addChild(parent); seen[parentPath] = parent; } parent.addChild(node); if (parent === root) { filesUnderRoot = true; } }); if (filesUnderRoot && arrayPrefix.length > 0) { arrayPrefix.pop(); //start at one level above tmp = root; tmpChildren = tmp.children; tmp.children = []; root = new Node(arrayPrefix.join(SEP) + SEP, 'dir'); root.addChild(tmp); tmpChildren.forEach(function (child) { if (child.kind === 'dir') { root.addChild(child); } else { tmp.addChild(child); } }); } this.fixupNodes(root, arrayPrefix.join(SEP) + SEP); this.calculateMetrics(root); this.root = root; this.map = {}; this.indexAndSortTree(root, this.map); }, fixupNodes: function (node, prefix, parent) { var that = this; if (node.name.indexOf(prefix) === 0) { node.name = node.name.substring(prefix.length); } if (node.name.charAt(0) === SEP) { node.name = node.name.substring(1); } if (parent) { if (parent.name !== '__root__' + SEP) { node.relativeName = node.name.substring(parent.name.length); } else { node.relativeName = node.name; } } else { node.relativeName = node.name.substring(prefix.length); } node.children.forEach(function (child) { that.fixupNodes(child, prefix, node); }); }, calculateMetrics: function (entry) { var that = this, fileChildren; if (entry.kind !== 'dir') {return; } entry.children.forEach(function (child) { that.calculateMetrics(child); }); entry.metrics = utils.mergeSummaryObjects.apply( null, entry.children.map(function (child) { return child.metrics; }) ); // calclulate "java-style" package metrics where there is no hierarchy // across packages fileChildren = entry.children.filter(function (n) { return n.kind !== 'dir'; }); if (fileChildren.length > 0) { entry.packageMetrics = utils.mergeSummaryObjects.apply( null, fileChildren.map(function (child) { return child.metrics; }) ); } else { entry.packageMetrics = null; } }, indexAndSortTree: function (node, map) { var that = this; map[node.name] = node; node.children.sort(function (a, b) { a = a.relativeName; b = b.relativeName; return a < b ? -1 : a > b ? 1 : 0; }); node.children.forEach(function (child) { that.indexAndSortTree(child, map); }); }, toJSON: function () { return { prefix: this.prefix, root: this.root.toJSON() }; } }; function TreeSummarizer() { this.summaryMap = {}; } TreeSummarizer.prototype = { addFileCoverageSummary: function (filePath, metrics) { this.summaryMap[filePath] = metrics; }, getTreeSummary: function () { var commonArrayPrefix = findCommonArrayPrefix(Object.keys(this.summaryMap)); return new TreeSummary(this.summaryMap, commonArrayPrefix); } }; module.exports = TreeSummarizer; istanbul-0.4.5/lib/util/writer.js000066400000000000000000000052651275640326200167550ustar00rootroot00000000000000/* Copyright (c) 2012, Yahoo! Inc. All rights reserved. Copyrights licensed under the New BSD License. See the accompanying LICENSE file for terms. */ var util = require('util'), EventEmitter = require('events').EventEmitter; function extend(cons, proto) { Object.keys(proto).forEach(function (k) { cons.prototype[k] = proto[k]; }); } /** * abstract interfaces for writing content * @class ContentWriter * @module io * @main io * @constructor */ //abstract interface for writing content function ContentWriter() { } ContentWriter.prototype = { /** * writes the specified string as-is * @method write * @param {String} str the string to write */ write: /* istanbul ignore next: abstract method */ function (/* str */) { throw new Error('write: must be overridden'); }, /** * writes the specified string with a newline at the end * @method println * @param {String} str the string to write */ println: function (str) { this.write(str + '\n'); } }; /** * abstract interface for writing files and assets. The caller is expected to * call `done` on the writer after it has finished writing all the required * files. The writer is an event-emitter that emits a `done` event when `done` * is called on it *and* all files have successfully been written. * * @class Writer * @constructor */ function Writer() { EventEmitter.call(this); } util.inherits(Writer, EventEmitter); extend(Writer, { /** * allows writing content to a file using a callback that is passed a content writer * @method writeFile * @param {String} file the name of the file to write * @param {Function} callback the callback that is called as `callback(contentWriter)` */ writeFile: /* istanbul ignore next: abstract method */ function (/* file, callback */) { throw new Error('writeFile: must be overridden'); }, /** * copies a file from source to destination * @method copyFile * @param {String} source the file to copy, found on the file system * @param {String} dest the destination path */ copyFile: /* istanbul ignore next: abstract method */ function (/* source, dest */) { throw new Error('copyFile: must be overridden'); }, /** * marker method to indicate that the caller is done with this writer object * The writer is expected to emit a `done` event only after this method is called * and it is truly done. * @method done */ done: /* istanbul ignore next: abstract method */ function () { throw new Error('done: must be overridden'); } }); module.exports = { Writer: Writer, ContentWriter: ContentWriter }; istanbul-0.4.5/lib/util/yui-load-hook.js000066400000000000000000000034431275640326200201160ustar00rootroot00000000000000/* Copyright (c) 2012, Yahoo! Inc. All rights reserved. Copyrights licensed under the New BSD License. See the accompanying LICENSE file for terms. */ //EXPERIMENTAL code: do not rely on this in anyway until the docs say it is allowed var path = require('path'), yuiRegexp = /yui-nodejs\.js$/; module.exports = function (matchFn, transformFn, verbose) { return function (file) { if (!file.match(yuiRegexp)) { return; } var YMain = require(file), YUI, loaderFn, origGet; if (YMain.YUI) { YUI = YMain.YUI; loaderFn = YUI.Env && YUI.Env.mods && YUI.Env.mods['loader-base'] ? YUI.Env.mods['loader-base'].fn : null; if (!loaderFn) { return; } if (verbose) { console.log('Applying YUI load post-hook'); } YUI.Env.mods['loader-base'].fn = function (Y) { loaderFn.call(null, Y); origGet = Y.Get._exec; Y.Get._exec = function (data, url, cb) { if (matchFn(url) || matchFn(path.resolve(url))) { //allow for relative paths as well if (verbose) { console.log('Transforming [' + url + ']'); } try { data = transformFn(data, url); } catch (ex) { console.error('Error transforming: ' + url + ' return original code'); console.error(ex.message || ex); if (ex.stack) { console.error(ex.stack); } } } return origGet.call(Y, data, url, cb); }; return Y; }; } }; }; istanbul-0.4.5/misc/000077500000000000000000000000001275640326200143035ustar00rootroot00000000000000istanbul-0.4.5/misc/ast/000077500000000000000000000000001275640326200150725ustar00rootroot00000000000000istanbul-0.4.5/misc/ast/assign.js000066400000000000000000000000361275640326200167130ustar00rootroot00000000000000var x = args[0]; output = x; istanbul-0.4.5/misc/ast/block-label.js000066400000000000000000000007111275640326200175760ustar00rootroot00000000000000 var tok = { type: 'keyword', length: 7 }, max_line_length = 6, prev_token = { type: "keyword" }; out: { if (tok.length > max_line_length) { if (prev_token) { if (prev_token.type == "keyword") break out; } switch (tok.type) { case "keyword": case "atom": case "name": case "punc": console.log(tok); break out; } } } istanbul-0.4.5/misc/ast/cond.js000066400000000000000000000001501275640326200163470ustar00rootroot00000000000000var x = 10, y, z; y = (z=10,x < 10) ? (z--,1) : (z++,2); console.log('Y=' + y); console.log('Z=' + z); istanbul-0.4.5/misc/ast/defun-compact.js000066400000000000000000000001351275640326200201540ustar00rootroot00000000000000var foo =function () { var ret = 'foo'; return ret; }; function bar() { return 'bar'; } istanbul-0.4.5/misc/ast/defun.js000066400000000000000000000002121275640326200165240ustar00rootroot00000000000000var foo =function () { var ret = 'foo'; return ret; }; function bar() { return 'bar'; } console.log(foo() + foo() + bar()); istanbul-0.4.5/misc/ast/do-statement.js000066400000000000000000000000451275640326200200330ustar00rootroot00000000000000var i = 5; do i--; while (i > 0); istanbul-0.4.5/misc/ast/dot.js000066400000000000000000000000731275640326200162160ustar00rootroot00000000000000var foo = { bar: { baz: [ 1, 2 ] } }; foo.bar.baz[0] = 3; istanbul-0.4.5/misc/ast/eq.js000066400000000000000000000000461275640326200160350ustar00rootroot00000000000000 if (1 === 2) { console.log('foo'); } istanbul-0.4.5/misc/ast/expr.js000066400000000000000000000000441275640326200164040ustar00rootroot00000000000000var a = 5, b = a > 0 && a < 4; istanbul-0.4.5/misc/ast/for-block.js000066400000000000000000000000621275640326200173040ustar00rootroot00000000000000for (var i =0; i<10; i++) { console.log(i); } istanbul-0.4.5/misc/ast/for-compact.js000066400000000000000000000000521275640326200176370ustar00rootroot00000000000000for (var i =0; i<10; i++) console.log(i); istanbul-0.4.5/misc/ast/for-multi.js000066400000000000000000000000751275640326200173500ustar00rootroot00000000000000for (var i = 0, j=0; i<10; i++, j++) console.log(i + j); istanbul-0.4.5/misc/ast/for-statement.js000066400000000000000000000000611275640326200202150ustar00rootroot00000000000000var i; for (i =0; i<10; i++) console.log(i); istanbul-0.4.5/misc/ast/forin-block.js000066400000000000000000000001261275640326200176340ustar00rootroot00000000000000var foo = { a: 10, b: 20 }; for (var k in foo) { console.log(k + '=' + foo[k]); } istanbul-0.4.5/misc/ast/forin-compact.js000066400000000000000000000001171275640326200201700ustar00rootroot00000000000000var foo = { a: 10, b: 20 }; for (var k in foo) console.log(k + '=' + foo[k]); istanbul-0.4.5/misc/ast/forin-statement.js000066400000000000000000000001231275640326200205430ustar00rootroot00000000000000var foo = { a: 10, b: 20 }; for (var k in foo) console.log(k + '=' + foo[k]); istanbul-0.4.5/misc/ast/func.js000066400000000000000000000001141275640326200163570ustar00rootroot00000000000000var x = 2, output; output = x < 5 ? (function() { return 42; }()) : 15; istanbul-0.4.5/misc/ast/if-block.js000066400000000000000000000001651275640326200171200ustar00rootroot00000000000000if (20 > 10) { console.log('>10'); } else if (x < 10) { console.log('<10'); } else { console.log('10'); }istanbul-0.4.5/misc/ast/if-compact.js000066400000000000000000000001361275640326200174520ustar00rootroot00000000000000if (20 > 10) console.log('>10'); else if (x < 10) console.log('<10'); else console.log('10'); istanbul-0.4.5/misc/ast/if-only.js000066400000000000000000000000421275640326200170010ustar00rootroot00000000000000if (20 > 10) console.log('Yay!'); istanbul-0.4.5/misc/ast/if-statement.js000066400000000000000000000001651275640326200200320ustar00rootroot00000000000000var x = 25; if (x > 10) console.log('>10'); else if (x < 10) console.log('<10'); else console.log('10'); istanbul-0.4.5/misc/ast/incr-slice.js000066400000000000000000000001061275640326200174550ustar00rootroot00000000000000var foo = { lineMap: { 1: 0 }}, bar = { 2: 0 }; foo.lineMap[1]++; istanbul-0.4.5/misc/ast/label.js000066400000000000000000000003721275640326200165110ustar00rootroot00000000000000var items= [],itemsPassed = 0; var i, j; function foo() { top: for (i = 0; i < items.length; i++){ for (j = 0; j < tests.length; j++) if (!tests[j].pass(items[i])) continue top; itemsPassed++; } } istanbul-0.4.5/misc/ast/nested-if.js000066400000000000000000000001121275640326200173000ustar00rootroot00000000000000if (process.argv[2]) if (process.argv[3]) console.log('TT'); istanbul-0.4.5/misc/ast/pfcall.js000066400000000000000000000000631275640326200166700ustar00rootroot00000000000000(function () { (function() { return 1; })(); })(); istanbul-0.4.5/misc/ast/preamble.js000066400000000000000000000070601275640326200172220ustar00rootroot00000000000000if (typeof __yct__ === 'undefined') { __yct__ = {}; } var __ycf14f8be05b4467d47fccdd98300cc2c30 = __yct__['/Users/ananthk/code/ananthk/cover/lib/cover.js'] = {"path":"/Users/ananthk/code/ananthk/cover/lib/cover.js", "lines":{ "2":0, "11":0, "12":0, "15":0, "16":0, "18":0, "19":0, "20":0, "23":0, "24":0, "31":0, "32":0, "35":0, "36":0, "37":0, "38":0, "39":0, "42":0, "45":0, "46":0, "48":0, "49":0, "50":0, "52":0, "63":0, "67":0, "68":0, "73":0, "76":0, "77":0, "78":0, "82":0, "84":0, "85":0, "86":0, "87":0, "92":0, "94":0, "95":0, "96":0, "97":0, "98":0, "104":0, "105":0, "106":0, "107":0, "108":0, "110":0, "111":0, "112":0, "123":0, "124":0, "127":0, "133":0, "134":0, "138":0, "162":0, "164":0, "165":0, "166":0, "170":0, "172":0, "178":0, "179":0, "180":0, "182":0, "183":0, "184":0, "185":0, "186":0, "188":0, "192":0, "193":0, "194":0, "195":0, "196":0, "197":0, "199":0, "203":0, "204":0, "205":0, "206":0, "212":0, "213":0, "214":0, "215":0, "216":0, "217":0, "218":0, "219":0, "220":0, "222":0, "224":0, "226":0, "229":0 }, "functions":{ "1":0, "2":0, "3":0, "4":0, "5":0, "6":0, "7":0, "8":0, "9":0, "10":0, "11":0, "12":0, "13":0, "14":0, "15":0, "16":0, "17":0, "18":0, "19":0, "20":0, "21":0, "22":0, "23":0, "24":0, "25":0, "26":0, "27":0 }, "fnMap":{ "1":{ "name":"getCoverageVar", "line":11 }, "2":{ "name":"getTrackerVar", "line":15 }, "3":{ "name":"preamble", "line":23 }, "4":{ "name":"protectedWalk", "line":31 }, "5":{ "name":"coverStatementAst", "line":45 }, "6":{ "name":"maybeCoverLine", "line":67 }, "7":{ "name":"statementTracker", "line":76 }, "8":{ "name":"anonymous (8)", "line":77 }, "9":{ "name":"anonymous (9)", "line":78 }, "10":{ "name":"lineCoverageInjector", "line":82 }, "11":{ "name":"anonymous (11)", "line":84 }, "12":{ "name":"anonymous (12)", "line":86 }, "13":{ "name":"anonymous (13)", "line":87 }, "14":{ "name":"funcCoverageInjector", "line":92 }, "15":{ "name":"anonymous (15)", "line":94 }, "16":{ "name":"anonymous (16)", "line":96 }, "17":{ "name":"anonymous (17)", "line":97 }, "18":{ "name":"getWalkers", "line":133 }, "19":{ "name":"transformAst", "line":162 }, "20":{ "name":"anonymous (20)", "line":165 }, "21":{ "name":"instrument", "line":170 }, "22":{ "name":"anonymous (22)", "line":195 }, "23":{ "name":"convertToYuiFormat", "line":203 }, "24":{ "name":"anonymous (24)", "line":205 }, "25":{ "name":"anonymous (25)", "line":216 }, "26":{ "name":"anonymous (26)", "line":217 }, "27":{ "name":"anonymous (27)", "line":219 } }};istanbul-0.4.5/misc/ast/switch-statement.js000066400000000000000000000003261275640326200207340ustar00rootroot00000000000000var foo = '10'; switch (foo) { case 5: case 10: console.log('10'); break; case 20: console.log('20'); break; default: console.log('other'); break; } istanbul-0.4.5/misc/ast/try-block.js000066400000000000000000000002131275640326200173320ustar00rootroot00000000000000try { if (process.argv[2]) { throw "foo"; } } catch(ex) { console.log(ex); } //finally { // console.log('done'); //}istanbul-0.4.5/misc/ast/try-statement.js000066400000000000000000000001511275640326200202450ustar00rootroot00000000000000try { if (process.argv[2]) throw "foo"; } catch(ex) { console.log(ex); } finally { console.log('done'); }istanbul-0.4.5/misc/ast/while-block.js000066400000000000000000000000571275640326200176320ustar00rootroot00000000000000var i = 10; while (i--) { console.log(i); }istanbul-0.4.5/misc/ast/while-compact.js000066400000000000000000000000451275640326200201630ustar00rootroot00000000000000var i=10; while (i--) console.log(i);istanbul-0.4.5/misc/ast/while-for.js000066400000000000000000000001171275640326200173230ustar00rootroot00000000000000var i = 10, j; while (i--) for (j=0; j", "license": "BSD-3-Clause", "contributors": [ "Reid Burke ", "Martin Cooper ", "Dav Glass ", "nowamasa ", "Miller Medeiros @millermedeiros ", "Daniel Perez Alvarez @unindented ", "Mathias Bynens @mathiasbynens ", "Nathan Brown @nbrownus ", "Brian Ng @existentialism ", "John Morrison @jrgm", "Tomaz Muraus @kami ", "Joe @jhansche ", "Vojta Jina @vojtajina ", "Dmitry Shirokov @runk ", "Chris Gladd @chrisgladd", "Sergey Belov ", "porneL @pornel ", "@asifrc", "Gergely Nemeth @gergelyke", "@bixdeng", "@mpderbec", "@jxiaodev", "Arpad Borsos @Swatinem ", "Ariya Hidayat @ariya", "@markyen", "Sam Saccone @samccone ", "Jason Cheatham @jason0x43", "@smikes", "Yasyf Mohamedali @yasyf ", "Fabio Crisci @piuccio ", "Ryan Roemer @ryan-roemer ", "Douglas Christopher Wilson @dougwilson", "Gustav Nikolaj @gustavnikolaj ", "Denis Sokolov @denis-sokolov ", "Yann Mainier @ymainier", "Yiyu He @dead-horse ", "Andrew Kelley @andrewrk ", "Will LaBranche @wlabranche ", "Mathieu Naouache @math-nao ", "Ron Korving @ronkorving", "Rob McGuire-Dale @robatron ", "Justin Johnson @booleangate", "Juan Gabriel Jiménez @juangabreil ", "Daniel Sabelnikov @dragn ", "Tony Lukasavage @tonylukasavage ", "Simon Ramsay @nexus-uw", "Dominykas Blyžė @dominykas", "Seth Pollack @sethpollack", "Benjamin E. Coe @bcoe ", "Yuren Ju ", "Aleksey Verkholantsev ", "Ed S ", "Mordy Tikotzky ", "Haoliang Gao @popomore ", "Roderick Hsiao @roderickhsiao", "Nikita Gusakov @nkt", "Alex Dunphy @alexdunphy ", "Artemy Tregubenko @arty-name ", "Arye Lukashevski @aryelu", "@sterlinghw", "Gord Tanner ", "Tom MacWright @tmcw ", "Kitson Kelly @kitsonk", "@asa-git", "@RoCat", "Ian Page Hands @iphands ", "Eddie Gurnee @pegurnee", "Kevin Decker @kpdecker ", "isaacs @isaacs ", "Steve Gray @steve-gray", "Prayag Verma @pra85 ", "Abe Fehr @abejfehr ", "Brian Woodward @doowb ", "@Victorystick", "@inversion", "@JamesMGreene", "@ChALkeR", "@graingert" ], "scripts": { "pretest": "jshint index.js lib/ test/ && ./download-escodegen-browser.sh", "test": "node --harmony test/run.js", "posttest": "node ./lib/cli.js check-coverage --statements 95 --branches 80", "docs": "npm install yuidocjs && node node_modules/yuidocjs/lib/cli.js ." }, "bin": { "istanbul": "./lib/cli.js" }, "files": [ "index.js", "lib/" ], "repository": { "type": "git", "url": "git://github.com/gotwarlost/istanbul.git" }, "dependencies": { "abbrev": "1.0.x", "async": "1.x", "escodegen": "1.8.x", "esprima": "2.7.x", "glob": "^5.0.15", "handlebars": "^4.0.1", "js-yaml": "3.x", "mkdirp": "0.5.x", "nopt": "3.x", "once": "1.x", "resolve": "1.1.x", "supports-color": "^3.1.0", "which": "^1.1.1", "wordwrap": "^1.0.0" }, "devDependencies": { "coveralls": "2.x", "jshint": "^2.8.0", "nodeunit": "0.9.x", "requirejs": "2.x", "rimraf": "^2.4.3" } } istanbul-0.4.5/test/000077500000000000000000000000001275640326200143275ustar00rootroot00000000000000istanbul-0.4.5/test/browser/000077500000000000000000000000001275640326200160125ustar00rootroot00000000000000istanbul-0.4.5/test/browser/support/000077500000000000000000000000001275640326200175265ustar00rootroot00000000000000istanbul-0.4.5/test/browser/support/index.html000066400000000000000000000121671275640326200215320ustar00rootroot00000000000000 Instrumentation test on the browser {{#if demo}}

Instrumentation tests

Pretty printed coverage object Compact code


{{/if}}




istanbul-0.4.5/test/browser/support/phantom-test.client.js000066400000000000000000000026011275640326200237630ustar00rootroot00000000000000/*global phantom, window,document */
var system = require('system'),
    page = require('webpage').create(),
    args = Array.prototype.slice.call(system.args),
    script = args.shift(),
    port = args.shift(),
    files = args,
    done;

console.log('Connecting to localhost, port: ' + port + ' (script=' + script + ')');
console.log(files.length + ' files to be tested');

page.onConsoleMessage = function (msg) { console.log(msg); };
page.open('http://localhost:' + port + '/', function (status) {
    if (status !== 'success') {
        console.log('Unable to load main page!');
        phantom.exit();
    }
    console.log('Page loaded');
    window.setInterval(function () {
        done = page.evaluate(function (files) {
            if (!document.runningTests) {
                console.log('Load wait...');
                if (document.runTests) {
                    console.log('Start tests ...');
                    document.runningTests = true;
                    document.runTests(files);
                }
            } else {
                console.log('Wait...');
                if (document.done) {
                    console.log('All tests done');
                    return true;
                }
            }
            return false;
        }, files);
        if (done) {
            console.log('Exit phantom');
            phantom.exit();
        }
    }, 1000);
});






istanbul-0.4.5/test/browser/support/server.js000066400000000000000000000116451275640326200214010ustar00rootroot00000000000000/*jslint nomen: true */
var handlebars = require('handlebars'),
    path = require('path'),
    fs = require('fs'),
    templateFor = function (name) { return handlebars.compile(fs.readFileSync(path.resolve(__dirname, name), 'utf8')); },
    reader = function () {
        var args = Array.prototype.slice.call(arguments),
            file;
        args.unshift(__dirname);
        file = path.resolve.apply(null, args);
        return fs.readFileSync(file, 'utf8');
    },
    esprimaSource = reader('..', '..', '..', 'node_modules', 'esprima', 'esprima.js'),
    escodegenSource = reader('..', '..', '..', 'node_modules', 'escodegen', 'escodegen.browser.min.js'),
    yuiSource = reader('vendor', 'yui-support.js'),
    vm = require('vm'),
    server;

function handleInitialPage(request, response) {
    response.setHeader('content-type', 'text/html');
    var template = templateFor('index.html');
    response.end(template({ demo: server.demo ? 'yes' : '' }));
}

function handleYui(request, response) {
    response.setHeader('content-type', 'application/javascript');
    response.end(yuiSource, 'utf8');
}

function handleEsprima(request, response) {
    response.setHeader('content-type', 'application/javascript');
    response.end(esprimaSource, 'utf8');
}

function handleEscodegen(request, response) {
    response.setHeader('content-type', 'application/javascript');
    response.end(escodegenSource, 'utf8');
}

function handleInstrumenter(request, response) {
    response.setHeader('content-type', 'application/javascript');
    var iPath = path.resolve(__dirname, '..', '..', '..', 'lib', 'instrumenter.js'),
        instrumenterSource = fs.readFileSync(iPath, 'utf8');

    if (server.instrumenter) {
        response.end(server.instrumenter.instrumentSync(instrumenterSource, iPath), 'utf8');
    } else {
        response.end(instrumenterSource, 'utf8');
    }
}

function handleFile(request, response) {
    try {
        var file = path.resolve(server.rootPath, request.url.substring(1)),
            content;
        content = fs.readFileSync(file, 'utf8');
        response.setHeader('content-type', 'text/plain');
        response.end(content, 'utf8');
    } catch (ex) {
        console.error(ex.message);
        response.statusCode = 404;
        response.end();
    }
}

function getData(request, callback) {
    var data = '';
    request.setEncoding('utf8');
    request.on('data', function (rdata) { data += rdata; });
    request.on('end', function () {
        callback(data);
    });
}

function handleInstrumented(request, response) {
    var script,
        ok = true,
        err = null;
    getData(request, function (data) {
        server.emit('instrumented', request.url.substring(1), data);
        try {
            script = vm.createScript(data, request.url);
        } catch (ex) {
            console.error('Invalid JS returned for:' + request.url);
            ok = false;
            err = ex.message || ex.toString();
        }
        response.setHeader('content-type', 'application/json');
        response.end(JSON.stringify({ ok: ok, err: err }), 'utf8');
    });
}

function handleEnd(request, response) {
    var ok = true,
        obj;
    getData(request, function (data) {
        try {
            obj = JSON.parse(data);
        } catch (ex) {
            console.error(ex.message || ex);
            console.error(ex.stack);
            ok = false;
        }
        server.emit('done', obj);
        response.end(JSON.stringify({ ok: ok }, 'utf8'));
    });
}

function handler(request, response) {
    console.log(request.method + ' ' + request.url);
    if (request.method === 'GET') {
        switch (request.url) {
        case '/':
            handleInitialPage(request, response);
            break;
        case '/_yui.js':
            handleYui(request, response);
            break;
        case '/_esprima.js':
            handleEsprima(request, response);
            break;
        case '/_escodegen.js':
            handleEscodegen(request, response);
            break;
        case '/_instrumenter.js':
            handleInstrumenter(request, response);
            break;
        default:
            handleFile(request, response);
            break;
        }
    } else if (request.method === 'POST') {
        switch (request.url) {
        case '/':
            handleEnd(request, response);
            break;
        default:
            handleInstrumented(request, response);
            break;
        }
    }
}

function create(port, instrumenter, root, demo) {
    server = require('http').createServer(handler);
    server.instrumenter = instrumenter;
    server.rootPath = root;
    server.listen(port);
    server.demo = demo;
    return server;
}

module.exports = {
    create: create
};

if (module === require.main) {
    (function () {
        var port = process.argv[2] || 9000;
        create(port, null, path.resolve(__dirname, '..', '..'), true);
        console.log('Listening on http://localhost:' + port + '/');
    }());
}
istanbul-0.4.5/test/browser/test-browser-instrumentation.js000066400000000000000000000067641275640326200242660ustar00rootroot00000000000000/*jslint nomen: true */
var which = require('which'),
    phantom,
    path = require('path'),
    fs = require('fs'),
    child_process = require('child_process'),
    filesFor = require('../../lib/util/file-matcher').filesFor,
    Instrumenter = require('../../lib/instrumenter'),
    instrumenter = new Instrumenter({ backdoor: { omitTrackerSuffix: true } }),
    ROOT = path.resolve(__dirname, '..', '..', 'lib'),
    common = require('../common'),
    filesToInstrument,
    exited,
    cp;

function runPhantom(cmd, script, port, files) {
    var args = [ script ];
    args.push(port);
    args.push.apply(args, files);
    console.log('Start phantom');
    cp = child_process.spawn(cmd, args);
    cp.stdout.on('data', function (data) { process.stdout.write(data); });
    cp.stderr.on('data', function (data) { process.stderr.write(data); });
    cp.on('exit', function () {
        exited = 1;
    });
}

module.exports = {
    setUp: function (cb) {
        try {
            phantom = which.sync('phantomjs');
        } catch (ex) {}
        filesFor({
            root: ROOT,
            includes: [ '**/*.js' ],
            xexcludes: [ '**/instrumenter.js' ],
            relative: true
        }, function (err, files) {
            filesToInstrument = files;
            cb();
        });
    },
    "should produce identical instrumentation results when instrumentation run on browser v/s nodejs": function (test) {
        if (!phantom) {
            console.error('******************************************************************');
            console.error('No phantomjs found on path, skipping browser instrumentation test');
            console.error('******************************************************************');
            test.done();
            return;
        }
        var server,
            port = 9000,
            finalFn = function () {
                if (server) { server.close(); }
                if (!cp.exited) { cp.kill(); }
            };
        try {
            console.log('Start server');
            server = require('./support/server').create(port, process.env.SELF_COVER ? instrumenter : null, ROOT);
            server.on('instrumented', function (file, instrumented) {
                var code = fs.readFileSync(path.resolve(ROOT, file), 'utf8'),
                    serverSideInstrumented = instrumenter.instrumentSync(code, file);
                test.equal(serverSideInstrumented, instrumented, 'No match for [' + file + ']');
            });
            server.on('done', function (coverage) {
                if (common.isSelfCover()) {
                    try {
                        if (coverage) {
                            fs.writeFileSync(path.resolve(common.getCoverageDir(), 'coverage-browser.json'), JSON.stringify(coverage), 'utf8');
                        } else {
                            console.log('No coverage found');
                        }
                    } catch (err) {
                        console.error(err.message || err);
                        console.error(err.stack);
                        test.ok(false, err.message || err);
                    }
                }
                finalFn();
                test.done();
            });
            runPhantom(phantom, path.resolve(__dirname, 'support', 'phantom-test.client.js'), port, filesToInstrument);
        } catch (ex) {
            console.error(ex.message || ex);
            console.error(ex.stack);
            test.ok(false, ex.message || ex);
            test.done();
        }

    }
};

istanbul-0.4.5/test/cli-helper.js000066400000000000000000000167441275640326200167250ustar00rootroot00000000000000/*jslint nomen: true */
var path = require('path'),
    fs = require('fs'),
    cp = require('child_process'),
    Module = require('module'),
    originalLoader = Module._extensions['.js'],
    common = require('./common'),
    MAIN_FILE = path.resolve(__dirname, '..', 'lib', 'cli.js'),
    DEFAULT_CWD = path.resolve(__dirname, 'cli', 'sample-project'),
    COVER_ROOT = path.resolve(__dirname, '..'),
    EXCLUDES = [ '**/node_modules/**', '**/test/**', '**/yui-load-hook.js'],
    COVER_VAR = '$$selfcover$$',
    seq = 0,
    verbose = false,
    OPTS = {
    };

/*
 * Danger, Will Robinson! A lot of small stuff has to fall in place just so
 * for self-coverage to work correctly for command line tests.
 *
 * In no-self-cover mode, this class does very little - which is:
 *
 *  1. Create a child process to run the supplied istanbul command
 *  2. Callback the test case with an object that allows the test case
 *      to grep on stdout and stderr, inspect exit codes etc.
 *  3. This is a "correct" test because we run the commands exactly as they
 *      would be run by the user.
 *
 * In self-cover mode, this works slightly differently:
 *
 *  1. Sets up a bunch of environment variables to keep track of what it was
 *      asked to do
 *  2. Creates a child process forking _itself_. Returns the same object to
 *      the caller for purposes of grepping stdout, inspecting status codes etc.
 *  3. In the child process,
 *      a. hooks the module loader to set up coverage for our library code
 *      b. Ensures that the `hook` module is not loaded until all this happens
 *         so that the hook module sees _our_ module loader hook as the original
 *         loader. This ensures that our hook will be used to instrument this
 *         library's code. Note that the hook set up by the `cover` command that
 *         is executed only instruments the modules of the sample test library.
 *      c. Calls Module.runMain on the command that it was asked to invoke
 *      d. Sets up an exit handler to write the coverage information for our
 *         library calls
 *   4. The exit handler is also set up in special ways because in order to
 *      instrument the `cover` command's exit handler, our exit handler has
 *      to be added later so as to be able to track coverage for the cover
 *      commands handler.
 *
 *      Since there is no general purpose way in which this can be correctly
 *      done, test cases have to indicate which ones expect the exit handler
 *      to be set up in the cover module and which ones do not. This is the
 *      reason `setOpts` exists.
 *
 */

function setVerbose(flag) {
    verbose = flag;
}
function setOpts(userOpts) {
    Object.keys(userOpts).forEach(function (k) { OPTS[k] = userOpts[k]; });
}

function resetOpts() {
    OPTS = {};
}

function runCommand(command, args, envVars, callback) {
    var cmd = 'node',
        selfCover = common.isSelfCover(),
        env = {},
        handle,
        out = '',
        err = '',
        exitCode = 1,
        grepper = function (array) {
            return function (pat) {
                var filtered = array.filter(function (item) {
                    return item.match(pat);
                });
                if (filtered.length === 0) {
                    if (verbose) {
                        console.log('~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~');
                        console.log('Could not find: ' + pat + ' in:');
                        console.log(array.join('\n'));
                        console.log('~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~');
                    }
                }
                return filtered.length > 0;
            };
        };

    if (selfCover) {
        seq += 1;
        env.COMMAND_NAME = command || '';
        env.COMMAND_ARGS = args.join('\t');
        env.COVERAGE_FILE = 'coverage-' + seq + '.json';
        env.LAZY_HOOK = OPTS.lazyHook ? '1' : '';
        args = [ __filename ];
    } else {
        if (command) {
            args.unshift(command);
        }
        args.unshift(MAIN_FILE);
    }

    if (!callback && typeof envVars === 'function') {
        callback = envVars;
        envVars = {};
    }

    Object.keys(process.env).forEach(function (key) {
        env[key] = process.env[key];
    });

    Object.keys(envVars).forEach(function (key) {
        env[key] = envVars[key];
    });

    handle = cp.spawn(cmd, args, { env: env, cwd: OPTS.cwd || DEFAULT_CWD });
    handle.stdout.setEncoding('utf8');
    handle.stderr.setEncoding('utf8');
    handle.stdout.on('data', function (data) {
        out += data;
        if (verbose) {
            process.stdout.write(data);
        }
    });
    handle.stderr.on('data', function (data) {
        err += data;
        if (verbose) {
            process.stderr.write(data);
        }
    });
    handle.on('close', function (code) {
        exitCode = code;
        out = out.split(/\r?\n/);
        err = err.split(/\r?\n/);
        callback({
            succeeded: function () { return exitCode === 0; },
            exitCode: exitCode,
            stdout: function () { return out; },
            stderr: function () { return err; },
            grepOutput: grepper(out),
            grepError: grepper(err)
        });
    });
}

function customHook(lazyHook, callback) {
    var Instrumenter = require('../lib/instrumenter'),
        instrumenter = new Instrumenter({ coverageVariable: COVER_VAR }),
        transformer = function (file) {
            return instrumenter.instrumentSync(fs.readFileSync(file, 'utf8'), file);
        },
        handler = function () {
            process.once('exit', function () {
                var file;
                if (typeof global[COVER_VAR] !== 'undefined') {
                    file = path.resolve(common.getCoverageDir(), process.env.COVERAGE_FILE);
                    fs.writeFileSync(file, JSON.stringify(global[COVER_VAR], undefined, 4), 'utf8');
                }
            });
        };

    require('../lib/util/file-matcher').matcherFor({ root : COVER_ROOT, excludes: EXCLUDES, includes: [ '**/*.js'] }, function (err, matcher) {
        var added = false;

        Module._extensions['.js'] = function (module, filename) {
            if (matcher(filename)) {
                module._compile(transformer(filename), filename);
            } else {
                originalLoader(module, filename);
            }
        };

        if (!lazyHook) {
            handler();
        } else {
            process.on('newListener', function (event) {
                if (event === 'exit') {
                    if (!added) {
                        added = true;
                        process.nextTick(function () {
                            handler();
                        });
                    }
                }
            });
        }
        callback();
    });
}

module.exports = {
    setVerbose: setVerbose,
    runCommand: runCommand,
    resetOpts: resetOpts,
    setOpts: setOpts
};

if (require.main === module) { //we are running ourselves under self-cover
    (function () {
        var command = process.env.COMMAND_NAME,
            args = process.env.COMMAND_ARGS ? process.env.COMMAND_ARGS.split(/\t/) : [],
            fullArgs = [];

        if (command) { fullArgs.push(command); }
        fullArgs.push.apply(fullArgs, args);
        customHook(process.env.LAZY_HOOK, function () {
            console.error('Running command using CLI object:' + JSON.stringify({ cmd: command, args: args }));
            require('../lib/cli').runToCompletion(fullArgs);
        });
    }());
}

istanbul-0.4.5/test/cli/000077500000000000000000000000001275640326200150765ustar00rootroot00000000000000istanbul-0.4.5/test/cli/package.json000066400000000000000000000002661275640326200173700ustar00rootroot00000000000000{
    "name": "istanbul-demo-project",
    "version": "0.0.1-12",
    "description": "Dummy project for testing istanbul CLI",
    "scripts": {
        "test": "test/run.js"
    }
}
istanbul-0.4.5/test/cli/sample-project/000077500000000000000000000000001275640326200200235ustar00rootroot00000000000000istanbul-0.4.5/test/cli/sample-project/amd/000077500000000000000000000000001275640326200205645ustar00rootroot00000000000000istanbul-0.4.5/test/cli/sample-project/amd/ipsum.js000066400000000000000000000001651275640326200222610ustar00rootroot00000000000000define(function(){

    function sum(a, b) {
        return a + b;
    }

    return {
        sum : sum
    };

});
istanbul-0.4.5/test/cli/sample-project/amd/lorem.js000066400000000000000000000001731275640326200222410ustar00rootroot00000000000000define(['./ipsum'], function (ipsum) {

    return function exec(a, b, c){
        return ipsum.sum(a, b) * c;
    };

});
istanbul-0.4.5/test/cli/sample-project/config-check-each.istanbul.yml000066400000000000000000000002051275640326200256010ustar00rootroot00000000000000check:
  each:
    statements: 72
    functions: 50
    branches: 72
    lines: 72
    excludes:
        - lib/bar.js # 40, 0, 40, 0
istanbul-0.4.5/test/cli/sample-project/config-check-global.istanbul.yml000066400000000000000000000004701275640326200261450ustar00rootroot00000000000000check:
  global:
    statements: 72
    functions: 50
    branches: 72
    lines: 72
    # Example: Could exclude all files covered (would pass checks and fail the tests).
    # excludes:
    #     - lib/foo.js
    #     - vendor/dummy_vendor_lib.js
    #     - lib/util/generate-names.js
    #     - lib/bar.js
istanbul-0.4.5/test/cli/sample-project/config-check-mixed.istanbul.yml000066400000000000000000000003231275640326200260100ustar00rootroot00000000000000check:
  global:
    statements: 80
    functions: 80
    branches: 40
    lines: 40
  each:
    statements: 72
    functions: 50
    branches: 72
    lines: 72
    excludes:
        - lib/bar.js # 40, 0, 40, 0
istanbul-0.4.5/test/cli/sample-project/config.istanbul.yml000066400000000000000000000001661275640326200236360ustar00rootroot00000000000000reporting:
  reports:
    - cobertura
    - html
    - lcovonly
  report-config:
    cobertura:
      file: 'foo.xml'
istanbul-0.4.5/test/cli/sample-project/includeAllSources/000077500000000000000000000000001275640326200234435ustar00rootroot00000000000000istanbul-0.4.5/test/cli/sample-project/includeAllSources/unloadedFile.js000066400000000000000000000000501275640326200263670ustar00rootroot00000000000000var foo = 'bar';

module.exports = foo;
istanbul-0.4.5/test/cli/sample-project/includeAllSources/unloadedFileWithFunctionDeclaration.js000066400000000000000000000000741275640326200331050ustar00rootroot00000000000000function foo() {
    return 'bar';
}

module.exports = foo;
istanbul-0.4.5/test/cli/sample-project/lib/000077500000000000000000000000001275640326200205715ustar00rootroot00000000000000istanbul-0.4.5/test/cli/sample-project/lib/bar.js000066400000000000000000000004111275640326200216670ustar00rootroot00000000000000var dep = require('dependency'),
    vendor = require('../vendor/dummy_vendor_lib'),
    generator = require('./util/generate-names');

module.exports = function (useDep) {
    if (useDep) {
        dep.depBar();
    } else {
        vendor.vendorBar();
    }
};


istanbul-0.4.5/test/cli/sample-project/lib/foo.js000066400000000000000000000006011275640326200217070ustar00rootroot00000000000000var dep = require('dependency'),
    vendor = require('../vendor/dummy_vendor_lib'),
    generator = require('./util/generate-names');

// export what we need
module.exports = function (input, useDep) {
    var base = generator.generateName(),
        output = useDep
            ? dep.depFoo(input)
            : vendor.vendorFoo(input);
    return { name: base, value: output };
};

istanbul-0.4.5/test/cli/sample-project/lib/util/000077500000000000000000000000001275640326200215465ustar00rootroot00000000000000istanbul-0.4.5/test/cli/sample-project/lib/util/bad.js000066400000000000000000000000301275640326200226230ustar00rootroot00000000000000var junk = 10 : 5 : 10;
istanbul-0.4.5/test/cli/sample-project/lib/util/es-module.js000066400000000000000000000001131275640326200237710ustar00rootroot00000000000000import foo from 'foo';

export default function bar() {
   return foo();
};istanbul-0.4.5/test/cli/sample-project/lib/util/generate-names.js000066400000000000000000000001601275640326200247740ustar00rootroot00000000000000module.exports = {
    generateName: function () {
        return 'generated-' + new Date().getTime();
    }
};
istanbul-0.4.5/test/cli/sample-project/lib/util/unused.js000066400000000000000000000001271275640326200234070ustar00rootroot00000000000000module.exports = {
    sayHello: function () {
        console.log('Hello');
    }
};

istanbul-0.4.5/test/cli/sample-project/node_modules/000077500000000000000000000000001275640326200225005ustar00rootroot00000000000000istanbul-0.4.5/test/cli/sample-project/node_modules/dependency/000077500000000000000000000000001275640326200246165ustar00rootroot00000000000000istanbul-0.4.5/test/cli/sample-project/node_modules/dependency/index.js000066400000000000000000000003211275640326200262570ustar00rootroot00000000000000module.exports = {
    depFoo: function (input) {
        return input >= 10 ? input : input + 100;
    },
    depBar: function () {
        throw new Error("Let's not call this function in tests");
    }
};

istanbul-0.4.5/test/cli/sample-project/node_modules/post-require/000077500000000000000000000000001275640326200251375ustar00rootroot00000000000000istanbul-0.4.5/test/cli/sample-project/node_modules/post-require/hook.js000066400000000000000000000006031275640326200264340ustar00rootroot00000000000000var path = require('path');
module.exports = function (matchFn, transformFn, verbose) {
    console.error('PRH: MatchFn was a ' + typeof matchFn);
    console.error('PRH: TransformFn was a ' + typeof transformFn);
    console.error('PRH: Verbose was ' + verbose);
    return function (file) {
        var base = path.basename(file);
        console.error('PRH: Saw ' + base);
    };
};

istanbul-0.4.5/test/cli/sample-project/node_modules/post-require/package.json000066400000000000000000000001171275640326200274240ustar00rootroot00000000000000{
    "name": "post-require",
    "version": "0.0.1",
    "main": "./hook.js"
}istanbul-0.4.5/test/cli/sample-project/test/000077500000000000000000000000001275640326200210025ustar00rootroot00000000000000istanbul-0.4.5/test/cli/sample-project/test/amd-run.js000066400000000000000000000005201275640326200227000ustar00rootroot00000000000000// RequireJS uses `vm.runInThisContext` see issue #23
// make sure we add hooks for it as well

var rjs = require('requirejs'),
    assert = require('assert');

rjs.config({
    baseUrl : __dirname,
    nodeRequire : require
});

rjs(['../amd/lorem'], function(lorem){
    var result = lorem(1, 2, 3);
    assert.equal(9, result);
});

istanbul-0.4.5/test/cli/sample-project/test/global-leak.js000066400000000000000000000014411275640326200235120ustar00rootroot00000000000000#!/usr/bin/env node

/*globals global */
var assert = require('assert'),
    g1 = {},
    g2 = {},
    keys;

function copyGlobals(target) {
    Object.keys(global).forEach(function (k) {
        target[k] = true;
    });
}

copyGlobals(g1);
//the intention is to ensure that no extra globals are created by the require below
//and that the coverage global is alive and well before the first piece of instrumented
//code is required
require('../lib/foo');
copyGlobals(g2);

Object.keys(g2).forEach(function (k) {
    if (g1[k]) { delete g2[k]; }
});

console.log(Object.keys(g2));
assert.equal(0, Object.keys(g2).length, 'New global var introduced in test');
keys = Object.keys(g1).filter(function (k) { return k.match(/\$\$cov_\d+\$\$/); });
assert.equal(1, keys.length, 'Coverage var not found!');

istanbul-0.4.5/test/cli/sample-project/test/run.js000066400000000000000000000010711275640326200221430ustar00rootroot00000000000000#!/usr/bin/env node

var assert = require('assert'),
    foo = require('../lib/foo'),
    bar = require('../lib/bar'),
    shouldFail = !!process.argv[2] && process.argv[2] !== '0';

var r1 = foo(3, true);
assert.ok(r1.name);
assert.ok(r1.name.match(/^gen/));
assert.equal(103, r1.value);

var r2 = foo(3, false);
assert.ok(r2.name);
assert.ok(r2.name.match(/^gen/));
assert.equal(3, r2.value);

var r3 = foo(30, false);
assert.ok(r3.name);
assert.ok(r3.name.match(/^gen/));
if (shouldFail) {
    assert.equal(40, r3.value);
} else {
    assert.equal(130, r3.value);
}
istanbul-0.4.5/test/cli/sample-project/vendor/000077500000000000000000000000001275640326200213205ustar00rootroot00000000000000istanbul-0.4.5/test/cli/sample-project/vendor/dummy_vendor_lib.js000066400000000000000000000003261275640326200252150ustar00rootroot00000000000000module.exports = {
    vendorFoo: function (input) {
        return input < 10 ? input : input + 100;
    },
    vendorBar: function () {
        throw new Error("Let's not call this function in tests");
    }
};

istanbul-0.4.5/test/cli/test-base-cli.js000066400000000000000000000035011275640326200200670ustar00rootroot00000000000000/*jslint nomen: true */
var helper = require('../cli-helper');

module.exports = {
    setUp: function (cb) {
        helper.resetOpts();
        cb();
    },
    "should provide helpful errors when nothing passed": function (test) {
        helper.runCommand(null, [], function (results) {
            test.ok(!results.succeeded());
            test.ok(results.grepError(/Try "istanbul help" for usage/));
            test.done();
        });
    },
    "should provide helpful errors when only flags passed in": function (test) {
        helper.runCommand(null, [ '-v', '-x' ], function (results) {
            test.ok(!results.succeeded());
            test.ok(results.grepError(/Try "istanbul help" for usage/));
            test.done();
        });
    },
    "should provide a good message on an invalid command": function (test) {
        helper.runCommand('instrumentation', [ '--root', 'a/nonexistent/path' ], function (results) {
            test.ok(!results.succeeded());
            test.ok(results.grepError(/Invalid command \[instrumentation\], allowed values/));
            test.ok(results.grepError(/Try "istanbul help" for usage/));
            test.done();
        });
    },
    "should print a stack trace on uncaught exception": function (test) {
        helper.runCommand('instrument', [ '--root', 'a/nonexistent/path' ], function (results) {
            test.ok(!results.succeeded());
            test.ok(!results.grepError(/Try "istanbul help" for usage/));
            test.ok(results.grepError(/ENOENT/));
            test.done();
        });
    },
    "should provide configuration help": function (test) {
        helper.runCommand('help', [ 'config' ], function (results) {
            test.ok(results.succeeded());
            test.ok(results.grepError(/Configuring istanbul/));
            test.done();
        });
    }
};
istanbul-0.4.5/test/cli/test-check-coverage-command.js000066400000000000000000000315601275640326200227000ustar00rootroot00000000000000/*jslint nomen: true */
var path = require('path'),
    fs = require('fs'),
    rimraf = require('rimraf'),
    mkdirp = require('mkdirp'),
    COMMAND = 'check-coverage',
    COVER_COMMAND = 'cover',
    DIR = path.resolve(__dirname, 'sample-project'),
    OUTPUT_DIR = path.resolve(DIR, 'coverage'),
    helper = require('../cli-helper'),
    existsSync = fs.existsSync || path.existsSync,
    run = helper.runCommand.bind(null, COMMAND),
    runCover = helper.runCommand.bind(null, COVER_COMMAND);

module.exports = {
    setUp: function (cb) {
        rimraf.sync(OUTPUT_DIR);
        mkdirp.sync(OUTPUT_DIR);
        helper.resetOpts();
        runCover([ 'test/run.js', '--report', 'none' ], function (/* results */) {

            // Mutate coverage.json to test relative key paths.
            var covObj = require('./sample-project/coverage/coverage.json');
            var relCovObj = {};
            var relCovDotSlashObj = {};
            Object.keys(covObj).forEach(function (key) {
                var relKey = path.relative(__dirname + '/sample-project', key);
                relCovObj[relKey] = covObj[key];
                relCovDotSlashObj['./' + relKey] = covObj[key];
            });
            fs.writeFileSync(path.resolve(__dirname, 'sample-project/coverage/relative.json'), JSON.stringify(relCovObj));
            fs.writeFileSync(path.resolve(__dirname, 'sample-project/coverage/relative-dot-slash.json'), JSON.stringify(relCovDotSlashObj));

            cb();
        });
    },
    tearDown: function (cb) {
        rimraf.sync(OUTPUT_DIR);
        cb();
    },
    "Global coverage": {
        "should fail on inadequate statement coverage": function (test) {
            test.ok(existsSync(path.resolve(OUTPUT_DIR, 'coverage.json')));
            run([ '--statements', '72' ], function (results) {
                test.ok(!results.succeeded());
                test.ok(!results.grepError(/lib[\\\/]cli.js:/));
                test.ok(results.grepError(/Coverage for statements .* global/));
                test.done();
            });
        },
        "should fail on inadequate branch coverage": function (test) {
            test.ok(existsSync(path.resolve(OUTPUT_DIR, 'coverage.json')));
            run([ '--branches', '72' ], function (results) {
                test.ok(!results.succeeded());
                test.ok(!results.grepError(/lib[\\\/]cli.js:/));
                test.ok(results.grepError(/Coverage for branches .* global/));
                test.done();
            });
        },
        "should fail on inadequate function coverage": function (test) {
            test.ok(existsSync(path.resolve(OUTPUT_DIR, 'coverage.json')));
            run([ '--functions', '72' ], function (results) {
                test.ok(!results.succeeded());
                test.ok(!results.grepError(/lib[\\\/]cli.js:/));
                test.ok(results.grepError(/Coverage for functions .* global/));
                test.done();
            });
        },
        "should fail on inadequate line coverage": function (test) {
            test.ok(existsSync(path.resolve(OUTPUT_DIR, 'coverage.json')));
            run([ '--lines', '72' ], function (results) {
                test.ok(!results.succeeded());
                test.ok(!results.grepError(/lib[\\\/]cli.js:/));
                test.ok(results.grepError(/Coverage for lines .* global/));
                test.done();
            });
        },
        "should fail with multiple reasons when multiple thresholds violated": function (test) {
            test.ok(existsSync(path.resolve(OUTPUT_DIR, 'coverage.json')));
            run([ '--statements=72', '--functions=50', '--branches=72', '--lines=72' ], function (results) {
                test.ok(!results.succeeded());
                test.ok(!results.grepError(/lib[\\\/]cli.js:/));
                test.ok(results.grepError(/Coverage for lines .* global/));
                test.ok(results.grepError(/Coverage for statements .* global/));
                test.ok(results.grepError(/Coverage for branches .* global/));
                test.ok(!results.grepError(/Coverage for functions .* global/));
                test.done();
            });
        },
        "should fail with multiple reasons from configuration file": function (test) {
            test.ok(existsSync(path.resolve(OUTPUT_DIR, 'coverage.json')));
            // YML equivalent to: '--statements=72', '--functions=50', '--branches=72', '--lines=72'
            run([ '--config', 'config-check-global.istanbul.yml' ], function (results) {
                test.ok(!results.succeeded());
                test.ok(!results.grepError(/lib[\\\/]cli.js:/));
                test.ok(results.grepError(/Coverage for lines .* global/));
                test.ok(results.grepError(/Coverage for statements .* global/));
                test.ok(results.grepError(/Coverage for branches .* global/));
                test.ok(!results.grepError(/Coverage for functions .* global/));
                test.done();
            });
        },
        "should fail with multiple reasons from configuration file and command line": function (test) {
            test.ok(existsSync(path.resolve(OUTPUT_DIR, 'coverage.json')));
            // YML equivalent to: '--statements=72', '--functions=50', '--branches=72', '--lines=72'
            run([ '--statements=10', '--config', 'config-check-global.istanbul.yml' ], function (results) {
                test.ok(!results.succeeded());
                test.ok(!results.grepError(/lib[\\\/]cli.js:/));
                test.ok(results.grepError(/Coverage for lines .* global/));
                test.ok(!results.grepError(/Coverage for statements .* global/));
                test.ok(results.grepError(/Coverage for branches .* global/));
                test.ok(!results.grepError(/Coverage for functions .* global/));
                test.done();
            });
        },
        "should fail with multiple reasons when multiple thresholds violated with negative thresholds": function (test) {
            test.ok(existsSync(path.resolve(OUTPUT_DIR, 'coverage.json')));
            run([ '--statements=-3', '--functions=-10', '--branches=-1', '--lines=-3' ], function (results) {
                test.ok(!results.succeeded());
                test.ok(!results.grepError(/lib[\\\/]cli.js:/));
                test.ok(results.grepError(/Uncovered count for lines .* global/));
                test.ok(results.grepError(/Uncovered count for statements .* global/));
                test.ok(results.grepError(/Uncovered count for branches .* global/));
                test.ok(!results.grepError(/Uncovered count for functions .* global/));
                test.done();
            });
        },
        "should pass with multiple reasons when all thresholds in check": function (test) {
            test.ok(existsSync(path.resolve(OUTPUT_DIR, 'coverage.json')));
            run([ '--statements=60', '--functions=50', '--branches=50', '--lines=60', '-v' ], function (results) {
                test.ok(results.succeeded());
                test.ok(!results.grepOutput(/\\"actuals\\"/), "Verbose message not printed as expected");
                test.done();
            });
        },
        "should fail when no coverage found": function (test) {
            test.ok(!existsSync(path.resolve(OUTPUT_DIR, 'no-matching-coverage.json')));
            run([ 'no-matching-coverage.json' ], function (results) {
                test.ok(!results.succeeded());
                test.ok(!results.grepError(/lib[\\\/]cli.js:/));
                test.ok(results.grepError(/No coverage files found./));
                test.done();
            });
        }
    },
    "Per-file coverage": {
        "should fail on inadequate statement and line coverage": function (test) {
            test.ok(existsSync(path.resolve(OUTPUT_DIR, 'coverage.json')));
            run([ '--config', 'config-check-each.istanbul.yml' ], function (results) {
                // vendor/dummy_vendor_lib.js (statements 66.67% vs. 72%)
                // vendor/dummy_vendor_lib.js (lines 66.67% vs. 72%)
                test.ok(!results.succeeded());
                test.ok(!results.grepError(/lib[\\\/]cli.js:/));
                test.ok(!results.grepError(/Coverage for lines .* global/));
                test.ok(results.grepError(/Coverage for lines .* per-file/));
                test.ok(results.grepError(/Coverage for statements .* per-file/));
                test.ok(!results.grepError(/Coverage for branches .* per-file/));
                test.ok(!results.grepError(/Coverage for functions .* per-file/));
                test.ok(results.grepError(/dummy_vendor_lib\.js/));
                test.ok(!results.grepError(/foo\.js/));
                test.ok(!results.grepError(/foo\.js/));
                test.done();
            });
        },
        "should fail on inadequate statement and line coverage with relative coverage": function (test) {
            test.ok(existsSync(path.resolve(OUTPUT_DIR, 'relative.json')));
            run([ '--config', 'config-check-each.istanbul.yml', 'coverage/relative.json' ], function (results) {
                test.ok(!results.succeeded());
                test.ok(!results.grepError(/lib[\\\/]cli.js:/));
                test.ok(!results.grepError(/Coverage for lines .* global/));
                test.ok(results.grepError(/Coverage for lines .* per-file/));
                test.ok(results.grepError(/Coverage for statements .* per-file/));
                test.ok(!results.grepError(/Coverage for branches .* per-file/));
                test.ok(!results.grepError(/Coverage for functions .* per-file/));
                test.ok(results.grepError(/dummy_vendor_lib\.js/));
                test.ok(!results.grepError(/foo\.js/));
                test.ok(!results.grepError(/foo\.js/));
                test.done();
            });
        },
        "should fail on inadequate statement and line coverage with relative './' key coverage": function (test) {
            test.ok(existsSync(path.resolve(OUTPUT_DIR, 'relative-dot-slash.json')));
            run([ '--config', 'config-check-each.istanbul.yml', 'coverage/relative-dot-slash.json' ], function (results) {
                test.ok(!results.succeeded());
                test.ok(!results.grepError(/lib[\\\/]cli.js:/));
                test.ok(!results.grepError(/Coverage for lines .* global/));
                test.ok(results.grepError(/Coverage for lines .* per-file/));
                test.ok(results.grepError(/Coverage for statements .* per-file/));
                test.ok(!results.grepError(/Coverage for branches .* per-file/));
                test.ok(!results.grepError(/Coverage for functions .* per-file/));
                test.ok(results.grepError(/dummy_vendor_lib\.js/));
                test.ok(!results.grepError(/foo\.js/));
                test.ok(!results.grepError(/foo\.js/));
                test.done();
            });
        },
        "should fail on inadequate mixed global args / each coverage": function (test) {
            test.ok(existsSync(path.resolve(OUTPUT_DIR, 'coverage.json')));
            run([ '--branches=100', '--functions=100', '--config', 'config-check-each.istanbul.yml' ], function (results) {
                test.ok(!results.succeeded());
                test.ok(!results.grepError(/lib[\\\/]cli.js:/));
                test.ok(!results.grepError(/Coverage for lines .* global/));
                test.ok(!results.grepError(/Coverage for statements .* global/));
                test.ok(results.grepError(/Coverage for branches .* global/));
                test.ok(results.grepError(/Coverage for functions .* global/));
                test.ok(results.grepError(/Coverage for lines .* per-file/));
                test.ok(results.grepError(/Coverage for statements .* per-file/));
                test.ok(!results.grepError(/Coverage for branches .* per-file/));
                test.ok(!results.grepError(/Coverage for functions .* per-file/));
                test.done();
            });
        },
        "should fail on inadequate mixed global / each configured coverage": function (test) {
            test.ok(existsSync(path.resolve(OUTPUT_DIR, 'coverage.json')));
            run([ '--config', 'config-check-mixed.istanbul.yml' ], function (results) {
                test.ok(!results.succeeded());
                test.ok(!results.grepError(/lib[\\\/]cli.js:/));
                test.ok(!results.grepError(/Coverage for lines .* global/));
                test.ok(results.grepError(/Coverage for statements .* global/));
                test.ok(!results.grepError(/Coverage for branches .* global/));
                test.ok(results.grepError(/Coverage for functions .* global/));
                test.ok(results.grepError(/Coverage for lines .* per-file/));
                test.ok(results.grepError(/Coverage for statements .* per-file/));
                test.ok(!results.grepError(/Coverage for branches .* per-file/));
                test.ok(!results.grepError(/Coverage for functions .* per-file/));
                test.done();
            });
        }
    }
};
istanbul-0.4.5/test/cli/test-clover-report.js000066400000000000000000000035371275640326200212240ustar00rootroot00000000000000/*jslint nomen: true */
var path = require('path'),
    fs = require('fs'),
    rimraf = require('rimraf'),
    mkdirp = require('mkdirp'),
    helper = require('../cli-helper'),
    DIR = path.resolve(__dirname, 'sample-project'),
    OUTPUT_DIR = path.resolve(DIR, 'coverage'),
    COVER_COMMAND = 'cover',
    runCover = helper.runCommand.bind(null, COVER_COMMAND),
    Reporter = require('../../lib/report/clover'),
    Collector = require('../../lib/collector'),
    existsSync = fs.existsSync || path.existsSync;

module.exports = {
    setUp: function (cb) {
        rimraf.sync(OUTPUT_DIR);
        mkdirp.sync(OUTPUT_DIR);
        helper.resetOpts();
        runCover([ 'test/run.js', '--report', 'none' ], function (/* results */) {
            cb();
        });
    },
    tearDown: function (cb) {
        rimraf.sync(OUTPUT_DIR);
        cb();
    },
    "should produce clover report consuming coverage file": function (test) {
        var file = path.resolve(OUTPUT_DIR, 'coverage.json'),
            reportFile = path.resolve(OUTPUT_DIR, 'clover.xml'),
            reporter = new Reporter({ dir: OUTPUT_DIR }),
            obj,
            collector = new Collector();

        obj = JSON.parse(fs.readFileSync(file, 'utf8'));
        collector.add(obj);
        reporter.writeReport(collector, true);
        test.ok(existsSync(reportFile));
        test.done();
    },
    "should produce clover.xml at cwd when no options specified": function (test) {
        var file = path.resolve(OUTPUT_DIR, 'coverage.json'),
            reportFile = path.resolve('clover.xml'),
            reporter = new Reporter(),
            collector = new Collector();

        collector.add(JSON.parse(fs.readFileSync(file, 'utf8')));
        reporter.writeReport(collector, true);
        test.ok(existsSync(reportFile));
        fs.unlinkSync(reportFile);
        test.done();
    }
};

istanbul-0.4.5/test/cli/test-cobertura-report.js000066400000000000000000000035771275640326200217240ustar00rootroot00000000000000/*jslint nomen: true */
var path = require('path'),
    fs = require('fs'),
    rimraf = require('rimraf'),
    mkdirp = require('mkdirp'),
    helper = require('../cli-helper'),
    DIR = path.resolve(__dirname, 'sample-project'),
    OUTPUT_DIR = path.resolve(DIR, 'coverage'),
    COVER_COMMAND = 'cover',
    runCover = helper.runCommand.bind(null, COVER_COMMAND),
    Reporter = require('../../lib/report/cobertura'),
    Collector = require('../../lib/collector'),
    existsSync = fs.existsSync || path.existsSync;

module.exports = {
    setUp: function (cb) {
        rimraf.sync(OUTPUT_DIR);
        mkdirp.sync(OUTPUT_DIR);
        helper.resetOpts();
        runCover([ 'test/run.js', '--report', 'none' ], function (/* results */) {
            cb();
        });
    },
    tearDown: function (cb) {
        rimraf.sync(OUTPUT_DIR);
        cb();
    },
    "should produce cobertura report consuming coverage file": function (test) {
        var file = path.resolve(OUTPUT_DIR, 'coverage.json'),
            reportFile = path.resolve(OUTPUT_DIR, 'cobertura-coverage.xml'),
            reporter = new Reporter({ dir: OUTPUT_DIR }),
            obj,
            collector = new Collector();

        obj = JSON.parse(fs.readFileSync(file, 'utf8'));
        collector.add(obj);
        reporter.writeReport(collector, true);
        test.ok(existsSync(reportFile));
        test.done();
    },
    "should produce coverage.xml at cwd when no options specified": function (test) {
        var file = path.resolve(OUTPUT_DIR, 'coverage.json'),
            reportFile = path.resolve('cobertura-coverage.xml'),
            reporter = new Reporter(),
            collector = new Collector();

        collector.add(JSON.parse(fs.readFileSync(file, 'utf8')));
        reporter.writeReport(collector, true);
        test.ok(existsSync(reportFile));
        fs.unlinkSync(reportFile);
        test.done();
    }
};

istanbul-0.4.5/test/cli/test-cover-command.js000066400000000000000000000345011275640326200211460ustar00rootroot00000000000000/*jslint nomen: true */
var path = require('path'),
    fs = require('fs'),
    rimraf = require('rimraf'),
    mkdirp = require('mkdirp'),
    COMMAND = 'cover',
    DIR = path.resolve(__dirname, 'sample-project'),
    DIR_LINK = path.resolve(__dirname, 'sample-project-link'),
    OUTPUT_DIR = path.resolve(DIR, 'coverage'),
    helper = require('../cli-helper'),
    existsSync = fs.existsSync || path.existsSync,
    run = helper.runCommand.bind(null, COMMAND),
    Report = require('../../lib/report');

module.exports = {
    setUp: function (cb) {
        rimraf.sync(OUTPUT_DIR);
        mkdirp.sync(OUTPUT_DIR);
        helper.resetOpts();
        cb();
    },
    tearDown: function (cb) {
        rimraf.sync(OUTPUT_DIR);
        cb();
    },
    "should cover tests as expected": function (test) {
        helper.setOpts({ lazyHook : true });
        run([ 'test/run.js', '-v' ], function (results) {
            test.ok(results.succeeded());
            test.ok(results.grepError(/Module load hook:/));
            test.ok(existsSync(path.resolve(OUTPUT_DIR, 'lcov.info')));
            test.ok(existsSync(path.resolve(OUTPUT_DIR, 'lcov-report')));
            test.ok(existsSync(path.resolve(OUTPUT_DIR, 'coverage.json')));
            var coverage = JSON.parse(fs.readFileSync(path.resolve(OUTPUT_DIR, 'coverage.json'), 'utf8')),
                filtered;
            filtered = Object.keys(coverage).filter(function (k) { return k.match(/foo/) || k.match(/bar/); });
            test.ok(filtered.length === 2);
            test.done();
        });
    },
    "should cover tests running every possible report": function (test) {
        helper.setOpts({ lazyHook : true });
        var cmd = [ 'test/run.js', '-v', '--print=none' ];
        Report.getReportList().forEach(function (r) {
            cmd.push('--report=' + r);
        });
        run(cmd, function (results) {
            test.ok(results.succeeded());
            test.ok(existsSync(path.resolve(OUTPUT_DIR, 'lcov.info')));
            test.ok(existsSync(path.resolve(OUTPUT_DIR, 'lcov-report')));
            test.ok(existsSync(path.resolve(OUTPUT_DIR, 'coverage.json')));
            test.ok(existsSync(path.resolve(OUTPUT_DIR, 'coverage-final.json')));
            test.ok(existsSync(path.resolve(OUTPUT_DIR, 'cobertura-coverage.xml')));
            test.ok(existsSync(path.resolve(OUTPUT_DIR, 'clover.xml')));
            test.done();
        });
    },
    "should include all files after running tests": function (test) {
        helper.setOpts({ lazyHook : true });
        run([ 'test/run.js', '0', '-v', '--include-all-sources', '-x', 'lib/util/bad.js', '-x', 'lib/util/es-module.js' ], function (results) {
            test.ok(results.succeeded());
            test.ok(existsSync(path.resolve(OUTPUT_DIR, 'lcov.info')));
            test.ok(existsSync(path.resolve(OUTPUT_DIR, 'lcov-report')));
            test.ok(existsSync(path.resolve(OUTPUT_DIR, 'coverage.json')));
            var coverage = JSON.parse(fs.readFileSync(path.resolve(OUTPUT_DIR, 'coverage.json'), 'utf8')),
                unloadedFilePath = path.resolve(OUTPUT_DIR, '..', 'includeAllSources', 'unloadedFile.js'),
                unloadedFileWithFnPath = path.resolve(OUTPUT_DIR, '..', 'includeAllSources', 'unloadedFileWithFunctionDeclaration.js'),
                unloadedFile = coverage[unloadedFilePath],
                unloadedFileWithFn = coverage[unloadedFileWithFnPath];

            Object.keys(unloadedFile.s).forEach(function (statement) {
                test.ok(unloadedFile.s[statement] === 0);
            });
            Object.keys(unloadedFileWithFn.s).forEach(function (statement) {
                test.ok(unloadedFileWithFn.s[statement] === 0);
            });

            test.done();
        });
    },
    "should include all files after running tests in back-compat mode": function (test) {
        helper.setOpts({ lazyHook : true });
        run([ 'test/run.js', '0', '-v', '--preload-sources', '-x', 'lib/util/bad.js', '-x', 'lib/util/es-module.js' ], function (results) {
            test.ok(results.succeeded());
            test.ok(existsSync(path.resolve(OUTPUT_DIR, 'lcov.info')));
            test.ok(existsSync(path.resolve(OUTPUT_DIR, 'lcov-report')));
            test.ok(existsSync(path.resolve(OUTPUT_DIR, 'coverage.json')));
            var coverage = JSON.parse(fs.readFileSync(path.resolve(OUTPUT_DIR, 'coverage.json'), 'utf8')),
                unloadedFilePath = path.resolve(OUTPUT_DIR, '..', 'includeAllSources', 'unloadedFile.js'),
                unloadedFileWithFnPath = path.resolve(OUTPUT_DIR, '..', 'includeAllSources', 'unloadedFileWithFunctionDeclaration.js'),
                unloadedFile = coverage[unloadedFilePath],
                unloadedFileWithFn = coverage[unloadedFileWithFnPath];

            Object.keys(unloadedFile.s).forEach(function (statement) {
                test.ok(unloadedFile.s[statement] === 0);
            });
            Object.keys(unloadedFileWithFn.s).forEach(function (statement) {
                test.ok(unloadedFileWithFn.s[statement] === 0);
            });

            test.ok(results.grepError(/The preload-sources option is deprecated/));
            test.done();
        });
    },
    "should cover tests as expected without extra noise and not covering excluded files": function (test) {
        helper.setOpts({ lazyHook : true });
        run([ 'test/run.js', '-x', '**/foo.js' ], function (results) {
            test.ok(results.succeeded());
            test.ok(!results.grepError(/Module load hook:/));
            test.ok(existsSync(path.resolve(OUTPUT_DIR, 'lcov.info')));
            test.ok(existsSync(path.resolve(OUTPUT_DIR, 'lcov-report')));
            test.ok(existsSync(path.resolve(OUTPUT_DIR, 'coverage.json')));
            var coverage = JSON.parse(fs.readFileSync(path.resolve(OUTPUT_DIR, 'coverage.json'), 'utf8')),
                filtered;
            filtered = Object.keys(coverage).filter(function (k) { return k.match(/foo/); });
            test.ok(filtered.length === 0);
            test.done();
        });
    },
    "should cover tests as expected without extra noise and using includes": function (test) {
        helper.setOpts({ lazyHook : true });
        run([ 'test/run.js', '-i', '**/foo.js' ], function (results) {
            test.ok(results.succeeded());
            test.ok(!results.grepError(/Module load hook:/));
            test.ok(existsSync(path.resolve(OUTPUT_DIR, 'lcov.info')));
            test.ok(existsSync(path.resolve(OUTPUT_DIR, 'lcov-report')));
            test.ok(existsSync(path.resolve(OUTPUT_DIR, 'coverage.json')));
            var coverage = JSON.parse(fs.readFileSync(path.resolve(OUTPUT_DIR, 'coverage.json'), 'utf8')),
                filtered;
            filtered = Object.keys(coverage).filter(function (k) { return k.match(/foo/); });
            test.ok(filtered.length !== 0);
            filtered = Object.keys(coverage).filter(function (k) { return k.match(/bar/); });
            test.ok(filtered.length === 0);
            test.done();
        });
    },
    "should skip reporting when requested": function (test) {
        helper.setOpts({ lazyHook : true });
        run([ 'test/run.js', '--report', 'none', '--print', 'detail' ], function (results) {
            test.ok(results.succeeded());
            test.ok(!existsSync(path.resolve(OUTPUT_DIR, 'lcov.info')));
            test.ok(!existsSync(path.resolve(OUTPUT_DIR, 'lcov-report')));
            test.ok(existsSync(path.resolve(OUTPUT_DIR, 'coverage.json')));
            test.done();
        });
    },
    "should use non-default report format when requested": function (test) {
        helper.setOpts({ lazyHook : true });
        run([ 'test/run.js', '--report', 'lcovonly' ], function (results) {
            test.ok(results.succeeded());
            test.ok(existsSync(path.resolve(OUTPUT_DIR, 'lcov.info')));
            test.ok(!existsSync(path.resolve(OUTPUT_DIR, 'lcov-report')));
            test.ok(existsSync(path.resolve(OUTPUT_DIR, 'coverage.json')));
            test.done();
        });
    },
    "should cover nothing when everything excluded": function (test) {
        helper.setOpts({ lazyHook : true });
        run([ 'test/run.js', '-x', '**/*.js' ], function (results) {
            test.ok(results.succeeded());
            test.ok(!existsSync(path.resolve(OUTPUT_DIR, 'lcov.info')));
            test.ok(!existsSync(path.resolve(OUTPUT_DIR, 'lcov-report')));
            test.ok(!existsSync(path.resolve(OUTPUT_DIR, 'coverage.json')));
            test.done();
        });
    },
    "should cover everything under the sun when default excludes are suppressed": function (test) {
        helper.setOpts({ lazyHook : true });
        run([ 'test/run.js', '--no-default-exclude' ], function (results) {
            test.ok(results.succeeded());
            test.ok(existsSync(path.resolve(OUTPUT_DIR, 'lcov.info')));
            test.ok(existsSync(path.resolve(OUTPUT_DIR, 'lcov-report')));
            test.ok(existsSync(path.resolve(OUTPUT_DIR, 'coverage.json')));
            var coverage = JSON.parse(fs.readFileSync(path.resolve(OUTPUT_DIR, 'coverage.json'), 'utf8')),
                filtered;
            filtered = Object.keys(coverage).filter(function (k) { return k.match(/node_modules/); });
            test.ok(filtered.length === 1);
            test.done();
        });
    },
    "should barf when no file is provided": function (test) {
        run([], function (results) {
            test.ok(!results.succeeded());
            test.ok(results.grepError(/Need a filename argument for the cover command/));
            test.done();
        });
    },
    "should barf on invalid command": function (test) {
        run([ 'foobar' ], function (results) {
            test.ok(!results.succeeded());
            test.ok(results.grepError(/Unable to resolve file/));
            test.done();
        });
    },
    "should work with RequireJS and AMD modules": function (test) {
        helper.setOpts({ lazyHook : true });
        run([ 'test/amd-run.js', '-v', '--hook-run-in-context' ], function (results) {
            test.ok(results.succeeded());
            test.ok(results.grepError(/Module load hook:/));
            test.ok(existsSync(path.resolve(OUTPUT_DIR, 'lcov.info')));
            test.ok(existsSync(path.resolve(OUTPUT_DIR, 'lcov-report')));
            test.ok(existsSync(path.resolve(OUTPUT_DIR, 'coverage.json')));
            var coverage = JSON.parse(fs.readFileSync(path.resolve(OUTPUT_DIR, 'coverage.json'), 'utf8')),
                ipsumPath = path.join('amd', 'ipsum'),
                loremPath = path.join('amd', 'lorem'),
                filtered;
            filtered = Object.keys(coverage).filter(function (k) { return k.indexOf(ipsumPath) >= 0 || k.indexOf(loremPath) >= 0; });
            test.ok(filtered.length === 2);
            test.ok(filtered.length === Object.keys(coverage).length);
            test.done();
        });
    },
    "should apply post-require-hook correctly when absolute path specified": function (test) {
        helper.setOpts({ lazyHook : true });
        run([ 'test/run.js', '-v', '-x', '**/foo.js', '--post-require-hook', 'node_modules/post-require/hook.js' ], function (results) {
            test.ok(results.succeeded());
            test.ok(results.grepError(/PRH: MatchFn was a function/));
            test.ok(results.grepError(/PRH: TransformFn was a function/));
            test.ok(results.grepError(/PRH: Verbose was true/));
            //yes, post require hook must be called always even when a file is not covered
            test.ok(results.grepError(/PRH: Saw foo\.js/));
            //and, of course, for covered files as well
            test.ok(results.grepError(/PRH: Saw bar\.js/));
            test.done();
        });
    },
    "should apply post-require-hook correctly when module name specified": function (test) {
        helper.setOpts({ lazyHook : true });
        run([ 'test/run.js', '-v', '-x', '**/foo.js', '--post-require-hook', 'post-require' ], function (results) {
            test.ok(results.succeeded());
            test.ok(results.grepError(/PRH: MatchFn was a function/));
            test.ok(results.grepError(/PRH: TransformFn was a function/));
            test.ok(results.grepError(/PRH: Verbose was true/));
            //yes, post require hook must be called always even when a file is not covered
            test.ok(results.grepError(/PRH: Saw foo\.js/));
            //and, of course, for covered files as well
            test.ok(results.grepError(/PRH: Saw bar\.js/));
            test.done();
        });
    },
    "should barf when  post-require-hook not available": function (test) {
        run([ 'test/run.js', '-v', '-x', '**/foo.js', '--post-require-hook', 'does-not-exist' ], function (results) {
            test.ok(!results.succeeded());
            test.ok(results.grepError(/Unable to resolve \[does-not-exist\] as a node module/));
            test.done();
        });
    },
    "should not introduce globals in the middle of running a test": function (test) {
        helper.setOpts({ lazyHook : true });
        run([ 'test/global-leak.js', '-v' ], function (results) {
            test.ok(results.succeeded());
            test.done();
        });
    },
    "should cover tests when under symlink": function (test) {
        try {
            fs.symlinkSync(DIR, DIR_LINK);
        } catch (ex) {
            if (ex.code === 'EPERM') {
                console.error('#');
                console.error('# Skipping symlink test');
                console.error('# ' + ex.message);
                console.error('#');
                test.ok(true);
                test.done();
            } else {
              throw ex;
            }
            return;
        }
        helper.setOpts({ cwd: DIR_LINK, lazyHook : true });
        run([ 'test/run.js', '-v' ], function (results) {
            test.ok(results.succeeded());
            test.ok(results.grepError(/Module load hook:/));
            test.ok(existsSync(path.resolve(OUTPUT_DIR, 'lcov.info')));
            test.ok(existsSync(path.resolve(OUTPUT_DIR, 'lcov-report')));
            test.ok(existsSync(path.resolve(OUTPUT_DIR, 'coverage.json')));
            var coverage = JSON.parse(fs.readFileSync(path.resolve(OUTPUT_DIR, 'coverage.json'), 'utf8')),
                filtered;
            filtered = Object.keys(coverage).filter(function (k) { return k.match(/foo/) || k.match(/bar/); });
            test.ok(filtered.length === 2);
            fs.unlinkSync(DIR_LINK);
            test.done();
        });
    }
};
istanbul-0.4.5/test/cli/test-html-report.js000066400000000000000000000265371275640326200207030ustar00rootroot00000000000000/*jslint nomen: true */
var path = require('path'),
    fs = require('fs'),
    rimraf = require('rimraf'),
    mkdirp = require('mkdirp'),
    helper = require('../cli-helper'),
    DIR = path.resolve(__dirname, 'sample-project'),
    OUTPUT_DIR = path.resolve(DIR, 'coverage'),
    COVER_COMMAND = 'cover',
    runCover = helper.runCommand.bind(null, COVER_COMMAND),
    Reporter = require('../../lib/report/html'),
    Collector = require('../../lib/collector'),
    existsSync = fs.existsSync || path.existsSync;

module.exports = {
    setUp: function (cb) {
        rimraf.sync(OUTPUT_DIR);
        mkdirp.sync(OUTPUT_DIR);
        helper.resetOpts();
        runCover([ 'test/run.js', '--report', 'none' ], function (/* results */) {
            cb();
        });
    },
    tearDown: function (cb) {
        rimraf.sync(OUTPUT_DIR);
        cb();
    },
    "should test files written": function (test) {
        var file = path.resolve(OUTPUT_DIR, 'coverage.json'),
            htmlReport = path.resolve(OUTPUT_DIR),
            reporter = new Reporter({ dir: OUTPUT_DIR, verbose: true }),
            obj,
            collector = new Collector(),
            fileFor = function () {
                var args = Array.prototype.slice.call(arguments);
                args.unshift(htmlReport);
                return path.resolve.apply(null, args);
            };

        obj = JSON.parse(fs.readFileSync(file, 'utf8'));
        collector.add(obj);
        reporter.writeReport(collector, true);
        test.ok(existsSync(htmlReport));
        test.ok(existsSync(fileFor('index.html')));
        test.ok(existsSync(fileFor('lib', 'index.html')));
        test.ok(existsSync(fileFor('lib', 'util', 'index.html')));
        test.ok(existsSync(fileFor('lib', 'foo.js.html')));
        test.ok(existsSync(fileFor('lib', 'bar.js.html')));
        test.ok(existsSync(fileFor('lib', 'util', 'generate-names.js.html')));
        test.ok(fs.readFileSync(fileFor('lib', 'bar.js.html'), 'utf8') !== '');
        test.done();
    },

    "should test files written with defaults": function (test) {
        var file = path.resolve(OUTPUT_DIR, 'coverage.json'),
            htmlReport = path.resolve(process.cwd(), 'html-report'),
            reporter = new Reporter(),
            obj,
            collector = new Collector(),
            fileFor = function () {
                var args = Array.prototype.slice.call(arguments);
                args.unshift(htmlReport);
                return path.resolve.apply(null, args);
            };

        obj = JSON.parse(fs.readFileSync(file, 'utf8'));
        collector.add(obj);
        reporter.writeReport(collector, true);
        test.ok(existsSync(htmlReport));
        test.ok(existsSync(fileFor('index.html')));
        test.ok(existsSync(fileFor('lib', 'bar.js.html')));
        test.ok(fs.readFileSync(fileFor('lib', 'bar.js.html'), 'utf8') !== '');
        test.done();
    },

    "should test files written with Unix Line Endings (LF)": function (test) {
        var barPath = path.resolve(DIR, 'lib', 'bar.js'),
            oldBar = fs.readFileSync(barPath, 'utf8'),
            eol = /(\r?\n|\r)/g,
            newBar = oldBar.replace(eol, "\n"),
            file = path.resolve(OUTPUT_DIR, 'coverage.json'),
            htmlReport = path.resolve(process.cwd(), 'html-report'),
            reporter = new Reporter(),
            obj,
            collector = new Collector(),
            fileFor = function () {
                var args = Array.prototype.slice.call(arguments);
                args.unshift(htmlReport);
                return path.resolve.apply(null, args);
            };

        fs.writeFileSync(barPath, newBar);
        obj = JSON.parse(fs.readFileSync(file, 'utf8'));
        collector.add(obj);
        try {
            reporter.writeReport(collector, true);
        } catch(err) {
            test.ok(false);
        } finally {
            fs.writeFileSync(barPath, oldBar);
        }
        test.ok(existsSync(htmlReport));
        test.ok(existsSync(fileFor('index.html')));
        test.ok(existsSync(fileFor('lib', 'bar.js.html')));
        test.ok(fs.readFileSync(fileFor('lib', 'bar.js.html'), 'utf8') !== '');
        test.done();
    },

    "should test files written with Windows Line Endings (CRLF)": function (test) {
        var barPath = path.resolve(DIR, 'lib', 'bar.js'),
            oldBar = fs.readFileSync(barPath, 'utf8'),
            eol = /(\r?\n|\r)/g,
            newBar = oldBar.replace(eol, "\r\n"),
            file = path.resolve(OUTPUT_DIR, 'coverage.json'),
            htmlReport = path.resolve(process.cwd(), 'html-report'),
            reporter = new Reporter(),
            obj,
            collector = new Collector(),
            fileFor = function () {
                var args = Array.prototype.slice.call(arguments);
                args.unshift(htmlReport);
                return path.resolve.apply(null, args);
            };

        fs.writeFileSync(barPath, newBar);
        obj = JSON.parse(fs.readFileSync(file, 'utf8'));
        collector.add(obj);
        try {
            reporter.writeReport(collector, true);
        } catch(err) {
            test.ok(false);
        } finally {
            fs.writeFileSync(barPath, oldBar);
        }
        test.ok(existsSync(htmlReport));
        test.ok(existsSync(fileFor('index.html')));
        test.ok(existsSync(fileFor('lib', 'bar.js.html')));
        test.ok(fs.readFileSync(fileFor('lib', 'bar.js.html'), 'utf8') !== '');
        test.done();
    },

    "should test files written with Macintosh Line Endings (CR)": function (test) {
        var barPath = path.resolve(DIR, 'lib', 'bar.js'),
            oldBar = fs.readFileSync(barPath, 'utf8'),
            eol = /(\r?\n|\r)/g,
            newBar = oldBar.replace(eol, "\r"),
            file = path.resolve(OUTPUT_DIR, 'coverage.json'),
            htmlReport = path.resolve(process.cwd(), 'html-report'),
            reporter = new Reporter(),
            obj,
            collector = new Collector(),
            fileFor = function () {
                var args = Array.prototype.slice.call(arguments);
                args.unshift(htmlReport);
                return path.resolve.apply(null, args);
            };

        fs.writeFileSync(barPath, newBar);
        obj = JSON.parse(fs.readFileSync(file, 'utf8'));
        collector.add(obj);
        try {
            reporter.writeReport(collector, true);
        } catch(err) {
            test.ok(false);
        } finally {
            fs.writeFileSync(barPath, oldBar);
        }
        test.ok(existsSync(htmlReport));
        test.ok(existsSync(fileFor('index.html')));
        test.ok(existsSync(fileFor('lib', 'bar.js.html')));
        test.ok(fs.readFileSync(fileFor('lib', 'bar.js.html'), 'utf8') !== '');
        test.done();
    },

    "should test files written when code packed into coverage object": function (test) {
        var file = path.resolve(OUTPUT_DIR, 'coverage.json'),
            htmlReport = path.resolve(OUTPUT_DIR),
            reporter = new Reporter({ dir: OUTPUT_DIR, verbose: true }),
            obj,
            copy = {},
            collector = new Collector(),
            mangler = function (name) {
                return name.replace(/\.js/,'-mangled.js');
            },
            fileFor = function () {
                var args = Array.prototype.slice.call(arguments);
                args.unshift(htmlReport);
                return mangler(path.resolve.apply(null, args));
            },
            contentFor = function (file) {
                return fs.readFileSync(file, 'utf8').split(/\r?\n/);
            };

        obj = JSON.parse(fs.readFileSync(file, 'utf8'));
        //stick in the code and mangle the file paths in the coverage object so that default behavior will not work
        Object.keys(obj).forEach(function (k) {
            var code = contentFor(k),
                mangled = mangler(k);
            obj[k].code = code;
            obj[k].path = mangled;
            copy[mangled] = obj[k];
            test.ok(mangled !== k); //verify something _did_ get mangled
            test.ok(copy[mangled].code);
        });
        collector.add(copy);
        reporter.writeReport(collector, true);
        test.ok(existsSync(htmlReport));
        test.ok(existsSync(fileFor('index.html')));
        test.ok(existsSync(fileFor('lib', 'index.html')));
        test.ok(existsSync(fileFor('lib', 'util', 'index.html')));
        test.ok(existsSync(fileFor('lib', 'foo.js.html')));
        test.ok(existsSync(fileFor('lib', 'bar.js.html')));
        test.ok(existsSync(fileFor('lib', 'util', 'generate-names.js.html')));
        test.ok(fs.readFileSync(fileFor('lib', 'bar.js.html'), 'utf8') !== '');
        test.done();
    },

    "test contents": function (test) {
        console.error('Figure out a way to run meaningful tests for HTML report contents');
        test.ok(1);
        test.done();
    },

    "should generate proper ancestorHref when path separator is \\" : function(test) {
        var pathSep = path.sep,
            reporter = new Reporter(),
            node, result;

        path.sep = '\\';
        node = {
            parent: {
                relativeName: 'lib\\util\\',
                parent: {
                    relativeName: '',
                    parent: null
                }
            },
            relativeName: 'generate-names-mangled.js'
        };

        result = reporter.standardLinkMapper().ancestorHref(node, 2);

        test.ok(result === '../../');

        path.sep = pathSep;
        test.done();
    },

    "should generate proper ancestorHref for files using linux paths when path separator is \\" : function(test) {
        var pathSep = path.sep,
            reporter = new Reporter(),
            node, result;

        path.sep = '\\';
        node = {
            parent: {
                relativeName: 'lib/util/',
                parent: {
                    relativeName: '',
                    parent: null
                }
            },
            relativeName: 'generate-names-mangled.js'
        };

        result = reporter.standardLinkMapper().ancestorHref(node, 2);

        test.ok(result === '../../');

        path.sep = pathSep;
        test.done();
    },

    "should generate proper ancestorHref when path separator is /" : function(test) {
        var pathSep = path.sep,
            reporter = new Reporter(),
            node, result;

        path.sep = '/';
        node = {
            parent: {
                relativeName: 'lib/util/',
                parent: {
                    relativeName: '',
                    parent: null
                }
            },
            relativeName: 'generate-names-mangled.js'
        };

        result = reporter.standardLinkMapper().ancestorHref(node, 2);

        test.ok(result === '../../');

        path.sep = pathSep;
        test.done();
    },

    "should ignore . parts in dir generating ancestorHref" : function(test) {
        var pathSep = path.sep,
            reporter = new Reporter(),
            node, result;

        path.sep = '/';

        node = {
            parent: {
                relativeName: 'lib/./util/',
                parent: {
                    relativeName: '',
                    parent: null
                }
            },
            relativeName: './generate-names-mangled.js'
        };

        result = reporter.standardLinkMapper().ancestorHref(node, 2);

        test.ok(result === '../../');

        path.sep = pathSep;
        test.done();
    }
};istanbul-0.4.5/test/cli/test-include-pid.js000066400000000000000000000065331275640326200206150ustar00rootroot00000000000000/*jslint nomen: true */
var path = require('path'),
    fs = require('fs'),
    glob = require('glob'),
    rimraf = require('rimraf'),
    mkdirp = require('mkdirp'),
    COMMAND = 'cover',
    DIR = path.resolve(__dirname, 'sample-project'),
    OUTPUT_DIR = path.resolve(DIR, 'coverage'),
    helper = require('../cli-helper'),
    existsSync = fs.existsSync || path.existsSync,
    run = helper.runCommand.bind(null, COMMAND);

module.exports = {
    setUp: function (cb) {
        rimraf.sync(OUTPUT_DIR);
        mkdirp.sync(OUTPUT_DIR);
        helper.resetOpts();
        cb();
    },
    tearDown: function (cb) {
        rimraf.sync(OUTPUT_DIR);
        cb();
    },
    "should have no pid by default": function (test) {
        helper.setOpts({ lazyHook : true });
        run([ 'test/run.js', '-v'], function (results) {
            test.ok(results.succeeded());
            test.ok(results.grepError(/Module load hook:/));
            test.ok(existsSync(path.resolve(OUTPUT_DIR, 'lcov.info')));
            test.ok(existsSync(path.resolve(OUTPUT_DIR, 'lcov-report')));
            test.ok(existsSync(path.resolve(OUTPUT_DIR, 'coverage.json')));
            var coverage = JSON.parse(fs.readFileSync(path.resolve(OUTPUT_DIR, 'coverage.json'), 'utf8')),
                filtered;
            filtered = Object.keys(coverage).filter(function (k) { return k.match(/foo/) || k.match(/bar/); });
            test.ok(filtered.length === 2);
            test.done();
        });
    },
    "should have no pid if requested": function (test) {
        helper.setOpts({ lazyHook : true });
        run([ 'test/run.js', '-v', '--no-include-pid'], function (results) {
            test.ok(results.succeeded());
            test.ok(results.grepError(/Module load hook:/));
            test.ok(existsSync(path.resolve(OUTPUT_DIR, 'lcov.info')));
            test.ok(existsSync(path.resolve(OUTPUT_DIR, 'lcov-report')));
            test.ok(existsSync(path.resolve(OUTPUT_DIR, 'coverage.json')));
            var coverage = JSON.parse(fs.readFileSync(path.resolve(OUTPUT_DIR, 'coverage.json'), 'utf8')),
                filtered;
            filtered = Object.keys(coverage).filter(function (k) { return k.match(/foo/) || k.match(/bar/); });
            test.ok(filtered.length === 2);
            test.done();
        });
    },
    "should have pid if requested": function (test) {
        helper.setOpts({ lazyHook : true });
        run([ 'test/run.js', '-v', '--include-pid'], function (results) {
            test.ok(results.succeeded());
            test.ok(results.grepError(/Module load hook:/));
            test.ok(existsSync(path.resolve(OUTPUT_DIR, 'lcov.info')));
            test.ok(existsSync(path.resolve(OUTPUT_DIR, 'lcov-report')));
            glob(path.resolve(OUTPUT_DIR, 'coverage-*.json'), function(err, matches) {
              test.ifError(err, 'pid coverage file glob error');
              test.ok(matches.length !== 0, 'pid coverage file not found');
              matches.forEach(function(file) {
                test.ok(existsSync(file));
                var coverage = JSON.parse(fs.readFileSync(file), 'utf8'),
                    filtered;
                filtered = Object.keys(coverage).filter(function (k) { return k.match(/foo/) || k.match(/bar/); });
                test.ok(filtered.length === 2);
              });
              test.done();
            });
        });
    }
};
istanbul-0.4.5/test/cli/test-instrument-command.js000066400000000000000000000173621275640326200222460ustar00rootroot00000000000000/*jslint nomen: true */
var path = require('path'),
    fs = require('fs'),
    existsSync = fs.existsSync || path.existsSync,
    vm = require('vm'),
    rimraf = require('rimraf'),
    mkdirp = require('mkdirp'),
    INPUT_DIR_CC = path.join(__dirname, '../other/data-complete-copy/'),
    OUTPUT_DIR = path.resolve(__dirname, 'output'),
    COMMAND = 'instrument',
    DIR = path.resolve(__dirname, 'sample-project'),
    helper = require('../cli-helper'),
    run = helper.runCommand.bind(null, COMMAND),
    INPUT_DIR_JS_FILE_COUNT = 0;

fs.readdirSync(INPUT_DIR_CC).forEach(function(file) {
    var extenstion = path.extname(file);

    if (extenstion === '.js') {
        INPUT_DIR_JS_FILE_COUNT += 1;
    }
});

module.exports = {
    setUp: function (cb) {
        rimraf.sync(OUTPUT_DIR);
        mkdirp.sync(OUTPUT_DIR);
        helper.resetOpts();
        cb();
    },
    tearDown: function (cb) {
        rimraf.sync(OUTPUT_DIR);
        cb();
    },
    "should work with default options for a single file": function (test) {
        run([ 'lib/foo.js' ], function (results) {
            test.ok(results.succeeded());
            test.doesNotThrow(function () {
                vm.createScript(results.stdout().join('\n'), path.resolve(DIR, 'lib', 'foo.js'));
            }, "Invalid code generated; logging interference perhaps?");
            test.done();
        });
    },
    "should preserve comments in output": function (test) {
        run([ 'lib/foo.js', '--preserve-comments' ], function (results) {
            var code = results.stdout().join('\n');
            test.ok(results.succeeded());
            test.doesNotThrow(function () {
                vm.createScript(code, path.resolve(DIR, 'lib', 'foo.js'));
            }, "Invalid code generated; logging interference perhaps?");
            test.ok(code.match(/\/\/ export what we need/), 'Could not find comment that should have been preserved');
            test.done();
        });
    },
    "should work with compact as default": function (test) {
        run([ 'lib/foo.js' ], function (results) {
            test.ok(results.succeeded());
            var compact = results.stdout().join('\n');
            run([ 'lib/foo.js', '--no-compact' ], function (results2) {
                var full = results2.stdout().join('\n');
                test.ok(full.length > compact.length);
                test.done();
            });
        });
    },
    "should work with explicit output option for a single file": function (test) {
        run([ 'lib/foo.js', '--output', path.resolve(OUTPUT_DIR, 'foo.js') ], function (results) {
            test.ok(results.succeeded());
            test.doesNotThrow(function () {
                vm.createScript(fs.readFileSync(path.resolve(OUTPUT_DIR, 'foo.js'), 'utf8'), path.resolve(DIR, 'lib', 'foo.js'));
            }, "Invalid code generated; logging interference perhaps?");
            test.done();
        });
    },
    "should instrument multiple files": function (test) {
        run([ 'lib', '--output', OUTPUT_DIR, '-v', '-x', 'util/es-module.js' ], function (results) {
            test.ok(results.succeeded());
            test.ok(existsSync(path.resolve(OUTPUT_DIR, 'foo.js')));
            test.ok(existsSync(path.resolve(OUTPUT_DIR, 'bar.js')));
            test.ok(existsSync(path.resolve(OUTPUT_DIR, 'util', 'generate-names.js')));
            test.equal(fs.readFileSync(path.resolve(DIR, 'lib', 'util', 'bad.js'), 'utf8'),
                fs.readFileSync(path.resolve(OUTPUT_DIR, 'util', 'bad.js'), 'utf8'));
            test.ok(results.grepOutput(/Processed: foo\.js/));
            test.ok(results.grepOutput(/Processed \[\d+\] files in/));
            test.ok(results.grepOutput(/The following 1 file\(s\) had errors and were copied as-is/));
            test.done();
        });
    },
    "should instrument multiple files without errors": function (test) {
        run([ 'lib', '--output', OUTPUT_DIR, '-x', '**/bad.js', '-x', 'util/es-module.js' ], function (results) {
            test.ok(results.succeeded());
            test.ok(existsSync(path.resolve(OUTPUT_DIR, 'foo.js')));
            test.ok(existsSync(path.resolve(OUTPUT_DIR, 'bar.js')));
            test.ok(!existsSync(path.resolve(OUTPUT_DIR, 'util', 'bad.js')));
            test.ok(existsSync(path.resolve(OUTPUT_DIR, 'util', 'generate-names.js')));
            test.ok(!results.grepOutput(/Processed: foo\.js/));
            test.ok(results.grepOutput(/Processed \[\d+\] files in/));
            test.ok(!results.grepOutput(/The following 1 file\(s\) had errors and were copied as-is/));
            test.done();
        });
    },
    "should save baseline coverage when requested": function (test) {
        var covFile = path.resolve(OUTPUT_DIR, 'cov.json');
        run([ 'lib/foo.js', '--save-baseline', '--baseline-file=' + covFile ], function (results) {
            test.ok(results.succeeded());
            test.ok(existsSync(covFile));
            test.ok(results.grepOutput(/Saving baseline coverage at/));
            test.done();
        });
    },
    "should barf on no args": function (test) {
        run([], function (results) {
            test.ok(!results.succeeded());
            test.ok(results.grepError(/Need exactly one filename\/ dirname argument/));
            test.done();
        });
    },
    "should barf on directory coverage when output option is not provided": function (test) {
        run([ 'lib'], function (results) {
            test.ok(!results.succeeded());
            test.ok(results.grepError(/Need an output directory/));
            test.done();
        });
    },
    "should barf on directory coverage when output === input": function (test) {
        run([ 'lib', '--output', 'lib'], function (results) {
            test.ok(!results.succeeded());
            test.ok(results.grepError(/Cannot instrument into the same directory/));
            test.done();
        });
    },
    "should not copy non js files when using no-complete-copy": function(test) {
        var inputFileCount;

        inputFileCount = fs.readdirSync(INPUT_DIR_CC).length;

        test.equal(fs.readdirSync(INPUT_DIR_CC).length, 6);
        test.equal(fs.readdirSync(OUTPUT_DIR).length, 0);

        run([ INPUT_DIR_CC, '--output', OUTPUT_DIR, '--no-complete-copy'], function (results) {
            test.ok(results.succeeded());
            test.equal(fs.readdirSync(OUTPUT_DIR).length, INPUT_DIR_JS_FILE_COUNT);
            test.done();
        });
    },
    "should not copy non js files when not specifying complete-copy": function(test) {
        // Backward compatibility test
        var inputFileCount;

        inputFileCount = fs.readdirSync(INPUT_DIR_CC).length;

        test.equal(fs.readdirSync(INPUT_DIR_CC).length, 6);
        test.equal(fs.readdirSync(OUTPUT_DIR).length, 0);

        run([ INPUT_DIR_CC, '--output', OUTPUT_DIR], function (results) {
            test.ok(results.succeeded());
            test.equal(fs.readdirSync(OUTPUT_DIR).length, INPUT_DIR_JS_FILE_COUNT);
            test.done();
        });
    },
    "should copy non js files when using complete-copy": function(test) {
        var inputFileCount;

        inputFileCount = fs.readdirSync(INPUT_DIR_CC).length;

        test.equal(fs.readdirSync(INPUT_DIR_CC).length, 6);
        test.equal(fs.readdirSync(OUTPUT_DIR).length, 0);

        run([ INPUT_DIR_CC, '--output', OUTPUT_DIR, '--complete-copy'], function (results) {
            test.ok(results.succeeded());
            test.equal(fs.readdirSync(OUTPUT_DIR).length, inputFileCount);
            test.ok(existsSync(path.resolve(OUTPUT_DIR, 'subdir', 'x.css')));
            test.done();
        });
    },
    "should instrument es modules": function(test) {
        run([ 'lib/util/es-module.js', '--es-modules'], function (results) {
            test.ok(results.succeeded());
            test.done();
        });
    }
};

istanbul-0.4.5/test/cli/test-json-report.js000066400000000000000000000041231275640326200206730ustar00rootroot00000000000000/*jslint nomen: true */
var path = require('path'),
    fs = require('fs'),
    rimraf = require('rimraf'),
    mkdirp = require('mkdirp'),
    helper = require('../cli-helper'),
    DIR = path.resolve(__dirname, 'sample-project'),
    OUTPUT_DIR = path.resolve(DIR, 'coverage'),
    COVER_COMMAND = 'cover',
    runCover = helper.runCommand.bind(null, COVER_COMMAND),
    Reporter = require('../../lib/report/json'),
    Collector = require('../../lib/collector'),
    existsSync = fs.existsSync || path.existsSync;

module.exports = {
    setUp: function (cb) {
        rimraf.sync(OUTPUT_DIR);
        mkdirp.sync(OUTPUT_DIR);
        helper.resetOpts();
        runCover([ 'test/run.js', '--report', 'none' ], function (/* results */) {
            cb();
        });
    },
    tearDown: function (cb) {
        rimraf.sync(OUTPUT_DIR);
        cb();
    },
    "should produce json report consuming coverage file": function (test) {
        var file = path.resolve(OUTPUT_DIR, 'coverage.json'),
            jsonFile = path.resolve(OUTPUT_DIR, 'coverage-final.json'),
            reporter = new Reporter({ dir: OUTPUT_DIR }),
            obj,
            reportObj,
            collector = new Collector();

        obj = JSON.parse(fs.readFileSync(file, 'utf8'));
        collector.add(obj);
        reporter.writeReport(collector, true);
        test.ok(existsSync(jsonFile));
        reportObj = JSON.parse(fs.readFileSync(jsonFile, 'utf8'));
        Object.keys(reportObj).forEach(function (k) {
            delete reportObj[k].l;
        });
        test.deepEqual(obj, reportObj);
        test.done();
    },
    "should produce coverage-final.json at cwd when no options specified": function (test) {
        var file = path.resolve(OUTPUT_DIR, 'coverage.json'),
            jsonFile = path.resolve('coverage-final.json'),
            reporter = new Reporter(),
            collector = new Collector();

        collector.add(JSON.parse(fs.readFileSync(file, 'utf8')));
        reporter.writeReport(collector, true);
        test.ok(existsSync(jsonFile));
        fs.unlinkSync(jsonFile);
        test.done();
    }
};

istanbul-0.4.5/test/cli/test-json-summary-report.js000066400000000000000000000046421275640326200223740ustar00rootroot00000000000000/*jslint nomen: true */
var path = require('path'),
    fs = require('fs'),
    rimraf = require('rimraf'),
    mkdirp = require('mkdirp'),
    helper = require('../cli-helper'),
    DIR = path.resolve(__dirname, 'sample-project'),
    OUTPUT_DIR = path.resolve(DIR, 'coverage'),
    COVER_COMMAND = 'cover',
    runCover = helper.runCommand.bind(null, COVER_COMMAND),
    Reporter = require('../../lib/report/json-summary'),
    Collector = require('../../lib/collector'),
    existsSync = fs.existsSync || path.existsSync,
    objectUtils = require('../../lib/object-utils');

module.exports = {
    setUp: function (cb) {
        rimraf.sync(OUTPUT_DIR);
        mkdirp.sync(OUTPUT_DIR);
        helper.resetOpts();
        runCover([ 'test/run.js', '--report', 'none' ], function (/* results */) {
            cb();
        });
    },
    tearDown: function (cb) {
        rimraf.sync(OUTPUT_DIR);
        cb();
    },
    "should produce json report consuming coverage file": function (test) {
        var file = path.resolve(OUTPUT_DIR, 'coverage.json'),
            jsonFile = path.resolve(OUTPUT_DIR, 'coverage-summary.json'),
            reporter = new Reporter({ dir: OUTPUT_DIR }),
            obj,
            reportObj,
            collector = new Collector();

        obj = JSON.parse(fs.readFileSync(file, 'utf8'));
        collector.add(obj);
        reporter.writeReport(collector, true);

        var summaries = [],
            finalSummary;
        collector.files().forEach(function (file) {
            summaries.push(objectUtils.summarizeFileCoverage(collector.fileCoverageFor(file)));
        });
        finalSummary = objectUtils.mergeSummaryObjects.apply(null, summaries);
        obj.total = finalSummary;

        test.ok(existsSync(jsonFile));
        reportObj = JSON.parse(fs.readFileSync(jsonFile, 'utf8'));
        test.deepEqual(Object.keys(obj).sort(), Object.keys(reportObj).sort());
        test.done();
    },
    "should produce coverage-summary.json at cwd when no options specified": function (test) {
        var file = path.resolve(OUTPUT_DIR, 'coverage.json'),
            jsonFile = path.resolve('coverage-summary.json'),
            reporter = new Reporter(),
            collector = new Collector();

        collector.add(JSON.parse(fs.readFileSync(file, 'utf8')));
        reporter.writeReport(collector, true);
        test.ok(existsSync(jsonFile));
        fs.unlinkSync(jsonFile);
        test.done();
    }
};

istanbul-0.4.5/test/cli/test-lcov-report.js000066400000000000000000000040401275640326200206630ustar00rootroot00000000000000/*jslint nomen: true */
var path = require('path'),
    fs = require('fs'),
    rimraf = require('rimraf'),
    mkdirp = require('mkdirp'),
    helper = require('../cli-helper'),
    DIR = path.resolve(__dirname, 'sample-project'),
    OUTPUT_DIR = path.resolve(DIR, 'coverage'),
    COVER_COMMAND = 'cover',
    runCover = helper.runCommand.bind(null, COVER_COMMAND),
    Reporter = require('../../lib/report/lcov'),
    Collector = require('../../lib/collector'),
    existsSync = fs.existsSync || path.existsSync;

module.exports = {
    setUp: function (cb) {
        rimraf.sync(OUTPUT_DIR);
        mkdirp.sync(OUTPUT_DIR);
        helper.resetOpts();
        runCover([ 'test/run.js', '--report', 'none' ], function (/* results */) {
            cb();
        });
    },
    tearDown: function (cb) {
        rimraf.sync(OUTPUT_DIR);
        cb();
    },
    "should produce lcov report consuming coverage file": function (test) {
        var file = path.resolve(OUTPUT_DIR, 'coverage.json'),
            lcovFile = path.resolve(OUTPUT_DIR, 'lcov.info'),
            lcovReport = path.resolve(OUTPUT_DIR, 'lcov-report'),
            reporter = new Reporter({ dir: OUTPUT_DIR }),
            obj,
            collector = new Collector();

        obj = JSON.parse(fs.readFileSync(file, 'utf8'));
        collector.add(obj);
        reporter.writeReport(collector, true);
        test.ok(existsSync(lcovFile));
        test.ok(existsSync(lcovReport));
        test.done();
    },
    "should produce lcov.info and lcov-report at cwd when no options specified": function (test) {
        var file = path.resolve(OUTPUT_DIR, 'coverage.json'),
            lcovFile = path.resolve('lcov.info'),
            lcovReport = path.resolve('lcov-report'),
            reporter = new Reporter(),
            collector = new Collector();

        collector.add(JSON.parse(fs.readFileSync(file, 'utf8')));
        reporter.writeReport(collector, true);
        test.ok(existsSync(lcovFile));
        fs.unlinkSync(lcovFile);
        rimraf.sync(lcovReport);
        test.done();
    }
};

istanbul-0.4.5/test/cli/test-lcovonly-report.js000066400000000000000000000044541275640326200215760ustar00rootroot00000000000000/*jslint nomen: true */
var path = require('path'),
    fs = require('fs'),
    rimraf = require('rimraf'),
    mkdirp = require('mkdirp'),
    helper = require('../cli-helper'),
    DIR = path.resolve(__dirname, 'sample-project'),
    OUTPUT_DIR = path.resolve(DIR, 'coverage'),
    COVER_COMMAND = 'cover',
    runCover = helper.runCommand.bind(null, COVER_COMMAND),
    Reporter = require('../../lib/report/lcovonly'),
    Collector = require('../../lib/collector'),
    existsSync = fs.existsSync || path.existsSync;

module.exports = {
    setUp: function (cb) {
        rimraf.sync(OUTPUT_DIR);
        mkdirp.sync(OUTPUT_DIR);
        helper.resetOpts();
        runCover([ 'test/run.js', '--report', 'none' ], function (/* results */) {
            cb();
        });
    },
    tearDown: function (cb) {
        rimraf.sync(OUTPUT_DIR);
        cb();
    },
    "should produce lcovonly report consuming coverage file": function (test) {
        var file = path.resolve(OUTPUT_DIR, 'coverage.json'),
            lcovFile = path.resolve(OUTPUT_DIR, 'lcov.info'),
            reporter = new Reporter({ dir: OUTPUT_DIR }),
            obj,
            collector = new Collector(),
            lines,
            numFiles;

        obj = JSON.parse(fs.readFileSync(file, 'utf8'));
        numFiles = Object.keys(obj).length;
        collector.add(obj);
        reporter.writeReport(collector, true);
        test.ok(existsSync(lcovFile));
        lines = fs.readFileSync(lcovFile, 'utf8').split(/\r?\n/);
        test.ok(lines.filter(function (line) { return line.indexOf('SF:') === 0; }).length, numFiles);
        test.ok(lines.filter(function (line) { return line.indexOf('TN:') === 0; }).length, numFiles);
        test.ok(lines.filter(function (line) { return line.indexOf('end_of_record') === 0; }).length, numFiles);
        test.done();
    },
    "should produce lcov.info at cwd when no options specified": function (test) {
        var file = path.resolve(OUTPUT_DIR, 'coverage.json'),
            lcovFile = path.resolve('lcov.info'),
            reporter = new Reporter(),
            collector = new Collector();

        collector.add(JSON.parse(fs.readFileSync(file, 'utf8')));
        reporter.writeReport(collector, true);
        test.ok(existsSync(lcovFile));
        fs.unlinkSync(lcovFile);
        test.done();
    }
};

istanbul-0.4.5/test/cli/test-lots-of-files.js000066400000000000000000000052051275640326200210760ustar00rootroot00000000000000/*jslint nomen: true */
/*jslint nomen: true */
var path = require('path'),
    fs = require('fs'),
    rimraf = require('rimraf'),
    mkdirp = require('mkdirp'),
    COMMAND = 'report',
    COVER_COMMAND = 'cover',
    outputDir = path.resolve(__dirname, 'output'),
    helper = require('../cli-helper'),
    existsSync = fs.existsSync || path.existsSync,
    run = helper.runCommand.bind(null, COMMAND),
    runCover = helper.runCommand.bind(null, COVER_COMMAND),
    MAX_FILES = 1000;

function fileFor(i) {
    return 'f' + i + '.js';
}

function createCode() {
    var i,
        libDir = path.resolve(outputDir, 'lib'),
        code;

    mkdirp.sync(libDir);

    for (i = 0; i < MAX_FILES; i += 1) {
        code = 'module.exports = function () { return "hello-' + i + '"; };\n';
        fs.writeFileSync(path.resolve(libDir, fileFor(i)), code, 'utf8');
    }
}

function createTest() {
    var i,
        testDir = path.resolve(outputDir, 'test'),
        code = 'var assert = require("assert");\n';

    mkdirp.sync(testDir);

    for (i = 0; i < MAX_FILES; i += 1) {
        code += 'assert.equal("hello-' + i + '", require("../lib/' + fileFor(i) + '")());\n';
    }
    fs.writeFileSync(path.resolve(testDir, 'test.js'), code, 'utf8');
}

module.exports = {
    setUp: function (cb) {
        helper.resetOpts();
        helper.setOpts({ cwd: outputDir });
        mkdirp.sync(outputDir);
        cb();
    },
    tearDown: function (cb) {
        rimraf.sync(outputDir);
        cb();
    },

    'should report correctly with 1000 code files': function (test) {
        var coverageDir = path.resolve(outputDir, 'coverage'),
            lcovFile = path.resolve(coverageDir, 'lcov.info'),
            lcovDir = path.resolve(coverageDir, 'lcov-report'),
            jsonFile = path.resolve(coverageDir, 'coverage.json');
        createCode();
        createTest();
        runCover([ 'test/test.js' ], function (results) {
            test.ok(results.succeeded());
            test.ok(existsSync(lcovFile));
            test.ok(existsSync(lcovDir));
            test.ok(existsSync(jsonFile));
            var obj = JSON.parse(fs.readFileSync(jsonFile, 'utf8'));
            test.equal(MAX_FILES, Object.keys(obj).length);
            rimraf.sync(lcovDir);
            fs.unlinkSync(lcovFile);
            run([], function (results) {
                test.ok(results.succeeded());
                test.ok(existsSync(lcovFile));
                test.ok(existsSync(lcovDir));
                test.ok(existsSync(jsonFile));
                test.ok(fs.readdirSync(path.resolve(lcovDir, 'lib')).length >= MAX_FILES);
                test.done();
            });
        });
    }
};



istanbul-0.4.5/test/cli/test-none-report.js000066400000000000000000000010471275640326200206630ustar00rootroot00000000000000/*jslint nomen: true */
var Reporter = require('../../lib/report/none'),
    Report = require('../../lib/report');

module.exports = {
    "should be a noop without any failures": function (test) {
        var reporter = new Reporter();
        test.doesNotThrow(function () { reporter.writeReport(); });
        test.done();
    },
    "should throw when Base report class writeMethod called": function (test) {
        var r = new Report();
        test.throws(function () { r.writeReport(); }, /must be overridden/);
        test.done();
    }
};

istanbul-0.4.5/test/cli/test-report-command.js000066400000000000000000000076441275640326200213530ustar00rootroot00000000000000/*jslint nomen: true */
var path = require('path'),
    fs = require('fs'),
    rimraf = require('rimraf'),
    mkdirp = require('mkdirp'),
    COMMAND = 'report',
    COVER_COMMAND = 'cover',
    DIR = path.resolve(__dirname, 'sample-project'),
    OUTPUT_DIR = path.resolve(DIR, 'coverage'),
    helper = require('../cli-helper'),
    existsSync = fs.existsSync || path.existsSync,
    run = helper.runCommand.bind(null, COMMAND),
    runCover = helper.runCommand.bind(null, COVER_COMMAND),
    Report = require('../../lib/report');

module.exports = {
    setUp: function (cb) {
        rimraf.sync(OUTPUT_DIR);
        mkdirp.sync(OUTPUT_DIR);
        helper.resetOpts();
        runCover([ 'test/run.js', '--report', 'none' ], function (/* results */) {
            cb();
        });
    },
    tearDown: function (cb) {
        rimraf.sync(OUTPUT_DIR);
        cb();
    },
    "should run reports consuming coverage file with lcov default": function (test) {
        test.ok(existsSync(path.resolve(OUTPUT_DIR, 'coverage.json')));
        run([], function (results) {
            test.ok(results.succeeded());
            test.ok(existsSync(path.resolve(OUTPUT_DIR, 'lcov.info')));
            test.ok(fs.readFileSync(path.resolve(OUTPUT_DIR, 'lcov.info'), 'utf8') !== '');
            test.ok(existsSync(path.resolve(OUTPUT_DIR, 'lcov-report')));
            test.done();
        });
    },
    "should run reports with specific format": function (test) {
        test.ok(existsSync(path.resolve(OUTPUT_DIR, 'coverage.json')));
        run([ 'html' ], function (results) {
            test.ok(results.succeeded());
            test.ok(!existsSync(path.resolve(OUTPUT_DIR, 'lcov.info')));
            test.ok(existsSync(path.resolve(OUTPUT_DIR, 'index.html')));
            test.done();
        });
    },
    "should barf on invalid format": function (test) {
        test.ok(existsSync(path.resolve(OUTPUT_DIR, 'coverage.json')));
        run([ 'gcov' ], function (results) {
            test.ok(!results.succeeded());
            test.ok(!existsSync(path.resolve(OUTPUT_DIR, 'lcov.info')));
            test.ok(results.grepError(/Invalid report format/));
            test.done();
        });
    },
    "should respect input pattern": function (test) {
        test.ok(existsSync(path.resolve(OUTPUT_DIR, 'coverage.json')));
        run([ 'lcovonly', '--include', '**/foobar.json' ], function (results) {
            test.ok(results.succeeded());
            test.ok(existsSync(path.resolve(OUTPUT_DIR, 'lcov.info')));
            test.equal('', fs.readFileSync(path.resolve(OUTPUT_DIR, 'lcov.info'), 'utf8'));
            test.done();
        });
    },
    "should respect legacy input pattern": function (test) {
        test.ok(existsSync(path.resolve(OUTPUT_DIR, 'coverage.json')));
        run([ 'lcovonly', '**/foobar.json' ], function (results) {
            test.ok(results.succeeded());
            test.ok(existsSync(path.resolve(OUTPUT_DIR, 'lcov.info')));
            test.equal('', fs.readFileSync(path.resolve(OUTPUT_DIR, 'lcov.info'), 'utf8'));
            test.ok(results.grepError(/DEPRECATION WARNING/));
            test.done();
        });
    },
    "should run all possible reports when requested": function (test) {
        test.ok(existsSync(path.resolve(OUTPUT_DIR, 'coverage.json')));
        run([ '-v' ].concat(Report.getReportList()), function (results) {
            test.ok(results.succeeded());
            test.ok(existsSync(path.resolve(OUTPUT_DIR, 'lcov.info')));
            test.ok(existsSync(path.resolve(OUTPUT_DIR, 'cobertura-coverage.xml')));
            test.done();
        });
    },
    "should default to configuration value": function (test) {
        test.ok(existsSync(path.resolve(OUTPUT_DIR, 'coverage.json')));
        run([ '--config', 'config.istanbul.yml' ], function (results) {
            test.ok(results.succeeded());
            test.ok(existsSync(path.resolve(OUTPUT_DIR, 'foo.xml')));
            test.done();
        });
    }
};istanbul-0.4.5/test/cli/test-teamcity-report.js000066400000000000000000000053511275640326200215450ustar00rootroot00000000000000/*jslint nomen: true */
var path = require('path'),
    fs = require('fs'),
    rimraf = require('rimraf'),
    mkdirp = require('mkdirp'),
    helper = require('../cli-helper'),
    DIR = path.resolve(__dirname, 'sample-project'),
    OUTPUT_DIR = path.resolve(DIR, 'coverage'),
    COVER_COMMAND = 'cover',
    runCover = helper.runCommand.bind(null, COVER_COMMAND),
    Reporter = require('../../lib/report/teamcity'),
    Collector = require('../../lib/collector'),
    existsSync = fs.existsSync || path.existsSync;

module.exports = {
    setUp: function (cb) {
        rimraf.sync(OUTPUT_DIR);
        mkdirp.sync(OUTPUT_DIR);
        helper.resetOpts();
        runCover([ 'test/run.js', '--report', 'none' ], function (/* results */) {
            cb();
        });
    },
    tearDown: function (cb) {
        rimraf.sync(OUTPUT_DIR);
        cb();
    },
    "should produce teamcity service messages": function (test) {
        var file = path.resolve(OUTPUT_DIR, 'coverage.json'),
            outFile = path.resolve(OUTPUT_DIR, 'teamcity.txt'),
            reporter = new Reporter({ dir: OUTPUT_DIR, file: "teamcity.txt" }),
            obj,
            reportLines,
            collector = new Collector();

        obj = JSON.parse(fs.readFileSync(file, 'utf8'));
        collector.add(obj);
        reporter.writeReport(collector, true);
        test.ok(existsSync(outFile));
        reportLines = fs.readFileSync(outFile, 'utf8');

        test.ok(reportLines.indexOf('Code Coverage Summary') > 0);
        test.ok(reportLines.indexOf('CodeCoverageB') > 0);
        test.ok(reportLines.indexOf('CodeCoverageAbsMCovered') > 0);
        test.ok(reportLines.indexOf('CodeCoverageAbsMTotal') > 0);
        test.ok(reportLines.indexOf('CodeCoverageM') > 0);
        test.ok(reportLines.indexOf('CodeCoverageAbsLCovered') > 0);
        test.ok(reportLines.indexOf('CodeCoverageAbsLTotal') > 0);
        test.ok(reportLines.indexOf('CodeCoverageL') > 0);

        test.done();
    },
    "should allow to set custom Block name": function(test){
    var file = path.resolve(OUTPUT_DIR, 'coverage.json'),
        outFile = path.resolve(OUTPUT_DIR, 'teamcity-named.txt'),
        reporter = new Reporter({ dir: OUTPUT_DIR, file: "teamcity-named.txt",  blockName: 'My Custom Block Name' }),
        obj,
        reportLines,
        collector = new Collector();

    obj = JSON.parse(fs.readFileSync(file, 'utf8'));
    collector.add(obj);
    reporter.writeReport(collector, true);
    test.ok(existsSync(outFile));
    reportLines = fs.readFileSync(outFile, 'utf8');

    test.ok(reportLines.indexOf('##teamcity[blockOpened name=\'My Custom Block Name\']') > 0);
    test.ok(reportLines.indexOf('##teamcity[blockClosed name=\'My Custom Block Name\']') > 0);
    test.done();
    }
};

istanbul-0.4.5/test/cli/test-test-command.js000066400000000000000000000031321275640326200210030ustar00rootroot00000000000000/*jslint nomen: true */
var path = require('path'),
    fs = require('fs'),
    rimraf = require('rimraf'),
    mkdirp = require('mkdirp'),
    COMMAND = 'test',
    DIR = path.resolve(__dirname, 'sample-project'),
    OUTPUT_DIR = path.resolve(DIR, 'coverage'),
    helper = require('../cli-helper'),
    existsSync = fs.existsSync || path.existsSync,
    run = helper.runCommand.bind(null, COMMAND);

module.exports = {
    setUp: function (cb) {
        rimraf.sync(OUTPUT_DIR);
        mkdirp.sync(OUTPUT_DIR);
        helper.resetOpts();
        cb();
    },
    tearDown: function (cb) {
        rimraf.sync(OUTPUT_DIR);
        cb();
    },
    "should skip coverage when npm coverage is disabled": function (test) {
        run([ 'test/run.js' ], { npm_config_coverage: '' }, function (results) {
            test.ok(results.succeeded());
            test.ok(!existsSync(path.resolve(OUTPUT_DIR, 'lcov.info')));
            test.ok(!existsSync(path.resolve(OUTPUT_DIR, 'lcov-report')));
            test.ok(!existsSync(path.resolve(OUTPUT_DIR, 'coverage.json')));
            test.done();
        });
    },
    "should run coverage when npm coverage is enabled": function (test) {
        helper.setOpts({ lazyHook : true });
        run([ 'test/run.js' ], { npm_config_coverage: '1' }, function (results) {
            test.ok(results.succeeded());
            test.ok(existsSync(path.resolve(OUTPUT_DIR, 'lcov.info')));
            test.ok(existsSync(path.resolve(OUTPUT_DIR, 'lcov-report')));
            test.ok(existsSync(path.resolve(OUTPUT_DIR, 'coverage.json')));
            test.done();
        });
    }
};istanbul-0.4.5/test/cli/test-text-lcov-report.js000066400000000000000000000025231275640326200216510ustar00rootroot00000000000000/*jslint nomen: true */
var path = require('path'),
    fs = require('fs'),
    mkdirp = require('mkdirp'),
    rimraf = require('rimraf'),
    helper = require('../cli-helper'),
    DIR = path.resolve(__dirname, 'sample-project'),
    OUTPUT_DIR = path.resolve(DIR, 'coverage'),
    COVER_COMMAND = 'cover',
    runCover = helper.runCommand.bind(null, COVER_COMMAND),
    Reporter = require('../../lib/report/text-lcov'),
    Collector = require('../../lib/collector');

module.exports = {
    setUp: function (cb) {
        rimraf.sync(OUTPUT_DIR);
        mkdirp.sync(OUTPUT_DIR);
        helper.resetOpts();
        runCover([ 'test/run.js', '--report', 'none' ], function (/* results */) {
            cb();
        });
    },
    tearDown: function (cb) {
        rimraf.sync(OUTPUT_DIR);
        cb();
    },
    "should print lcov.info to standard out": function (test) {
        var output = '',
            file = path.resolve(OUTPUT_DIR, 'coverage.json'),
            reporter = new Reporter({
                log: function (ln) {
                    output += ln;
                }
            }),
            collector = new Collector();

        collector.add(JSON.parse(fs.readFileSync(file, 'utf8')));
        reporter.writeReport(collector, true);
        test.ok(output.match('TN:SF:'), 'failed to output report');
        test.done();
    }
};
istanbul-0.4.5/test/common.js000066400000000000000000000007031275640326200161550ustar00rootroot00000000000000/*jslint nomen:true*/
var path = require('path'),
    buildDir = path.resolve(__dirname, '..', 'build');

module.exports = {
    setSelfCover: function (flag) {
        process.env.SELF_COVER = flag ? 1 : '';
    },
    isSelfCover: function () {
        return process.env.SELF_COVER;
    },
    getBuildDir: function () {
        return buildDir;
    },
    getCoverageDir: function () {
        return path.resolve(buildDir, 'coverage');
    }
};

istanbul-0.4.5/test/es6.js000066400000000000000000000027751275640326200153750ustar00rootroot00000000000000var esprima = require('esprima');

function tryThis(str, feature) {
    try {
        /*jshint evil: true */
        eval(str);
    } catch (ex) {
        console.error('ES6 feature [' + feature + '] is not available in this environment');
        return false;
    }

   // esprima parses sources with sourceType 'script' per default.
   // The only way to enable `import`/`export` is to parse as sourceType 'module'.
   try {
       try {
           esprima.parse(str);
       } catch (ex) {
           esprima.parse(str, { sourceType: 'module' });
       }
   } catch (ex) {
       console.error('ES6 feature [' + feature + '] is not yet supported by esprima mainline');
       return false;
   }

    return true;
}

module.exports = {
    isYieldAvailable: function () {
        return tryThis('function *foo() { yield 1; }', 'yield');
    },

    isSuperAvailable: function () {
        return tryThis('class Test extends Object { constructor() { super(); } }\nnew Test();', 'super');
    },

    isForOfAvailable: function () {
        return tryThis('function *foo() { yield 1; }\n' +
            'for (var k of foo()) {}', 'for-of');
    },

    isArrowFnAvailable: function () {
        return tryThis('[1 ,2, 3].map(x => x * x)', 'arrow function');
    },

    isImportAvailable: function () {
        return tryThis('import fs from "fs"', 'import');
    },

    isExportAvailable: function () {
        // We can test instrumentation of exports even if the environment doesn't support them.
        return true;
    }
};
istanbul-0.4.5/test/helper.js000066400000000000000000000116631275640326200161530ustar00rootroot00000000000000/*jslint nomen: true */
var Instrumenter = require('../lib/instrumenter'),
    vm = require('vm'),
    NO_OP = function () {},
    utils = require('../lib/object-utils');


function Verifier(opts) {
    this.file = opts.file;
    this.fn = opts.fn;
    this.code = opts.code;
    this.generatedCode = opts.generatedCode;
    this.err = opts.err;
    this.debug = opts.debug;
    this.coverageVariable = opts.coverageVariable || '__coverage__';
}

function pad(str, len) {
    var blanks = '                                             ';
    if (str.length >= len) {
        return str;
    }
    return blanks.substring(0, len - str.length) + str;
}

function annotatedCode(code) {
    var line = 0,
        annotated = code.map(function (str) { line += 1; return pad(line, 6) + ': ' + str; });
    return annotated.join('\n');
}

Verifier.prototype = {

    verify: function (test, args, expectedOutput, expectedCoverage) {

        if (this.err) {
            test.ok(false, "Cannot call verify when errors present");
            return;
        } else if (this.fn === NO_OP) {
            test.ok(false, "Cannot call verify for noop");
            return;
        }
        var actualOutput = this.fn(args),
            fullCov = global[this.coverageVariable],
            cov = fullCov[Object.keys(fullCov)[0]];

        utils.addDerivedInfo(global[this.coverageVariable]);
        test.ok(cov && typeof cov === 'object', 'No coverage found for [' + this.file + ']');
        test.deepEqual(expectedOutput, actualOutput, 'Output mismatch');
        test.deepEqual(expectedCoverage.lines, cov.l, 'Line coverage mismatch');
        test.deepEqual(expectedCoverage.functions, cov.f, 'Function coverage mismatch');
        test.deepEqual(expectedCoverage.branches, cov.b, 'Branch coverage mismatch');
        test.deepEqual(expectedCoverage.statements, cov.s, 'Statement coverage mismatch');
    },

    getCoverage: function () {
        return global[this.coverageVariable];
    },

    getFileCoverage: function () {
        var cov = this.getCoverage();
        return cov[Object.keys(cov)[0]];
    },

    verifyError: function (test) {
        test.ok(this.err && typeof this.err === 'object', 'Error should be an object');
    },

    verifyNoError: function (test) {
        test.ok(!(this.err && typeof this.err === 'object'), 'Error should not be present');
    }
};

function setup(file, codeArray, opts) {

    opts = opts || {};
    opts.file = file;
    opts.debug = opts.debug || process.env.DEBUG;

    var expectError = opts.expectError,
        //exercise the case where RE substitutions for the preamble have $ signs
        coverageVariable = typeof opts.coverageVariable === 'undefined' ? '$$coverage$$' : opts.coverageVariable,
        ps = opts.embedSource || false,
        pc = opts.preserveComments || false,
        verifier,
        cover = new Instrumenter({
            debug: opts.debug,
            walkDebug: opts.walkDebug,
            noAutoWrap: opts.noAutoWrap,
            coverageVariable: coverageVariable,
            embedSource: ps,
            preserveComments: pc,
            esModules: opts.esModules
        }),
        args = [ codeArray.join("\n")],
        callback = function (err, generated) {
            if (err) {
                if (expectError) {
                    verifier = new Verifier({ debug: opts.debug, file: file, fn: NO_OP, code: codeArray });
                } else {
                    console.error(err);
                    console.error(err.stack);
                    verifier = new Verifier({ debug: opts.debug, file: file, err: err, code: codeArray });
                }
                return;
            }

            // `export`/`import` cannot be wrapped inside a function.
            // For our purposes, simply remove the `export` from export declarations.
            if ( opts.esModules ) {
                generated = generated.replace(/export (var|function|let|const)/g, '$1');
            }

            var wrappedCode = '(function (args) { var output;\n' + generated + '\nreturn output;\n})',
                fn;
            global[coverageVariable] = undefined;
            fn = vm.runInThisContext(wrappedCode, __filename);
            verifier = new Verifier({ debug: opts.debug, file: file, fn: fn, code: codeArray,
                generatedCode: generated, coverageVariable: coverageVariable });
            if (opts.debug) {
                console.log('================== Original ============================================');
                console.log(annotatedCode(codeArray));
                console.log('================== Generated ===========================================');
                console.log(generated);
                console.log('========================================================================');
            }
        };

    if (file) { args.push(file); }
    args.push(callback);
    delete opts.expectError;
    cover.instrument.apply(cover, args);

    return verifier;
}

exports.verifier = setup;
istanbul-0.4.5/test/instrumentation/000077500000000000000000000000001275640326200175725ustar00rootroot00000000000000istanbul-0.4.5/test/instrumentation/test-do.js000066400000000000000000000050521275640326200215110ustar00rootroot00000000000000/*jslint nomen: true */
var helper = require('../helper'),
    code,
    verifier;

/*jshint maxlen: 500 */
module.exports = {
    "with a simple do-while": {
        setUp: function (cb) {
            code = [
                'var x = args[0], i=0;',
                'do { i++; } while (i < x);',
                'output = i;'
            ];
            verifier = helper.verifier(__filename, code);
            cb();
        },

        "should provide correct line coverage": function (test) {
            verifier.verify(test, [ 10 ], 10, { lines: { 1: 1, 2: 10, 3: 1 }, branches: {}, functions: {}, statements: { '1': 1, '2': 1, '3': 10, '4': 1 } });
            test.done();
        },

        "should cover single-entry into while": function (test) {
            verifier.verify(test, [ -1 ], 1, { lines: { 1: 1, 2: 1, 3: 1 }, branches: {}, functions: {}, statements: { '1': 1, '2': 1, '3': 1, '4': 1 } });
            test.done();
        }
    },
    "with a block do-while on separate line": {
        setUp: function (cb) {
            code = [
                'var x = args[0], i=0;',
                'do { ',
                '   i++; ',
                '} while (i < x);',
                'output = i;'
            ];
            verifier = helper.verifier(__filename, code);
            cb();
        },

        "should provide correct line coverage": function (test) {
            verifier.verify(test, [ 10 ], 10, { lines: { 1: 1, 2: 1, 3: 10, 5: 1 }, branches: {}, functions: {}, statements: { '1': 1, '2': 1, '3': 10, '4': 1 } });
            test.done();
        },

        "should cover single-entry into while": function (test) {
            verifier.verify(test, [ -1 ], 1, { lines: { 1: 1, 2: 1, 3: 1, 5: 1 }, branches: {}, functions: {}, statements:  { '1': 1, '2': 1, '3': 1, '4': 1 } });
            test.done();
        }
    },
    "with an ignore inside a do-while": {
        setUp: function (cb) {
            code = [
                'do { ',
                '   /* istanbul ignore next */ ',
                '   console.log("hi");',
                '} while (false);',
                'output = 10'
            ];
            verifier = helper.verifier(__filename, code);
            cb();
        },

        "should mark statement as skipped": function (test) {
            verifier.verify(test, [], 10, { lines: { '1': 1, '3': 1, '5': 1 }, branches: {}, functions: {}, statements: { '1': 1, '2': 1, '3': 1 } });
            var cov = verifier.getFileCoverage();
            test.equal(true, cov.statementMap[2].skip);
            test.done();
        }
    }
};

istanbul-0.4.5/test/instrumentation/test-es6-arrow-fn.js000066400000000000000000000035641275640326200233430ustar00rootroot00000000000000/*jslint nomen: true */
var helper = require('../helper'),
    code,
    verifier;

/*jshint maxlen: 500 */
if (require('../es6').isArrowFnAvailable()) {
    module.exports = {
        "with an expression arrow expression": {
            setUp: function (cb) {
                code = [
                    'var input = args',
                    'output = input.map(x => x * x)'
                ];
                verifier = helper.verifier(__filename, code);
                cb();
            },

            "should cover it correctly": function (test) {
                verifier.verify(test, [1, 2, 3, 4], [1, 4, 9, 16], { lines: { '1': 1, '2': 4 }, branches: {}, functions: {}, statements: { '1': 1, '2': 1, '3': 4} });
                test.done();
            },

            "should report no calls correctly": function (test) {
                verifier.verify(test, [], [], { lines: { '1': 1, '2': 1 }, branches: {}, functions: {}, statements: { '1': 1, '2': 1, '3': 0} });
                test.done();
            }
        },
        "with a block arrow expression": {
            setUp: function (cb) {
                code = [
                    'var input = args',
                    'output = input.map(x => { return x * x; })'
                ];
                verifier = helper.verifier(__filename, code);
                cb();
            },

            "should cover it correctly": function (test) {
                verifier.verify(test, [1, 2, 3, 4], [1, 4, 9, 16], { lines: { '1': 1, '2': 4 }, branches: {}, functions: {}, statements: { '1': 1, '2': 1, '3': 4} });
                test.done();
            },

            "should report no calls correctly": function (test) {
                verifier.verify(test, [], [], { lines: { '1': 1, '2': 1 }, branches: {}, functions: {}, statements: { '1': 1, '2': 1, '3': 0} });
                test.done();
            }
        }
    };
}

istanbul-0.4.5/test/instrumentation/test-es6-export.js000066400000000000000000000024201275640326200231170ustar00rootroot00000000000000/*jslint nomen: true */
var helper = require('../helper'),
   code,
   verifier;

if (require('../es6').isExportAvailable()) {
    module.exports = {
        'should cover export statements': function (test) {
            code = [
                'export function bar() { return 2 }',
                'output = bar()'
            ];
            verifier = helper.verifier(__filename, code, {
                esModules: true,
                noAutoWrap: true
            });
            verifier.verify(test, [], 2, {
                lines: {'1': 1, '2': 1},
                branches: {},
                functions: {'1': 1},
                statements: {'1': 1, '2': 1, '3': 1}
            });
            test.done();
        },

        'should cover export declarations': function (test) {
            code = [
                'export var a = 2, b = 3;',
                'output = a + b'
            ];
            verifier = helper.verifier(__filename, code, {
                esModules: true,
                noAutoWrap: true
            });
            verifier.verify(test, [], 5, {
                lines: {'1':1, '2': 1},
                branches: {},
                functions: {},
                statements: {'1': 1, '2': 1}
            });
            test.done();
        }
    };
}
istanbul-0.4.5/test/instrumentation/test-es6-forof.js000066400000000000000000000032571275640326200227220ustar00rootroot00000000000000/*jslint nomen: true */
var helper = require('../helper'),
    code,
    verifier;

/*jshint maxlen: 500 */
if (require('../es6').isForOfAvailable()) {
    module.exports = {
        "with a simple for-in": {
            setUp: function (cb) {
                code = [
                    'function *x() { yield 1; yield 2; };',
                    'var k;',
                    'output = 0;',
                    'for (k of x()) {',
                    '   output += k;',
                    '}'
                ];
                verifier = helper.verifier(__filename, code);
                cb();
            },

            "should cover loop exactly once": function (test) {
                verifier.verify(test, [], 3, { lines: { '1': 1, '2': 1, '3': 1, '4': 1, '5': 2 }, branches: {}, functions: { '1': 1 }, statements: { '1': 1, '2': 1, '3': 1, '4': 1, '5': 1, '6': 1, '7': 2 } });
                test.done();
            }
        },
        "with a simple for-of declaring the loop initializer": {
            setUp: function (cb) {
                code = [
                    'function *x() { yield 1; yield 2; };',
                    'output = 0;',
                    'for (var k of x()) {',
                    '   output += k;',
                    '}'
                ];
                verifier = helper.verifier(__filename, code);
                cb();
            },

            "should cover loop exactly once": function (test) {
                verifier.verify(test, [], 3, { lines: { '1': 1, '2': 1, '3': 1, '4': 2 }, branches: {}, functions: { '1': 1 }, statements: { '1': 1, '2': 1, '3': 1, '4': 1, '5': 1, '6': 2 } });
                test.done();
            }
        }
    };
}

istanbul-0.4.5/test/instrumentation/test-es6-import.js000066400000000000000000000013521275640326200231130ustar00rootroot00000000000000/*jslint nomen: true */
var helper = require('../helper'),
   code,
   verifier;

if (require('../es6').isImportAvailable()) {
    module.exports = {
        'should cover import statements': function (test) {
            code = [
                'import util from "util";',
                'output = util.format(args[0], args[1]);'
            ];
            verifier = helper.verifier(__filename, code, {
                esModules: true,
                noAutoWrap: true
            });
            verifier.verify(test, ['foo:%s', 'bar'], 'foo:bar', {
                lines: {'2': 1},
                branches: {},
                functions: {},
                statements: {'1': 1}
            });
            test.done();
        }
    };
}
istanbul-0.4.5/test/instrumentation/test-es6-super.js000066400000000000000000000042231275640326200227370ustar00rootroot00000000000000/*jslint nomen: true */
var helper = require('../helper'),
    code,
    verifier;

if (require('../es6').isSuperAvailable()) {
    module.exports = {
        'should cover super in constructor': function (test) {
            code = [
                'class A {',
                '   constructor(x) {',
                '      this.x = x;',
                '   }',
                '   getX() {',
                '      return "x";',
                '   }',
                '}',
                'class B extends A {',
                '   constructor(x) {',
                '      super(x);',
                '   }',
                '   getX() {',
                '      return this.x;',
                '   }',
                '}',
                'output = (new B("super")).getX();'
            ];
            verifier = helper.verifier(__filename, code);
            verifier.verify(test, ['super'], 'super', {
                lines: { 3: 1, 6: 0, 11: 1, 14: 1, 17: 1 },
                branches: {},
                functions: { 1: 1, 2: 0, 3: 1, 4: 1 },
                statements: { 1: 1, 2: 0, 3: 1, 4: 1, 5: 1 }
            });
            test.done();
        },
        'should cover super in method': function (test) {
            code = [
                'class A {',
                '   constructor(x) {',
                '      this.x = x;',
                '   }',
                '   getX() {',
                '      return "x";',
                '   }',
                '}',
                'class B extends A {',
                '   constructor(x) {',
                '      super(x);',
                '   }',
                '   getX() {',
                '      return super.getX();',
                '   }',
                '}',
                'output = (new B("super")).getX();'
            ];
            verifier = helper.verifier(__filename, code);
            verifier.verify(test, ['super'], 'x', {
                lines: { 3: 1, 6: 1, 11: 1, 14: 1, 17: 1 },
                branches: {},
                functions: { 1: 1, 2: 1, 3: 1, 4: 1 },
                statements: { 1: 1, 2: 1, 3: 1, 4: 1, 5: 1 }
            });
            test.done();
        }
    };
}



istanbul-0.4.5/test/instrumentation/test-es6-yield.js000066400000000000000000000020011275640326200226770ustar00rootroot00000000000000/*jslint nomen: true */
var helper = require('../helper'),
    code,
    verifier;

if (require('../es6').isYieldAvailable()) {
    module.exports = {
        'should cover yield statements in generators': function (test) {
            code = [
                'function *yielder() {',
                '   yield 1;',
                '   yield 2;',
                '   yield 3;',
                '}',
                'var x = 0, y = yielder();',
                'for (var i = 0; i < 2; i += 1 ) {',
                '   x += y.next().value;',
                '}',
                'output = x;'
            ];
            verifier = helper.verifier(__filename, code);
            verifier.verify(test, [], 3, {
                lines: { '1': 1, '2': 1, '3': 1, '4': 0, '6': 1, '7': 1, '8': 2, '10': 1 },
                branches: {},
                functions: { 1: 1 },
                statements: { '1': 1, '2': 1, '3': 1, '4': 0, '5': 1, '6': 1, '7': 2, '8': 1 }
            });
            test.done();
        }
    };
}



istanbul-0.4.5/test/instrumentation/test-expressions.js000066400000000000000000000054541275640326200234770ustar00rootroot00000000000000/*jslint nomen: true */
var helper = require('../helper'),
    code,
    verifier;

/*jshint maxlen: 500 */
module.exports = {
    "with a simple expression": {
        setUp: function (cb) {
            code = [
                'var x = args[0] > 0 && args[0] < 5;',
                'output = x;'
            ];
            verifier = helper.verifier(__filename, code);
            cb();
        },

        "should cover line and one branch": function (test) {
            verifier.verify(test, [ -1 ], false, { lines: { 1: 1, 2: 1 }, branches: { 1: [ 1, 0 ]}, functions: {}, statements: { '1': 1, '2': 1 } });
            test.done();
        },
        "should cover line, both branches but return false": function (test) {
            verifier.verify(test, [ 10 ], false, { lines: { 1: 1, 2: 1 }, branches: { 1: [ 1, 1 ]}, functions: {}, statements:  { '1': 1, '2': 1 } });
            test.done();
        },
        "should cover line, both branches and return true": function (test) {
            verifier.verify(test, [ 3 ], true, { lines: { 1: 1, 2: 1 }, branches: { 1: [ 1, 1 ]}, functions: {}, statements:  { '1': 1, '2': 1 } });
            test.done();
        }
    },
    "with a complex expression": {
        setUp: function (cb) {
            code = [
                'var x = args[0] > 0 && (args[0] < 5 || args[0] > 10);',
                'output = x;'
            ];
            verifier = helper.verifier(__filename, code);
            cb();
        },

        "should cover line and one branch": function (test) {
            verifier.verify(test, [ -1 ], false, { lines: { 1: 1, 2: 1 }, branches: { 1: [ 1, 0, 0 ]}, functions: {}, statements: { '1': 1, '2': 1 } });
            test.done();
        },
        "should cover line, both branches but return false": function (test) {
            verifier.verify(test, [ 9 ], false, { lines: { 1: 1, 2: 1 }, branches: { 1: [ 1, 1, 1 ]}, functions: {}, statements: { '1': 1, '2': 1 } });
            test.done();
        },
        "should cover line, both branches and return true": function (test) {
            verifier.verify(test, [ 3 ], true, { lines: { 1: 1, 2: 1 }, branches: { 1: [ 1, 1, 0 ]}, functions: {}, statements: { '1': 1, '2': 1 } });
            test.done();
        }
    },
    "with an array expression with empty positions": {
        setUp: function (cb) {
            code = [
                'var x = [args[0], , args[1], ];',
                'output = x.indexOf(args[1]) === x.length - 1 && x[0] !== x[1];'
            ];
            verifier = helper.verifier(__filename, code);
            cb();
        },

        "should not barf in any way": function (test) {
            verifier.verify(test, [ 1, 5 ], true, { lines: { 1: 1, 2: 1 }, branches: { 1: [ 1, 1 ]}, functions: {}, statements: { '1': 1, '2': 1 } });
            test.done();
        }
    }
};

istanbul-0.4.5/test/instrumentation/test-for.js000066400000000000000000000150161275640326200216760ustar00rootroot00000000000000/*jslint nomen: true */
var helper = require('../helper'),
    code,
    verifier;

module.exports = {
    "with a simple for": {
        setUp: function (cb) {
            code = [
                'var x = args[0], i, j = -10;',
                'for (i =0; i < x; i++) j = i;',
                'output = j;'
            ];
            verifier = helper.verifier(__filename, code);
            cb();
        },

        "should cover loop exactly once": function (test) {
            verifier.verify(test, [ 10 ], 9, {
                lines: { 1: 1, 2: 10, 3: 1 },
                branches: { },
                functions: {},
                statements: { '1': 1, '2': 1, '3': 10, '4': 1 }
            });
            test.done();
        }
    },
    "with a simple for - declaring the loop variable in initializer": {
        setUp: function (cb) {
            code = [
                'var x = args[0], j = -10;',
                'for (var i =0; i < x; i++) j = i;',
                'output = j;'
            ];
            verifier = helper.verifier(__filename, code);
            cb();
        },

        "should cover loop exactly once": function (test) {
            verifier.verify(test, [ 10 ], 9, {
                lines: { 1: 1, 2: 10, 3: 1 },
                branches: { },
                functions: {},
                statements: { '1': 1, '2': 1, '3': 10, '4': 1 }
            });
            test.done();
        }
    },
    "with a simple for - not having an initializer": {
        setUp: function (cb) {
            code = [
                'var x = args[0], j = -10, i=0;',
                'for (; i < x; i++) j = i;',
                'output = j;'
            ];
            verifier = helper.verifier(__filename, code);
            cb();
        },

        "should cover loop exactly once": function (test) {
            verifier.verify(test, [ 10 ], 9, {
                lines: { 1: 1, 2: 10, 3: 1 },
                branches: { },
                functions: {},
                statements: { '1': 1, '2': 1, '3': 10, '4': 1 }
            });
            test.done();
        }
    },
    "with a simple for - statement on a different line": {
        setUp: function (cb) {
            code = [
                'var x = args[0], i, j = -10;',
                'for (i =0; i < x; i++)',
                '   j = i;',
                'output = j;'
            ];
            verifier = helper.verifier(__filename, code);
            cb();
        },

        "should cover loop one time": function (test) {
            verifier.verify(test, [ 10 ], 9, {
                lines: { 1: 1, 2: 1, 3: 10, 4: 1 },
                branches: {},
                functions: {},
                statements: { '1': 1, '2': 1, '3': 10, '4': 1 }
            });
            test.done();
        },

        "should not cover loop at all": function (test) {
            verifier.verify(test, [ -1 ], -10, {
                lines: { 1: 1, 2: 1, 3: 0, 4: 1 },
                branches: {},
                functions: {},
                statements: { '1': 1, '2': 1, '3': 0, '4': 1 }
            });
            test.done();
        }
    },
    "with a simple for in block": {
        setUp: function (cb) {
            code = [
                'var x = args[0], i, j = -10;',
                'for (i =0; i < x; i++) { j = i; }',
                'output = j;'
            ];
            verifier = helper.verifier(__filename, code);
            cb();
        },

        "should cover multi-loop exactly once": function (test) {
            verifier.verify(test, [ 10 ], 9, {
                lines: { 1: 1, 2: 10, 3: 1 },
                branches: { },
                functions: {},
                statements: { '1': 1, '2': 1, '3': 10, '4': 1 }
            });
            test.done();
        },

        "should not cover loop at all": function (test) {
            verifier.verify(test, [ -1 ], -10, {
                lines: { 1: 1, 2: 1, 3: 1 },
                branches: {},
                functions: {},
                statements: { '1': 1, '2': 1, '3': 0, '4': 1 }
            });
            test.done();
        }
    },
    "with a labeled for": {
        setUp: function (cb) {
            code = [
                'var x = args[0], i, j = -10;',
                'outer:for (i =0; i < x; i++) { j = i; }',
                'output = j;'
            ];
            verifier = helper.verifier(__filename, code);
            cb();
        },

        "should cover multi-loop exactly once": function (test) {
            verifier.verify(test, [ 10 ], 9, {
                lines: { 1: 1, 2: 10, 3: 1 },
                branches: {},
                functions: {},
                statements: { '1': 1, '2': 1, '3': 1, '4': 10, '5': 1 }
            });
            test.done();
        }
    },
    "with a nested labeled for": {
        setUp: function (cb) {
            code = [
                'var x = args[0], i, j, k = 0;',
                'outer:for (i = 0; i < x; i++)',
                '   for (j=0; j < i ; j++) {',
                '       if (j === 2) continue outer;',
                '       k++;',
                '   }',
                'output = k;'
            ];
            verifier = helper.verifier(__filename, code);
            cb();
        },

        "should cover multi-loop exactly once": function (test) {
            verifier.verify(test, [ 10 ], 17, {
                lines: { '1': 1, '2': 1, '3': 10, '4': 24, '5': 17, '7': 1 },
                branches: { '1': [ 7, 17 ] },
                functions: {},
                statements: { '1': 1, '2': 1, '3': 1, '4': 10, '5': 24, '6': 7, '7': 17, '8': 1 }
            });
            test.done();
        }
    },
    "with a nested labeled for (label on different line)": {
        setUp: function (cb) {
            code = [
                'var x = args[0], i, j, k = 0;',
                'outer:',
                'for (i = 0; i < x; i++)',
                '   for (j=0; j < i ; j++) {',
                '       if (j === 2) continue outer;',
                '       k++;',
                '   }',
                'output = k;'
            ];
            verifier = helper.verifier(__filename, code);
            cb();
        },

        "should cover multi-loop exactly once": function (test) {
            verifier.verify(test, [ 10 ], 17, {
                lines: { '1': 1, '2': 1, '3': 1, '4': 10, '5': 24, '6': 17, '8': 1 },
                branches: { '1': [ 7, 17 ] },
                functions: {},
                statements: { '1': 1, '2': 1, '3': 1, '4': 10, '5': 24, '6': 7, '7': 17, '8': 1 }
            });
            test.done();
        }
    }
};

istanbul-0.4.5/test/instrumentation/test-forin.js000066400000000000000000000031711275640326200222240ustar00rootroot00000000000000/*jslint nomen: true */
var helper = require('../helper'),
    code,
    verifier;

/*jshint maxlen: 500 */
module.exports = {
    "with a simple for-in": {
        setUp: function (cb) {
            code = [
                'var x = { a: args[0], b: args[1] }, k;',
                'output = 0;',
                'for (k in x) {',
                '   if (x.hasOwnProperty(k) && x[k]) {',
                '       output += x[k];',
                '   }',
                '}'
            ];
            verifier = helper.verifier(__filename, code);
            cb();
        },

        "should cover loop exactly once": function (test) {
            verifier.verify(test, [ 10, 0 ], 10, { lines: { 1: 1, 2: 1, 3: 1, 4: 2, 5: 1 }, branches: { 1: [1, 1], 2: [ 2, 2 ] }, functions: {}, statements: { 1: 1, 2: 1, 3: 1, 4: 2, 5: 1 } });
            test.done();
        }
    },

    "with a simple for-in declaring the loop initializer": {
        setUp: function (cb) {
            code = [
                'var x = { a: args[0], b: args[1] };',
                'output = 0;',
                'for (var k in x) {',
                '   if (x.hasOwnProperty(k) && x[k]) {',
                '       output += x[k];',
                '   }',
                '}'
            ];
            verifier = helper.verifier(__filename, code);
            cb();
        },

        "should cover loop exactly once": function (test) {
            verifier.verify(test, [ 10, 0 ], 10, { lines: { 1: 1, 2: 1, 3: 1, 4: 2, 5: 1 }, branches: { 1: [1, 1], 2: [ 2, 2 ] }, functions: {}, statements: { 1: 1, 2: 1, 3: 1, 4: 2, 5: 1 } });
            test.done();
        }
    }
};

istanbul-0.4.5/test/instrumentation/test-functions.js000066400000000000000000000073221275640326200231210ustar00rootroot00000000000000/*jslint nomen: true */
var helper = require('../helper'),
    code,
    verifier;

/*jshint maxlen: 500 */
module.exports = {
    "with a simple function": {
        setUp: function (cb) {
            code = [
                'var x = args[0];',
                'function foo() {',
                '   return 42;',
                '}',
                'output = x < 5 ? foo() : 15;'
            ];
            verifier = helper.verifier(__filename, code);
            cb();
        },

        "should cover line and function": function (test) {
            verifier.verify(test, [ 2 ], 42, { lines: { 1: 1, 2: 1, 3: 1, 5: 1 }, branches: { 1: [1, 0 ] }, functions: { 1: 1 }, statements: { '1': 1, '2': 1, '3': 1, '4': 1 } });
            test.done();
        },

        "should not cover function": function (test) {
            verifier.verify(test, [ 10 ], 15, { lines: { 1: 1, 2: 1, 3: 0, 5: 1 }, branches: { 1: [ 0, 1 ]}, functions: { 1: 0 }, statements: { '1': 1, '2': 1, '3': 0, '4': 1 } });
            test.done();
        }
    },

    "with an anonymous function": {
        setUp: function (cb) {
            code = [
                'var x = args[0];',
                'output = x < 5 ? (function() { return 42; }()) : 15;'
            ];
            verifier = helper.verifier(__filename, code);
            cb();
        },

        "should cover line and function": function (test) {
            verifier.verify(test, [ 2 ], 42, { lines: { 1: 1, 2: 1 }, branches: { 1: [1, 0 ] }, functions: { 1: 1 }, statements: { '1': 1, '2': 1, '3': 1 } });
            test.done();
        },

        "should not cover function": function (test) {
            verifier.verify(test, [ 10 ], 15, { lines: { 1: 1, 2: 1 }, branches: { 1: [ 0, 1 ]}, functions: { 1: 0 }, statements: { '1': 1, '2': 1, '3': 0 } });
            test.done();
        }
    },

    "with an anonymous function on newline": {
        setUp: function (cb) {
            code = [
                'var x = args[0];',
                'output = x < 5 ? ',
                '   (function() { ',
                '   return 42; ',
                '}())',
                ' : 15;'
            ];
            verifier = helper.verifier(__filename, code);
            cb();
        },

        "should cover line and function": function (test) {
            verifier.verify(test, [ 2 ], 42, { lines: { 1: 1, 2: 1, 4: 1 }, branches: { 1: [1, 0 ] }, functions: { 1: 1 }, statements: { 1: 1, 2: 1, 3: 1} });
            test.done();
        },

        "should not cover function": function (test) {
            verifier.verify(test, [ 10 ], 15, { lines: { 1: 1, 2: 1, 4: 0 }, branches: { 1: [ 0, 1 ]}, functions: { 1: 0 }, statements: { 1: 1, 2: 1, 3: 0 } });
            test.done();
        }
    },

    "with a function declared in an 'unreachable' place": {
        setUp: function (cb) {
            code = [
                'function foo(x) {',
                '   return bar(x);',
                '   function bar(y) { return y * 2 }',
                '}',
                'output = args[0] < 2 ? 2: foo(args[0]);'
            ];
            verifier = helper.verifier(__filename, code);
            cb();
        },

        "should not cover functions but should cover declarations": function (test) {
            verifier.verify(test, [ 1 ], 2, { lines: { 1: 1, 2: 0, 3: 1, 5: 1 }, branches: { 1: [1, 0 ] }, functions: { 1: 0, 2: 0 }, statements: { 1: 1, 2: 0, 3: 1, 4: 0, 5: 1} });
            test.done();
        },

        "should cover functions and declarations": function (test) {
            verifier.verify(test, [ 10 ], 20, { lines: { 1: 1, 2: 1, 3: 1, 5: 1 }, branches: { 1: [ 0, 1 ]}, functions: { 1: 1, 2: 1 }, statements: { 1: 1, 2: 1, 3: 1, 4: 1, 5: 1 } });
            test.done();
        }
    }
};

istanbul-0.4.5/test/instrumentation/test-if-with-hints.js000066400000000000000000000177711275640326200236140ustar00rootroot00000000000000/*jslint nomen: true */
var helper = require('../helper'),
    code,
    verifier;

/*jshint maxlen: 500 */
module.exports = {
    "with a simple if": {
        "as a statement": {
            setUp: function (cb) {
                code = [
                    'output = -1;',
                    '/* hint */',
                    'if (args[0] > args [1])',
                    '   output = args[0];'
                ];
                cb(null);
            },
            "should cover then path": function (test) {
                code[1] = '/* istanbul ignore else */';
                verifier = helper.verifier(__filename, code);
                verifier.verify(test, [ 20, 10 ], 20, { lines: { 1: 1, 3: 1, 4: 1 }, branches: { '1': [ 1, 0 ] }, functions: {}, statements: { '1': 1, '2': 1, '3': 1 } });
                var cov = verifier.getFileCoverage();
                test.equal(true, cov.branchMap[1].locations[1].skip);
                test.done();
            },
            "should cover else path": function (test) {
                code[1] = '/* istanbul ignore if */';
                verifier = helper.verifier(__filename, code);
                verifier.verify(test, [ 10, 20 ], -1, { lines: { 1: 1, 3: 1, 4: 1 }, branches: { '1': [ 0, 1 ] }, functions: {}, statements: { '1': 1, '2': 1, '3': 0 } });
                var cov = verifier.getFileCoverage();
                test.equal(true, cov.branchMap[1].locations[0].skip);
                test.equal(true, cov.statementMap[3].skip);
                test.done();
            }
        },
        "as a block": {
            setUp: function (cb) {
                code = [
                    'output = -1;',
                    '/* hint */',
                    'if (args[0] > args [1]) {',
                    '   output = args[0];',
                    '}'
                ];
                cb();
            },
            "should cover then path": function (test) {
                code[1] = '/* istanbul ignore else */';
                verifier = helper.verifier(__filename, code);
                verifier.verify(test, [ 20, 10 ], 20, { lines: { 1: 1, 3: 1, 4: 1 }, branches: { '1': [ 1, 0 ] }, functions: {}, statements: { '1': 1, '2': 1, '3': 1 } });
                var cov = verifier.getFileCoverage();
                test.equal(true, cov.branchMap[1].locations[1].skip);
                test.done();
            },
            "should cover else path": function (test) {
                code[1] = '/* istanbul ignore if */';
                verifier = helper.verifier(__filename, code);
                verifier.verify(test, [ 10, 20 ], -1, { lines: { 1: 1, 3: 1, 4: 1 }, branches: { '1': [ 0, 1 ] }, functions: {}, statements: { '1': 1, '2': 1, '3': 0 } });
                var cov = verifier.getFileCoverage();
                test.equal(true, cov.branchMap[1].locations[0].skip);
                test.equal(true, cov.statementMap[3].skip);
                test.done();
            }
        },
        "on a single line": {
            "as statement": {
                setUp: function (cb) {
                    code = [
                        'output = -1;',
                        '/* hint */',
                        'if (args[0] > args [1]) output = args[0];'
                    ];
                    cb();
                },
                "should cover then path": function (test) {
                    code[1] = '/* istanbul ignore else */';
                    verifier = helper.verifier(__filename, code);
                    verifier.verify(test, [ 20, 10 ], 20, { lines: { 1: 1, 3: 1 }, branches: { '1': [ 1, 0 ] }, functions: {}, statements: { '1': 1, '2': 1, '3': 1 } });
                    var cov = verifier.getFileCoverage();
                    test.equal(true, cov.branchMap[1].locations[1].skip);
                    test.done();
                },
                "should cover else path": function (test) {
                    code[1] = '/* istanbul ignore if */';
                    verifier = helper.verifier(__filename, code);
                    verifier.verify(test, [ 10, 20 ], -1, { lines: { 1: 1, 3: 1 }, branches: { '1': [ 0, 1 ] }, functions: {}, statements: { '1': 1, '2': 1, '3': 0 } });
                    var cov = verifier.getFileCoverage();
                    test.equal(true, cov.branchMap[1].locations[0].skip);
                    test.equal(true, cov.statementMap[3].skip);
                    test.done();
                }
            },
            "as block": {
                setUp: function (cb) {
                    code = [
                        'output = -1;',
                        '/* hint */',
                        'if (args[0] > args [1]) { output = args[0]; }'
                    ];
                    cb();
                },
                "should cover then path": function (test) {
                    code[1] = '/* istanbul ignore else */';
                    verifier = helper.verifier(__filename, code);
                    verifier.verify(test, [ 20, 10 ], 20, { lines: { 1: 1, 3: 1 }, branches: { '1': [ 1, 0 ] }, functions: {}, statements: { '1': 1, '2': 1, '3': 1 } });
                    var cov = verifier.getFileCoverage();
                    test.equal(true, cov.branchMap[1].locations[1].skip);
                    test.done();
                },
                "should cover else path": function (test) {
                    code[1] = '/* istanbul ignore if */';
                    verifier = helper.verifier(__filename, code);
                    verifier.verify(test, [ 10, 20 ], -1, { lines: { 1: 1, 3: 1 }, branches: { '1': [ 0, 1 ] }, functions: {}, statements: { '1': 1, '2': 1, '3': 0 } });
                    var cov = verifier.getFileCoverage();
                    test.equal(true, cov.branchMap[1].locations[0].skip);
                    test.equal(true, cov.statementMap[3].skip);
                    test.done();
                },
                "should skip if statement completely": function (test) {
                    code[1] = '/* istanbul ignore next */';
                    verifier = helper.verifier(__filename, code);
                    verifier.verify(test, [ 10, 20 ], -1, { lines: { 1: 1, 3: 1 }, branches: { '1': [ 0, 1 ] }, functions: {}, statements: { '1': 1, '2': 1, '3': 0 } });
                    var cov = verifier.getFileCoverage();
                    test.equal(true, cov.branchMap[1].locations[0].skip);
                    test.equal(true, cov.branchMap[1].locations[1].skip);
                    test.equal(true, cov.statementMap[3].skip);
                    test.done();
                }
            }
        }
    },
    "with a simple if-else": {
        "as a statement": {
            setUp: function (cb) {
                code = [
                    '// hint',
                    'if (args[0] > args [1])',
                    '   output = args[0];',
                    'else',
                    '   output = args[1];'
                ];
                cb(null);
            },
            "should cover then path": function (test) {
                code[0] = '// istanbul ignore else';
                verifier = helper.verifier(__filename, code);
                verifier.verify(test, [ 20, 10 ], 20, { lines: { 2: 1, 3: 1, 5: 1 }, branches: { '1': [ 1, 0 ] }, functions: {}, statements: { '1': 1, '2': 1, '3': 0 } });
                var cov = verifier.getFileCoverage();
                test.equal(true, cov.branchMap[1].locations[1].skip);
                test.equal(true, cov.statementMap[3].skip);
                test.done();
            },
            "should cover else path": function (test) {
                code[0] = '// istanbul ignore if ';
                verifier = helper.verifier(__filename, code);
                verifier.verify(test, [ 10, 20 ], 20, { lines: { 2: 1, 3: 1, 5: 1 }, branches: { '1': [ 0, 1 ] }, functions: {}, statements: { '1': 1, '2': 0, '3': 1 } });
                var cov = verifier.getFileCoverage();
                test.equal(true, cov.branchMap[1].locations[0].skip);
                test.equal(true, cov.statementMap[2].skip);
                test.done();
            }
        }
    }
};

istanbul-0.4.5/test/instrumentation/test-if.js000066400000000000000000000527101275640326200215100ustar00rootroot00000000000000/*jslint nomen: true */
var helper = require('../helper'),
    code,
    verifier;

/*jshint maxlen: 500 */
module.exports = {
    "with a simple if": {
        "as a statement": {
            setUp: function (cb) {
                code = [
                    'output = -1;',
                    'if (args[0] > args [1])',
                    '   output = args[0];'
                ];
                verifier = helper.verifier(__filename, code);
                cb(null);
            },
            "should cover then path": function (test) {
                verifier.verify(test, [ 20, 10 ], 20, { lines: { 1: 1, 2: 1, 3: 1 }, branches: { '1': [ 1, 0 ] }, functions: {}, statements: { '1': 1, '2': 1, '3': 1 } });
                test.done();
            },
            "should cover else path": function (test) {
                verifier.verify(test, [ 10, 20 ], -1, { lines: { 1: 1, 2: 1, 3: 0 }, branches: { '1': [ 0, 1 ] }, functions: {}, statements: { '1': 1, '2': 1, '3': 0 } });
                test.done();
            }
        },
        "as a block": {
            setUp: function (cb) {
                code = [
                    'output = -1;',
                    'if (args[0] > args [1]) {',
                    '   output = args[0];',
                    '}'
                ];
                verifier = helper.verifier(__filename, code);
                cb();
            },
            "should cover then path": function (test) {
                verifier.verify(test, [ 20, 10 ], 20, { lines: { 1: 1, 2: 1, 3: 1 }, branches: { '1': [ 1, 0 ] }, functions: {}, statements: { '1': 1, '2': 1, '3': 1 } });
                test.done();
            },
            "should cover else path": function (test) {
                verifier.verify(test, [ 10, 20 ], -1, { lines: { 1: 1, 2: 1, 3: 0 }, branches: { '1': [ 0, 1 ] }, functions: {}, statements: { '1': 1, '2': 1, '3': 0 } });
                test.done();
            }
        },
        "on a single line": {
            "as statement": {
                setUp: function (cb) {
                    code = [
                        'output = -1;',
                        'if (args[0] > args [1]) output = args[0];'
                    ];
                    verifier = helper.verifier(__filename, code);
                    cb();
                },
                "should cover then path": function (test) {
                    verifier.verify(test, [ 20, 10 ], 20, { lines: { 1: 1, 2: 1 }, branches: { '1': [ 1, 0 ] }, functions: {}, statements: { '1': 1, '2': 1, '3': 1 } });
                    test.done();
                },
                "should cover else path": function (test) {
                    verifier.verify(test, [ 10, 20 ], -1, { lines: { 1: 1, 2: 1 }, branches: { '1': [ 0, 1 ] }, functions: {}, statements: { '1': 1, '2': 1, '3': 0 } });
                    test.done();
                }
            },
            "as block": {
                setUp: function (cb) {
                    code = [
                        'output = -1;',
                        'if (args[0] > args [1]) { output = args[0]; }'
                    ];
                    verifier = helper.verifier(__filename, code);
                    cb();
                },
                "should cover then path": function (test) {
                    verifier.verify(test, [ 20, 10 ], 20, { lines: { 1: 1, 2: 1 }, branches: { '1': [ 1, 0 ] }, functions: {}, statements: { '1': 1, '2': 1, '3': 1 } });
                    test.done();
                },
                "should cover else path": function (test) {
                    verifier.verify(test, [ 10, 20 ], -1, { lines: { 1: 1, 2: 1 }, branches: { '1': [ 0, 1 ] }, functions: {}, statements: { '1': 1, '2': 1, '3': 0 } });
                    test.done();
                }
            }
        }
    },
    "with a simple if-else": {
        "as a statement": {
            setUp: function (cb) {
                code = [
                    'if (args[0] > args [1])',
                    '   output = args[0];',
                    'else',
                    '   output = args[1];'
                ];
                verifier = helper.verifier(__filename, code);
                cb(null);
            },
            "should cover then path": function (test) {
                verifier.verify(test, [ 20, 10 ], 20, { lines: { 1: 1, 2: 1, 4: 0 }, branches: { '1': [ 1, 0 ] }, functions: {}, statements: { '1': 1, '2': 1, '3': 0 } });
                test.done();
            },
            "should cover else path": function (test) {
                verifier.verify(test, [ 10, 20 ], 20, { lines: { 1: 1, 2: 0, 4: 1 }, branches: { '1': [ 0, 1 ] }, functions: {}, statements: { '1': 1, '2': 0, '3': 1 } });
                test.done();
            }
        },
        "as a block": {
            setUp: function (cb) {
                code = [
                    'if (args[0] > args [1]) {',
                    '   output = args[0];',
                    '} else {',
                    '   output = args[1];',
                    '}'
                ];
                verifier = helper.verifier(__filename, code);
                cb();
            },
            "should cover then path": function (test) {
                verifier.verify(test, [ 20, 10 ], 20, { lines: { 1: 1, 2: 1, 4: 0 }, branches: { '1': [ 1, 0 ] }, functions: {}, statements: { '1': 1, '2': 1, '3': 0 } });
                test.done();
            },
            "should cover else path": function (test) {
                verifier.verify(test, [ 10, 20 ], 20, { lines: { 1: 1, 2: 0, 4: 1 }, branches: { '1': [ 0, 1 ] }, functions: {}, statements: { '1': 1, '2': 0, '3': 1 } });
                test.done();
            }
        },
        "on a single line": {
            "as statement": {
                setUp: function (cb) {
                    code = [
                        'if (args[0] > args [1]) output = args[0]; else output = args[1];'
                    ];
                    verifier = helper.verifier(__filename, code);
                    cb();
                },
                "should cover then path": function (test) {
                    verifier.verify(test, [ 20, 10 ], 20, { lines: { 1: 1 }, branches: { '1': [ 1, 0 ] }, functions: {}, statements: { '1': 1, '2': 1, '3': 0 } });
                    test.done();
                },
                "should cover else path": function (test) {
                    verifier.verify(test, [ 10, 20 ], 20, { lines: { 1: 1 }, branches: { '1': [ 0, 1 ] }, functions: {}, statements: { '1': 1, '2': 0, '3': 1 } });
                    test.done();
                }
            },
            "as block": {
                setUp: function (cb) {
                    code = [
                        'if (args[0] > args [1]) { output = args[0]; } else { output = args[1]; }'
                    ];
                    verifier = helper.verifier(__filename, code);
                    cb();
                },
                "should cover then path": function (test) {
                    verifier.verify(test, [ 20, 10 ], 20, { lines: { 1: 1 }, branches: { '1': [ 1, 0 ] }, functions: {}, statements: { '1': 1, '2': 1, '3': 0 } });
                    test.done();
                },
                "should cover else path": function (test) {
                    verifier.verify(test, [ 10, 20 ], 20, { lines: { 1: 1 }, branches: { '1': [ 0, 1 ] }, functions: {}, statements: { '1': 1, '2': 0, '3': 1 } });
                    test.done();
                }
            },
            "as mixed with then-block": {
                setUp: function (cb) {
                    code = [
                        'if (args[0] > args [1]) { output = args[0]; } else output = args[1];'
                    ];
                    verifier = helper.verifier(__filename, code);
                    cb();
                },
                "should cover then path": function (test) {
                    verifier.verify(test, [ 20, 10 ], 20, { lines: { 1: 1 }, branches: { '1': [ 1, 0 ] }, functions: {}, statements: { '1': 1, '2': 1, '3': 0 } });
                    test.done();
                },
                "should cover else path": function (test) {
                    verifier.verify(test, [ 10, 20 ], 20, { lines: { 1: 1 }, branches: { '1': [ 0, 1 ] }, functions: {}, statements: { '1': 1, '2': 0, '3': 1 } });
                    test.done();
                }
            },
            "as mixed with else-block": {
                setUp: function (cb) {
                    code = [
                        'if (args[0] > args [1]) output = args[0]; else { output = args[1]; }'
                    ];
                    verifier = helper.verifier(__filename, code);
                    cb();
                },
                "should cover then path": function (test) {
                    verifier.verify(test, [ 20, 10 ], 20, { lines: { 1: 1 }, branches: { '1': [ 1, 0 ] }, functions: {}, statements: { '1': 1, '2': 1, '3': 0 } });
                    test.done();
                },
                "should cover else path": function (test) {
                    verifier.verify(test, [ 10, 20 ], 20, { lines: { 1: 1 }, branches: { '1': [ 0, 1 ] }, functions: {}, statements: { '1': 1, '2': 0, '3': 1 } });
                    test.done();
                }
            }
        }
    },
    "with nested ifs": {
        "without an else": {
            "and no blocks": {
                setUp: function (cb) {
                    code = [
                        'output = -1;',
                        'if (args[0] > args[1]) if (args[1] > args[2]) output = args[2];'
                    ];
                    verifier = helper.verifier(__filename, code);
                    cb();
                },
                "should cover first else and nothing below": function (test) {
                    verifier.verify(test, [ 10, 20, 15 ], -1, { lines: { 1: 1, 2: 1 }, branches: { '1': [ 0, 1 ], '2' : [ 0, 0 ] }, functions: {}, statements: { '1': 1, '2': 1, '3': 0, '4': 0 } });
                    test.done();
                },
                "should cover first then": function (test) {
                    verifier.verify(test, [ 20, 10, 15 ], -1, { lines: { 1: 1, 2: 1 }, branches: { '1': [ 1, 0 ], '2' : [ 0, 1 ] }, functions: {}, statements: { '1': 1, '2': 1, '3': 1, '4': 0 } });
                    test.done();
                },
                "should cover first then and second then": function (test) {
                    verifier.verify(test, [ 20, 10, 5 ], 5, { lines: { 1: 1, 2: 1 }, branches: { '1': [ 1, 0 ], '2' : [ 1, 0 ] }, functions: {}, statements: { '1': 1, '2': 1, '3': 1, '4': 1 } });
                    test.done();
                }
            },
            "and blocks": {
                setUp: function (cb) {
                    code = [
                        'output = -1;',
                        'if (args[0] > args[1]) { if (args[1] > args[2]) { output = args[2]; } }'
                    ];
                    verifier = helper.verifier(__filename, code);
                    cb();
                },
                "should cover first else and nothing below": function (test) {
                    verifier.verify(test, [ 10, 20, 15 ], -1, { lines: { 1: 1, 2: 1 }, branches: { '1': [ 0, 1 ], '2' : [ 0, 0 ] }, functions: {}, statements: { '1': 1, '2': 1, '3': 0, '4': 0 } });
                    test.done();
                },
                "should cover first then": function (test) {
                    verifier.verify(test, [ 20, 10, 15 ], -1, { lines: { 1: 1, 2: 1 }, branches: { '1': [ 1, 0 ], '2' : [ 0, 1 ] }, functions: {}, statements: { '1': 1, '2': 1, '3': 1, '4': 0 } });
                    test.done();
                },
                "should cover first then and second then": function (test) {
                    verifier.verify(test, [ 20, 10, 5 ], 5, { lines: { 1: 1, 2: 1 }, branches: { '1': [ 1, 0 ], '2' : [ 1, 0 ] }, functions: {}, statements: { '1': 1, '2': 1, '3': 1, '4': 1 } });
                    test.done();
                }
            }
        },
        "with elses": {
            "and no blocks": {
                setUp: function (cb) {
                    code = [
                        'output = -1;',
                        'if (args[0] > args[1]) if (args[1] > args[2]) output = args[2]; else output = args[1];'
                    ];
                    verifier = helper.verifier(__filename, code);
                    cb();
                },
                "should cover first else and nothing below": function (test) {
                    verifier.verify(test, [ 10, 20, 15 ], -1, { lines: { 1: 1, 2: 1 }, branches: { '1': [ 0, 1 ], '2' : [ 0, 0 ] }, functions: {}, statements: { '1': 1, '2': 1, '3': 0, '4': 0, '5': 0 } });
                    test.done();
                },
                "should cover first then": function (test) {
                    verifier.verify(test, [ 20, 10, 15 ], 10, { lines: { 1: 1, 2: 1 }, branches: { '1': [ 1, 0 ], '2' : [ 0, 1 ] }, functions: {}, statements: { '1': 1, '2': 1, '3': 1, '4': 0, '5': 1 } });
                    test.done();
                },
                "should cover first then and second then": function (test) {
                    verifier.verify(test, [ 20, 10, 5 ], 5, { lines: { 1: 1, 2: 1 }, branches: { '1': [ 1, 0 ], '2' : [ 1, 0 ] }, functions: {}, statements: { '1': 1, '2': 1, '3': 1, '4': 1, '5': 0 } });
                    test.done();
                }
            },
            "including else ifs with blocks": {
                setUp: function (cb) {
                    code = [
                        'if (args[0] < args[1]) {',
                        '    output = args[0];',
                        '} else if (args[1] < args[2]) {',
                        '    output = args[1];',
                        '} else if (args[2] < args[3]) {',
                        '    output = args[2];',
                        '} else {',
                        '    output = args[3];',
                        '}'
                    ];
                    verifier = helper.verifier(__filename, code);
                    cb();
                },
                "should cover all else paths": function (test) {
                    verifier.verify(test, [ 4, 3, 2, 1 ], 1, { lines: { 1: 1, 2: 0, 3: 1, 4: 0, 5: 1, 6: 0, 8: 1 }, branches: { '1': [ 0, 1 ], '2' : [ 0, 1 ], '3': [ 0, 1 ] }, functions: {}, statements: { '1': 1, '2': 0, '3': 1, '4': 0, '5': 1, '6': 0, '7': 1 } });
                    test.done();
                },
                "should cover one then path": function (test) {
                    verifier.verify(test, [ 4, 3, 1, 2 ], 1, { lines: { 1: 1, 2: 0, 3: 1, 4: 0, 5: 1, 6: 1, 8: 0 }, branches: { '1': [ 0, 1 ], '2' : [ 0, 1 ], '3': [ 1, 0 ] }, functions: {}, statements: { '1': 1, '2': 0, '3': 1, '4': 0, '5': 1, '6': 1, '7': 0 } });
                    test.done();
                },
                "should cover upper then paths": function (test) {
                    verifier.verify(test, [ 4, 2, 3, 1 ], 2, { lines: { 1: 1, 2: 0, 3: 1, 4: 1, 5: 0, 6: 0, 8: 0 }, branches: { '1': [ 0, 1 ], '2' : [ 1, 0 ], '3': [ 0, 0 ] }, functions: {}, statements: { '1': 1, '2': 0, '3': 1, '4': 1, '5': 0, '6': 0, '7': 0 } });
                    test.done();
                },
                "should cover uppermost then paths": function (test) {
                    verifier.verify(test, [ 1, 2, 3, 1 ], 1, { lines: { 1: 1, 2: 1, 3: 0, 4: 0, 5: 0, 6: 0, 8: 0 }, branches: { '1': [ 1, 0 ], '2' : [ 0, 0 ], '3': [ 0, 0 ] }, functions: {}, statements: { '1': 1, '2': 1, '3': 0, '4': 0, '5': 0, '6': 0, '7': 0 } });
                    test.done();
                }
            },
            "including else ifs without blocks": {
                setUp: function (cb) {
                    code = [
                        'if (args[0] < args[1])',
                        '    output = args[0];',
                        'else if (args[1] < args[2])',
                        '    output = args[1];',
                        'else if (args[2] < args[3])',
                        '    output = args[2];',
                        'else',
                        '    output = args[3];'
                    ];
                    verifier = helper.verifier(__filename, code);
                    cb();
                },
                "should cover all else paths": function (test) {
                    verifier.verify(test, [ 4, 3, 2, 1 ], 1, { lines: { 1: 1, 2: 0, 3: 1, 4: 0, 5: 1, 6: 0, 8: 1 }, branches: { '1': [ 0, 1 ], '2' : [ 0, 1 ], '3': [ 0, 1 ] }, functions: {}, statements: { '1': 1, '2': 0, '3': 1, '4': 0, '5': 1, '6': 0, '7': 1 } });
                    test.done();
                },
                "should cover one then path": function (test) {
                    verifier.verify(test, [ 4, 3, 1, 2 ], 1, { lines: { 1: 1, 2: 0, 3: 1, 4: 0, 5: 1, 6: 1, 8: 0 }, branches: { '1': [ 0, 1 ], '2' : [ 0, 1 ], '3': [ 1, 0 ] }, functions: {}, statements: { '1': 1, '2': 0, '3': 1, '4': 0, '5': 1, '6': 1, '7': 0 } });
                    test.done();
                },
                "should cover upper then paths": function (test) {
                    verifier.verify(test, [ 4, 2, 3, 1 ], 2, { lines: { 1: 1, 2: 0, 3: 1, 4: 1, 5: 0, 6: 0, 8: 0 }, branches: { '1': [ 0, 1 ], '2' : [ 1, 0 ], '3': [ 0, 0 ] }, functions: {}, statements: { '1': 1, '2': 0, '3': 1, '4': 1, '5': 0, '6': 0, '7': 0 } });
                    test.done();
                },
                "should cover uppermost then paths": function (test) {
                    verifier.verify(test, [ 1, 2, 3, 1 ], 1, { lines: { 1: 1, 2: 1, 3: 0, 4: 0, 5: 0, 6: 0, 8: 0 }, branches: { '1': [ 1, 0 ], '2' : [ 0, 0 ], '3': [ 0, 0 ] }, functions: {}, statements: { '1': 1, '2': 1, '3': 0, '4': 0, '5': 0, '6': 0, '7': 0 } });
                    test.done();
                }
            },
            "including else ifs without blocks (compact)": {
                setUp: function (cb) {
                    code = [
                        'if (args[0] < args[1]) output = args[0]; else if (args[1] < args[2]) output = args[1]; else if (args[2] < args[3]) output = args[2]; else output = args[3];'
                    ];
                    verifier = helper.verifier(__filename, code);
                    cb();
                },
                "should cover all else paths": function (test) {
                    verifier.verify(test, [ 4, 3, 2, 1 ], 1, { lines: { 1: 1 }, branches: { '1': [ 0, 1 ], '2' : [ 0, 1 ], '3': [ 0, 1 ] }, functions: {}, statements: { '1': 1, '2': 0, '3': 1, '4': 0, '5': 1, '6': 0, '7': 1 } });
                    test.done();
                },
                "should cover one then path": function (test) {
                    verifier.verify(test, [ 4, 3, 1, 2 ], 1, { lines: { 1: 1 }, branches: { '1': [ 0, 1 ], '2' : [ 0, 1 ], '3': [ 1, 0 ] }, functions: {}, statements: { '1': 1, '2': 0, '3': 1, '4': 0, '5': 1, '6': 1, '7': 0 } });
                    test.done();
                },
                "should cover upper then paths": function (test) {
                    verifier.verify(test, [ 4, 2, 3, 1 ], 2, { lines: { 1: 1 }, branches: { '1': [ 0, 1 ], '2' : [ 1, 0 ], '3': [ 0, 0 ] }, functions: {}, statements: { '1': 1, '2': 0, '3': 1, '4': 1, '5': 0, '6': 0, '7': 0 } });
                    test.done();
                },
                "should cover uppermost then paths": function (test) {
                    verifier.verify(test, [ 1, 2, 3, 1 ], 1, { lines: { 1: 1 }, branches: { '1': [ 1, 0 ], '2' : [ 0, 0 ], '3': [ 0, 0 ] }, functions: {}, statements: { '1': 1, '2': 1, '3': 0, '4': 0, '5': 0, '6': 0, '7': 0 } });
                    test.done();
                }
            },
            "including else ifs without blocks (modified)": {
                setUp: function (cb) {
                    code = [
                        'output = args[3]; if (args[0] < args[1])',
                        '    output = args[0];',
                        'else if (args[1] < args[2])',
                        '    output = args[1];',
                        'else if (args[2] < args[3])',
                        '    output = args[2];'
                    ];
                    verifier = helper.verifier(__filename, code);
                    cb();
                },
                "should cover all else paths": function (test) {
                    verifier.verify(test, [ 4, 3, 2, 1 ], 1, { lines: { 1: 1, 2: 0, 3: 1, 4: 0, 5: 1, 6: 0 }, branches: { '1': [ 0, 1 ], '2' : [ 0, 1 ], '3': [ 0, 1 ] }, functions: {}, statements: { '1': 1, '2': 1, '3': 0, '4': 1, '5': 0, '6': 1, '7': 0 } });
                    test.done();
                },
                "should cover one then path": function (test) {
                    verifier.verify(test, [ 4, 3, 1, 2 ], 1, { lines: { 1: 1, 2: 0, 3: 1, 4: 0, 5: 1, 6: 1 }, branches: { '1': [ 0, 1 ], '2' : [ 0, 1 ], '3': [ 1, 0 ] }, functions: {}, statements: { '1': 1, '2': 1, '3': 0, '4': 1, '5': 0, '6': 1, '7': 1 } });
                    test.done();
                },
                "should cover upper then paths": function (test) {
                    verifier.verify(test, [ 4, 2, 3, 1 ], 2, { lines: { 1: 1, 2: 0, 3: 1, 4: 1, 5: 0, 6: 0 }, branches: { '1': [ 0, 1 ], '2' : [ 1, 0 ], '3': [ 0, 0 ] }, functions: {}, statements: { '1': 1, '2': 1, '3': 0, '4': 1, '5': 1, '6': 0, '7': 0 } });
                    test.done();
                },
                "should cover uppermost then paths": function (test) {
                    verifier.verify(test, [ 1, 2, 3, 1 ], 1, { lines: { 1: 1, 2: 1, 3: 0, 4: 0, 5: 0, 6: 0 }, branches: { '1': [ 1, 0 ], '2' : [ 0, 0 ], '3': [ 0, 0 ] }, functions: {}, statements: { '1': 1, '2': 1, '3': 1, '4': 0, '5': 0, '6': 0, '7': 0 } });
                    test.done();
                }
            }
        }
    }
};

istanbul-0.4.5/test/instrumentation/test-locations.js000066400000000000000000000102621275640326200231010ustar00rootroot00000000000000/*jslint nomen: true */
var helper = require('../helper'),
    verifier,
    code;

/*jshint maxlen: 500 */
module.exports = {
    "with a function and if branch all in one line": {
        setUp: function (cb) {
            code = [
                'function foo() { if (true) { return "bar"; } else { return "baz"; } } output = foo();'
            ];
            verifier = helper.verifier(__filename, code);
            cb();
        },
        "should report locations correctly": function (test) {
            verifier.verify(test, [], 'bar', { lines: { '1': 1 }, branches: { 1: [ 1, 0 ] }, functions: { 1: 1 }, statements: { '1': 1, '2': 1, '3': 1, '4': 0, '5': 1 }});
            var cov = verifier.getFileCoverage();
            test.deepEqual({
                "1": {
                    "name": "foo",
                        "line": 1,
                        "loc": {
                        "start": {
                            "line": 1,
                                "column": 0
                        },
                        "end": {
                            "line": 1,
                                "column": 15
                        }
                    }
                }
            }, cov.fnMap);
            test.deepEqual({
                "1": {
                    "line": 1,
                    "type": "if",
                    "locations": [
                        {
                            "start": {
                                "line": 1,
                                "column": 17
                            },
                            "end": {
                                "line": 1,
                                "column": 17
                            }
                        },
                        {
                            "start": {
                                "line": 1,
                                "column": 17
                            },
                            "end": {
                                "line": 1,
                                "column": 17
                            }
                        }
                    ]
                }
            }, cov.branchMap);
            test.deepEqual({
                "start": {
                    "line": 1,
                    "column": 0
                },
                "end": {
                    "line": 1,
                    "column": 69
                }
            }, cov.statementMap[1]);
            test.done();
        }
    },
    "with a switch statement all in one line": {
        setUp: function (cb) {
            code = [
                [
                    'output = "unknown";',
                    'switch (args[0]) {',
                    '   case "1": output = "one"; break;',
                    '   case "2": output = "two"; break;',
                    '}'
                ].join(' ')
            ];
            verifier = helper.verifier(__filename, code);
            cb();
        },
        "should report locations correctly": function (test) {
            verifier.verify(test, [ "1" ], "one", { lines: { 1: 1 }, branches: { '1': [ 1, 0 ] }, functions: {}, statements: { 1: 1, 2: 1, 3: 1, 4: 1, 5: 0, 6: 0 } });
            var cov = verifier.getFileCoverage();
            test.deepEqual({
                "1": {
                    "line": 1,
                    "type": "switch",
                    "locations": [
                        {
                            "start": {
                                "line": 1,
                                "column": 42
                            },
                            "end": {
                                "line": 1,
                                "column": 74
                            }
                        },
                        {
                            "start": {
                                "line": 1,
                                "column": 78
                            },
                            "end": {
                                "line": 1,
                                "column": 110
                            }
                        }
                    ]
                }
            }, cov.branchMap);
            test.done();
        }
    }
};

istanbul-0.4.5/test/instrumentation/test-misc.js000066400000000000000000000044771275640326200220540ustar00rootroot00000000000000/*jslint nomen: true */
var Instrumenter = require('../../lib/instrumenter'),
    esprima = require('esprima'),
    instrumenter;

module.exports = {
    "missing type attributes": {
        setUp: function (cb) {
            instrumenter = new Instrumenter();
            cb();
        },
        "barfs when a type attr is missing for a general node": function (test) {
            var ast = esprima.parse('var foo = 1;', { loc: true });
            delete ast.body[0].type;
            try {
                instrumenter.instrumentASTSync(ast);
                test.fail('instrumentation succeeded when it should not have');
            } catch (ex) {
                //ok
            }
            test.done();
        },
        "but succeeds when a property node in an AST does not have the type attr": function (test) {
            var ast = esprima.parse('var foo = { a: 1 };', { loc: true });
            delete ast.body[0].declarations[0].init.properties[0].type;
            try {
                instrumenter.instrumentASTSync(ast);
            } catch (ex) {
                test.fail('instrumentation should have succeeded but did not');
            }
            test.done();
        }
    },
    "sourcemap generation": {
        setUp: function (cb) {
            instrumenter = new Instrumenter();
            cb();
        },
        "lastSourceMap returns sourcemap (if available) for last instrumented file": function (test) {
            var instrumenterOpts = instrumenter.opts.codegenerationoptions;
            instrumenter.opts.codegenerationoptions = {
                sourceMap: 'bar',
                sourceMapWithCode: true
            };
            try {
                instrumenter.instrumentSync('var foo = { a: 1 };');
                if (typeof instrumenter.lastSourceMap() !== 'object') {
                    test.fail('sourcemap should be available');
                }

                instrumenter.opts.codegenerationoptions = instrumenterOpts;
                instrumenter.instrumentSync('var foo = { a: 1 };');
                if (instrumenter.lastSourceMap() !== null) {
                    test.fail('sourcemap should not be available');
                }
            } catch (ex) {
                test.fail('instrumentation should have succeeded but did not');
            }
            test.done();
        }
    }
};

istanbul-0.4.5/test/instrumentation/test-statement-with-hints.js000066400000000000000000000210221275640326200252020ustar00rootroot00000000000000/*jslint nomen: true */
var helper = require('../helper'),
    code,
    verifier;

module.exports = {
    "with a simple statement": {
        setUp: function (cb) {
            code = [
                '/* istanbul ignore next */',
                'var x = args[0] > 5 ? args[0] : "undef";',
                'output = x;'
            ];
            verifier = helper.verifier(__filename, code);
            cb();
        },

        "should cover line and one branch": function (test) {
            verifier.verify(test, [ 10 ], 10, { lines: { 2: 1, 3: 1 }, branches: { 1: [1, 0 ]}, functions: {}, statements: { 1: 1, 2: 1 } });
            var cov = verifier.getFileCoverage();
            test.equal(true, cov.statementMap[1].skip);
            test.equal(true, cov.branchMap[1].locations[0].skip);
            test.equal(true, cov.branchMap[1].locations[1].skip);
            test.done();
        }
    },
    "with a function declaration": {
        setUp: function (cb) {
            code = [
                '/* istanbul ignore next */',
                'function foo(x) { return x; }',
                'output = args[0];'
            ];
            verifier = helper.verifier(__filename, code);
            cb();
        },

        "should not cover function call": function (test) {
            verifier.verify(test, [ 10 ], 10, { lines: { 2: 1, 3: 1 }, branches: {}, functions: { 1: 0 }, statements: { 1: 1, 2: 0, 3: 1 } });
            var cov = verifier.getFileCoverage();
            test.equal(true, cov.statementMap[1].skip);
            test.equal(true, cov.statementMap[2].skip);
            test.equal(true, cov.fnMap[1].skip);
            test.done();
        }
    },
    "with a function expression": {
        setUp: function (cb) {
            code = [
                '/* istanbul ignore next */',
                '(function () { output = args[0]; })();'
            ];
            verifier = helper.verifier(__filename, code);
            cb();
        },

        "should cover function call": function (test) {
            verifier.verify(test, [ 10 ], 10, { lines: { 2: 1  }, branches: {}, functions: { 1: 1 }, statements: { 1: 1, 2: 1 } });
            var cov = verifier.getFileCoverage();
            test.equal(true, cov.statementMap[1].skip);
            test.equal(true, cov.statementMap[2].skip);
            test.equal(true, cov.fnMap[1].skip);
            test.done();
        }
    },
    "with a disabled switch statement": {
        setUp: function (cb) {
            code = [
                '/* istanbul ignore next */',
                'switch (args[0]) {',
                'case "1": output = 2; break;',
                'default: output = 1;',
                '}'
            ];
            verifier = helper.verifier(__filename, code);
            cb();
        },

        "should ignore all branches": function (test) {
            verifier.verify(test, [ "1" ], 2, { lines: { 2: 1, 3: 1, 4: 1  }, branches: { 1: [ 1, 0 ]},
                functions: {}, statements: { 1: 1, 2: 1, 3: 1, 4: 0 } });
            var cov = verifier.getFileCoverage();
            test.equal(true, cov.statementMap[1].skip);
            test.equal(true, cov.statementMap[2].skip);
            test.equal(true, cov.statementMap[3].skip);
            test.equal(true, cov.statementMap[4].skip);
            test.equal(true, cov.branchMap[1].locations[0].skip);
            test.equal(true, cov.branchMap[1].locations[1].skip);
            test.done();
        }
    },
    "with a disabled case statement": {
        setUp: function (cb) {
            code = [
                'switch (args[0]) {',
                '/* istanbul ignore next */',
                'case "1": output = 2; break;',
                'default: output = 1;',
                '}'
            ];
            verifier = helper.verifier(__filename, code);
            cb();
        },

        "should ignore specific case": function (test) {
            verifier.verify(test, [ "2" ], 1, { lines: { 1: 1, 3: 1, 4: 1  }, branches: { 1: [ 0, 1 ]},
                functions: {}, statements: { 1: 1, 2: 0, 3: 0, 4: 1 } });
            var cov = verifier.getFileCoverage();
            test.equal(true, cov.branchMap[1].locations[0].skip);
            test.equal(true, cov.statementMap[2].skip);
            test.equal(true, cov.statementMap[3].skip);
            test.done();
        }
    },

    "with disabled conditional statement": {
        setUp: function (cb) {
            code = [
                '/* istanbul ignore next */',
                'output = args[0] === 1 ? 1: 0;'
            ];
            verifier = helper.verifier(__filename, code);
            cb();
        },

        "should ignore conditions": function (test) {
            verifier.verify(test, [ 2 ], 0, { lines: { 2: 1  }, branches: { 1: [ 0, 1 ]},
                functions: {}, statements: { 1: 1 } });
            var cov = verifier.getFileCoverage();
            test.equal(true, cov.branchMap[1].locations[0].skip);
            test.equal(true, cov.branchMap[1].locations[1].skip);
            test.equal(true, cov.statementMap[1].skip);
            test.done();
        }

    },
    "with disabled condition": {
        setUp: function (cb) {
            code = [
                'output = args[0] === 1 ? /* istanbul ignore next */ 1 : 0;'
            ];
            verifier = helper.verifier(__filename, code);
            cb();
        },

        "should ignore conditions": function (test) {
            verifier.verify(test, [ 2 ], 0, { lines: { 1: 1  }, branches: { 1: [ 0, 1 ]},
                functions: {}, statements: { 1: 1 } });
            var cov = verifier.getFileCoverage();
            test.equal(true, cov.branchMap[1].locations[0].skip);
            test.equal(undefined, cov.branchMap[1].locations[1].skip);
            test.equal(undefined, cov.statementMap[1].skip);
            test.done();
        }
    },

    "with a simple logical expression": {
        setUp: function (cb) {
            code = [
                'if (args[0] === 1  || /* istanbul ignore next */ args[0] === 2 ) {',
                '   output = args[0] + 10;',
                '} else {',
                '   output = 20;',
                '}'
            ];
            verifier = helper.verifier(__filename, code);
            cb();
        },

        "should ignore conditions": function (test) {
            verifier.verify(test, [ 1 ], 11, { lines: { 1: 1, 2: 1, 4: 0 }, branches: { 1: [ 1, 0 ], 2: [ 1, 0 ] },
                functions: {}, statements: { 1: 1, 2: 1, 3: 0 } });
            var cov = verifier.getFileCoverage();
            test.equal(true, cov.branchMap[2].locations[1].skip);
            test.done();
        }
    },

    "with a slightly complicated logical expression": {
        setUp: function (cb) {
            code = [
                'if (args[0] === 1  || /* istanbul ignore next */ (args[0] === 2  || args[0] === 3)) {',
                '   output = args[0] + 10;',
                '} else {',
                '   output = 20;',
                '}'
            ];
            verifier = helper.verifier(__filename, code);
            cb();
        },

        "should ignore conditions": function (test) {
            verifier.verify(test, [ 1 ], 11, { lines: { 1: 1, 2: 1, 4: 0 }, branches: { 1: [ 1, 0 ], 2: [ 1, 0, 0 ] },
                functions: {}, statements: { 1: 1, 2: 1, 3: 0 } });
            var cov = verifier.getFileCoverage();
            test.equal(undefined, cov.branchMap[2].locations[0].skip);
            test.equal(true, cov.branchMap[2].locations[1].skip);
            test.equal(true, cov.branchMap[2].locations[2].skip);
            test.done();
        }
    },

    "with a complicated logical expression involving implied operator precedence": {
        setUp: function (cb) {
            code = [
                'if (args[0] === 1  || /* istanbul ignore next */ args[0] === 2  && args[1] === 2) {',
                '   output = args[0] + 10;',
                '} else {',
                '   output = 20;',
                '}'
            ];
            verifier = helper.verifier(__filename, code);
            cb();
        },

        "should ignore conditions": function (test) {
            verifier.verify(test, [ 1, 1 ], 11, { lines: { 1: 1, 2: 1, 4: 0 }, branches: { 1: [ 1, 0 ], 2: [ 1, 0, 0 ] },
                functions: {}, statements: { 1: 1, 2: 1, 3: 0 } });
            var cov = verifier.getFileCoverage();
            test.equal(undefined, cov.branchMap[2].locations[0].skip);
            test.equal(true, cov.branchMap[2].locations[1].skip);
            test.equal(true, cov.branchMap[2].locations[2].skip);
            test.done();
        }
    }
};
istanbul-0.4.5/test/instrumentation/test-statement.js000066400000000000000000000207461275640326200231220ustar00rootroot00000000000000/*jslint nomen: true */
var helper = require('../helper'),
    Instrumenter = require('../../lib/instrumenter'),
    code,
    verifier;

module.exports = {
    "with a simple statement": {
        setUp: function (cb) {
            code = [
                'var x = args[0] > 5 ? args[0] : "undef";',
                'output = x;'
            ];
            verifier = helper.verifier(__filename, code);
            cb();
        },

        "should cover line and one branch": function (test) {
            verifier.verify(test, [ 10 ], 10, { lines: { 1: 1, 2: 1 }, branches: { 1: [1, 0 ]}, functions: {}, statements: { 1: 1, 2: 1 } });
            test.done();
        },
        "should cover line and other branch": function (test) {
            verifier.verify(test, [ 1 ], "undef", { lines: { 1: 1, 2: 1 }, branches: { 1: [ 0, 1 ]}, functions: {}, statements: { 1: 1, 2: 1 } });
            test.done();
        }
    },
    "with a metaproperty": {
        setUp: function (cb) {
            code = [
                'class FooClass {',
                '   constructor() {',
                '       if (new.target === FooClass) {',
                '           throw new Error(\'Cannot instanciate directly.\');',
                '       }',
                '   }',
                '}'
            ];
            verifier = helper.verifier(__filename, code);
            cb();
        },
        "should be able to parse": function (test) {
            // Do we need to test coverage here really? We're just checking
            // that a new type of statement is parsable, the if-else logic
            // is already covered in statement-if.
            test.done();
        }
    },
    "with no filename": {
        setUp: function (cb) {
            code = [
                'output = args[0];'
            ];
            verifier = helper.verifier(null, code, { debug: true, walkDebug: true });
            cb();
        },
        "should not barf in setup": function (test) {
            verifier.verify(test, [ 1 ], 1, { lines: { 1: 1 }, branches: {}, functions: {}, statements: { 1: 1 } });
            test.done();
        }
    },
    "with a windows style file path": {
        setUp: function (cb) {
            code = [
                'var x = args[0] > 5 ? args[0] : "undef";',
                'output = x;'
            ];
            verifier = helper.verifier("c:\\a\\b\\c\\d\\e.js", code);
            cb();
        },
        "should have correct key in coverage variable": function (test) {
            verifier.verify(test, [ 1 ], "undef", { lines: { 1: 1, 2: 1 }, branches: { 1: [ 0, 1 ]}, functions: {}, statements: { 1: 1, 2: 1 } });
            var coverage = verifier.getCoverage(),
                key = Object.keys(coverage)[0];
            test.equals("c:\\a\\b\\c\\d\\e.js", key);
            test.done();
        }

    },
    "with junk code": {
        setUp: function (cb) {
            code = [
                'output = args[0] : 1 : 2;'
            ];
            verifier = helper.verifier(null, code, { debug: true, walkDebug: true });
            cb();
        },
        "should have verification errors": function (test) {
            verifier.verifyError(test);
            test.done();
        }
    },
    "with code that is not a string": {
        "should have verification errors": function (test) {
            test.throws(function () {
                var instrumenter = new Instrumenter();
                instrumenter.instrumentSync({}, 'foo.js');
            }, Error, 'Code must be a string');
            test.done();
        }
    },
    "with shebang code": {
        setUp: function (cb) {
            code = [
                '#!/usr/bin/env node',
                'var x = args[0] > 5 ? args[0] : "undef";',
                'output = x;'
            ];
            verifier = helper.verifier(__filename, code);
            cb();
        },

        "should cover line and one branch": function (test) {
            verifier.verify(test, [ 10 ], 10, { lines: { 2: 1, 3: 1 }, branches: { 1: [1, 0 ]}, functions: {}, statements: { 1: 1, 2: 1 } });
            test.done();
        },
        "should cover line and other branch": function (test) {
            verifier.verify(test, [ 1 ], "undef", { lines: { 2: 1, 3: 1 }, branches: { 1: [ 0, 1 ]}, functions: {}, statements: { 1: 1, 2: 1 } });
            test.done();
        }
    },
    "with source code packed in": {
        setUp: function (cb) {
            code = [
                'var x = args[0] > 5 ? args[0] : "undef";',
                'output = x;'
            ];
            verifier = helper.verifier(__filename, code, { embedSource: true, coverageVariable: null });
            cb();
        },
        "coverage should have code packed in": function (test) {
            verifier.verifyNoError(test);
            verifier.verify(test, [ 10 ], 10, { lines: { 1: 1, 2: 1 }, branches: { 1: [1, 0 ]}, functions: {}, statements: { 1: 1, 2: 1 } });
            var cov = verifier.getCoverage(),
                fileCov = cov[Object.keys(cov)[0]];
            test.ok(fileCov.code.length > 1);
            test.ok(fileCov.code[1] === 'output = x;');
            test.done();
        },
        "code packed in does not have \\r characters": function (test) {
            code = [
                'var x = args[0] > 5 ? args[0] : "undef";\r',
                'output = x;\r'
            ];
            verifier = helper.verifier(__filename + '2', code, { embedSource: true, coverageVariable: null });
            verifier.verifyNoError(test);
            verifier.verify(test, [ 10 ], 10, { lines: { 1: 1, 2: 1 }, branches: { 1: [1, 0 ]}, functions: {}, statements: { 1: 1, 2: 1 } });
            var cov = verifier.getCoverage(),
                fileCov = cov[Object.keys(cov)[0]];
            test.ok(fileCov.code.length > 1);
            test.ok(fileCov.code[1] === 'output = x;');
            test.done();
        }
    },
    "with code having a return statement on mainline": {
        setUp: function (cb) {
            code = [
                'return 10;'
            ];
            verifier = helper.verifier(__filename, code);
            cb();
        },

        "should pass coverage": function (test) {
            verifier.verifyNoError(test);
            test.done();
        }
    },
    "with code having a return statement on mainline and no autowrap": {
        setUp: function (cb) {
            code = [
                'return 10;'
            ];
            verifier = helper.verifier(__filename, code, { noAutoWrap: true });
            cb();
        },

        "should fail coverage": function (test) {
            verifier.verifyError(test);
            test.done();
        }
    },
    "with no mainline returns and no autowrap": {
        setUp: function (cb) {
            code = [
                '#!/usr/bin/env node',
                'var x = args[0] > 5 ? args[0] : "undef";',
                'output = x;'
            ];
            verifier = helper.verifier(__filename, code, { noAutoWrap: true });
            cb();
        },

        "should cover line and one branch": function (test) {
            verifier.verify(test, [ 10 ], 10, { lines: { 2: 1, 3: 1 }, branches: { 1: [1, 0 ]}, functions: {}, statements: { 1: 1, 2: 1 } });
            test.done();
        },
        "should cover line and other branch": function (test) {
            verifier.verify(test, [ 1 ], "undef", { lines: { 2: 1, 3: 1 }, branches: { 1: [ 0, 1 ]}, functions: {}, statements: { 1: 1, 2: 1 } });
            test.done();
        }
    },
    "with code having comments": {
        setUp: function (cb) {
            code = [
                '#!/usr/bin/env node',
                'var x = args[0] > 5 ? args[0] : "undef";',
                '/* set the output */',
                'output = x;'
            ];
            verifier = helper.verifier(__filename, code, { noAutoWrap: true, preserveComments: true });
            cb();
        },

        "should preserve comments in generated code": function (test) {
            test.ok(verifier.generatedCode.match(/\/\* set the output \*\//));
            test.done();
        },

        "should cover line and one branch": function (test) {
            verifier.verify(test, [ 10 ], 10, { lines: { 2: 1, 4: 1 }, branches: { 1: [1, 0 ]}, functions: {}, statements: { 1: 1, 2: 1 } });
            test.done();
        },
        "should cover line and other branch": function (test) {
            verifier.verify(test, [ 1 ], "undef", { lines: { 2: 1, 4: 1 }, branches: { 1: [ 0, 1 ]}, functions: {}, statements: { 1: 1, 2: 1 } });
            test.done();
        }
    }
};

istanbul-0.4.5/test/instrumentation/test-strict.js000066400000000000000000000100451275640326200224150ustar00rootroot00000000000000/*jslint nomen: true */
var helper = require('../helper'),
    code,
    verifier;

module.exports = {
    "with a function expression that uses strict": {
        setUp: function (cb) {
            code = [
                '(function () {',
                '    "use strict";',
                '    var x = Object.freeze({ foo: 1 });',
                '    try {',
                '        x.foo = 2;',
                '        output = "fail";',
                '    } catch (ex) {',
                '        output = "pass";',
                '    }',
                '}());'
            ];
            verifier = helper.verifier(__filename, code);
            cb();
        },

        "should cover one statement less": function (test) {
            verifier.verify(test, [], "pass", {
                statements: { 1: 1, 2: 1, 3: 1, 4: 1, 5: 0, 6: 1 },
                lines: { 1: 1, 3: 1, 4: 1, 5: 1, 6: 0, 8: 1 },
                branches: {},
                functions: { 1: 1}
            });
            test.done();
        }
    },
    "with a function declaration that uses strict": {
        setUp: function (cb) {
            code = [
                'function foo() {',
                '    "use strict";',
                '    var x = Object.freeze({ foo: 1 });',
                '    try {',
                '        x.foo = 2;',
                '        output = "fail";',
                '    } catch (ex) {',
                '        output = "pass";',
                '    }',
                '}',
                'foo();'
            ];
            verifier = helper.verifier(__filename, code);
            cb();
        },

        "should cover one statement less": function (test) {
            verifier.verify(test, [], "pass", {
                statements: { 1: 1, 2: 1, 3: 1, 4: 1, 5: 0, 6: 1, 7: 1 },
                lines: { 1: 1, 3: 1, 4: 1, 5: 1, 6: 0, 8: 1, 11: 1 },
                branches: {},
                functions: { 1: 1}
            });
            test.done();
        }
    },
    "with a function declaration that looks like strict but is not": {
        setUp: function (cb) {
            code = [
                'function foo() {',
                '    1;',
                '    "use strict";',
                '    var x = Object.freeze({ foo: 1 });',
                '    try {',
                '        x.foo = 2;',
                '        output = "fail";',
                '    } catch (ex) {',
                '        output = "pass";',
                '    }',
                '}',
                'foo();'
            ];
            verifier = helper.verifier(__filename, code);
            cb();
        },

        "should cover all statements as usual": function (test) {
            verifier.verify(test, [], "fail", {
                statements: { 1: 1, 2: 1, 3: 1, 4: 1, 5: 1, 6: 1, 7: 1, 8: 0, 9: 1 },
                lines: { 1: 1, 2: 1, 3: 1, 4: 1, 5: 1, 6: 1, 7: 1, 9: 0, 12: 1 },
                branches: {},
                functions: { 1: 1}
            });
            test.done();
        }
    },
    "with a file-level strict declaration": {
        setUp: function (cb) {
            code = [
                '    "use strict";',
                '    var x = Object.freeze({ foo: 1 });',
                '    try {',
                '        x.foo = 2;',
                '        output = "fail";',
                '    } catch (ex) {',
                '        output = "pass";',
                '    }'
            ];
            verifier = helper.verifier(__filename, code);
            cb();
        },

        "should correctly interpret the strict statement": function (test) {
            // use strict semantics still do not work since it is not top-level but called from vm.runInThisContext
            verifier.verify(test, [], "fail", {
                //however statements and lines should line up
                statements: { 1: 1, 2: 1, 3: 1, 4: 1, 5: 0 },
                lines: { 2: 1, 3: 1, 4: 1, 5: 1, 7: 0 },
                branches: {},
                functions: {}
            });
            test.done();
        }
    }
};

istanbul-0.4.5/test/instrumentation/test-switch.js000066400000000000000000000172101275640326200224070ustar00rootroot00000000000000/*jslint nomen: true */
var helper = require('../helper'),
    code,
    verifier;

/*jshint maxlen: 500 */
module.exports = {
    "with an empty switch": {
        setUp: function (cb) {
            code = [
                'output = "unknown";',
                'switch (args[0]) {',
                '}'
            ];
            verifier = helper.verifier(__filename, code);
            cb(null);
        },
        "should not barf in any way": function (test) {
            verifier.verify(test, [ "1" ], "unknown", { lines: { 1: 1, 2: 1 }, branches: {}, functions: {}, statements: { 1: 1, 2: 1 } });
            test.done();
        }
    },
    "with a simple switch": {
        "and 2 cases without a default": {
            setUp: function (cb) {
                code = [
                    'output = "unknown";',
                    'switch (args[0]) {',
                    '   case "1": output = "one"; break;',
                    '   case "2": output = "two"; break;',
                    '}'
                ];
                verifier = helper.verifier(__filename, code);
                cb(null);
            },
            "should cover one path": function (test) {
                verifier.verify(test, [ "1" ], "one", { lines: { 1: 1, 2: 1, 3: 1, 4: 0 }, branches: { '1': [ 1, 0 ] }, functions: {}, statements: { 1: 1, 2: 1, 3: 1, 4: 1, 5: 0, 6: 0 } });
                test.done();
            },
            "should cover two path": function (test) {
                verifier.verify(test, [ "2" ], "two", { lines: { 1: 1, 2: 1, 3: 0, 4: 1 }, branches: { '1': [ 0, 1 ] }, functions: {}, statements: { 1: 1, 2: 1, 3: 0, 4: 0, 5: 1, 6: 1 } });
                test.done();
            },
            "should cover unknown path": function (test) {
                verifier.verify(test, [ "3" ], "unknown", { lines: { 1: 1, 2: 1, 3: 0, 4: 0 }, branches: { '1': [ 0, 0 ] }, functions: {}, statements: { 1: 1, 2: 1, 3: 0, 4: 0, 5: 0, 6: 0 } });
                test.done();
            }
        },
        "and 2 cases with a default": {
            setUp: function (cb) {
                code = [
                    'output = "unknown";',
                    'switch (args[0]) {',
                    '   case "1": output = "one"; break;',
                    '   case "2": output = "two"; break;',
                    '   default: output = "three";',
                    '}'
                ];
                verifier = helper.verifier(__filename, code);
                cb(null);
            },
            "should cover one path": function (test) {
                verifier.verify(test, [ "1" ], "one", { lines: { 1: 1, 2: 1, 3: 1, 4: 0, 5: 0 }, branches: { '1': [ 1, 0, 0 ] }, functions: {}, statements: { '1': 1, '2': 1, '3': 1, '4': 1, '5': 0, '6': 0, '7': 0 } });
                test.done();
            },
            "should cover two path": function (test) {
                verifier.verify(test, [ "2" ], "two", { lines: { 1: 1, 2: 1, 3: 0, 4: 1, 5: 0 }, branches: { '1': [ 0, 1, 0 ] }, functions: {}, statements: { '1': 1, '2': 1, '3': 0, '4': 0, '5': 1, '6': 1, '7': 0 } });
                test.done();
            },
            "should cover unknown path": function (test) {
                verifier.verify(test, [ "4" ], "three", { lines: { 1: 1, 2: 1, 3: 0, 4: 0, 5: 1 }, branches: { '1': [ 0, 0, 1 ] }, functions: {}, statements: { '1': 1, '2': 1, '3': 0, '4': 0, '5': 0, '6': 0, '7': 1 } });
                test.done();
            }
        },
        "and 2 cases with a default everything squeezed on one line": {
            setUp: function (cb) {
                code = [
                    'output = "unknown";',
                    'switch (args[0]) {' +
                        '   case "1": output = "one"; break;' +
                        '   case "2": output = "two"; break;' +
                        '   default: output = "three";' +
                        '}'
                ];
                verifier = helper.verifier(__filename, code);
                cb(null);
            },
            "should cover one path": function (test) {
                verifier.verify(test, [ "1" ], "one", { lines: { 1: 1, 2: 1 }, branches: { '1': [ 1, 0, 0 ] }, functions: {}, statements: { '1': 1, '2': 1, '3': 1, '4': 1, '5': 0, '6': 0, '7': 0 } });
                test.done();
            },
            "should cover two path": function (test) {
                verifier.verify(test, [ "2" ], "two", { lines: { 1: 1, 2: 1 }, branches: { '1': [ 0, 1, 0 ] }, functions: {}, statements: { '1': 1, '2': 1, '3': 0, '4': 0, '5': 1, '6': 1, '7': 0 } });
                test.done();
            },
            "should cover unknown path": function (test) {
                verifier.verify(test, [ "4" ], "three", { lines: { 1: 1, 2: 1  }, branches: { '1': [ 0, 0, 1 ] }, functions: {}, statements: { '1': 1, '2': 1, '3': 0, '4': 0, '5': 0, '6': 0, '7': 1 } });
                test.done();
            }
        },
        "and 2 cases with a default and fall-thru": {
            setUp: function (cb) {
                code = [
                    'output = "";',
                    'switch (args[0]) {',
                    '   case "1": output += "one";',
                    '   case "2": output += "two";',
                    '   default: output += "three";',
                    '}'
                ];
                verifier = helper.verifier(__filename, code);
                cb(null);
            },
            "should cover one path": function (test) {
                verifier.verify(test, [ "1" ], "onetwothree", { lines: { 1: 1, 2: 1, 3: 1, 4: 1, 5: 1 }, branches: { '1': [ 1, 1, 1 ] }, functions: {}, statements: { '1': 1, '2': 1, '3': 1, '4': 1, '5': 1 } });
                test.done();
            },
            "should cover two path": function (test) {
                verifier.verify(test, [ "2" ], "twothree", { lines: { 1: 1, 2: 1, 3: 0, 4: 1, 5: 1 }, branches: { '1': [ 0, 1, 1 ] }, functions: {}, statements: { '1': 1, '2': 1, '3': 0, '4': 1, '5': 1 } });
                test.done();
            },
            "should cover unknown path": function (test) {
                verifier.verify(test, [ "4" ], "three", { lines: { 1: 1, 2: 1, 3: 0, 4: 0, 5: 1 }, branches: { '1': [ 0, 0, 1 ] }, functions: {}, statements: { '1': 1, '2': 1, '3': 0, '4': 0, '5': 1 } });
                test.done();
            }
        },
        "and 2 cases with a default and fall-thru all on one line": {
            setUp: function (cb) {
                code = [
                    'output = "";',
                    'switch (args[0]) {' +
                        '   case "1": output += "one";' +
                        '   case "2": output += "two";' +
                        '   default: output += "three";' +
                        '}'
                ];
                verifier = helper.verifier(__filename, code);
                cb(null);
            },
            "should cover one path": function (test) {
                verifier.verify(test, [ "1" ], "onetwothree", { lines: { 1: 1, 2: 1 }, branches: { '1': [ 1, 1, 1 ] }, functions: {}, statements: { '1': 1, '2': 1, '3': 1, '4': 1, '5': 1 } });
                test.done();
            },
            "should cover two path": function (test) {
                verifier.verify(test, [ "2" ], "twothree", { lines: { 1: 1, 2: 1 }, branches: { '1': [ 0, 1, 1 ] }, functions: {}, statements: { '1': 1, '2': 1, '3': 0, '4': 1, '5': 1 } });
                test.done();
            },
            "should cover unknown path": function (test) {
                verifier.verify(test, [ "4" ], "three", { lines: { 1: 1, 2: 1 }, branches: { '1': [ 0, 0, 1 ] }, functions: {}, statements: { '1': 1, '2': 1, '3': 0, '4': 0, '5': 1 } });
                test.done();
            }
        }
    }
};

istanbul-0.4.5/test/instrumentation/test-try.js000066400000000000000000000022431275640326200217240ustar00rootroot00000000000000/*jslint nomen: true */
var helper = require('../helper'),
    code,
    verifier;

/*jshint maxlen: 500 */
module.exports = {
    "with an simple try catch": {
        setUp: function (cb) {
            code = [
                'try {',
                '   if (args[0] === "X") { throw "foo"; }',
                '   output = args[0];',
                '} catch (ex) {',
                '   output="Y";',
                '} finally {',
                '   output += 1;',
                '}'
            ];
            verifier = helper.verifier(__filename, code);
            cb();
        },

        "should cover happy path correctly": function (test) {
            verifier.verify(test, [1], 2, { lines: { '1': 1, '2': 1, 3: 1, 5: 0, 7: 1 }, branches: { 1: [ 0, 1 ] }, functions: {}, statements: { '1': 1, '2': 1, '3': 0, 4: 1, 5: 0, 6: 1} });
            test.done();
        },
        "should cover sad path correctly": function (test) {
            verifier.verify(test, ['X'], 'Y1', { lines: { '1': 1, '2': 1, 3: 0, 5: 1, 7: 1 }, branches: { 1: [ 1, 0 ] }, functions: {}, statements: { '1': 1, '2': 1, '3': 1, 4: 0, 5: 1, 6: 1} });
            test.done();
        }
    }
};

istanbul-0.4.5/test/instrumentation/test-while.js000066400000000000000000000073501275640326200222220ustar00rootroot00000000000000/*jslint nomen: true */
var helper = require('../helper'),
    code,
    verifier;

/*jshint maxlen: 500 */
module.exports = {
    "with a simple while": {
        setUp: function (cb) {
            code = [
                'var x = args[0], i=0;',
                'while (i < x) i++;',
                'output = i;'
            ];
            verifier = helper.verifier(__filename, code);
            cb();
        },

        "should cover loop exactly once": function (test) {
            verifier.verify(test, [ 1 ], 1, { lines: { 1: 1, 2: 1, 3: 1 }, branches: {}, functions: {}, statements: { '1': 1, '2': 1, '3': 1, '4': 1 } });
            test.done();
        },
        "should cover loop multiple times": function (test) {
            verifier.verify(test, [ 10 ], 10, { lines: { 1: 1, 2: 10, 3: 1 }, branches: {}, functions: {}, statements: { '1': 1, '2': 1, '3': 10, '4': 1 } });
            test.done();
        }
    },
    "with a simple while - statement on a different line": {
        setUp: function (cb) {
            code = [
                'var x = args[0], i=0;',
                'while (i < x)',
                '   i++;',
                'output = i;'
            ];
            verifier = helper.verifier(__filename, code);
            cb();
        },

        "should cover loop one time": function (test) {
            verifier.verify(test, [ 10 ], 10, { lines: { 1: 1, 2: 1, 3: 10, 4: 1 }, branches: {}, functions: {}, statements: { '1': 1, '2': 1, '3': 10, '4': 1 } });
            test.done();
        },

        "should not cover loop at all": function (test) {
            verifier.verify(test, [ -1 ], 0, { lines: { 1: 1, 2: 1, 3: 0, 4: 1 }, branches: {}, functions: {}, statements: { '1': 1, '2': 1, '3': 0, '4': 1 } });
            test.done();
        }
    },
    "with a simple while in block": {
        setUp: function (cb) {
            code = [
                'var x = args[0], i=0;',
                'while (i < x) { i++; }',
                'output = i;'
            ];
            verifier = helper.verifier(__filename, code);
            cb();
        },

        "should cover multi-loop exactly once": function (test) {
            verifier.verify(test, [ 10 ], 10, { lines: { 1: 1, 2: 10, 3: 1 }, branches: {}, functions: {}, statements: { '1': 1, '2': 1, '3': 10, '4': 1 } });
            test.done();
        }
    },
    "with a labeled while": {
        setUp: function (cb) {
            code = [
                'var x = args[0], i=0, j=0, output = 0;',
                'outer:',
                '   while (i++ < x) {',
                '       j =0;',
                '       while (j++ < i) {',
                '           output++;',
                '           if (j === 2) continue outer;',
                '       }',
                '   }'
            ];
            verifier = helper.verifier(__filename, code);
            cb();
        },

        "should provide line/branch coverage when all branches exercised": function (test) {
            verifier.verify(test, [ 10 ], 19, {
                lines: { '1': 1, '2': 1, '3': 1, '4': 10, '5': 10, '6': 19, '7': 19 },
                branches: { '1': [ 9, 10 ] },
                functions: {},
                statements: { '1': 1, '2': 1, '3': 1, '4': 10, '5': 10, '6': 19, '7': 19, '8': 9 }
            });
            test.done();
        },

        "should provide line/branch coverage when nothing exercised": function (test) {
            verifier.verify(test, [ -1 ], 0, {
                lines: { '1': 1, '2': 1, '3': 1, '4': 0, '5': 0, '6': 0, '7': 0 },
                branches: { '1': [ 0, 0 ] },
                functions: {},
                statements: { '1': 1, '2': 1, '3': 1, '4': 0, '5': 0, '6': 0, '7': 0, '8': 0 }
            });
            test.done();
        }
    }
};

istanbul-0.4.5/test/instrumentation/test-with.js000066400000000000000000000021171275640326200220610ustar00rootroot00000000000000/*jslint nomen: true */
var helper = require('../helper'),
    code,
    verifier;

/*jshint maxlen: 500 */
module.exports = {
    "with a with statement - no blocks": {
        setUp: function (cb) {
            code = [
                'with (Math) output = abs(args[0]);'
            ];
            verifier = helper.verifier(__filename, code);
            cb();
        },

        "should cover everything correctly": function (test) {
            verifier.verify(test, [ -1 ], 1, { lines: { 1: 1 }, branches: {}, functions: {}, statements: { '1': 1, '2': 1 } });
            test.done();
        }
    },
    "with a with statement - in a block": {
        setUp: function (cb) {
            code = [
                'with (Math) { output = abs(args[0]); }'
            ];
            verifier = helper.verifier(__filename, code);
            cb();
        },

        "should cover everything correctly": function (test) {
            verifier.verify(test, [ -1 ], 1, { lines: { 1: 1 }, branches: {}, functions: {}, statements: { '1': 1, '2': 1 } });
            test.done();
        }
    }
};

istanbul-0.4.5/test/loader.js000066400000000000000000000021421275640326200161320ustar00rootroot00000000000000/*jslint nomen: true, regexp: true */
var path = require('path'),
    fs = require('fs');

require('../lib/register-plugins');

function loadDirTests(dir, pat) {
    var files = fs.readdirSync(path.resolve(__dirname, dir))
        .filter(function (f) {
            //return f.indexOf('browser') > 0;
            return pat.exec(f) && f.indexOf('.js') > 0 && f.indexOf('client.js') < 0 && f !== 'server.js';
        })
        .map(function (f) { return path.relative(process.cwd(), path.resolve(__dirname, dir, f)); });
    return files;
}

function runTests(pat, reporter, opts, callback) {
    var files,
        files2,
        files3,
        files4;

    pat = pat || /(.*)+\.js$/;
    if (typeof pat === 'string') { pat = new RegExp(pat); }
    files = loadDirTests('instrumentation', pat);
    files2 = loadDirTests('other', pat);
    files3 = loadDirTests('cli', pat);
    files4 = loadDirTests('browser', pat);
    files.push.apply(files, files2);
    files.push.apply(files, files3);
    files.push.apply(files, files4);
    reporter.run(files, opts, callback);
}

module.exports = {
    runTests: runTests
};

istanbul-0.4.5/test/other/000077500000000000000000000000001275640326200154505ustar00rootroot00000000000000istanbul-0.4.5/test/other/config-data/000077500000000000000000000000001275640326200176245ustar00rootroot00000000000000istanbul-0.4.5/test/other/config-data/.istanbul.yml000066400000000000000000000001511275640326200222430ustar00rootroot00000000000000instrumentation:
  compact: false
  save-baseline: true
reporting:
  reports:
    - lcov
    - cobertura
istanbul-0.4.5/test/other/config-data/cfg.json000066400000000000000000000002021275640326200212500ustar00rootroot00000000000000{
    "instrumentation": {
        "save-baseline": true
    },
    "hooks": {
        "post-require-hook": "yui-istanbul"
    }
}istanbul-0.4.5/test/other/data-complete-copy/000077500000000000000000000000001275640326200211375ustar00rootroot00000000000000istanbul-0.4.5/test/other/data-complete-copy/baz.js000066400000000000000000000000751275640326200222530ustar00rootroot00000000000000module.exports = {
    baz: function () { return 'baz'; }
};
istanbul-0.4.5/test/other/data-complete-copy/fixture.xml000066400000000000000000000000101275640326200233360ustar00rootroot00000000000000
istanbul-0.4.5/test/other/data-complete-copy/foo.js000066400000000000000000000000761275640326200222630ustar00rootroot00000000000000module.exports = {
    foo: function () { return 'foo'; }
};

istanbul-0.4.5/test/other/data-complete-copy/myfile1000066400000000000000000000000001275640326200224160ustar00rootroot00000000000000istanbul-0.4.5/test/other/data-complete-copy/myfile2000066400000000000000000000000151275640326200224250ustar00rootroot00000000000000some content
istanbul-0.4.5/test/other/data-complete-copy/subdir/000077500000000000000000000000001275640326200224275ustar00rootroot00000000000000istanbul-0.4.5/test/other/data-complete-copy/subdir/x.css000066400000000000000000000000421275640326200234040ustar00rootroot00000000000000body {
    font-family: verdana;
}istanbul-0.4.5/test/other/data/000077500000000000000000000000001275640326200163615ustar00rootroot00000000000000istanbul-0.4.5/test/other/data/bar.es6000066400000000000000000000000761275640326200175470ustar00rootroot00000000000000module.exports = {
    bar: function () { return 'bar'; }
};

istanbul-0.4.5/test/other/data/baz.js000066400000000000000000000000751275640326200174750ustar00rootroot00000000000000module.exports = {
    baz: function () { return 'baz'; }
};
istanbul-0.4.5/test/other/data/foo.js000066400000000000000000000000761275640326200175050ustar00rootroot00000000000000module.exports = {
    foo: function () { return 'foo'; }
};

istanbul-0.4.5/test/other/data/matcher/000077500000000000000000000000001275640326200200045ustar00rootroot00000000000000istanbul-0.4.5/test/other/data/matcher/general/000077500000000000000000000000001275640326200214215ustar00rootroot00000000000000istanbul-0.4.5/test/other/data/matcher/general/general.js000066400000000000000000000000561275640326200233750ustar00rootroot00000000000000module.exports = function () { return 42; };

istanbul-0.4.5/test/other/data/matcher/general/node_modules/000077500000000000000000000000001275640326200240765ustar00rootroot00000000000000istanbul-0.4.5/test/other/data/matcher/general/node_modules/mod-file.js000066400000000000000000000000561275640326200261310ustar00rootroot00000000000000module.exports = function () { return 42; };

istanbul-0.4.5/test/other/data/matcher/lib/000077500000000000000000000000001275640326200205525ustar00rootroot00000000000000istanbul-0.4.5/test/other/data/matcher/lib/lib-top.js000066400000000000000000000000561275640326200224570ustar00rootroot00000000000000module.exports = function () { return 42; };

istanbul-0.4.5/test/other/data/matcher/node_modules/000077500000000000000000000000001275640326200224615ustar00rootroot00000000000000istanbul-0.4.5/test/other/data/matcher/node_modules/dep/000077500000000000000000000000001275640326200232315ustar00rootroot00000000000000istanbul-0.4.5/test/other/data/matcher/node_modules/dep/mod-file.js000066400000000000000000000000561275640326200252640ustar00rootroot00000000000000module.exports = function () { return 42; };

istanbul-0.4.5/test/other/data/matcher/node_modules/mod-file.js000066400000000000000000000000561275640326200245140ustar00rootroot00000000000000module.exports = function () { return 42; };

istanbul-0.4.5/test/other/data/matcher/top.js000066400000000000000000000000561275640326200211450ustar00rootroot00000000000000module.exports = function () { return 32; };

istanbul-0.4.5/test/other/test-collector.js000066400000000000000000000067411275640326200207610ustar00rootroot00000000000000var Collector = require('../../lib/collector'),
    utils = require('../../lib/object-utils'),
    coverageObj,
    coverageObj2,
    collector;

module.exports = {
    "tearDown": function (cb) { collector.dispose(); cb(); },
    "with a single coverage object": {
        setUp: function (cb) {
            var loc = { start: { line: 1, column: 5 }, end: { line: 1, column: 10 } };
            coverageObj = {
                foo: {
                    statementMap: { 1: loc, 2: loc, 3: loc },
                    s: { 1: 0, 2: 3, 3: 5 },
                    b: { 1: [5, 7], 2: [3, 0], 3: [0, 5] },
                    f: { 1: 4, 2: 0, 3: 1},
                    path: 'foo'
                },
                bar: {
                    statementMap: { 1: loc, 2: loc, 3: loc, 4: loc },
                    s: { 1: 0, 2: 0, 3: 17, 4: 3 },
                    b: { 1: [ 0, 8, 9 ], 2: [4, 1], 3: [0, 5] },
                    f: { 1: 7, 2: 78, 3: 9, 4: 0 },
                    path: 'bar'
                }
            };
            collector = new Collector();
            cb();
        },
        "collector should not molest any stats": function (test) {
            collector.add(JSON.parse(JSON.stringify(coverageObj)));
            utils.addDerivedInfo(coverageObj);
            test.deepEqual(['foo', 'bar'], collector.files());
            test.deepEqual(coverageObj, collector.getFinalCoverage());
            test.deepEqual(coverageObj.foo, collector.fileCoverageFor('foo'));
            test.deepEqual(coverageObj.bar, collector.fileCoverageFor('bar'));
            test.done();
        }
    },
    "with multiple coverage objects": {
        setUp: function (cb) {
            var loc = { start: { line: 1, column: 5 }, end: { line: 1, column: 10 } };
            coverageObj = {
                foo: {
                    s: { 1: 0, 2: 3, 3: 5 },
                    statementMap: { 1: loc, 2: loc, 3: loc },
                    b: { 1: [5, 7], 2: [3, 0], 3: [0, 5] },
                    f: { 1: 4, 2: 0, 3: 1},
                    path: 'foo'
                },
                bar: {
                    s: { 1: 0, 2: 0, 3: 17, 4: 3 },
                    statementMap: { 1: loc, 2: loc, 3: loc, 4: loc },
                    b: { 1: [ 0, 8, 9 ], 2: [4, 1], 3: [0, 5] },
                    f: { 1: 7, 2: 78, 3: 9, 4: 0 },
                    path: 'bar'
                }
            };
            coverageObj2 = {
                foo: {
                    s: { 1: 1, 2: 5, 3: 88 },
                    statementMap: { 1: loc, 2: loc, 3: loc },
                    b: { 1: [0, 9], 2: [5, 6], 3: [55, 8] },
                    f: { 1: 0, 2: 0, 3: 9 },
                    path: 'foo'
                }
            };
            collector = new Collector();
            cb();
        },
        "collector should merge foo coverage": function (test) {
            var mergedFoo = utils.mergeFileCoverage(coverageObj.foo, coverageObj2.foo),
                mergedFinal = { foo: mergedFoo, bar: coverageObj.bar };
            collector.add(JSON.parse(JSON.stringify(coverageObj)));
            collector.add(JSON.parse(JSON.stringify(coverageObj2)));
            utils.addDerivedInfo(mergedFinal);
            test.deepEqual(['foo', 'bar'], collector.files());
            test.deepEqual(mergedFinal, collector.getFinalCoverage());
            test.deepEqual(mergedFinal.foo, collector.fileCoverageFor('foo'));
            test.deepEqual(mergedFinal.bar, collector.fileCoverageFor('bar'));
            test.done();
        }
    }
};istanbul-0.4.5/test/other/test-command-xface.js000066400000000000000000000024101275640326200214620ustar00rootroot00000000000000var Command = require('../../lib/command');

module.exports = {
    "should return command list": function (test) {
        var cmdList = Command.getCommandList();
        test.ok(cmdList.length > 0);
        cmdList.forEach(function (name) {
            var command = Command.create(name);
            test.ok(command.synopsis && command.synopsis());
            test.ok(command.usage && typeof command.usage === 'function');
            command.usage();
            test.ok(command.run && typeof command.run === 'function');
        });
        test.done();
    },
    "should run help for all commands without any barfs": function (test) {
        var cmdList = Command.getCommandList(),
            help = Command.create('help'),
            handler = function (err) { test.ok(!err); };
        help.run([], handler);
        help.run(['foobar'], handler);
        help.run(['foobar', 'foobar', 'foobar'], handler);
        cmdList.forEach(function (name) {
            if (name !== 'help') {
                help.run([ name ], handler);
            }
        });
        test.done();
    },
    "should throw on non-existent command": function (test) {
        test.throws(function () { Command.create('nonexistent-command'); },
            /existent/);
        test.done();
    }
};istanbul-0.4.5/test/other/test-config.js000066400000000000000000000221611275640326200202320ustar00rootroot00000000000000var path = require('path'),
    configuration = require('../../lib/config'),
    oldCwd = process.cwd(),
    newCwd = path.resolve(__dirname, 'config-data'),
    config;

module.exports = {
    "when no explicit config is present": {
        setUp: function (cb) {
            config = configuration.loadObject(null);
            cb();
        },
        "verbose option should be set": function (test) {
            test.equal(false, config.verbose);
            test.done();
        },
        "default instrumentation options should be correct": function (test) {
            var iOpts = config.instrumentation;
            test.equal(process.cwd(), iOpts.root());
            test.equal(true, iOpts.defaultExcludes());
            test.equal(false, iOpts.embedSource());
            test.equal('__coverage__', iOpts.variable());
            test.equal(true, iOpts.compact());
            test.equal(false, iOpts.preserveComments());
            test.equal(false, iOpts.completeCopy());
            test.equal(false, iOpts.saveBaseline());
            test.equal(false, iOpts.includeAllSources());
            test.equal('./coverage/coverage-baseline.json', iOpts.baselineFile());
            test.deepEqual(['**/node_modules/**'], iOpts.excludes());
            test.deepEqual(['**/node_modules/**', '**/test/**', '**/tests/**'], iOpts.excludes(true));
            test.done();
        },
        "default reporting options should be correct": function (test) {
            var rOpts = config.reporting;
            test.equal('summary', rOpts.print());
            test.deepEqual( ['lcov'], rOpts.reports());
            test.equal('./coverage', rOpts.dir());
            test.equal('lcov.info', rOpts.reportConfig().lcovonly.file);
            test.done();
        },
        "default hook options should be correct": function (test) {
            var hOpts = config.hooks;
            test.equal(false, hOpts.hookRunInContext());
            test.equal(null, hOpts.postRequireHook());
            test.done();
        }
    },
    "when overrides passed in": {
        "as initial object": {
            "should use overrides": function (test) {
                config = configuration.loadObject({ instrumentation: { compact: false, 'save-baseline': true }});
                test.equal(false, config.instrumentation.compact());
                test.equal(true, config.instrumentation.saveBaseline());
                test.done();
            }
        },
        "as override object": {
            "should use overrides": function (test) {
                config = configuration.loadObject({},
                    { verbose: true, instrumentation: { compact: false, 'save-baseline': true }}
                );
                test.equal(true, config.verbose);
                test.equal(false, config.instrumentation.compact());
                test.equal(true, config.instrumentation.saveBaseline());
                test.done();
            }
        },
        "at both levels": {
            "should use override object values": function (test) {
                config = configuration.loadObject(
                    { verbose: true, instrumentation: { compact: false, 'save-baseline': true } },
                    { verbose: false, instrumentation: { compact: true, 'save-baseline': false } }
                );
                test.equal(false, config.verbose);
                test.equal(true, config.instrumentation.compact());
                test.equal(false, config.instrumentation.saveBaseline());
                test.done();
            }
        },
        "deeper in the tree": {
            "should use overrides": function (test) {
                config = configuration.loadObject({
                    reporting: {
                        'report-config': {
                            'lcovonly': {
                                file: 'foo.info'
                            }
                        }
                    }
                });
                test.equal('foo.info', config.reporting.reportConfig().lcovonly.file);
                test.equal('clover.xml', config.reporting.reportConfig().clover.file);
                test.equal(null, config.reporting.reportConfig().text.file);
                test.done();
            }
        }
    },
    "excludes array changes based on default excludes": {
        "should honor default excludes setting - when set" : function (test) {
            config = configuration.loadObject({
                instrumentation: {
                    "excludes": [ '**/vendor/**' ]
                }
            });
            var iOpts = config.instrumentation;
            test.deepEqual(['**/node_modules/**', '**/vendor/**'], iOpts.excludes());
            test.deepEqual(['**/node_modules/**', '**/test/**', '**/tests/**', '**/vendor/**'], iOpts.excludes(true));
            test.done();
        },
        "should honor default excludes setting - when not set" : function (test) {
            config = configuration.loadObject({
                instrumentation: {
                    "default-excludes": null,
                    "excludes": [ '**/vendor/**' ]
                }
            });
            var iOpts = config.instrumentation;
            test.deepEqual(['**/vendor/**'], iOpts.excludes());
            test.deepEqual(['**/vendor/**'], iOpts.excludes(true));
            test.done();
        },
        "should return nothing when defaults off and no excludes" : function (test) {
            config = configuration.loadObject({
                instrumentation: {
                    "default-excludes": null
                }
            });
            var iOpts = config.instrumentation;
            test.deepEqual([], iOpts.excludes());
            test.deepEqual([], iOpts.excludes(true));
            test.done();
        }
    },
    "when loading files": {
        "should fail on bad config file": function (test) {
            try {
                config = configuration.loadFile('/a/non/existent/path.js');
                test.fail();
            } catch (ex) {
                test.ok(1);
            }
            test.done();
        },
        "should use default config when no default file found": function (test) {
            config = configuration.loadFile();
            var defaultConfig = configuration.loadObject();
            test.deepEqual(config, defaultConfig);
            test.done();
        },
        "when files present": {
            setUp: function (cb) {
                process.chdir(newCwd);
                cb();
            },
            tearDown: function (cb) {
                process.chdir(oldCwd);
                cb();
            },
            "should use default YAML config when not explicit": function (test) {
                config = configuration.loadFile(undefined, { verbose: true });
                test.equal(false, config.instrumentation.compact());
                test.deepEqual(['lcov', 'cobertura'], config.reporting.reports());
                test.done();
            },
            "should use explicit file when provided": function (test) {
                config = configuration.loadFile('cfg.json', { verbose: true });
                test.equal(true, config.instrumentation.compact());
                test.deepEqual(['lcov'], config.reporting.reports());
                test.equal(true, config.instrumentation.saveBaseline());
                test.equal('yui-istanbul', config.hooks.postRequireHook());
                test.done();
            }
        }
    },
    "with custom watermarks": {
        "should load from sparse config": function (test) {
            config = configuration.loadObject({ reporting: { watermarks: { statements: [ 10, 90] } } });
            var w = config.reporting.watermarks();
            test.deepEqual([ 10, 90 ], w.statements);
            test.deepEqual([ 50, 80 ], w.branches);
            test.deepEqual([ 50, 80 ], w.functions);
            test.deepEqual([ 50, 80 ], w.lines);
            test.done();
        },
        "should not load any junk config": function (test) {
            config = configuration.loadObject({
                reporting: {
                    watermarks: {
                        statements: [ 10, 90, 95],
                        branches: [ -10, 70 ],
                        lines: [ 70, 110 ],
                        functions: [ 'a', 10 ]
                    }
                }
            });
            var w = config.reporting.watermarks();
            test.deepEqual([ 50, 80 ], w.statements);
            test.deepEqual([ 50, 80 ], w.branches);
            test.deepEqual([ 50, 80 ], w.functions);
            test.deepEqual([ 50, 80 ], w.lines);
            test.done();
        },
        "should not load any junk config (2)": function (test) {
            config = configuration.loadObject({
                reporting: {
                    watermarks: {
                        statements: [ 90, 80 ]
                    }
                }
            });
            var w = config.reporting.watermarks();
            test.deepEqual([ 50, 80 ], w.statements);
            test.deepEqual([ 50, 80 ], w.branches);
            test.deepEqual([ 50, 80 ], w.functions);
            test.deepEqual([ 50, 80 ], w.lines);
            test.done();
        }
    }
};
istanbul-0.4.5/test/other/test-file-writer.js000066400000000000000000000036621275640326200212230ustar00rootroot00000000000000/*jslint nomen: true */

var path = require('path'),
    fs = require('fs'),
    mkdirp = require('mkdirp'),
    rimraf = require('rimraf'),
    FileWriter = require('../../lib/util/file-writer'),
    outputDir = path.resolve(__dirname, 'output'),
    FOO = path.resolve(outputDir, 'foo-written.js'),
    writer,
    MAX_FILES = 1000,
    LINES = [ 'This is line 1', 'This is line 2', 'This is line 3' ];

function testContents(test) {
    test.equal(LINES.join('\n') + '\n', fs.readFileSync(FOO, 'utf8'));
    test.done();
}

function fileNames() {
    var i,
        ret = [];
    for (i = 0; i < MAX_FILES; i += 1) {
        ret.push(path.resolve(outputDir, 'file' + i + '.txt'));
    }
    return ret;
}

function testAllFiles(test) {
    var files = fileNames();
    files.forEach(function (file) {
        test.equal(file + '\n', fs.readFileSync(file, 'utf8'));
    });
    test.done();
}

function battery(sync) {
    return {
        setUp: function (cb) {
            mkdirp.sync(outputDir);
            writer = new FileWriter(sync);
            cb();
        },
        tearDown: function (cb) {
            writer = null;
            rimraf.sync(outputDir);
            cb();
        },
        "should write file in callback mode": function (test) {
            writer.on('done', testContents.bind(null, test));
            writer.writeFile(FOO, function (w) {
                LINES.forEach(function (line) { w.println(line); });
            });
            writer.done();
        },
        "should write 1000 files": function (test) {
            var files = fileNames();
            writer.on('done', testAllFiles.bind(null, test));
            files.forEach(function (file) {
                writer.writeFile(file, function (w) {
                    w.println(file);
                });
            });
            writer.done();
        }
    };
}

module.exports = {
    "when in sync mode": battery(true),
    "when in async mode": battery(false)
};
istanbul-0.4.5/test/other/test-help-formatter.js000066400000000000000000000026531275640326200217220ustar00rootroot00000000000000var formatOption = require('../../lib/util/help-formatter').formatOption,
    THRESHOLD = 20;

module.exports = {
    "should format small option + text on a single line": function (test) {
        var formatted = formatOption('-f', 'specify force override of defaults');
        console.log(formatted);
        test.ok(formatted.indexOf('\n') < 0);
        test.done();
    },
    "should format small option + big text on multiple lines": function (test) {
        var formatted = formatOption('-f', 'an option whose description should exceed the length of a line if we have done our job well');
        console.log(formatted);
        test.ok(formatted.indexOf('\n') > THRESHOLD);
        test.done();
    },
    "should format long option + small text on multiple lines": function (test) {
        var formatted = formatOption('--[no]force', 'specify force override of defaults');
        console.log(formatted);
        test.ok(formatted.indexOf('\n') < THRESHOLD);
        test.ok(formatted.lastIndexOf('\n') < THRESHOLD);
        test.done();
    },
    "should format long option + big text on 3 lines": function (test) {
        var formatted = formatOption('--[no]force', 'an option whose description should exceed the length of a line if we have done our job well');
        console.log(formatted);
        test.ok(formatted.indexOf('\n') < THRESHOLD);
        test.ok(formatted.lastIndexOf('\n') > THRESHOLD);
        test.done();
    }
};istanbul-0.4.5/test/other/test-hook.js000066400000000000000000000140571275640326200177320ustar00rootroot00000000000000/*jslint nomen: true */
var hook = require('../../lib/hook'),
    currentHook,
    matcher = function (file) { return file.indexOf('foo.js') > 0; },
    matcher2 = function (file) { return file.indexOf('bar.es6') > 0; },
    transformer = function () { return 'module.exports.bar = function () { return "bar"; };'; },
    transformer2 = function () { return 'module.exports.blah = function () { return "blah"; };'; },
    badTransformer = function () { throw "Boo!"; },
    scriptTransformer = function () { return '(function () { return 42; }());'; };

module.exports = {
    "when require is hooked": {
        setUp: function (cb) {
            currentHook = require('module')._extensions['.js'];
            hook.hookRequire(matcher, transformer, { verbose: true });
            cb();
        },
        tearDown: function (cb) {
            hook.unloadRequireCache(matcher);
            require('module')._extensions['.js'] = currentHook;
            cb();
        },

        "foo should be transformed": function (test) {
            var foo = require('./data/foo');
            test.ok(foo.bar);
            test.equals('bar', foo.bar());
            test.done();
        },

        "but baz should be skipped": function (test) {
            var foo = require('./data/baz');
            test.ok(foo.baz);
            test.equals('baz', foo.baz());
            test.done();
        },

        "twice it should save original code": function (test) {
            hook.hookRequire(matcher, transformer, { verbose: true });
            hook.unhookRequire();
            var foo = require('./data/foo');
            test.ok(foo.foo);
            test.equals('foo', foo.foo());
            test.done();
        },

        "postLoadHook should be called": function (test) {
            var called = null,
                opts = { postLoadHook: function (file) { called = file; }},
                foo;

            hook.unhookRequire();
            hook.hookRequire(matcher, transformer, opts);
            foo = require('./data/foo');
            test.ok(called.match(/foo\.js/));
            test.done();
        },

        "and cache should be un- and reloaded": function (test) {
            hook.unhookRequire();
            hook.hookRequire(matcher, transformer2);
            var foo = require('./data/foo');
            test.ok(foo.blah);
            test.equals('blah', foo.blah());
            test.done();
        },

        "bad transformer should return original code": function (test) {
            hook.unhookRequire();
            hook.hookRequire(matcher, badTransformer);
            var foo = require('./data/foo');
            test.ok(foo.foo);
            test.equals('foo', foo.foo());
            test.done();
        }
    },
    "when extensions are passed to hookRequire": {
        setUp: function(cb) {
            hook.hookRequire(matcher2, transformer2, { verbose: true, extensions: ['.es6'] });
            cb();
        },
        tearDown: function(cb) {
            hook.unloadRequireCache(matcher2);
            delete require('module')._extensions['.es6'];
            cb();
        },
        "bar should be transformed": function(test) {
            var bar = require('./data/bar');
            test.ok(bar.blah);
            test.equals('blah', bar.blah());
            test.done();
        },

        "but foo should be skipped": function (test) {
            var foo = require('./data/foo');
            test.ok(foo.foo);
            test.equals('foo', foo.foo());
            test.done();
        },
        "bad transformer should return original code": function (test) {
            hook.unhookRequire();
            hook.hookRequire(matcher2, badTransformer, { verbose: true, extensions: ['.es6'] });
            var bar = require('./data/bar');
            test.ok(bar.bar);
            test.equals('bar', bar.bar());
            test.done();
        }
    },
    "when createScript is hooked": {
        setUp: function (cb) {
            currentHook = require('vm').createScript;
            cb();
        },
        tearDown: function (cb) {
            require('vm').createScript = currentHook;
            cb();
        },
        "foo should be transformed (without any options)": function (test) {
            var s;
            hook.hookCreateScript(matcher, scriptTransformer);
            s = require('vm').createScript('(function () { return 10; }());', '/bar/foo.js');
            test.equals(42, s.runInThisContext());
            hook.unhookCreateScript();
            s = require('vm').createScript('(function () { return 10; }());', '/bar/foo.js');
            test.equals(10, s.runInThisContext());
            test.done();
        }
    },
	"when runInThisContext is hooked": {
        setUp: function (cb) {
            currentHook = require('vm').runInThisContext;
            cb();
        },
        tearDown: function (cb) {
            require('vm').runInThisContext = currentHook;
            cb();
        },
        "foo should be transformed": function (test) {
            var s;
            hook.hookRunInThisContext(matcher, scriptTransformer);
            s = require('vm').runInThisContext('(function () { return 10; }());', '/bar/foo.js');
            test.equals(42, s);
            hook.unhookRunInThisContext();
            s = require('vm').runInThisContext('(function () { return 10; }());', '/bar/foo.js');
            test.equals(10, s);
            test.done();
        },
        "code with no filename should not be transformed": function (test) {
            var s;
            hook.hookRunInThisContext(matcher, scriptTransformer);
            s = require('vm').runInThisContext('(function () { return 10; }());');
            test.equals(10, s);
            hook.unhookCreateScript();
            test.done();
        },
        "code with non-string filename should not be transformed": function (test) {
            var s;
            hook.hookRunInThisContext(matcher, scriptTransformer);
            s = require('vm').runInThisContext('(function () { return 10; }());', {});
            test.equals(10, s);
            hook.unhookCreateScript();
            test.done();
        }
	},
};
istanbul-0.4.5/test/other/test-index-xface.js000066400000000000000000000012661275640326200211630ustar00rootroot00000000000000var main = require('../../index');


module.exports = {
    "xface": function (test) {
        [ 'Instrumenter', 'Store', 'Collector', 'Report', 'Reporter', '_yuiLoadHook'].forEach(function (key) {
            test.ok(main[key] && typeof main[key] === 'function', key + ' was not exported as a function!');
        });
        [ 'hook', 'utils', 'config' ].forEach(function (key) {
            test.ok(main[key] && typeof main[key] === 'object', key + ' was not exported as an object!');
        });
        [ 'assetsDir'].forEach(function (key) {
            test.ok(main[key] && typeof main[key] === 'string', key + ' was not exported as a string!');
        });
        test.done();
    }
};istanbul-0.4.5/test/other/test-input-error.js000066400000000000000000000005031275640326200212470ustar00rootroot00000000000000var inputError = require('../../lib/util/input-error');

module.exports = {
    "should produce an Error object distinguishable from unexpected errors": function (test) {
        var e = inputError.create('bad user!');
        test.equal('bad user!', e.message);
        test.ok(e.inputError);
        test.done();
    }
};istanbul-0.4.5/test/other/test-insertion-text.js000066400000000000000000000106351275640326200217640ustar00rootroot00000000000000/*jslint nomen: true */
var InsertionText = require('../../lib/util/insertion-text'),
    it;

module.exports = {
    "with a regular insertion text": {
        setUp: function (cb) {
            it = new InsertionText("hello world");
            cb();
        },
        "should be able to span hello": function (test) {
            it.insertAt(0, '');
            it.insertAt(5, '');
            test.equals(it.toString(), 'hello world');
            test.done();
        },
        "should be able to span world": function (test) {
            it.insertAt(6, '');
            it.insertAt(11, '');
            test.equals(it.toString(), 'hello world');
            test.done();
        },
        "should be able to nest spans around hello (improperly)": function (test) {
            it.insertAt(0, '');
            it.insertAt(5, '');
            it.insertAt(0, '
'); it.insertAt(5, '
'); test.equals(it.toString(), '
hello
world'); test.done(); }, "should be able to nest spans around hello (properly)": function (test) { it.insertAt(0, '', true); it.insertAt(5, ''); it.insertAt(0, '
', true); it.insertAt(5, '
'); test.equals(it.toString(), '
hello
world'); test.done(); }, "should be able to use syntactic sugar for wrapping text": function (test) { it.wrap(0, '', 5, ''); it.wrap(0, '
', 5, '
'); test.equals(it.toString(), '
hello
world'); test.done(); }, "should be able to chain calls for wrapping text": function (test) { it.wrap(0, '', 5, ''). wrap(0, '
', 5, '
'); test.equals(it.toString(), '
hello
world'); test.done(); }, "should be able to insert text between two insertions": function (test) { it.wrap(0, '', 5, ''). insertAt(3, "w"); test.equals(it.toString(), 'helwlo world'); test.done(); }, "should prepend on negative insertion index": function (test) { it.insertAt(-3, "XXX"); test.equals(it.toString(), 'XXXhello world'); test.done(); }, "should append on out-of-bounds insertion index": function (test) { it.insertAt(100, "XXX"); test.equals(it.toString(), 'hello worldXXX'); test.done(); }, "should wrap entire line correctly": function (test) { it.wrapLine('', ''); test.equals(it.toString(), 'hello world'); test.done(); }, "should be able to consume blanks in an individual call": function (test) { it = new InsertionText(" hello world "); it.wrap(2, '', 13, '', true); test.equals(it.toString(), ' hello world '); test.done(); } }, "with a blank consuming insertion text and input with spaces on either end": { setUp: function (cb) { it = new InsertionText(" hello world ", true); cb(); }, "spanning first word should consume leading spaces": function (test) { it.wrap(4, '', 9, ''); test.equals(it.toString(), ' hello world '); test.done(); }, "spanning second word should consume trailing spaces": function (test) { it.wrap(10, '', 15, ''); test.equals(it.toString(), ' hello world '); test.done(); }, "should be able to suppress blank consumption in an individual call": function (test) { it = new InsertionText(" hello world "); it.wrap(2, '', 13, '', false); test.equals(it.toString(), ' hello world '); test.done(); } } }; istanbul-0.4.5/test/other/test-matcher.js000066400000000000000000000077031275640326200204150ustar00rootroot00000000000000/*jslint nomen: true */ var path = require('path'), glob = require('glob'), root = path.resolve(__dirname, 'data', 'matcher'), src = '../../lib/util/file-matcher.js', fileMatcher = require(src), allFiles; module.exports = { setUp: function (cb) { if (!allFiles) { glob('**/*.js', { cwd: root}, function (err, files) { allFiles = files.map(function (file) { return path.resolve(root, file); }); cb(); }); } else { cb(); } }, "should return all files except those under node_modules by default": function (test) { fileMatcher.filesFor(function (err, files) { test.ok(!err); allFiles.forEach(function (file) { var matcher = function (f) { return f === file; }, shouldMatch = file.indexOf('file.js') < 0; if (shouldMatch) { test.ok(files.filter(matcher).length, 'Should match [' + file + '] but did not'); } else { test.ok(!files.filter(matcher).length, 'Should NOT match [' + file + '] but did'); } }); test.done(); }); }, "should return relative filenames when requested": function (test) { fileMatcher.filesFor({ root: root, relative: true }, function (err, files) { test.ok(!err); allFiles.forEach(function (file) { var matcher = function (f) { return path.resolve(root, f) === file; }, shouldMatch = file.indexOf('file.js') < 0; if (shouldMatch) { test.ok(files.filter(matcher).length, 'Should match [' + file + '] but did not'); } else { test.ok(!files.filter(matcher).length, 'Should NOT match [' + file + '] but did'); } }); test.done(); }); }, "should match stuff under cwd": function (test) { fileMatcher.matcherFor(function (err, matchFn) { test.ok(!err); test.ok(matchFn(path.resolve(__dirname, src)), 'should match itself'); test.done(); }); }, "should match stuff under cwd overriding relative opts passed in": function (test) { fileMatcher.matcherFor({ relative: true }, function (err, matchFn) { test.ok(!err); test.ok(matchFn(path.resolve(__dirname, src)), 'should match itself'); test.done(); }); }, "should ignore node_modules": function (test) { fileMatcher.matcherFor({ root: root }, function (err, matchFn) { test.ok(!err); test.ok(matchFn.files); test.deepEqual(allFiles.filter(function (f) { return !f.match(/node_modules/); }).sort(), matchFn.files.sort()); allFiles.forEach(function (file) { var shouldMatch = file.indexOf('file.js') < 0; if (shouldMatch) { test.ok(matchFn(file), 'Should match [' + file + '] but did not'); } else { test.ok(!matchFn(file), 'Should NOT match [' + file + '] but did'); } }); test.done(); }); }, "should match stuff with explicit includes and excludes": function (test) { fileMatcher.matcherFor({ root: root, includes: [ '**/general/**/*.js' ], excludes: [ '**/general.js' ] }, function (err, matchFn) { test.ok(!err); allFiles.forEach(function (file) { if (file.indexOf('/general/') < 0) { return; } var shouldMatch = file.indexOf('file.js') >= 0; if (shouldMatch) { test.ok(matchFn(file), 'Should match [' + file + '] but did not'); } else { test.ok(!matchFn(file), 'Should NOT match [' + file + '] but did'); } }); test.done(); }); } }; istanbul-0.4.5/test/other/test-object-utils.js000066400000000000000000000236121275640326200213730ustar00rootroot00000000000000/*jslint nomen: true */ var utils = require('../../lib/object-utils'), it, it2, it3; module.exports = { "with some coverage info": { setUp: function (cb) { it = { foo: { s: { 1: 1, 2: 2, 3: 3, 4: 4 }, statementMap: { 1: { start: { line: 1, column: 1}, end: { line: 1, column: 10 }}, 2: { start: { line: 2, column: 1}, end: { line: 1, column: 9 }}, 3: { start: { line: 2, column: 10 }, end: { line: 2, column: 20 }}, 4: { start: { line: 7, column: 0 }, end: { line: 40, column: 10 }} } } }; cb(); }, "should add/ remove derived coverage correctly": function (test) { utils.addDerivedInfo(it); var l = it.foo.l; test.ok(l); test.equals(3, Object.keys(l).length); test.equals(1, l[1]); test.equals(3, l[2]); test.equals(4, l[7]); l[7] = 0; utils.addDerivedInfo(it); test.equals(0, it.foo.l[7]); //does not change utils.removeDerivedInfo(it); test.ok(!it.foo.l); test.done(); } }, "with full coverage info": { setUp: function (cb) { it = { foo: { s: { 1: 1, 2: 2, 3: 3, 4: 4, 5: 0 }, statementMap: { 1: { start: { line: 1, column: 1}, end: { line: 1, column: 10 }}, 2: { start: { line: 2, column: 1}, end: { line: 1, column: 9 }}, 3: { start: { line: 2, column: 10 }, end: { line: 2, column: 20 }}, 4: { start: { line: 7, column: 0 }, end: { line: 40, column: 10 }}, 5: { start: { line: 41, column: 0 }, end: { line: 42, column: 10 }} }, f: { 1: 10, 2: 0 }, fnMap: { 1: { name: 'foo', line: 1 }, 2: { name: 'anonymous_1', line: 7 } }, b: { 1: [ 10, 0, 2], 2: [ 0, 0] }, branchMap: { 1: { line: 2, type: 'switch', locations: [ ] }, 2: { line: 3, type: 'if', locations: [ ] } } } }; it2 = it; it3 = { foo: { s: { 1: 2, 2: 1, 3: 78, 4: 99, 5: 0 }, statementMap: { 1: { start: { line: 1, column: 1}, end: { line: 1, column: 10 }}, 2: { start: { line: 2, column: 1}, end: { line: 1, column: 9 }}, 3: { start: { line: 2, column: 10 }, end: { line: 2, column: 20 }}, 4: { start: { line: 7, column: 0 }, end: { line: 40, column: 10 }}, 5: { start: { line: 41, column: 0 }, end: { line: 42, column: 10 }} }, f: { 1: 9, 2: 1 }, fnMap: { 1: { name: 'foo', line: 1 }, 2: { name: 'anonymous_1', line: 7 } }, b: { 1: [ 0, 1, 1], 2: [ 3, 0] }, branchMap: { 1: { line: 2, type: 'switch', locations: [ ] }, 2: { line: 3, type: 'if', locations: [ ] } } } }; cb(); }, "should calculate correct summary": function (test) { var ret = utils.summarizeFileCoverage(it.foo); test.deepEqual({ total: 4, covered: 3, pct: 75, skipped: 0 }, ret.lines); test.deepEqual({ total: 5, covered: 4, pct: 80, skipped: 0 }, ret.statements); test.deepEqual({ total: 2, covered: 1, pct: 50, skipped: 0 }, ret.functions); test.deepEqual({ total: 5, covered: 2, pct: 40, skipped: 0 }, ret.branches); test.done(); }, "should return a pct of 100 when nothing is available": function (test) { it.foo.b = {}; var ret = utils.summarizeFileCoverage(it.foo); test.deepEqual({ total: 4, covered: 3, pct: 75, skipped: 0 }, ret.lines); test.deepEqual({ total: 5, covered: 4, pct: 80, skipped: 0 }, ret.statements); test.deepEqual({ total: 2, covered: 1, pct: 50, skipped: 0 }, ret.functions); test.deepEqual({ total: 0, covered: 0, pct: 100, skipped: 0 }, ret.branches); test.done(); }, "should merge summary correctly": function (test) { var s1 = utils.summarizeFileCoverage(it.foo), s2 = utils.summarizeFileCoverage(it2.foo), ret = utils.mergeSummaryObjects(s1, s2); test.deepEqual({ total: 8, covered: 6, pct: 75, skipped: 0 }, ret.lines); test.deepEqual({ total: 10, covered: 8, pct: 80, skipped: 0 }, ret.statements); test.deepEqual({ total: 4, covered: 2, pct: 50, skipped: 0 }, ret.functions); test.deepEqual({ total: 10, covered: 4, pct: 40, skipped: 0 }, ret.branches); test.done(); }, "should merge summary correctly in one call": function (test) { var coverage = { foo: it.foo, 'bar': it2.foo }, ret = utils.summarizeCoverage(coverage); test.deepEqual({ total: 8, covered: 6, pct: 75, skipped: 0 }, ret.lines); test.deepEqual({ total: 10, covered: 8, pct: 80, skipped: 0 }, ret.statements); test.deepEqual({ total: 4, covered: 2, pct: 50, skipped: 0 }, ret.functions); test.deepEqual({ total: 10, covered: 4, pct: 40, skipped: 0 }, ret.branches); test.done(); }, "can merge with a blank object in first position": function (test) { var s1 = null, s2 = utils.summarizeFileCoverage(it2.foo), ret = utils.mergeSummaryObjects(s1, s2); test.deepEqual({ total: 4, covered: 3, pct: 75, skipped: 0 }, ret.lines); test.deepEqual({ total: 5, covered: 4, pct: 80, skipped: 0 }, ret.statements); test.deepEqual({ total: 2, covered: 1, pct: 50, skipped: 0 }, ret.functions); test.deepEqual({ total: 5, covered: 2, pct: 40, skipped: 0 }, ret.branches); test.done(); }, "can merge with a blank object in second position": function (test) { var s1 = utils.summarizeFileCoverage(it2.foo), s2 = null, ret = utils.mergeSummaryObjects(s1, s2); test.deepEqual({ total: 4, covered: 3, pct: 75, skipped: 0 }, ret.lines); test.deepEqual({ total: 5, covered: 4, pct: 80, skipped: 0 }, ret.statements); test.deepEqual({ total: 2, covered: 1, pct: 50, skipped: 0 }, ret.functions); test.deepEqual({ total: 5, covered: 2, pct: 40, skipped: 0 }, ret.branches); test.done(); }, "can turn it into a YUI coverage object": function (test) { var ret = utils.toYUICoverage(it); test.deepEqual({ '1': 1, '2': 3, '7': 4, '41': 0 }, ret.foo.lines); test.deepEqual({ 'foo:1': 10, 'anonymous_1:7': 0 }, ret.foo.functions); test.equal(3, ret.foo.calledLines); test.equal(4, ret.foo.coveredLines); test.equal(1, ret.foo.calledFunctions); test.equal(2, ret.foo.coveredFunctions); test.done(); }, "merge two like coverage objects for the same file correctly": function (test) { var base = JSON.parse(JSON.stringify(it.foo)), ret = utils.mergeFileCoverage(base, it3.foo), foo = it.foo; foo.s[1] += 2; foo.s[2] += 1; foo.s[3] += 78; foo.s[4] += 99; foo.f[1] += 9; foo.f[2] += 1; foo.b[1][1] += 1; foo.b[1][2] += 1; foo.b[2][0] += 3; test.deepEqual(ret, foo); test.done(); }, "increment ignored totals": { setUp: function (cb) { it = { l: {}, s: { 1: 0 }, b: { 1: [0, 0] }, f: { 1: 0 }, statementMap: { 1: {skip: true} }, branchMap: { 1: { locations: [ {}, {skip: true} ] } }, fnMap: { 1: {skip: true} } }; cb(); }, "should return file coverage object with incremented hits": function (test) { var result = utils.incrementIgnoredTotals(it); test.equal(1, result.s['1']); test.deepEqual([0, 1], result.b['1']); test.equal(1, result.f['1']); test.done(); } } } }; istanbul-0.4.5/test/other/test-store.js000066400000000000000000000077201275640326200201250ustar00rootroot00000000000000/*jslint nomen: true */ var Store = require('../../lib/store'), path = require('path'), fs = require('fs'), util = require('util'), foo = path.resolve(__dirname, 'data', 'foo.js'), baz = path.resolve(__dirname, 'data', 'baz.js'), anon = path.resolve(__dirname, 'data', 'does_not_exist.js'), fooContents = fs.readFileSync(foo, 'utf8'), bazContents = fs.readFileSync(baz, 'utf8'); module.exports = { "fslookup store should work correctly": function (test) { var store = Store.create('fslookup'); store.set(foo, 'content'); test.ok(store.hasKey(foo)); test.ok(store.hasKey(baz)); test.ok(!store.hasKey(anon)); test.equal(0, store.keys().length); test.equals(fooContents, store.get(foo)); test.equals(bazContents, store.get(baz)); test.throws(function () { store.get(anon); }); test.throws(function () { store.set(anon, 'foo'); }, Error, /non-existent/); test.done(); }, "tmp store should work as expected": function (test) { var store = Store.create('tmp'); test.equal(0, store.keys().length); store.set(foo, 'content'); test.ok(store.hasKey(foo)); test.ok(!store.hasKey(baz)); test.equal(1, store.keys().length); store.set(baz, 'baz content'); test.equal(2, store.keys().length); test.equals('content', store.get(foo)); test.equals('baz content', store.get(baz)); test.throws(function () { store.get(anon); }); store.dispose(); test.ok(!store.hasKey(foo)); test.equal(0, store.keys().length); test.done(); }, "memory store should work as expected": function (test) { var store = Store.create('memory'); test.equal(0, store.keys().length); store.set(foo, 'content'); test.ok(store.hasKey(foo)); test.ok(!store.hasKey(baz)); test.equal(1, store.keys().length); store.set(baz, 'baz content'); test.equal(2, store.keys().length); test.equals('content', store.get(foo)); test.equals('baz content', store.get(baz)); test.throws(function () { store.get(anon); }); store.dispose(); test.ok(!store.hasKey(foo)); test.equal(0, store.keys().length); test.done(); }, "should be able to register a new store": function (test) { function NStore() { } NStore.prototype = { set: function (/* file, content */) { return 'x'; } }; NStore.TYPE = 'nstore'; Store.register(NStore); var store = Store.create('nstore'); test.ok(store); test.equals('x', store.set('foo', 'bar')); test.done(); }, "should not be able to register an invalid store": function (test) { function NStore() {} test.throws(function () { Store.register(NStore); }, /TYPE/); test.done(); }, "invalid store should not be created": function (test) { test.throws( function () { Store.create('foo'); }, /Invalid store \[foo\], allowed values are / ); test.done(); }, "should require overriding of all overrideables": function (test) { function NStore() {} NStore.TYPE = 'nstore'; util.inherits(NStore, Store); Store.register(NStore); var store = Store.create('nstore'), asserter = function (fn) { test.throws(function () { fn(); }, /must be overridden/); }; asserter(function () { store.set('foo', 'bar'); }); asserter(function () { store.get('foo'); }); asserter(function () { store.hasKey('foo'); }); asserter(function () { store.keys(); }); test.doesNotThrow(function () { store.dispose(); }); test.done(); } };istanbul-0.4.5/test/other/test-summarizer.js000066400000000000000000000214041275640326200211620ustar00rootroot00000000000000var path = require('path'), SEP = path.sep || '/', TreeSummarizer = require('../../lib/util/tree-summarizer'), utils = require('../../lib/object-utils'), summarizer, tree, s1, s2, s3; module.exports = { setUp: function (cb) { summarizer = new TreeSummarizer(); s1 = { statements: { covered: 5, total: 10, pct: 50, skipped: 0 }, lines: { covered: 5, total: 10, pct: 50, skipped: 0 }, functions: { covered: 15, total: 20, pct: 75, skipped: 0 }, branches: { covered: 100, total: 200, pct: 50, skipped: 0 }, linesCovered: { '320': 1, '420': 10 } }; s2 = { statements: { covered: 10, total: 20, pct: 50, skipped: 0 }, lines: { covered: 10, total: 20, pct: 50, skipped: 0 }, functions: { covered: 75, total: 100, pct: 75, skipped: 0 }, branches: { covered: 1, total: 2, pct: 50, skipped: 0 }, linesCovered: { '320': 3, '500': 0 } }; s3 = { statements: { covered: 9, total: 10, pct: 90, skipped: 0 }, lines: { covered: 9, total: 10, pct: 90, skipped: 0 }, functions: { covered: 15, total: 15, pct: 100, skipped: 0 }, branches: { covered: 101, total: 101, pct: 100, skipped: 0 }, linesCovered: {} }; cb(); }, "with a few files in a few dirs": { setUp: function (cb) { summarizer.addFileCoverageSummary(SEP + path.join('tmp', 'lib', 'foo.js'), s1); summarizer.addFileCoverageSummary(SEP + path.join('tmp', 'lib', 'bar.js'), s2); summarizer.addFileCoverageSummary(SEP + path.join('tmp', 'lib', 'util', 'baz.js'), s3); tree = summarizer.getTreeSummary(); cb(); }, "should be JSON serializable": function (test) { test.ok(JSON.stringify(tree)); test.done(); }, "should have the correct tree with root hoisted one level above code": function (test) { var node = tree.root, utilSummary = utils.mergeSummaryObjects(s3), libSummary = utils.mergeSummaryObjects(s1, s2), fullSummary = utils.mergeSummaryObjects(utilSummary, libSummary); test.equal(SEP + 'tmp' + SEP, node.fullPath()); test.equal('', node.displayShortName()); test.equal(2, node.children.length); test.deepEqual(fullSummary, node.metrics); test.ok(node === tree.getNode('')); node = tree.root.children[0]; test.equal(SEP + ['tmp', 'lib'].join(SEP) + SEP, node.fullPath()); test.equal('lib' + SEP, node.displayShortName()); test.equal(2, node.children.length); test.deepEqual(libSummary, node.metrics); test.deepEqual(s2, node.children[0].metrics); test.deepEqual(s1, node.children[1].metrics); test.ok(node === tree.getNode('lib' + SEP)); node = tree.root.children[1]; test.equal(SEP + ['tmp', 'lib', 'util'].join(SEP) + SEP, node.fullPath()); test.equal(['lib', 'util'].join(SEP) + SEP, node.displayShortName()); test.equal(1, node.children.length); test.deepEqual(utilSummary, node.metrics); test.deepEqual(s3, node.children[0].metrics); test.ok(node === tree.getNode(path.join('lib', 'util') + SEP)); test.ok(tree.getNode(path.join('lib', 'foo.js'))); test.ok(tree.getNode(path.join('lib', 'bar.js'))); test.ok(tree.getNode(path.join('lib', 'util', 'baz.js'))); test.done(); }, "should combine linesCovered across multiple summary objects": function (test) { var summary = utils.mergeSummaryObjects(s1, s2); test.equal(4, summary.linesCovered['320']); test.equal(10, summary.linesCovered['420']); test.equal(0, summary.linesCovered['500']); test.done(); } }, "with the same few files organized differently": { setUp: function (cb) { summarizer.addFileCoverageSummary(SEP + path.join('tmp', 'lib', 'main', 'foo.js'), s1); summarizer.addFileCoverageSummary(SEP + path.join('tmp', 'lib', 'main', 'bar.js'), s2); summarizer.addFileCoverageSummary(SEP + path.join('tmp', 'lib', 'util', 'baz.js'), s3); tree = summarizer.getTreeSummary(); cb(); }, "should have the correct tree with no root hoisting": function (test) { var node = tree.root, utilSummary = utils.mergeSummaryObjects(s3), libSummary = utils.mergeSummaryObjects(s1, s2), fullSummary = utils.mergeSummaryObjects(utilSummary, libSummary); test.equal(SEP + ['tmp', 'lib'].join(SEP) + SEP, node.fullPath()); test.equal('', node.displayShortName()); test.equal(2, node.children.length); test.deepEqual(fullSummary, node.metrics); test.ok(node === tree.getNode('')); node = tree.root.children[0]; test.equal(SEP + ['tmp', 'lib', 'main'].join(SEP) + SEP, node.fullPath()); test.equal('main' + SEP, node.displayShortName()); test.equal(2, node.children.length); test.deepEqual(libSummary, node.metrics); test.deepEqual(s2, node.children[0].metrics); test.deepEqual(s1, node.children[1].metrics); test.ok(node === tree.getNode('main' + SEP)); node = tree.root.children[1]; test.equal(SEP + ['tmp', 'lib', 'util'].join(SEP) + SEP, node.fullPath()); test.equal('util' + SEP, node.displayShortName()); test.equal(1, node.children.length); test.deepEqual(utilSummary, node.metrics); test.deepEqual(s3, node.children[0].metrics); test.ok(node === tree.getNode('util' + SEP)); test.ok(tree.getNode(path.join('main', 'foo.js'))); test.ok(tree.getNode(path.join('main', 'bar.js'))); test.ok(tree.getNode(path.join('util', 'baz.js'))); test.done(); } }, "with no room for hoisting": { setUp: function (cb) { summarizer.addFileCoverageSummary(SEP + 'foo.js', s1); summarizer.addFileCoverageSummary(SEP + 'bar.js', s2); summarizer.addFileCoverageSummary(SEP + path.join('util', 'baz.js'), s3); tree = summarizer.getTreeSummary(); //console.log(JSON.stringify(tree, undefined, 2)); cb(); }, "should build a correct tree but with a mangled name for the main dir": function (test) { var node = tree.root, utilSummary = utils.mergeSummaryObjects(s3), libSummary = utils.mergeSummaryObjects(s1, s2), fullSummary = utils.mergeSummaryObjects(utilSummary, libSummary); test.equal(SEP, node.fullPath()); test.equal('', node.displayShortName()); test.equal(2, node.children.length); test.deepEqual(fullSummary, node.metrics); test.ok(node === tree.getNode('')); node = tree.root.children[0]; test.equal(SEP + '__root__' + SEP, node.fullPath()); test.equal('__root__' + SEP, node.displayShortName()); test.equal(2, node.children.length); test.deepEqual(libSummary, node.metrics); test.deepEqual(s2, node.children[0].metrics); test.deepEqual(s1, node.children[1].metrics); test.ok(node === tree.getNode('__root__' + SEP)); node = tree.root.children[1]; test.equal(SEP + 'util' + SEP, node.fullPath()); test.equal('util' + SEP, node.displayShortName()); test.equal(1, node.children.length); test.deepEqual(utilSummary, node.metrics); test.deepEqual(s3, node.children[0].metrics); test.ok(node === tree.getNode('util' + SEP)); test.ok(tree.getNode('foo.js')); test.ok(tree.getNode('bar.js')); test.ok(tree.getNode(path.join('util', 'baz.js'))); test.done(); } }, "with no summaries provided at all": { setUp: function (cb) { tree = summarizer.getTreeSummary(); cb(); }, "should build a blank tree with a root": function (test) { var node = tree.root, blank = utils.blankSummary(); blank.statements.pct = blank.lines.pct = blank.branches.pct = blank.functions.pct = 100; test.ok(node); test.equal(SEP, node.fullPath()); test.equal('', node.displayShortName()); test.equal(0, node.children.length); test.deepEqual(blank, node.metrics); test.done(); } } }; istanbul-0.4.5/test/run-again.js000077500000000000000000000006241275640326200165530ustar00rootroot00000000000000#!/usr/bin/env node var nodeunit = require('nodeunit'), mkdirp = require('mkdirp'), loader = require('./loader'), common = require('./common'); function runTests() { var outputDir = common.getBuildDir(); mkdirp.sync(outputDir); loader.runTests(process.argv[2], nodeunit.reporters['default'], undefined, function (err) { if (err) { throw err; } }); } runTests(); istanbul-0.4.5/test/run.js000077500000000000000000000111551275640326200154770ustar00rootroot00000000000000#!/usr/bin/env node /* * Test runner for all unit tests, also set up as the "npm test" command for the package. * * Usage: run.js * where is a string to run only those test cases that have the string in the filename * is set to, say, 1, to indicate that the tests also need to be run in self-cover * mode. Otherwise it is assumed to be true if no pattern is passed in * * How self-cover works: * * 1. First run all the required tests and make sure that they pass * 2. Set an environment variable to indicate that self-coverage is in progress * 3. Run the equivalent of `istanbul cover test/run-junit.js` which * will "turn on" coverage for all simple unit tests. * 4. The CLI tests need to turn on coverage across a process boundary so that * coverage is performed in a sub-process when the commands are forked. * The `cli-helper.js` has specific code to turn this on. * It should be noted that the behavior of CLI tests is not exactly * identical in regular v/s self-cover mode. It is possible for CLI * tests to fail in one mode and pass in the other because of this. * The bottom line is: make changes to `cli-helper.js` with great care :) * 5. The browser test is a similar beast in that the local server sends regular * versus covered versions of instrumenter.js to the phantomJS browser * based on the coverage environment variable. This test is less * sensitive to modes of operation. */ /*jslint nomen: true */ var nodeunit = require('nodeunit'), fs = require('fs'), path = require('path'), loader = require('./loader'), child_process = require('child_process'), rimraf = require('rimraf'), common = require('./common'), cliHelper = require('./cli-helper'); function runTests(pat, forceCover) { var defaultReporter = nodeunit.reporters['default'], selfCover = forceCover || !pat, args, proc; cliHelper.setVerbose(process.env.VERBOSE); loader.runTests(pat, defaultReporter, undefined, function (err) { var coverageDir = common.getCoverageDir(); //if any test failed then we cannot obviously run self-coverage if (err) { throw err; } if (selfCover) { //delet the build dir rimraf.sync(common.getBuildDir()); //set up environment variable to set CLI and browser //tests know that they need to run in self-cover mode common.setSelfCover(true); console.log('Running self-coverage....'); // run the equivalent of // $ istanbul cover run-again.js -- args = [ '--harmony', path.resolve(__dirname, '..', 'lib', 'cli.js'), 'cover', '--self-test', '--dir', coverageDir, '--report', 'none', '--x', '**/node_modules/**', '--x', '**/test/**', '--x', '**/yui-load-hook.js', path.resolve(__dirname, 'run-again.js'), '--', pat || '' ]; console.log('Run node ' + args.join(' ')); proc = child_process.spawn('node', args); proc.stdout.on('data', function (data) { process.stdout.write(data); }); proc.stderr.on('data', function (data) { process.stderr.write(data); }); proc.on('exit', function (exitCode) { if (exitCode !== 0) { throw new Error('self-cover returned exit code [' + exitCode + ']'); } var Collector = require('../lib/collector'), collector = new Collector(), Report = require('../lib/report'), reporter = Report.create('lcov', { dir: coverageDir }), summary = Report.create('text-summary'), detail = Report.create('text'); fs.readdirSync(coverageDir).forEach(function (file) { if (file.indexOf('cov') === 0 && file.indexOf('.json') > 0) { collector.add(JSON.parse(fs.readFileSync(path.resolve(coverageDir, file), 'utf8'))); } }); reporter.writeReport(collector, true); detail.writeReport(collector, true); summary.writeReport(collector, true); }); } }); } runTests(process.argv[2], process.argv[3]); istanbul-0.4.5/yui-coverage-comparison.md000066400000000000000000000111161275640326200204410ustar00rootroot00000000000000Comparison with YUI coverage ============================ Differences in features ----------------------- * YUI coverage - line and function coverage. Istanbul - statement, function and branch coverage. Reverse-engineers line-coverage from statement coverage with 100% fidelity at reporting time. * Output report formats are not as extensive as YUI coverage reporting. It supports LCOV and a custom HTML format (all-JS implementation) that only highlights missing coverage. "Standard" LCOV HTML reports can be gotten by running `genhtml` on the `lcov.info` file. Notice that this will also have branch coverage information, assuming you are using a recent `lcov` version. Differences in processing ------------------------- YUI coverage emits a function call to increment line/ function coverage as appropriate. The function call also tests to see if that specific line/ function was covered before and, if not, increments the coverage count for lines/ functions as appropriate. Istanbul emits code that does simple post-increments of object attributes without resorting to function calls at all. This also means that it does not keep track of derived information and does not bother to keep track of "how many statements/ branches/ functions have been covered so far", preferring to do this at report generation time. Differences in coverage object and generated code ------------------------------------------------- These are the ways in which the objects differ: * The YUI coverage object tracks line and function execution counts and also keeps track of how many lines/ functions were covered. The Istanbul coverage object only keeps tracks of statement, function and branch execution counts. * Due to the backwards-incompatible format of the Istanbul coverage object, it is not named `_yuitest_coverage` but `__coverage__` by default. You can change this to any name you want using an instrumenter option. * The YUI coverage object packs the entire source code of the file into an array for every file. The Istanbul coverage objects prefers to not do this by default and thereby does not automatically double the size of every JS file. YAGNI. Unless you do, in which case you have to ask for it. * All access to the YUI coverage object is of the form `global_object['/path/to/file'].property` Istanbul generates a temporary variable name based on the MD5 hash of the file path and uses that for assignment, as in: `_covRanDomJunk = global_object['/path/to/file']` and, subsequently `_covRanDomJunk.property[index]++` - this avoids a hash lookup for every increment call and also makes the generated code size smaller. * YUI coverage emits code that is human-readable and tries to keep the lines of the generated code in around the same place as the source. Istanbul emits minified code by default unless told not to. There is no reason the covered code needs to look anything like the original as long as it works the same way. In short, Istanbul provides smaller code size and faster execution at the expense of maintaining only raw data and no derived information (even though it tracks one extra metric). Differences in tooling ---------------------- * Istanbul wants to be as unobtrusive as possible and provides module load hooks (for `require` and `vm.createScript`) to transparently instrument code in a `node` environment. YUI coverage, being java, necessitates a pre-processing build step (or an expensive runtime one). * The same concept should be applicable for instrumenting and testing JS code meant for the browser. Just serve your file using a nodejs server (or Yeti) and instrument your code using custom middleware/ interceptors/ whatever. * The instrumentation command accepts wildcarded exclusion patterns so as to be able to run all the required instrumentation in one command for the 'pre-processing step' case. Known bugs in YUI coverage fixed -------------------------------- The following known bugs in YUI coverage do not exist in Istanbul. This, of course, says nothing about what _other_ bugs Istanbul might have :) * No function calls for incrementing execution counts which implies no "Deep recursion" message from older IE (or something like that) when IE sees a bunch of repeated function calls to the same function. * Statements with labels correctly handled * `if (a) foo() else if (b) bar() else baz()` case correctly handled * Coverage object for a file is assigned to the global object at module load only if it already does not exist in it. This handles cases where a module is reloaded in node (as a result of nuking the `require` cache) correctly and preserves all execution counts from the previously loaded version. istanbul-0.4.5/yuidoc.json000066400000000000000000000003251275640326200155370ustar00rootroot00000000000000{ "name": "The Istanbul API", "description": "The Istanbul API: a code coverage library", "options": { "outdir": "./public/apidocs", "exclude": "vendor,node_modules,misc,test" } }