# module Coverage

`Coverage` provides coverage measurement feature for Ruby. This feature is experimental, so these APIs may be changed in future.

# Usage¶ ↑

1. require “coverage”

2. do `Coverage.start`

3. require or load Ruby source file

4. `Coverage.result` will return a hash that contains filename as key and coverage array as value. A coverage array gives, for each line, the number of line execution by the interpreter. A `nil` value means coverage is disabled for this line (lines like `else` and `end`).

# Examples¶ ↑

    [foo.rb]
    s = 0
    10.times do |x|
      s += x
    end

    if s == 45
      p :ok
    else
      p :ng
    end
    [EOF]

    require "coverage"
    Coverage.start
    require "foo.rb"
    p Coverage.result  #=> {"foo.rb"=>[1, 1, 10, nil, nil, 1, 1, nil, 0, nil]}

## Lines `Coverage`¶ ↑

If a coverage mode is not explicitly specified when starting coverage, lines coverage is what will run. It reports the number of line executions for each line.

    require "coverage"
    Coverage.start(lines: true)
    require "foo.rb"
    p Coverage.result #=> {"foo.rb"=>{:lines=>[1, 1, 10, nil, nil, 1, 1, nil, 0, nil]}}

The value of the lines coverage result is an array containing how many times each line was executed. Order in this array is important. For example, the first item in this array, at index 0, reports how many times line 1 of this file was executed while coverage was run (which, in this example, is one time).

A `nil` value means coverage is disabled for this line (lines like `else` and `end`).

## Oneshot Lines `Coverage`¶ ↑

Oneshot lines coverage tracks and reports on the executed lines while coverage is running. It will not report how many times a line was executed, only that it was executed.

    require "coverage"
    Coverage.start(oneshot_lines: true)
    require "foo.rb"
    p Coverage.result #=> {"foo.rb"=>{:oneshot_lines=>[1, 2, 3, 6, 7]}}

The value of the oneshot lines coverage result is an array containing the line numbers that were executed.

## Branches `Coverage`¶ ↑

Branches coverage reports how many times each branch within each conditional was executed.

    require "coverage"
    Coverage.start(branches: true)
    require "foo.rb"
    p Coverage.result #=> {"foo.rb"=>{:branches=>{[:if, 0, 6, 0, 10, 3]=>{[:then, 1, 7, 2, 7, 7]=>1, [:else, 2, 9, 2, 9, 7]=>0}}}}

Each entry within the branches hash is a conditional, the value of which is another hash where each entry is a branch in that conditional. The values are the number of times the method was executed, and the keys are identifying information about the branch.

The information that makes up each key identifying branches or conditionals is the following, from left to right:

1. A label for the type of branch or conditional.

2. A unique identifier.

3. The starting line number it appears on in the file.

4. The starting column number it appears on in the file.

5. The ending line number it appears on in the file.

6. The ending column number it appears on in the file.

## Methods `Coverage`¶ ↑

Methods coverage reports how many times each method was executed.

    [foo_method.rb]
    class Greeter
      def greet
        "welcome!"
      end
    end

    def hello
      "Hi"
    end

    hello()
    Greeter.new.greet()
    [EOF]

    require "coverage"
    Coverage.start(methods: true)
    require "foo_method.rb"
    p Coverage.result #=> {"foo_method.rb"=>{:methods=>{[Object, :hello, 7, 0, 9, 3]=>1, [Greeter, :greet, 2, 2, 4, 5]=>1}}}

Each entry within the methods hash represents a method. The values in this hash are the number of times the method was executed, and the keys are identifying information about the method.

The information that makes up each key identifying a method is the following, from left to right:

1. The class.

2. The method name.

3. The starting line number the method appears on in the file.

4. The starting column number the method appears on in the file.

5. The ending line number the method appears on in the file.

6. The ending column number the method appears on in the file.

## All `Coverage` Modes¶ ↑

You can also run all modes of coverage simultaneously with this shortcut. Note that running all coverage modes does not run both lines and oneshot lines. Those modes cannot be run simultaneously. Lines coverage is run in this case, because you can still use it to determine whether or not a line was executed.

    require "coverage"
    Coverage.start(:all)
    require "foo.rb"
    p Coverage.result #=> {"foo.rb"=>{:lines=>[1, 1, 10, nil, nil, 1, 1, nil, 0, nil], :branches=>{[:if, 0, 6, 0, 10, 3]=>{[:then, 1, 7, 2, 7, 7]=>1, [:else, 2, 9, 2, 9, 7]=>0}}, :methods=>{}}}

### Public Class Methods

line_stub(file) click to toggle source

    # File ext/coverage/lib/coverage.rb, line 4
    def self.line_stub(file)
      lines = File.foreach(file).map { nil }
      iseqs = [RubyVM::InstructionSequence.compile_file(file)]
      until iseqs.empty?
        iseq = iseqs.pop
        iseq.trace_points.each {|n, type| lines[n - 1] = 0 if type == :line }
        iseq.each_child {|child| iseqs << child }
      end
      lines
    end

peek_result → hash click to toggle source

Returns a hash that contains filename as key and coverage array as value. This is the same as `Coverage.result(stop: false, clear: false)`.

    {
      "file.rb" => [1, 2, nil],
      ...
    }

    static VALUE
    rb_coverage_peek_result(VALUE klass)
    {
        VALUE coverages = rb_get_coverages();
        VALUE ncoverages = rb_hash_new();
        if (!RTEST(coverages)) {
            rb_raise(rb_eRuntimeError, "coverage measurement is not enabled");
        }
        OBJ_WB_UNPROTECT(coverages);
        st_foreach(RHASH_TBL_RAW(coverages), coverage_peek_result_i, ncoverages);

        if (current_mode & COVERAGE_TARGET_METHODS) {
            rb_objspace_each_objects(method_coverage_i, &ncoverages);
        }

        rb_hash_freeze(ncoverages);
        return ncoverages;
    }

result(stop: true, clear: true) → hash click to toggle source

Returns a hash that contains filename as key and coverage array as value. If `clear` is true, it clears the counters to zero. If `stop` is true, it disables coverage measurement.

    static VALUE
    rb_coverage_result(int argc, VALUE *argv, VALUE klass)
    {
        VALUE ncoverages;
        VALUE opt;
        int stop = 1, clear = 1;

        rb_scan_args(argc, argv, "01", &opt);

        if (argc == 1) {
            opt = rb_convert_type(opt, T_HASH, "Hash", "to_hash");
            stop = RTEST(rb_hash_lookup(opt, ID2SYM(rb_intern("stop"))));
            clear = RTEST(rb_hash_lookup(opt, ID2SYM(rb_intern("clear"))));
        }

        ncoverages = rb_coverage_peek_result(klass);
        if (stop && !clear) {
            rb_warn("stop implies clear");
            clear = 1;
        }
        if (clear) {
            rb_clear_coverages();
            if (!NIL_P(me2counter)) rb_hash_foreach(me2counter, clear_me2counter_i, Qnil);
        }
        if (stop) {
            rb_reset_coverages();
            me2counter = Qnil;
        }
        return ncoverages;
    }

running? → bool click to toggle source

Returns true if coverage stats are currently being collected (after `Coverage.start` call, but before `Coverage.result` call)

    static VALUE
    rb_coverage_running(VALUE klass)
    {
        VALUE coverages = rb_get_coverages();
        return RTEST(coverages) ? Qtrue : Qfalse;
    }

start → nil click to toggle source

Enables coverage measurement.

    static VALUE
    rb_coverage_start(int argc, VALUE *argv, VALUE klass)
    {
        VALUE coverages, opt;
        int mode;

        rb_scan_args(argc, argv, "01", &opt);

        if (argc == 0) {
            mode = 0; /* compatible mode */
        }
        else if (opt == ID2SYM(rb_intern("all"))) {
            mode = COVERAGE_TARGET_LINES | COVERAGE_TARGET_BRANCHES | COVERAGE_TARGET_METHODS;
        }
        else {
            mode = 0;
            opt = rb_convert_type(opt, T_HASH, "Hash", "to_hash");

            if (RTEST(rb_hash_lookup(opt, ID2SYM(rb_intern("lines")))))
                mode |= COVERAGE_TARGET_LINES;
            if (RTEST(rb_hash_lookup(opt, ID2SYM(rb_intern("branches")))))
                mode |= COVERAGE_TARGET_BRANCHES;
            if (RTEST(rb_hash_lookup(opt, ID2SYM(rb_intern("methods")))))
                mode |= COVERAGE_TARGET_METHODS;
            if (RTEST(rb_hash_lookup(opt, ID2SYM(rb_intern("oneshot_lines"))))) {
                if (mode & COVERAGE_TARGET_LINES)
                    rb_raise(rb_eRuntimeError, "cannot enable lines and oneshot_lines simultaneously");
                mode |= COVERAGE_TARGET_LINES;
                mode |= COVERAGE_TARGET_ONESHOT_LINES;
            }
        }

        if (mode & COVERAGE_TARGET_METHODS) {
            me2counter = rb_ident_hash_new();
        }
        else {
            me2counter = Qnil;
        }

        coverages = rb_get_coverages();
        if (!RTEST(coverages)) {
            coverages = rb_hash_new();
            rb_obj_hide(coverages);
            current_mode = mode;
            if (mode == 0) mode = COVERAGE_TARGET_LINES;
            rb_set_coverages(coverages, mode, me2counter);
        }
        else if (current_mode != mode) {
            rb_raise(rb_eRuntimeError, "cannot change the measuring target during coverage measurement");
        }
        return Qnil;
    }