Skip to content
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

Add graceful exit on SIGINT #1331

Merged
merged 1 commit into from
May 2, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion Changelog.md
Original file line number Diff line number Diff line change
@@ -1,9 +1,13 @@
# Unreleased
# v0.11.10 2022-05-02

* [#1328](https://github.com/mbj/mutant/pull/1328)

Fix incomplete mutations for named regexp capture groups. As an example, `/(?<name>\w\d)/` now gets mutated to `/(?<name>\W\d)/` and `/(?<name>\w\D)/` instead of just the former case.

* [#1331](https://github.com/mbj/mutant/pull/1331)

Add graceful but urgent exit on SIGINT.

# v0.11.9 2022-05-01

* [#1327](https://github.com/mbj/mutant/pull/1327)
Expand Down
20 changes: 18 additions & 2 deletions lib/mutant/parallel/driver.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ module Mutant
module Parallel
# Driver for parallelized execution
class Driver
include Adamantium, Anima.new(
include Anima.new(
:threads,
:var_active_jobs,
:var_final,
Expand All @@ -16,18 +16,34 @@ class Driver

private(*anima.attribute_names)

def initialize(**attributes)
@alive = true
super
end

# Wait for computation to finish, with timeout
#
# @param [Float] timeout
#
# @return [Variable::Result<Sink#status>]
# current status
def wait_timeout(timeout)
var_final.take_timeout(timeout)
var_final.take_timeout(timeout) if @alive

finalize(status)
end

# Stop parallel computation
#
# This will cause all work to be immediately stopped.
#
# @return [self]
def stop
@alive = false
threads.each(&:kill)
self
end

private

def finalize(status)
Expand Down
4 changes: 4 additions & 0 deletions lib/mutant/runner.rb
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,10 @@ def self.run_mutation_analysis(env)
private_class_method :run_mutation_analysis

def self.run_driver(reporter, driver)
Signal.trap('INT') do
driver.stop
end

loop do
status = driver.wait_timeout(reporter.delay)
break status.payload if status.done?
Expand Down
3 changes: 2 additions & 1 deletion spec/support/xspec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,8 @@ def error(message)
"#{message},
observation:
#{observation.inspect}
expectation: #{expectation.inspect}"
expectation:
#{expectation.inspect}"
MESSAGE
end
end # Verifier
Expand Down
163 changes: 113 additions & 50 deletions spec/unit/mutant/parallel/driver_spec.rb
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# frozen_string_literal: true

RSpec.describe Mutant::Parallel::Driver do
RSpec.describe Mutant::Parallel::Driver, mutant_expression: 'Mutant::Parallel::Driver*' do
let(:active_jobs) { [] }
let(:sink_status) { instance_double(Object) }
let(:thread_a) { instance_double(Thread, alive?: true) }
Expand Down Expand Up @@ -35,6 +35,31 @@
)
end

describe '#stop' do
def apply
subject.stop
end

let(:raw_expectations) do
[
{
receiver: thread_a,
selector: :kill
},
{
receiver: thread_b,
selector: :kill
}
]
end

it 'returns self' do
verify_events do
expect(apply).to eql(subject)
end
end
end

describe '#wait_timeout' do
def apply
subject.wait_timeout(timeout)
Expand All @@ -57,77 +82,115 @@ def apply
end
end

let(:raw_expectations) do
[
{
receiver: var_final,
selector: :take_timeout,
arguments: [timeout],
reaction: { return: Mutant::Variable.const_get(:Result)::Timeout.new }
},
{
receiver: var_active_jobs,
selector: :with,
reaction: { yields: [active_jobs] }
},
{
receiver: var_sink,
selector: :with,
reaction: { yields: [sink] }
}
]
end
shared_examples 'when done' do
context 'when done' do
before do
allow(thread_a).to receive_messages(alive?: false)
allow(thread_b).to receive_messages(alive?: false)
end

context 'when not done' do
let(:expected_status) do
Mutant::Parallel::Status.new(
active_jobs: active_jobs,
done: false,
payload: sink_status
)
end
let(:raw_expectations) do
[
*super(),
{
receiver: worker_a,
selector: :join
},
{
receiver: worker_b,
selector: :join
},
{
receiver: thread_a,
selector: :join
},
{
receiver: thread_b,
selector: :join
}
]

end

include_examples 'returns expected status'
let(:expected_status) do
Mutant::Parallel::Status.new(
active_jobs: active_jobs,
done: true,
payload: sink_status
)
end

include_examples 'returns expected status'
end
end

context 'when done' do
before do
allow(thread_a).to receive_messages(alive?: false)
allow(thread_b).to receive_messages(alive?: false)
context 'when stopped' do
def apply
subject.stop
super()
end

let(:raw_expectations) do
[
*super(),
{
receiver: worker_a,
selector: :join
receiver: thread_a,
selector: :kill
},
{
receiver: worker_b,
selector: :join
receiver: thread_b,
selector: :kill
},
{
receiver: thread_a,
selector: :join
receiver: var_active_jobs,
selector: :with,
reaction: { yields: [active_jobs] }
},
{
receiver: thread_b,
selector: :join
receiver: var_sink,
selector: :with,
reaction: { yields: [sink] }
}
]
end

include_examples 'when done'
end

context 'when not stopped' do
let(:raw_expectations) do
[
{
receiver: var_final,
selector: :take_timeout,
arguments: [timeout],
reaction: { return: Mutant::Variable.const_get(:Result)::Timeout.new }
},
{
receiver: var_active_jobs,
selector: :with,
reaction: { yields: [active_jobs] }
},
{
receiver: var_sink,
selector: :with,
reaction: { yields: [sink] }
}
]
end

let(:expected_status) do
Mutant::Parallel::Status.new(
active_jobs: active_jobs,
done: true,
payload: sink_status
)
context 'when not done' do
let(:expected_status) do
Mutant::Parallel::Status.new(
active_jobs: active_jobs,
done: false,
payload: sink_status
)
end

include_examples 'returns expected status'
end

include_examples 'returns expected status'
include_examples 'when done'
end
end
end
Loading