-
Notifications
You must be signed in to change notification settings - Fork 21
Allow collecting coverage in parallel using DRb #400
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,6 +1,10 @@ | ||
| # frozen_string_literal: true | ||
|
|
||
| require_relative 'coverage/plan' | ||
| require_relative 'coverage/tracker' | ||
| require_relative 'coverage/covered_request' | ||
| require_relative 'coverage/covered_response' | ||
| require 'drb' | ||
|
|
||
| module OpenapiFirst | ||
| module Test | ||
|
|
@@ -12,19 +16,20 @@ module Coverage | |
|
|
||
| Result = Data.define(:plans, :coverage) | ||
|
|
||
| @current_run = {} | ||
|
|
||
| class << self | ||
| # @visibility private | ||
| def install | ||
| raise NoMethodError, 'Coverage.install was removed. Please use Test.setup instead' | ||
| end | ||
|
|
||
| def start(skip_response: nil, skip_route: nil) | ||
| @current_run = Test.definitions.values.to_h do |oad| | ||
| plan = Plan.for(oad, skip_response:, skip_route:) | ||
| [oad.key, plan] | ||
| end | ||
| return if @drb_uri | ||
|
|
||
| tracker = Tracker.new(Test.definitions, skip_response:, skip_route:) | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I am looking for code to distinguish the main process from "remote" process… but that is not needed here, because There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes I assume here that Do you consider the Coverage module to be public API? In the Test module we at least safe-guard against multiple calls to
Owner
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Good question. I think I started out with Test::Coverage being public API, but added Test.setup after that. So I think Coverage should be considered private. …which could make some class methods on Coverage obsolete. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If you want to we can add something like
I thought about this as well while exchanging the
Owner
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Hey @richardboehme, I think both approaches are fine. I don't have a strong opinion on this things right now. Maybe the second variant is simpler and respects the lines between Test and Coverage? Do you want to add that guard in this PR?
I There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Okay I added a small guard on top of the |
||
|
|
||
| # We need a custom DRbServer (not using DRb.start_service) because otherwise | ||
| # we'd conflict with Rails's DRb server | ||
| @drb_uri = DRb::DRbServer.new(nil, tracker).uri | ||
| end | ||
|
|
||
| # @visibility private | ||
|
|
@@ -34,15 +39,45 @@ def uninstall | |
|
|
||
| # Clear current coverage run | ||
| def reset | ||
| @current_run = {} | ||
| @tracker = nil | ||
|
|
||
| return unless @drb_uri | ||
|
|
||
| service = DRb.fetch_server(@drb_uri) | ||
| service&.stop_service | ||
| @drb_uri = nil | ||
| end | ||
|
|
||
| def track_request(request, oad) | ||
| current_run[oad.key]&.track_request(request) | ||
| return unless request.known? | ||
|
|
||
| # The call to `track_request` may happen remotely in the main process that started | ||
| # the coverage collection. | ||
| # To make this work we need to keep arguments trivial, which is the reason the request | ||
| # is wrapped in a CoveredRequest data object. | ||
| tracker&.track_request( | ||
| oad.key, | ||
| CoveredRequest.new( | ||
| key: request.request_definition.key, | ||
| error: request.error | ||
| ) | ||
| ) | ||
| end | ||
|
|
||
| def track_response(response, _request, oad) | ||
| current_run[oad.key]&.track_response(response) | ||
| return unless response.known? | ||
|
|
||
| # The call to `track_response` may happen remotely in the main process that started | ||
| # the coverage collection. | ||
| # To make this work we need to keep arguments trivial, which is the reason the response | ||
| # is wrapped in a CoveredResponse data object. | ||
| tracker&.track_response( | ||
| oad.key, | ||
| CoveredResponse.new( | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 👍🏼 |
||
| key: response.response_definition.key, | ||
| error: response.error | ||
| ) | ||
| ) | ||
| end | ||
|
|
||
| def result | ||
|
|
@@ -51,11 +86,19 @@ def result | |
|
|
||
| private | ||
|
|
||
| attr_reader :current_run | ||
| def current_run | ||
| tracker.plans_by_key | ||
| end | ||
|
|
||
| # Returns all plans (Plan) that were registered for this run | ||
| def plans | ||
| current_run.values | ||
| tracker&.plans || [] | ||
| end | ||
|
|
||
| def tracker | ||
| return unless @drb_uri | ||
|
|
||
| @tracker ||= DRbObject.new_with_uri(@drb_uri) | ||
| end | ||
|
|
||
| def coverage | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,11 @@ | ||
| # frozen_string_literal: true | ||
|
|
||
| module OpenapiFirst | ||
| module Test | ||
| module Coverage | ||
| CoveredRequest = Data.define(:key, :error) do | ||
| def valid? = error.nil? | ||
| end | ||
| end | ||
| end | ||
| end |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,11 @@ | ||
| # frozen_string_literal: true | ||
|
|
||
| module OpenapiFirst | ||
| module Test | ||
| module Coverage | ||
| CoveredResponse = Data.define(:key, :error) do | ||
| def valid? = error.nil? | ||
| end | ||
| end | ||
| end | ||
| end |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,32 @@ | ||
| # frozen_string_literal: true | ||
|
|
||
| module OpenapiFirst | ||
| module Test | ||
| module Coverage | ||
| # Class that allows tracking requests and response for OAD definitions. | ||
| # For each definition it builds a plan and forwards tracking to the correct plan. | ||
| class Tracker | ||
| attr_reader :plans_by_key | ||
|
|
||
| def initialize(definitions, skip_response: nil, skip_route: nil) | ||
| @plans_by_key = definitions.values.to_h do |oad| | ||
| plan = Plan.for(oad, skip_response:, skip_route:) | ||
| [oad.key, plan] | ||
| end | ||
| end | ||
|
|
||
| def track_request(key, request) | ||
| @plans_by_key[key]&.track_request(request) | ||
| end | ||
|
|
||
| def track_response(key, response) | ||
| @plans_by_key[key]&.track_response(response) | ||
| end | ||
|
|
||
| def plans | ||
| @plans_by_key.values | ||
| end | ||
| end | ||
| end | ||
| end | ||
| end |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍🏼