Spec-Zone.ru › Ruby 3.2

модуль Coverage

Coverage предоставляет функцию измерения покрытия для Ruby. Эта функция экспериментальная, поэтому эти API могут быть изменены в будущем.

Примечание: В настоящее время поддерживается только глобальное измерение покрытия процесса. Вы не можете измерять покрытие по потокам.

Использование

  1. require “coverage”

  2. do Coverage.start

  3. require или загрузите файл исходного кода Ruby

  4. Coverage.result вернёт хеш, который содержит имя файла в качестве ключа и массив покрытия в качестве значения. Массив покрытия даёт для каждой строки число выполнений строки интерпретатором. Значение nil означает, что покрытие отключено для этой строки (строки, подобные else и end).

Примеры

[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]}

Строки Coverage

Если режим покрытия не указан явно при запуске покрытия, будет запущено покрытие строк. Оно сообщает о количестве выполнений каждой строки.

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]}}

Значение результата покрытия строк — массив, содержащий количество раз, когда каждая строка была выполнена. Порядок в этом массиве важен. Например, первый элемент в этом массиве с индексом 0 сообщает, сколько раз строка 1 этого файла была выполнена во время выполнения покрытия (в данном примере — один раз).

Значение nil означает, что покрытие отключено для этой строки (строки, подобные else и end).

Однократное выполнение строк Coverage

Покрытие однократного выполнения строк отслеживает и сообщает о выполненных строках во время выполнения покрытия. Оно не будет сообщать, сколько раз строка была выполнена, а только то, что она была выполнена.

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

Значение результата покрытия однократного выполнения строк — массив, содержащий номера строк, которые были выполнены.

Покрытие ветвей Coverage

Покрытие ветвей сообщает о количестве раз, когда каждая ветвь в каждом условном операторе была выполнена.

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}}}}

Каждый элемент в хеше ветвей — это условный оператор, значение которого — другой хеш, где каждый элемент — ветвь в этом условном операторе. Значения — количество раз, когда метод был выполнен, а ключи — идентификационная информация о ветви.

Информация, составляющая каждый ключ, идентифицирующий ветви или условные операторы, приведена слева направо:

  1. Метка типа ветви или условного оператора.

  2. Уникальный идентификатор.

  3. Номер начальной строки, на которой он появляется в файле.

  4. Номер начальной колонки, на которой он появляется в файле.

  5. Номер конечной строки, на которой он появляется в файле.

  6. Номер конечной колонки, на которой он появляется в файле.

Покрытие методов Coverage

Покрытие методов сообщает о количестве раз, когда каждый метод был выполнен.

[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}}}

Каждый элемент в хеше методов представляет собой метод. Значения в этом хеше — количество раз, когда метод был выполнен, а ключи — идентификационная информация о методе.

Информация, составляющая каждый ключ, идентифицирующий метод, приведена слева направо:

  1. Класс.

  2. Имя метода.

  3. Номер начальной строки, на которой появляется метод в файле.

  4. Номер начальной колонки, на которой появляется метод в файле.

  5. Номер конечной строки, на которой появляется метод в файле.

  6. Номер конечной колонки, на которой появляется метод в файле.

Все Coverage режимы

Вы также можете запустить все режимы покрытия одновременно с этим сокращением. Обратите внимание, что запуск всех режимов покрытия не запускает и режимы строк и однократного выполнения строк. Эти режимы не могут быть запущены одновременно. В этом случае выполняется покрытие строк, потому что вы всё ещё можете использовать его, чтобы определить, была ли строка выполнена или нет.

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=>{}}}

Публичные методы класса

line_stub(file) Показать исходный код
# 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 Показать исходный код
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;
}

Возвращает хеш, который содержит имя файла в качестве ключа и массив покрытия в качестве значения. Это то же самое, что и ‘Coverage.result(stop: false, clear: false)`.

{
  "file.rb" => [1, 2, nil],
  ...
}
result(stop: true, clear: true) → hash Показать исходный код
static VALUE
rb_coverage_result(int argc, VALUE *argv, VALUE klass)
{
    VALUE ncoverages;
    VALUE opt;
    int stop = 1, clear = 1;

    if (current_state == IDLE) {
        rb_raise(rb_eRuntimeError, "coverage measurement is not enabled");
    }

    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) {
        if (current_state == RUNNING) {
            rb_coverage_suspend(klass);
        }
        rb_reset_coverages();
        me2counter = Qnil;
        current_state = IDLE;
    }
    return ncoverages;
}

Возвращает хеш, который содержит имя файла в качестве ключа и массив покрытия в качестве значения. Если clear равно true, счётчики обнуляются. Если stop равно true, измерение покрытия отключается.

resume → nil Показать исходный код
VALUE
rb_coverage_resume(VALUE klass)
{
    if (current_state == IDLE) {
        rb_raise(rb_eRuntimeError, "coverage measurement is not set up yet");
    }
    if (current_state == RUNNING) {
        rb_raise(rb_eRuntimeError, "coverage measurement is already running");
    }
    rb_resume_coverages();
    current_state = RUNNING;
    return Qnil;
}

Запустить/возобновить измерение покрытия.

Примечание: В настоящее время поддерживается только глобальное измерение покрытия процесса. Вы не можете измерять покрытие по потокам. Если ваш процесс имеет несколько потоков, использование Coverage.resume/suspend для захвата кода покрытия, выполненного только из ограниченного блока кода, может привести к получению вводящих в заблуждение результатов.

running? → bool Показать исходный код
static VALUE
rb_coverage_running(VALUE klass)
{
    return current_state == RUNNING ? Qtrue : Qfalse;
}

Возвращает true, если статистика покрытия в настоящее время собирается (после вызова Coverage.start, но перед вызовом Coverage.result)

setup → nil Показать исходный код
setup(:all) → nil
setup(lines: bool, branches: bool, methods: bool, eval: bool) → nil
setup(oneshot_lines: true) → nil
static VALUE
rb_coverage_setup(int argc, VALUE *argv, VALUE klass)
{
    VALUE coverages, opt;
    int mode;

    if (current_state != IDLE) {
        rb_raise(rb_eRuntimeError, "coverage measurement is already setup");
    }

    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 | COVERAGE_TARGET_EVAL;
    }
    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 (RTEST(rb_hash_lookup(opt, ID2SYM(rb_intern("eval")))))
            mode |= COVERAGE_TARGET_EVAL;
    }

    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);
        current_state = SUSPENDED;
    }
    else if (current_mode != mode) {
        rb_raise(rb_eRuntimeError, "cannot change the measuring target during coverage measurement");
    }

    return Qnil;
}

Настроить измерение покрытия.

Обратите внимание, что этот метод сам не запускает измерение. Используйте Coverage.resume для запуска измерения.

Возможно, вам захочется использовать Coverage.start для настройки и запуска измерения.

start → nil Показать исходный код
start(:all) → nil
start(lines: bool, branches: bool, methods: bool, eval: bool) → nil
start(oneshot_lines: true) → nil
static VALUE
rb_coverage_start(int argc, VALUE *argv, VALUE klass)
{
    rb_coverage_setup(argc, argv, klass);
    rb_coverage_resume(klass);
    return Qnil;
}

Включает измерение покрытия. См. документацию класса Coverage для подробностей. Это эквивалентно вызовам Coverage.setup и Coverage.resume.

state → :idle, :suspended, :running Показать исходный код
static VALUE
rb_coverage_state(VALUE klass)
{
    switch (current_state) {
        case IDLE: return ID2SYM(rb_intern("idle"));
        case SUSPENDED: return ID2SYM(rb_intern("suspended"));
        case RUNNING: return ID2SYM(rb_intern("running"));
    }
    return Qnil;
}

Возвращает состояние измерения покрытия.

supported?(mode) → true or false Показать исходный код
static VALUE
rb_coverage_supported(VALUE self, VALUE _mode)
{
    ID mode = RB_SYM2ID(_mode);

    return RBOOL(
        mode == rb_intern("lines") ||
        mode == rb_intern("branches") ||
        mode == rb_intern("methods") ||
        mode == rb_intern("eval")
    );
}

Возвращает true, если измерение покрытия поддерживается для данного режима.

Режим должен быть одним из следующих символов: :lines, :branches, :methods, :eval.

Пример:

Coverage.supported?(:lines)  #=> true
Coverage.supported?(:all)    #=> false
suspend → nil Показать исходный код
VALUE
rb_coverage_suspend(VALUE klass)
{
    if (current_state != RUNNING) {
        rb_raise(rb_eRuntimeError, "coverage measurement is not running");
    }
    rb_suspend_coverages();
    current_state = SUSPENDED;
    return Qnil;
}

Приостановить измерение покрытия. Вы можете использовать Coverage.resume для перезапуска измерения.

Ruby Core © 1993–2022 Yukihiro Matsumoto
Licensed under the Ruby License.
Ruby Standard Library © contributors
Licensed under their own licenses.

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API