Project

General

Profile

Actions

Feature #18176

closed

Make Coverage suspendable

Added by mame (Yusuke Endoh) over 2 years ago. Updated over 2 years ago.

Status:
Closed
Target version:
-
[ruby-core:105321]

Description

I'd like to add Coverage.suspend, Coverage.resume, and some methods.

Synopsis

 1: # target.rb
 2: def foo
 3:   :foo
 4: end
 5:
 6: def bar
 7:   :bar
 8: end
 9:
10: def baz
11:   :baz
12: end
require "coverage"

# Similar to Coverage.start, but does not start the measurement itself
Coverage.setup(oneshot_lines: true)

load "target.rb"

foo              # This call is not counted
Coverage.resume  # Start the measurement
bar              # This call is counted
Coverage.suspend # Stop the measure
baz              # This call is not counted

# The result is only for Line 7, the body of method "bar"
p Coverage.result #=> {"target.rb"=>{:oneshot_lines=>[7]}}

Background

The motivation is to divide modules for large web services. For web services with a long history, we tend to lose track of the dependencies between modules. Using this proposal and oneshot coverage, we can gather information about the code used to process a particular endpoint with almost no runtime cost. Gathering the information for some endpoints will give a hint to isolate the modules.

I've received similar requests in the past to make Coverage restartable but I didn't understand the need for it. (Sorry about that!) I heard directly from those who were actually in trouble in our company, and I finally understand. Also, the introduction of oneshot coverage, which can now be measured at almost no cost, has increased the demand for suspendable coverage.

New APIs

  • Coverage.setup: Almost the same as Coverage.start but does not start the measurement itself.
  • Coverage.resume: Start/resume the coverage measurement.
  • Coverage.suspend: Suspend the coverage measurement; it is restartable by using Coverage.resume.
  • Coverage.state: Returns the current state: :idle, :suspended, and :running.

Coverage.start(...) is now the same as Coverage.start(...); Coverage.resume.
Coverage.running? is the same is Coverage.state == :running.

Discussion

  • Currently, I think Coverage.suspend makes sense only for oneshot coverage, but it supports traditional coverage too, for a unknown use case. However, I may disallow it if we find any problems.
  • It is ideal to measure multiple oneshot coverage for each endpoint together, but it was difficult for me to implement it efficiently. My co-workers say that this feature is still valuable even with the limitation.
  • Another idea is to use TracePoint. However, I'd like to introduce this feature to the coverage library because (1) the runtime cost of TracePoint seems not to be negligible according to our preliminary experiment, (2) we can use an ecosystem for oneshot coverage (e.g., https://github.com/riseshia/oneshot_coverage), and (3) the changeset for coverage is not so large.

Implementation

https://github.com/ruby/ruby/pull/4856

Any comments are welcome.

Actions

Also available in: Atom PDF

Like0
Like0Like0