Skip to content

Commit d257b77

Browse files
Merge pull request #76 from hanneskaeufler/hk-cluster-run
Hk cluster run
2 parents 0f1d408 + 454e9e2 commit d257b77

File tree

10 files changed

+131
-137
lines changed

10 files changed

+131
-137
lines changed

Makefile

+6
Original file line numberDiff line numberDiff line change
@@ -8,3 +8,9 @@ bin/crytic:
88
bin: build
99
mkdir -p $(SHARD_BIN)
1010
cp ./bin/crytic $(SHARD_BIN)
11+
12+
test-unit:
13+
docker run --rm -it -v "$(shell pwd):/src" -w /src crystallang/crystal:0.32.0 /bin/sh -c "./bin/test-unit"
14+
15+
test:
16+
docker run --rm -it -v "$(shell pwd):/src" -w /src crystallang/crystal:0.32.0 /bin/sh -c "./bin/test"

spec/fake_reporter.cr

-30
This file was deleted.

spec/mutation/no_mutation_spec.cr

+4-4
Original file line numberDiff line numberDiff line change
@@ -6,18 +6,18 @@ module Crytic::Mutation
66
describe "#run" do
77
it "runs crystal spec with a single spec file" do
88
fake = FakeProcessRunner.new
9-
mutation = NoMutation.with(["./single/test_spec.cr"], fake)
9+
mutation = NoMutation.with(["./single/test_spec.cr"])
1010

11-
mutation.run
11+
mutation.run(side_effects(process_runner: fake))
1212

1313
fake.cmd_with_args.last.should eq "crystal spec ./single/test_spec.cr"
1414
end
1515

1616
it "runs crystal spec with multiple spec files" do
1717
fake = FakeProcessRunner.new
18-
mutation = NoMutation.with(["./a/b_spec.cr", "./a/c_spec.cr"], fake)
18+
mutation = NoMutation.with(["./a/b_spec.cr", "./a/c_spec.cr"])
1919

20-
mutation.run
20+
mutation.run(side_effects(process_runner: fake))
2121

2222
fake.cmd_with_args.last.should eq "crystal spec ./a/b_spec.cr ./a/c_spec.cr"
2323
end

spec/runner/sequential_spec.cr

+49-51
Original file line numberDiff line numberDiff line change
@@ -9,73 +9,71 @@ module Crytic::Runner
99

1010
describe Sequential do
1111
describe "#run" do
12-
it "takes a list of subjects" do
13-
reporter = FakeReporter.new
14-
runner = Sequential.new(
15-
threshold: 100.0,
16-
generator: FakeGenerator.new,
17-
reporters: [reporter] of Crytic::Reporter::Reporter,
18-
no_mutation_factory: fake_no_mutation_factory)
19-
20-
runner.run(
21-
subjects(["./fixtures/require_order/blog.cr", "./fixtures/require_order/pages/blog/archive.cr"]),
22-
["./fixtures/simple/bar_spec.cr"]).should eq false
12+
it "returns the runs final result" do
13+
run = FakeRun.new
14+
run.final_result = true
15+
16+
Sequential.new.run(run, side_effects).should eq true
17+
18+
run.final_result = false
19+
Sequential.new.run(run, side_effects).should eq false
2320
end
2421

25-
it "doesn't execute mutations if the initial suite run fails" do
26-
reporter = FakeReporter.new
27-
runner = Sequential.new(
28-
threshold: 100.0,
29-
generator: FakeGenerator.new,
30-
reporters: [reporter] of Crytic::Reporter::Reporter,
31-
no_mutation_factory: ->(specs : Array(String)) {
32-
process_runner = Crytic::FakeProcessRunner.new
33-
no_mutation = Crytic::Mutation::NoMutation.with(specs, process_runner)
34-
process_runner.exit_code = [1, 0]
35-
no_mutation
36-
})
37-
38-
runner.run(
39-
subjects(["./fixtures/require_order/blog.cr", "./fixtures/require_order/pages/blog/archive.cr"]),
40-
["./fixtures/simple/bar_spec.cr"]).should eq false
22+
it "returns false if the original spec suite fails" do
23+
run = FakeRun.new
24+
run.original_exit_code = 1
25+
26+
Sequential.new.run(run, side_effects).should eq false
4127
end
4228

43-
it "reports events in order" do
44-
reporter = FakeReporter.new
45-
runner = Sequential.new(
46-
threshold: 100.0,
47-
generator: FakeGenerator.new([fake_mutation]),
48-
reporters: [reporter] of Crytic::Reporter::Reporter,
49-
no_mutation_factory: fake_no_mutation_factory)
29+
it "reports neutral results before mutation results" do
30+
run = FakeRun.new
31+
run.mutations = [FakeMutation.new.as(Crytic::Mutation::Mutation)]
5032

51-
runner.run(subjects(["./fixtures/simple/bar.cr"]), ["./fixtures/simple/bar_spec.cr"])
33+
Sequential.new.run(run, side_effects)
5234

53-
reporter.events.should eq ["report_original_result", "report_mutations", "report_neutral_result", "report_result", "report_summary", "report_msi"]
35+
run.events.should eq ["report_neutral_result", "report_result"]
5436
end
5537

5638
it "skips the mutations if the neutral result errored" do
57-
reporter = FakeReporter.new
39+
run = FakeRun.new
5840
mutation = fake_mutation
59-
runner = Sequential.new(
60-
threshold: 100.0,
61-
generator: FakeGenerator.new(
62-
neutral: erroring_mutation,
63-
mutations: [mutation]),
64-
reporters: [reporter] of Crytic::Reporter::Reporter,
65-
no_mutation_factory: fake_no_mutation_factory)
41+
run.neutral = FakeMutation.new(Crytic::Mutation::Status::Errored)
42+
run.mutations = [mutation]
6643

67-
runner.run(subjects(["./fixtures/simple/bar.cr"]), ["./fixtures/simple/bar_spec.cr"])
44+
Sequential.new.run(run, side_effects)
6845

69-
reporter.events.should_not contain("report_result")
46+
run.events.should_not contain("report_result")
7047
mutation.as(FakeMutation).run_call_count.should eq 0
7148
end
7249
end
7350
end
7451
end
7552

76-
private def runner
77-
Crytic::Runner::Sequential.new(
78-
threshold: 100.0,
79-
reporters: [] of Crytic::Reporter::Reporter,
80-
generator: FakeGenerator.new)
53+
private class FakeRun
54+
property mutations = [] of Crytic::Mutation::Mutation
55+
property events = [] of String
56+
property original_exit_code = 0
57+
property final_result = true
58+
property neutral = FakeMutation.new.as(Crytic::Mutation::Mutation)
59+
60+
def generate_mutations
61+
[Crytic::Generator::MutationSet.new(neutral, mutations)]
62+
end
63+
64+
def report_neutral_result(result)
65+
events << "report_neutral_result"
66+
end
67+
68+
def report_result(result)
69+
events << "report_result"
70+
end
71+
72+
def report_final(results)
73+
final_result
74+
end
75+
76+
def execute_original_test_suite(side_effects)
77+
Crytic::Mutation::OriginalResult.new(exit_code: original_exit_code, output: "")
78+
end
8179
end

spec/spec_helper.cr

+1-2
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@ require "./fake_generator"
66
require "./fake_http_client"
77
require "./fake_mutation"
88
require "./fake_process_runner"
9-
require "./fake_reporter"
109
require "compiler/crystal/syntax/*"
1110
require "spec"
1211

@@ -99,7 +98,7 @@ end
9998

10099
def fake_no_mutation_factory
101100
->(specs : Array(String)) {
102-
Crytic::Mutation::NoMutation.with(specs, Crytic::FakeProcessRunner.new)
101+
Crytic::Mutation::NoMutation.with(specs)
103102
}
104103
end
105104

src/crytic/command/test.cr

+5-2
Original file line numberDiff line numberDiff line change
@@ -12,10 +12,13 @@ class Crytic::Command::Test
1212
def execute(args)
1313
options = parse_options(args)
1414
generator = build_generator(options)
15+
factory = ->(specs : Array(String)) {
16+
Mutation::NoMutation.with(specs)
17+
}
1518

1619
Crytic::Runner::Sequential
17-
.new(options.msi_threshold, options.reporters, generator)
18-
.run(options.subject, options.spec_files)
20+
.new
21+
.run(Crytic::Runner::Run.from_options(options, generator, factory), @side_effects)
1922
end
2023

2124
private def parse_options(args)

src/crytic/mutation/no_mutation.cr

+5-6
Original file line numberDiff line numberDiff line change
@@ -4,19 +4,18 @@ require "./original_result"
44

55
module Crytic::Mutation
66
class NoMutation
7-
def run
7+
def run(side_effects)
88
io = IO::Memory.new
99
args = ["spec"] + @specs_file_paths
10-
exit_code = @process_runner.run("crystal", args, output: io, error: io)
10+
exit_code = side_effects.execute("crystal", args, output: io, error: io)
1111
OriginalResult.new(exit_code: exit_code, output: io.to_s)
1212
end
1313

14-
def self.with(specs : Array(String), process_runner)
15-
new(specs, process_runner)
14+
def self.with(specs : Array(String))
15+
new(specs)
1616
end
1717

18-
private def initialize(@specs_file_paths : Array(String),
19-
@process_runner : ProcessRunner)
18+
private def initialize(@specs_file_paths : Array(String))
2019
end
2120
end
2221
end

src/crytic/reporter/reporter.cr

+2
Original file line numberDiff line numberDiff line change
@@ -10,4 +10,6 @@ module Crytic::Reporter
1010
abstract def report_summary(results : Mutation::ResultSet)
1111
abstract def report_msi(results : Mutation::ResultSet)
1212
end
13+
14+
alias Reporters = Array(Reporter)
1315
end

src/crytic/runner/run.cr

+48
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
require "../mutation/no_mutation"
2+
require "../reporter/reporter"
3+
require "../subject"
4+
5+
module Crytic::Runner
6+
alias NoMutationFactory = (Array(String)) -> Mutation::NoMutation
7+
8+
class Run
9+
def initialize(
10+
@msi_threshold : Float64,
11+
@reporters : Crytic::Reporter::Reporters,
12+
@spec_files : Array(String),
13+
@subjects : Array(Subject),
14+
@generator : Generator::Generator,
15+
@no_mutation_factory : NoMutationFactory
16+
)
17+
end
18+
19+
def self.from_options(options, generator, no_mutation_factory)
20+
new(options.msi_threshold, options.reporters, options.spec_files, options.subject, generator, no_mutation_factory)
21+
end
22+
23+
def execute_original_test_suite(side_effects)
24+
original_result = @no_mutation_factory.call(@spec_files).run(side_effects)
25+
report_original_result(original_result)
26+
original_result
27+
end
28+
29+
def generate_mutations
30+
mutations = @generator.mutations_for(@subjects, @spec_files)
31+
report_mutations(mutations)
32+
mutations
33+
end
34+
35+
{% for method in [:original_result, :mutations, :neutral_result, :result, :msi, :summary] %}
36+
def report_{{ method.id }}(result)
37+
@reporters.each(&.report_{{ method.id }}(result))
38+
end
39+
{% end %}
40+
41+
def report_final(results)
42+
report_summary(results)
43+
report_msi(results)
44+
45+
!results.empty? && MsiCalculator.new(results).msi.passes?(@msi_threshold)
46+
end
47+
end
48+
end

src/crytic/runner/sequential.cr

+11-42
Original file line numberDiff line numberDiff line change
@@ -1,55 +1,24 @@
1-
require "../generator/generator"
2-
require "../msi_calculator"
3-
require "../mutation/no_mutation"
41
require "../mutation/result"
52
require "../mutation/result_set"
6-
require "../reporter/reporter"
3+
require "./run"
74

85
module Crytic::Runner
96
class Sequential
10-
alias Threshold = Float64
11-
alias NoMutationFactory = (Array(String)) -> Mutation::NoMutation
12-
13-
def initialize(
14-
@threshold : Threshold,
15-
@reporters : Array(Reporter::Reporter),
16-
@generator : Generator::Generator,
17-
@no_mutation_factory : NoMutationFactory = ->(specs : Array(String)) {
18-
Mutation::NoMutation.with(specs, ProcessProcessRunner.new)
19-
}
20-
)
21-
end
22-
23-
def run(subjects : Array(Subject), specs : Array(String)) : Bool
24-
original_result = run_original_test_suite(specs)
7+
def run(run, side_effects) : Bool
8+
original_result = run.execute_original_test_suite(side_effects)
259

2610
return false unless original_result.successful?
2711

28-
mutations = determine_possible_mutations(subjects, specs)
29-
results = Mutation::ResultSet.new(run_all_mutations(mutations))
30-
31-
@reporters.each(&.report_summary(results))
32-
@reporters.each(&.report_msi(results))
33-
34-
!results.empty? && MsiCalculator.new(results).msi.passes?(@threshold)
35-
end
36-
37-
private def run_original_test_suite(specs)
38-
original_result = @no_mutation_factory.call(specs).run
39-
@reporters.each(&.report_original_result(original_result))
40-
original_result
41-
end
12+
mutations = run.generate_mutations
13+
results = Mutation::ResultSet.new(run_all_mutations(mutations, run))
4214

43-
private def determine_possible_mutations(subject, specs)
44-
mutations = @generator.mutations_for(subject, specs)
45-
@reporters.each(&.report_mutations(mutations))
46-
mutations
15+
run.report_final(results)
4716
end
4817

49-
private def run_mutations_for_single_subject(mutation_set)
18+
private def run_mutations_for_single_subject(mutation_set, run)
5019
mutation_set.mutated.map do |mutation|
5120
result = mutation.run
52-
@reporters.each(&.report_result(result))
21+
run.report_result(result)
5322
result
5423
end
5524
end
@@ -58,15 +27,15 @@ module Crytic::Runner
5827
[] of Mutation::Result
5928
end
6029

61-
private def run_all_mutations(mutations)
30+
private def run_all_mutations(mutations, run)
6231
mutations.map do |mutation_set|
6332
neutral_result = mutation_set.neutral.run
64-
@reporters.each(&.report_neutral_result(neutral_result))
33+
run.report_neutral_result(neutral_result)
6534

6635
if neutral_result.errored?
6736
discard_further_mutations_for_single_subject
6837
else
69-
run_mutations_for_single_subject(mutation_set)
38+
run_mutations_for_single_subject(mutation_set, run)
7039
end
7140
end.flatten
7241
end

0 commit comments

Comments
 (0)