knapsack 1.18.0 → 3.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.github/workflows/ruby.yml +117 -0
- data/CHANGELOG.md +141 -65
- data/README.md +36 -1
- data/knapsack.gemspec +4 -5
- data/lib/knapsack/adapters/minitest_adapter.rb +3 -13
- data/lib/knapsack/adapters/rspec_adapter.rb +5 -2
- data/lib/knapsack/config/env.rb +2 -2
- data/lib/knapsack/distributors/report_distributor.rb +24 -45
- data/lib/knapsack/presenter.rb +9 -6
- data/lib/knapsack/tracker.rb +15 -15
- data/lib/knapsack/version.rb +1 -1
- data/spec/knapsack/adapters/rspec_adapter_spec.rb +5 -5
- data/spec/knapsack/config/env_spec.rb +10 -0
- data/spec/knapsack/distributors/report_distributor_spec.rb +87 -11
- data/spec/knapsack/tracker_spec.rb +7 -1
- data/spec/spec_helper.rb +0 -3
- data/test_examples/fast/shared_examples_test.rb +1 -1
- data/test_examples/fast/spec_test.rb +14 -2
- data/test_examples/test_helper.rb +0 -9
- metadata +19 -35
- data/.travis.yml +0 -116
- data/bin/print_header.sh +0 -5
data/README.md
CHANGED
@@ -1 +1,36 @@
|
|
1
|
-
#
|
1
|
+
# knapsack gem
|
2
|
+
|
3
|
+
Knapsack splits tests evenly across parallel CI nodes to run fast CI build and save you time.
|
4
|
+
|
5
|
+
| | knapsack gem | knapsack_pro gem |
|
6
|
+
| -------------------------------------------- | ------------ | ---------------- |
|
7
|
+
| __Is free__ | ✓ Yes | ✓ Yes, [free plan](https://knapsackpro.com?utm_source=github&utm_medium=readme&utm_campaign=knapsack_gem&utm_content=free_plan) |
|
8
|
+
| __Regular Mode - a static tests split__ | ✓ Yes | ✓ Yes |
|
9
|
+
| __Queue Mode - a dynamic tests split__ <br>([ensures all CI nodes finish work at the same time](https://docs.knapsackpro.com/2020/how-to-speed-up-ruby-and-javascript-tests-with-ci-parallelisation)) | No | ✓ Yes |
|
10
|
+
| __Auto [split slow RSpec test file](https://knapsackpro.com/faq/question/how-to-split-slow-rspec-test-files-by-test-examples-by-individual-it) between parallel CI nodes__ <br>(a single test file can be auto split by test examples between parallel jobs) | No | ✓ Yes |
|
11
|
+
| Tracking tests timing per commit, branch | No | ✓ Yes |
|
12
|
+
| Support for other programming languages | No | ✓ Yes |
|
13
|
+
| Support for CI providers | limited | ✓ Yes |
|
14
|
+
| __Installation README__ | [Install README](http://docs.knapsackpro.com/ruby/knapsack) | [Install README](https://docs.knapsackpro.com/integration/) |
|
15
|
+
|
16
|
+
[Features of knapsack vs knapsack_pro Ruby gem](https://knapsackpro.com/features/ruby_knapsack_pro_vs_knapsack?utm_source=github&utm_medium=readme&utm_campaign=knapsack_gem&utm_content=ruby_knapsack_pro_vs_knapsack)
|
17
|
+
|
18
|
+
# Do you use Heroku?
|
19
|
+
|
20
|
+
Do you know Knapsack Pro Ruby gem is available as Heroku add-on that's currently in beta and it's free to all beta users? It works with your current CI server.
|
21
|
+
https://elements.heroku.com/addons/knapsack-pro
|
22
|
+
|
23
|
+
Knapsack Pro has Queue Mode that will split Ruby & JS tests in a dynamic way across parallel CI nodes to ensure each parallel job takes a similar time. Thanks to that there is no bottleneck in your CI pipelines.
|
24
|
+
|
25
|
+
__See introduction how the Knapsack Pro add-on works__
|
26
|
+
https://youtu.be/rmXES2N0_QU
|
27
|
+
|
28
|
+
You may also find useful article how to run parallel dynos on Heroku CI to complete tests faster
|
29
|
+
https://docs.knapsackpro.com/2019/how-to-run-tests-faster-on-heroku-ci-with-parallel-dynos
|
30
|
+
|
31
|
+
## Do you know
|
32
|
+
|
33
|
+
* Knapsack Pro is risk-free integration! Knapsack Pro runs tests in Fallback Mode if your CI servers can't reach our API for any reason.
|
34
|
+
* We don't need access to your repository. Knapsack Pro is just wrapper around test runner like RSpec, Cucumber, Minitest etc.
|
35
|
+
* Hundreds of developers use Knapsack Pro every day to run fast CI builds.
|
36
|
+
* It works with other programming languages.
|
data/knapsack.gemspec
CHANGED
@@ -10,7 +10,7 @@ Gem::Specification.new do |spec|
|
|
10
10
|
spec.email = ["arturtrzop@gmail.com"]
|
11
11
|
spec.summary = %q{Knapsack splits tests across CI nodes and makes sure that tests will run comparable time on each node.}
|
12
12
|
spec.description = %q{Parallel tests across CI server nodes based on each test file's time execution. It generates a test time execution report and uses it for future test runs.}
|
13
|
-
spec.homepage = "https://github.com/
|
13
|
+
spec.homepage = "https://github.com/KnapsackPro/knapsack"
|
14
14
|
spec.license = "MIT"
|
15
15
|
|
16
16
|
spec.files = `git ls-files -z`.split("\x0")
|
@@ -18,17 +18,16 @@ Gem::Specification.new do |spec|
|
|
18
18
|
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
19
19
|
spec.require_paths = ["lib"]
|
20
20
|
|
21
|
-
spec.required_ruby_version = '>=
|
21
|
+
spec.required_ruby_version = '>= 2.2'
|
22
22
|
|
23
23
|
spec.add_dependency 'rake', '>= 0'
|
24
24
|
|
25
25
|
spec.add_development_dependency 'bundler', '>= 1.6'
|
26
26
|
spec.add_development_dependency 'rspec', '~> 3.0', '>= 2.10.0'
|
27
|
-
spec.add_development_dependency 'rspec-its', '~> 1.
|
27
|
+
spec.add_development_dependency 'rspec-its', '~> 1.3'
|
28
28
|
spec.add_development_dependency 'cucumber', '>= 0'
|
29
29
|
spec.add_development_dependency 'spinach', '>= 0.8'
|
30
30
|
spec.add_development_dependency 'minitest', '>= 5.0.0'
|
31
|
-
spec.add_development_dependency 'codeclimate-test-reporter', '~> 0'
|
32
31
|
spec.add_development_dependency 'pry', '~> 0'
|
33
|
-
spec.add_development_dependency 'timecop', '>= 0.
|
32
|
+
spec.add_development_dependency 'timecop', '>= 0.9.4'
|
34
33
|
end
|
@@ -23,20 +23,20 @@ module Knapsack
|
|
23
23
|
def bind_time_tracker
|
24
24
|
::Minitest::Test.send(:include, BindTimeTrackerMinitestPlugin)
|
25
25
|
|
26
|
-
|
26
|
+
Minitest.after_run do
|
27
27
|
Knapsack.logger.info(Presenter.global_time)
|
28
28
|
end
|
29
29
|
end
|
30
30
|
|
31
31
|
def bind_report_generator
|
32
|
-
|
32
|
+
Minitest.after_run do
|
33
33
|
Knapsack.report.save
|
34
34
|
Knapsack.logger.info(Presenter.report_details)
|
35
35
|
end
|
36
36
|
end
|
37
37
|
|
38
38
|
def bind_time_offset_warning
|
39
|
-
|
39
|
+
Minitest.after_run do
|
40
40
|
Knapsack.logger.log(
|
41
41
|
Presenter.time_offset_log_level,
|
42
42
|
Presenter.time_offset_warning
|
@@ -64,16 +64,6 @@ module Knapsack
|
|
64
64
|
# test_path will look like ./test/dir/unit_test.rb
|
65
65
|
test_path
|
66
66
|
end
|
67
|
-
|
68
|
-
private
|
69
|
-
|
70
|
-
def add_post_run_callback(&block)
|
71
|
-
if Minitest.respond_to?(:after_run)
|
72
|
-
Minitest.after_run { block.call }
|
73
|
-
else
|
74
|
-
Minitest::Unit.after_tests { block.call }
|
75
|
-
end
|
76
|
-
end
|
77
67
|
end
|
78
68
|
end
|
79
69
|
end
|
@@ -6,6 +6,10 @@ module Knapsack
|
|
6
6
|
|
7
7
|
def bind_time_tracker
|
8
8
|
::RSpec.configure do |config|
|
9
|
+
config.prepend_before(:context) do
|
10
|
+
Knapsack.tracker.start_timer
|
11
|
+
end
|
12
|
+
|
9
13
|
config.prepend_before(:each) do
|
10
14
|
current_example_group =
|
11
15
|
if ::RSpec.respond_to?(:current_example)
|
@@ -14,10 +18,9 @@ module Knapsack
|
|
14
18
|
example.metadata
|
15
19
|
end
|
16
20
|
Knapsack.tracker.test_path = RSpecAdapter.test_path(current_example_group)
|
17
|
-
Knapsack.tracker.start_timer
|
18
21
|
end
|
19
22
|
|
20
|
-
config.append_after(:
|
23
|
+
config.append_after(:context) do
|
21
24
|
Knapsack.tracker.stop_timer
|
22
25
|
end
|
23
26
|
|
data/lib/knapsack/config/env.rb
CHANGED
@@ -7,11 +7,11 @@ module Knapsack
|
|
7
7
|
end
|
8
8
|
|
9
9
|
def ci_node_total
|
10
|
-
ENV['CI_NODE_TOTAL'] || ENV['CIRCLE_NODE_TOTAL'] || ENV['SEMAPHORE_JOB_COUNT'] || ENV['SEMAPHORE_THREAD_COUNT'] || ENV['BUILDKITE_PARALLEL_JOB_COUNT'] || ENV['SNAP_WORKER_TOTAL'] || 1
|
10
|
+
ENV['CI_NODE_TOTAL'] || ENV['CIRCLE_NODE_TOTAL'] || ENV['SEMAPHORE_JOB_COUNT'] || ENV['SEMAPHORE_THREAD_COUNT'] || ENV['BUILDKITE_PARALLEL_JOB_COUNT'] || ENV['SNAP_WORKER_TOTAL'] || ENV['BITBUCKET_PARALLEL_STEP_COUNT'] || 1
|
11
11
|
end
|
12
12
|
|
13
13
|
def ci_node_index
|
14
|
-
gitlab_ci_node_index || ENV['CI_NODE_INDEX'] || ENV['CIRCLE_NODE_INDEX'] || semaphore_job_index || semaphore_current_thread || ENV['BUILDKITE_PARALLEL_JOB'] || snap_ci_worker_index || 0
|
14
|
+
gitlab_ci_node_index || ENV['CI_NODE_INDEX'] || ENV['CIRCLE_NODE_INDEX'] || semaphore_job_index || semaphore_current_thread || ENV['BUILDKITE_PARALLEL_JOB'] || snap_ci_worker_index || ENV['BITBUCKET_PARALLEL_STEP'] || 0
|
15
15
|
end
|
16
16
|
|
17
17
|
def test_file_pattern
|
@@ -2,7 +2,7 @@ module Knapsack
|
|
2
2
|
module Distributors
|
3
3
|
class ReportDistributor < BaseDistributor
|
4
4
|
def sorted_report
|
5
|
-
@sorted_report ||= report.sort_by { |
|
5
|
+
@sorted_report ||= report.sort_by { |_test_path, time| -time }
|
6
6
|
end
|
7
7
|
|
8
8
|
def sorted_report_with_existing_tests
|
@@ -10,7 +10,7 @@ module Knapsack
|
|
10
10
|
end
|
11
11
|
|
12
12
|
def total_time_execution
|
13
|
-
@total_time_execution ||= sorted_report_with_existing_tests.map
|
13
|
+
@total_time_execution ||= sorted_report_with_existing_tests.map { |_test_path, time| time }.reduce(0, :+).to_f
|
14
14
|
end
|
15
15
|
|
16
16
|
def node_time_execution
|
@@ -20,73 +20,52 @@ module Knapsack
|
|
20
20
|
private
|
21
21
|
|
22
22
|
def post_assign_test_files_to_node
|
23
|
-
|
24
|
-
assign_remaining_test_files
|
23
|
+
assign_test_files
|
25
24
|
sort_assigned_test_files
|
26
25
|
end
|
27
26
|
|
28
27
|
def sort_assigned_test_files
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
28
|
+
node_tests.map do |node|
|
29
|
+
node[:test_files_with_time]
|
30
|
+
.sort_by! { |file_name, _time| file_name }
|
31
|
+
.reverse!
|
32
|
+
.sort_by! { |_file_name, time| time }
|
33
|
+
.reverse!
|
35
34
|
end
|
36
35
|
end
|
37
36
|
|
38
37
|
def post_tests_for_node(node_index)
|
39
38
|
node_test = node_tests[node_index]
|
40
39
|
return unless node_test
|
41
|
-
node_test[:test_files_with_time].map
|
40
|
+
node_test[:test_files_with_time].map { |file_name, _time| file_name }
|
42
41
|
end
|
43
42
|
|
44
43
|
def default_node_tests
|
45
|
-
@node_tests =
|
46
|
-
|
47
|
-
@node_tests << {
|
44
|
+
@node_tests = Array.new(ci_node_total) do |index|
|
45
|
+
{
|
48
46
|
node_index: index,
|
49
47
|
time_left: node_time_execution,
|
50
|
-
test_files_with_time: []
|
48
|
+
test_files_with_time: [],
|
49
|
+
weight: 0
|
51
50
|
}
|
52
51
|
end
|
53
52
|
end
|
54
53
|
|
55
|
-
def
|
56
|
-
|
57
|
-
|
58
|
-
sorted_report_with_existing_tests.each do |test_file_with_time|
|
59
|
-
assign_slow_test_file(node_index, test_file_with_time)
|
60
|
-
node_index += 1
|
61
|
-
node_index %= ci_node_total
|
62
|
-
end
|
63
|
-
end
|
54
|
+
def assign_test_files
|
55
|
+
sorted_report_with_existing_tests.map do |test_file_with_time|
|
56
|
+
test_execution_time = test_file_with_time.last
|
64
57
|
|
65
|
-
|
66
|
-
time = test_file_with_time[1]
|
67
|
-
time_left = node_tests[node_index][:time_left] - time
|
58
|
+
current_lightest_node = node_tests.min_by { |node| node[:weight] }
|
68
59
|
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
end
|
75
|
-
end
|
60
|
+
updated_node_data = {
|
61
|
+
time_left: current_lightest_node[:time_left] - test_execution_time,
|
62
|
+
weight: current_lightest_node[:weight] + test_execution_time,
|
63
|
+
test_files_with_time: current_lightest_node[:test_files_with_time] << test_file_with_time
|
64
|
+
}
|
76
65
|
|
77
|
-
|
78
|
-
@not_assigned_test_files.each do |test_file_with_time|
|
79
|
-
index = node_with_max_time_left
|
80
|
-
time = test_file_with_time[1]
|
81
|
-
node_tests[index][:time_left] -= time
|
82
|
-
node_tests[index][:test_files_with_time] << test_file_with_time
|
66
|
+
current_lightest_node.merge!(updated_node_data)
|
83
67
|
end
|
84
68
|
end
|
85
|
-
|
86
|
-
def node_with_max_time_left
|
87
|
-
node_test = node_tests.max { |a,b| a[:time_left] <=> b[:time_left] }
|
88
|
-
node_test[:node_index]
|
89
|
-
end
|
90
69
|
end
|
91
70
|
end
|
92
71
|
end
|
data/lib/knapsack/presenter.rb
CHANGED
@@ -51,10 +51,14 @@ module Knapsack
|
|
51
51
|
}
|
52
52
|
if Knapsack.tracker.time_exceeded?
|
53
53
|
str << %{
|
54
|
-
|
54
|
+
Test on this CI node ran for longer than the max allowed node time execution.
|
55
55
|
Please regenerate your knapsack report.
|
56
|
-
|
57
|
-
or bump time_offset_in_seconds setting.
|
56
|
+
|
57
|
+
If that doesn't help, you can split your slowest test files into smaller files, or bump up the time_offset_in_seconds setting.
|
58
|
+
|
59
|
+
You can also allow the knapsack_pro gem to automatically divide your slow test files across parallel CI nodes.
|
60
|
+
https://knapsackpro.com/faq/question/how-to-auto-split-test-files-by-test-cases-on-parallel-jobs-ci-nodes?utm_source=knapsack_gem&utm_medium=knapsack_gem_output&utm_campaign=knapsack_gem_time_offset_warning
|
61
|
+
}
|
58
62
|
else
|
59
63
|
str << %{
|
60
64
|
Global time execution for this CI node is fine.
|
@@ -63,9 +67,8 @@ Happy testing!}
|
|
63
67
|
str << "\n\nNeed explanation? See FAQ:"
|
64
68
|
str << "\nhttps://docs.knapsackpro.com/ruby/knapsack#faq"
|
65
69
|
str << "\n=================================================\n"
|
66
|
-
str << %{
|
67
|
-
|
68
|
-
https://youtu.be/hUEB1XDKEFY
|
70
|
+
str << %{Read up on the benefits of a dynamic test split with Knapsack Pro Queue Mode:
|
71
|
+
https://docs.knapsackpro.com/2020/how-to-speed-up-ruby-and-javascript-tests-with-ci-parallelisation
|
69
72
|
|
70
73
|
Sign up for Knapsack Pro here:
|
71
74
|
https://knapsackpro.com}
|
data/lib/knapsack/tracker.rb
CHANGED
@@ -23,15 +23,19 @@ module Knapsack
|
|
23
23
|
end
|
24
24
|
|
25
25
|
def stop_timer
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
26
|
+
execution_time = now_without_mock_time.to_f - @start_time
|
27
|
+
|
28
|
+
if test_path
|
29
|
+
update_global_time(execution_time)
|
30
|
+
update_test_file_time(execution_time)
|
31
|
+
@test_path = nil
|
32
|
+
end
|
33
|
+
|
34
|
+
execution_time
|
30
35
|
end
|
31
36
|
|
32
37
|
def test_path
|
33
|
-
|
34
|
-
@test_path.sub(/^\.\//, '')
|
38
|
+
@test_path.sub(/^\.\//, '') if @test_path
|
35
39
|
end
|
36
40
|
|
37
41
|
def time_exceeded?
|
@@ -62,13 +66,13 @@ module Knapsack
|
|
62
66
|
@test_path = nil
|
63
67
|
end
|
64
68
|
|
65
|
-
def update_global_time
|
66
|
-
@global_time +=
|
69
|
+
def update_global_time(execution_time)
|
70
|
+
@global_time += execution_time
|
67
71
|
end
|
68
72
|
|
69
|
-
def update_test_file_time
|
73
|
+
def update_test_file_time(execution_time)
|
70
74
|
@test_files_with_time[test_path] ||= 0
|
71
|
-
@test_files_with_time[test_path] +=
|
75
|
+
@test_files_with_time[test_path] += execution_time
|
72
76
|
end
|
73
77
|
|
74
78
|
def report_distributor
|
@@ -81,11 +85,7 @@ module Knapsack
|
|
81
85
|
end
|
82
86
|
|
83
87
|
def now_without_mock_time
|
84
|
-
|
85
|
-
Time.now_without_mock_time
|
86
|
-
else
|
87
|
-
Time.raw_now
|
88
|
-
end
|
88
|
+
Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
89
89
|
end
|
90
90
|
end
|
91
91
|
end
|
data/lib/knapsack/version.rb
CHANGED
@@ -24,8 +24,9 @@ describe Knapsack::Adapters::RSpecAdapter do
|
|
24
24
|
end
|
25
25
|
|
26
26
|
it do
|
27
|
+
expect(config).to receive(:prepend_before).with(:context).and_yield
|
27
28
|
expect(config).to receive(:prepend_before).with(:each).and_yield
|
28
|
-
expect(config).to receive(:append_after).with(:
|
29
|
+
expect(config).to receive(:append_after).with(:context).and_yield
|
29
30
|
expect(config).to receive(:after).with(:suite).and_yield
|
30
31
|
expect(::RSpec).to receive(:configure).and_yield(config)
|
31
32
|
|
@@ -33,10 +34,9 @@ describe Knapsack::Adapters::RSpecAdapter do
|
|
33
34
|
expect(described_class).to receive(:test_path).with(example_group).and_return(test_path)
|
34
35
|
|
35
36
|
allow(Knapsack).to receive(:tracker).and_return(tracker)
|
36
|
-
expect(tracker).to receive(:
|
37
|
-
expect(tracker).to receive(:
|
38
|
-
|
39
|
-
expect(tracker).to receive(:stop_timer)
|
37
|
+
expect(tracker).to receive(:start_timer).ordered
|
38
|
+
expect(tracker).to receive(:test_path=).with(test_path).ordered
|
39
|
+
expect(tracker).to receive(:stop_timer).ordered
|
40
40
|
|
41
41
|
expect(Knapsack::Presenter).to receive(:global_time).and_return(global_time)
|
42
42
|
expect(logger).to receive(:info).with(global_time)
|
@@ -46,6 +46,11 @@ describe Knapsack::Config::Env do
|
|
46
46
|
before { stub_const("ENV", { 'SNAP_WORKER_TOTAL' => 6 }) }
|
47
47
|
it { should eql 6 }
|
48
48
|
end
|
49
|
+
|
50
|
+
context 'when BITBUCKET_PARALLEL_STEP_COUNT has value' do
|
51
|
+
before { stub_const("ENV", { 'BITBUCKET_PARALLEL_STEP_COUNT' => 8 }) }
|
52
|
+
it { should eql 8 }
|
53
|
+
end
|
49
54
|
end
|
50
55
|
|
51
56
|
context "when ENV doesn't exist" do
|
@@ -91,6 +96,11 @@ describe Knapsack::Config::Env do
|
|
91
96
|
before { stub_const("ENV", { 'SNAP_WORKER_INDEX' => 4 }) }
|
92
97
|
it { should eql 3 }
|
93
98
|
end
|
99
|
+
|
100
|
+
context 'when BITBUCKET_PARALLEL_STEP has value' do
|
101
|
+
before { stub_const("ENV", { 'BITBUCKET_PARALLEL_STEP' => 7 }) }
|
102
|
+
it { should eql 7 }
|
103
|
+
end
|
94
104
|
end
|
95
105
|
|
96
106
|
context "when ENV doesn't exist" do
|
@@ -118,7 +118,7 @@ describe Knapsack::Distributors::ReportDistributor do
|
|
118
118
|
'c_spec.rb' => 2.0,
|
119
119
|
'd_spec.rb' => 2.5,
|
120
120
|
'a_spec.rb' => 1.0,
|
121
|
-
'b_spec.rb' => 1.5
|
121
|
+
'b_spec.rb' => 1.5
|
122
122
|
}
|
123
123
|
end
|
124
124
|
let(:custom_args) { { ci_node_total: 3 } }
|
@@ -134,6 +134,7 @@ describe Knapsack::Distributors::ReportDistributor do
|
|
134
134
|
expect(distributor.node_tests[0]).to eql({
|
135
135
|
:node_index => 0,
|
136
136
|
:time_left => -0.5,
|
137
|
+
:weight => 9.0,
|
137
138
|
:test_files_with_time => [
|
138
139
|
["g_spec.rb", 9.0]
|
139
140
|
]
|
@@ -143,12 +144,12 @@ describe Knapsack::Distributors::ReportDistributor do
|
|
143
144
|
it do
|
144
145
|
expect(distributor.node_tests[1]).to eql({
|
145
146
|
:node_index => 1,
|
146
|
-
:time_left => 0.
|
147
|
+
:time_left => 0.5,
|
148
|
+
:weight => 8.0,
|
147
149
|
:test_files_with_time => [
|
148
150
|
["f_spec.rb", 3.5],
|
149
151
|
["d_spec.rb", 2.5],
|
150
|
-
["
|
151
|
-
["a_spec.rb", 1.0]
|
152
|
+
["c_spec.rb", 2.0]
|
152
153
|
]
|
153
154
|
})
|
154
155
|
end
|
@@ -156,11 +157,13 @@ describe Knapsack::Distributors::ReportDistributor do
|
|
156
157
|
it do
|
157
158
|
expect(distributor.node_tests[2]).to eql({
|
158
159
|
:node_index => 2,
|
159
|
-
:time_left => 0.
|
160
|
+
:time_left => 0.0,
|
161
|
+
:weight => 8.5,
|
160
162
|
:test_files_with_time => [
|
161
|
-
["i_spec.rb", 3.0],
|
162
163
|
["h_spec.rb", 3.0],
|
163
|
-
["
|
164
|
+
["i_spec.rb", 3.0],
|
165
|
+
["b_spec.rb", 1.5],
|
166
|
+
["a_spec.rb", 1.0]
|
164
167
|
]
|
165
168
|
})
|
166
169
|
end
|
@@ -170,10 +173,9 @@ describe Knapsack::Distributors::ReportDistributor do
|
|
170
173
|
context 'when node exists' do
|
171
174
|
it do
|
172
175
|
expect(distributor.tests_for_node(1)).to eql([
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
'a_spec.rb'
|
176
|
+
"f_spec.rb",
|
177
|
+
"d_spec.rb",
|
178
|
+
"c_spec.rb"
|
177
179
|
])
|
178
180
|
end
|
179
181
|
end
|
@@ -183,4 +185,78 @@ describe Knapsack::Distributors::ReportDistributor do
|
|
183
185
|
end
|
184
186
|
end
|
185
187
|
end
|
188
|
+
|
189
|
+
describe 'algorithmic efficiency' do
|
190
|
+
subject(:node_weights) do
|
191
|
+
distro = distributor
|
192
|
+
distro.assign_test_files_to_node
|
193
|
+
distro.node_tests.map { |node| node[:weight] }
|
194
|
+
end
|
195
|
+
|
196
|
+
before do
|
197
|
+
allow(distributor).to receive(:all_tests).and_return(report.keys)
|
198
|
+
end
|
199
|
+
|
200
|
+
let(:custom_args) { { ci_node_total: 3 } }
|
201
|
+
|
202
|
+
context 'with the most simple example' do
|
203
|
+
let(:report) do
|
204
|
+
{
|
205
|
+
'a_spec.rb' => 1.0,
|
206
|
+
'b_spec.rb' => 1.0,
|
207
|
+
'c_spec.rb' => 1.0,
|
208
|
+
'd_spec.rb' => 1.0,
|
209
|
+
'e_spec.rb' => 1.0,
|
210
|
+
'f_spec.rb' => 1.0,
|
211
|
+
'g_spec.rb' => 1.0,
|
212
|
+
'h_spec.rb' => 1.0,
|
213
|
+
'i_spec.rb' => 1.0
|
214
|
+
}
|
215
|
+
end
|
216
|
+
|
217
|
+
it 'assigns all nodes equally' do
|
218
|
+
expect(node_weights.uniq).to contain_exactly 3.0
|
219
|
+
end
|
220
|
+
end
|
221
|
+
|
222
|
+
context 'with a medium difficulty example' do
|
223
|
+
let(:report) do
|
224
|
+
{
|
225
|
+
'a_spec.rb' => 1.0,
|
226
|
+
'b_spec.rb' => 2.0,
|
227
|
+
'c_spec.rb' => 3.0,
|
228
|
+
'd_spec.rb' => 1.0,
|
229
|
+
'e_spec.rb' => 2.0,
|
230
|
+
'f_spec.rb' => 3.0,
|
231
|
+
'g_spec.rb' => 1.0,
|
232
|
+
'h_spec.rb' => 2.0,
|
233
|
+
'i_spec.rb' => 3.0
|
234
|
+
}
|
235
|
+
end
|
236
|
+
|
237
|
+
it 'assigns all nodes equally' do
|
238
|
+
expect(node_weights.uniq).to contain_exactly 6.0
|
239
|
+
end
|
240
|
+
end
|
241
|
+
|
242
|
+
context 'with a difficult example' do
|
243
|
+
let(:report) do
|
244
|
+
{
|
245
|
+
'a_spec.rb' => 2.0,
|
246
|
+
'b_spec.rb' => 2.0,
|
247
|
+
'c_spec.rb' => 3.0,
|
248
|
+
'd_spec.rb' => 1.0,
|
249
|
+
'e_spec.rb' => 1.0,
|
250
|
+
'f_spec.rb' => 1.0,
|
251
|
+
'g_spec.rb' => 9.0,
|
252
|
+
'h_spec.rb' => 1.0,
|
253
|
+
'i_spec.rb' => 10.0
|
254
|
+
}
|
255
|
+
end
|
256
|
+
|
257
|
+
it 'assigns all nodes equally' do
|
258
|
+
expect(node_weights.uniq).to contain_exactly 10.0
|
259
|
+
end
|
260
|
+
end
|
261
|
+
end
|
186
262
|
end
|