modul Coverage
Coverage bietet eine Funktion zur Messung der Codeabdeckung für Ruby. Diese Funktion ist experimentell, daher können sich diese APIs in Zukunft ändern.
Vorsicht: Derzeit wird nur die prozessweite Codeabdeckung unterstützt. Sie können keine Thread-spezifische Abdeckung messen.
Verwendung
-
require “coverage”
-
require oder laden Sie eine Ruby-Quelldatei
-
Coverage.resultgibt einen Hash zurück, der den Dateinamen als Schlüssel und ein Abdeckungsarray als Wert enthält. Ein Abdeckungsarray gibt für jede Zeile die Anzahl der Zeilenausführungen durch den Interpreter an. Ein Wert vonnilbedeutet, dass die Abdeckung für diese Zeile deaktiviert ist (Zeilen wieelseundend).
Beispiele
[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]}
Zeilen-Coverage
Wenn beim Starten der Abdeckung kein Abdeckungsmodus explizit angegeben wird, wird die Zeilenabdeckung ausgeführt. Sie meldet die Anzahl der Zeilenausführungen für jede Zeile.
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]}}
Der Wert des Zeilenabdeckungsergebnisses ist ein Array, das angibt, wie oft jede Zeile ausgeführt wurde. Die Reihenfolge in diesem Array ist wichtig. Beispielsweise meldet das erste Element in diesem Array, an Index 0, wie oft Zeile 1 dieser Datei ausgeführt wurde, während die Abdeckung lief (was in diesem Beispiel einmal ist).
Ein Wert von nil bedeutet, dass die Abdeckung für diese Zeile deaktiviert ist (Zeilen wie else und end).
Einmalige Zeilen-Coverage
Die einmalige Zeilenabdeckung verfolgt und meldet die ausgeführten Zeilen, während die Abdeckung läuft. Sie meldet nicht, wie oft eine Zeile ausgeführt wurde, sondern nur, dass sie ausgeführt wurde.
require "coverage" Coverage.start(oneshot_lines: true) require "foo.rb" p Coverage.result #=> {"foo.rb"=>{:oneshot_lines=>[1, 2, 3, 6, 7]}}
Der Wert des Ergebnisses der einmaligen Zeilenabdeckung ist ein Array, das die Nummern der ausgeführten Zeilen enthält.
Zweigstellen-Coverage
Die Zweigstellenabdeckung meldet, wie oft jede Zweigstelle innerhalb jeder Bedingung ausgeführt wurde.
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}}}}
Jeder Eintrag im Zweigstellen-Hash ist eine Bedingung, deren Wert ein weiterer Hash ist, bei dem jeder Eintrag eine Zweigstelle in dieser Bedingung darstellt. Die Werte sind die Anzahl der Ausführungen der Methode, und die Schlüssel sind identifizierende Informationen über die Zweigstelle.
Die Informationen, die jeden Schlüssel zur Identifizierung von Zweigstellen oder Bedingungen bilden, sind von links nach rechts die folgenden:
-
Ein Label für den Typ der Zweigstelle oder Bedingung.
-
Eine eindeutige Kennung.
-
Die Startzeilennummer, unter der sie in der Datei erscheint.
-
Die Startspaltennummer, unter der sie in der Datei erscheint.
-
Die Endzeilennummer, unter der sie in der Datei erscheint.
-
Die Endspaltennummer, unter der sie in der Datei erscheint.
Methoden-Coverage
Die Methodenabdeckung meldet, wie oft jede Methode ausgeführt wurde.
[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}}}
Jeder Eintrag im Methoden-Hash stellt eine Methode dar. Die Werte in diesem Hash sind die Anzahl der Ausführungen der Methode, und die Schlüssel sind identifizierende Informationen über die Methode.
Die Informationen, die jeden Schlüssel zur Identifizierung einer Methode bilden, sind von links nach rechts die folgenden:
-
Die Klasse.
-
Der Methodenname.
-
Die Startzeilennummer, unter der die Methode in der Datei erscheint.
-
Die Startspaltennummer, unter der die Methode in der Datei erscheint.
-
Die Endzeilennummer, unter der die Methode in der Datei erscheint.
-
Die Endspaltennummer, unter der die Methode in der Datei erscheint.
Alle Abdeckungsmodi
Sie können auch alle Abdeckungsmodi gleichzeitig mit dieser Verknüpfung ausführen. Beachten Sie, dass die Ausführung aller Abdeckungsmodi nicht sowohl Zeilen als auch einmalige Zeilen ausführt. Diese Modi können nicht gleichzeitig ausgeführt werden. Die Zeilenabdeckung wird in diesem Fall ausgeführt, da Sie sie immer noch verwenden können, um festzustellen, ob eine Zeile ausgeführt wurde oder nicht.
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=>{}}}
Öffentliche Klassenmethoden
Source
# File ext/coverage/lib/coverage.rb, line 9 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
Eine einfache Hilfsfunktion, die das „Stub“ der Zeilenabdeckung aus einem gegebenen Quellcode erstellt.
Source
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");
}
rb_hash_foreach(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;
}
Gibt einen Hash zurück, der den Dateinamen als Schlüssel und ein Abdeckungsarray als Wert enthält. Dies ist dasselbe wie Coverage.result(stop: false, clear: false).
{
"file.rb" => [1, 2, nil],
...
}
Source
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;
}
Gibt einen Hash zurück, der den Dateinamen als Schlüssel und ein Abdeckungsarray als Wert enthält. Wenn clear true ist, werden die Zähler auf Null zurückgesetzt. Wenn stop true ist, wird die Abdeckungsmessung deaktiviert.
Source
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;
}
Startet/setzt die Abdeckungsmessung fort.
Vorsicht: Derzeit wird nur die prozessweite Codeabdeckung unterstützt. Sie können keine Thread-spezifische Abdeckung messen. Wenn Ihr Prozess mehrere Threads hat, kann die Verwendung von Coverage.resume/suspend, um die von einem begrenzten Codeblock ausgeführte Codeabdeckung zu erfassen, zu irreführenden Ergebnissen führen.
Source
static VALUE
rb_coverage_running(VALUE klass)
{
return current_state == RUNNING ? Qtrue : Qfalse;
}
Gibt true zurück, wenn Abdeckungsstatistiken gerade gesammelt werden (nach einem Aufruf von Coverage.start, aber vor einem Aufruf von Coverage.result).
Source
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;
}
Richtet die Abdeckungsmessung ein.
Beachten Sie, dass diese Methode die Messung nicht selbst startet. Verwenden Sie Coverage.resume, um die Messung zu starten.
Möglicherweise möchten Sie Coverage.start verwenden, um die Messung einzurichten und dann zu starten.
Source
static VALUE
rb_coverage_start(int argc, VALUE *argv, VALUE klass)
{
rb_coverage_setup(argc, argv, klass);
rb_coverage_resume(klass);
return Qnil;
}
Aktiviert die Abdeckungsmessung. Siehe die Dokumentation der Klasse Coverage im Detail. Dies ist äquivalent zu Coverage.setup und Coverage.resume.
Source
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;
}
Gibt den Status der Abdeckungsmessung zurück.
Source
static VALUE
rb_coverage_supported(VALUE self, VALUE _mode)
{
ID mode = RB_SYM2ID(_mode);
return RBOOL(
mode == rb_intern("lines") ||
mode == rb_intern("oneshot_lines") ||
mode == rb_intern("branches") ||
mode == rb_intern("methods") ||
mode == rb_intern("eval")
);
}
Gibt true zurück, wenn die Abdeckungsmessung für den angegebenen Modus unterstützt wird.
Der Modus sollte eines der folgenden Symbole sein: :lines, :oneshot_lines, :branches, :methods, :eval.
Beispiel
Coverage.supported?(:lines) #=> true Coverage.supported?(:all) #=> false
Source
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;
}
Pausiert die Abdeckungsmessung. Sie können Coverage.resume verwenden, um die Messung neu zu starten.