knapsack 0.0.3 → 0.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.
Files changed (49) hide show
  1. checksums.yaml +4 -4
  2. data/.travis.yml +12 -1
  3. data/CHANGELOG.md +11 -0
  4. data/LICENSE.txt +1 -1
  5. data/README.md +71 -24
  6. data/Rakefile +3 -1
  7. data/TODO.md +5 -0
  8. data/knapsack_report.json +9 -12
  9. data/lib/knapsack.rb +25 -6
  10. data/lib/knapsack/adapters/{base.rb → base_adapter.rb} +18 -8
  11. data/lib/knapsack/adapters/{rspec.rb → rspec_adapter.rb} +13 -6
  12. data/lib/knapsack/allocator.rb +65 -0
  13. data/lib/knapsack/distributors/base_distributor.rb +67 -0
  14. data/lib/knapsack/distributors/leftover_distributor.rb +49 -0
  15. data/lib/knapsack/distributors/report_distributor.rb +76 -0
  16. data/lib/knapsack/presenter.rb +38 -5
  17. data/lib/knapsack/report.rb +27 -12
  18. data/lib/knapsack/task_loader.rb +11 -0
  19. data/lib/knapsack/tracker.rb +30 -13
  20. data/lib/knapsack/version.rb +1 -1
  21. data/lib/tasks/knapsack.rake +21 -0
  22. data/spec/knapsack/adapters/base_adapter_spec.rb +91 -0
  23. data/spec/knapsack/adapters/rspec_adapter_spec.rb +29 -0
  24. data/spec/knapsack/allocator_spec.rb +57 -0
  25. data/spec/knapsack/distributors/base_distributor_spec.rb +85 -0
  26. data/spec/knapsack/distributors/leftover_distributor_spec.rb +110 -0
  27. data/spec/knapsack/distributors/report_distributor_spec.rb +152 -0
  28. data/spec/knapsack/presenter_spec.rb +83 -0
  29. data/spec/knapsack/report_spec.rb +73 -0
  30. data/spec/knapsack/task_loader_spec.rb +10 -0
  31. data/spec/knapsack/tracker_spec.rb +84 -20
  32. data/spec/knapsack_spec.rb +23 -2
  33. data/spec/spec_helper.rb +16 -0
  34. data/spec_examples/fast/1_spec.rb +1 -4
  35. data/spec_examples/fast/2_spec.rb +1 -4
  36. data/spec_examples/fast/3_spec.rb +1 -4
  37. data/spec_examples/fast/4_spec.rb +1 -4
  38. data/spec_examples/fast/5_spec.rb +1 -4
  39. data/spec_examples/fast/6_spec.rb +1 -4
  40. data/spec_examples/leftover/1_spec.rb +4 -0
  41. data/spec_examples/leftover/a_spec.rb +8 -0
  42. data/spec_examples/slow/a_spec.rb +1 -3
  43. data/spec_examples/slow/b_spec.rb +3 -5
  44. data/spec_examples/slow/c_spec.rb +1 -3
  45. data/spec_examples/spec_helper.rb +5 -2
  46. metadata +32 -7
  47. data/spec_examples/slow/d_spec.rb +0 -7
  48. data/spec_examples/slow/e_spec.rb +0 -7
  49. data/spec_examples/slow/f_spec.rb +0 -7
@@ -0,0 +1,29 @@
1
+ describe Knapsack::Adapters::RspecAdapter do
2
+ before do
3
+ expect(::RSpec).to receive(:configure)
4
+ end
5
+
6
+ describe '#bind_time_tracker' do
7
+ it do
8
+ expect {
9
+ subject.bind_time_tracker
10
+ }.not_to raise_error
11
+ end
12
+ end
13
+
14
+ describe '#bind_report_generator' do
15
+ it do
16
+ expect {
17
+ subject.bind_report_generator
18
+ }.not_to raise_error
19
+ end
20
+ end
21
+
22
+ describe '#bind_time_offset_warning' do
23
+ it do
24
+ expect {
25
+ subject.bind_time_offset_warning
26
+ }.not_to raise_error
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,57 @@
1
+ describe Knapsack::Allocator do
2
+ let(:spec_pattern) { nil }
3
+ let(:args) do
4
+ {
5
+ ci_node_total: nil,
6
+ ci_node_index: nil,
7
+ spec_pattern: spec_pattern,
8
+ report: nil
9
+ }
10
+ end
11
+ let(:report_distributor) { instance_double(Knapsack::Distributors::ReportDistributor) }
12
+ let(:leftover_distributor) { instance_double(Knapsack::Distributors::LeftoverDistributor) }
13
+ let(:report_specs) { ['a_spec.rb', 'b_spec.rb'] }
14
+ let(:leftover_specs) { ['c_spec.rb', 'd_spec.rb'] }
15
+ let(:node_specs) { report_specs + leftover_specs }
16
+ let(:allocator) { described_class.new(args) }
17
+
18
+ before do
19
+ expect(Knapsack::Distributors::ReportDistributor).to receive(:new).with(args).and_return(report_distributor)
20
+ expect(Knapsack::Distributors::LeftoverDistributor).to receive(:new).with(args).and_return(leftover_distributor)
21
+ allow(report_distributor).to receive(:specs_for_current_node).and_return(report_specs)
22
+ allow(leftover_distributor).to receive(:specs_for_current_node).and_return(leftover_specs)
23
+ end
24
+
25
+ describe '#report_node_specs' do
26
+ subject { allocator.report_node_specs }
27
+ it { should eql report_specs }
28
+ end
29
+
30
+ describe '#leftover_node_specs' do
31
+ subject { allocator.leftover_node_specs }
32
+ it { should eql leftover_specs }
33
+ end
34
+
35
+ describe '#node_specs' do
36
+ subject { allocator.node_specs }
37
+ it { should eql node_specs }
38
+ end
39
+
40
+ describe '#stringify_node_specs' do
41
+ subject { allocator.stringify_node_specs }
42
+ it { should eql node_specs.join(' ') }
43
+ end
44
+
45
+ describe '#custom_spec_dir' do
46
+ subject { allocator.custom_spec_dir }
47
+
48
+ context 'when spec pattern exists' do
49
+ let(:spec_pattern) { "custom_spec_dir/**/*_spec.rb" }
50
+ it { should eql 'custom_spec_dir/' }
51
+ end
52
+
53
+ context "when spec pattern doesn't exist" do
54
+ it { should be_nil }
55
+ end
56
+ end
57
+ end
@@ -0,0 +1,85 @@
1
+ describe Knapsack::Distributors::BaseDistributor do
2
+ let(:args) { {} }
3
+ let(:default_report) { { 'default_report_spec.rb' => 1.0 } }
4
+
5
+ let(:distributor) { described_class.new(args) }
6
+
7
+ before do
8
+ allow(Knapsack).to receive(:report) {
9
+ instance_double(Knapsack::Report, open: default_report)
10
+ }
11
+ end
12
+
13
+ describe '#report' do
14
+ subject { distributor.report }
15
+
16
+ context 'when report is given' do
17
+ let(:report) { { 'a_spec.rb' => 2.0 } }
18
+ let(:args) { { report: report } }
19
+
20
+ it { should eql(report) }
21
+ end
22
+
23
+ context 'when report is not given' do
24
+ it { should eql(default_report) }
25
+ end
26
+ end
27
+
28
+ describe '#ci_node_total' do
29
+ subject { distributor.ci_node_total }
30
+
31
+ context 'when ci_node_total is given' do
32
+ let(:args) { { ci_node_total: 4 } }
33
+
34
+ it { should eql 4 }
35
+ end
36
+
37
+ context 'when ci_node_total is not given' do
38
+ it { should eql 1 }
39
+ end
40
+ end
41
+
42
+ describe '#ci_node_index' do
43
+ subject { distributor.ci_node_index }
44
+
45
+ context 'when ci_node_index is given' do
46
+ let(:args) { { ci_node_index: 3 } }
47
+
48
+ it { should eql 3 }
49
+ end
50
+
51
+ context 'when ci_node_index is not given' do
52
+ it { should eql 0 }
53
+ end
54
+ end
55
+
56
+ describe '#specs_for_current_node' do
57
+ let(:args) do
58
+ {
59
+ ci_node_total: 3,
60
+ ci_node_index: ci_node_index
61
+ }
62
+ end
63
+ let(:specs) { double }
64
+
65
+ subject { distributor.specs_for_current_node }
66
+
67
+ context 'when ci_node_index not set' do
68
+ let(:ci_node_index) { nil }
69
+
70
+ it do
71
+ expect(distributor).to receive(:specs_for_node).with(0).and_return(specs)
72
+ expect(subject).to eql specs
73
+ end
74
+ end
75
+
76
+ context 'when ci_node_index set' do
77
+ let(:ci_node_index) { 2 }
78
+
79
+ it do
80
+ expect(distributor).to receive(:specs_for_node).with(ci_node_index).and_return(specs)
81
+ expect(subject).to eql specs
82
+ end
83
+ end
84
+ end
85
+ end
@@ -0,0 +1,110 @@
1
+ describe Knapsack::Distributors::LeftoverDistributor do
2
+ let(:args) { {} }
3
+ let(:default_report) do
4
+ {
5
+ 'a_spec.rb' => 1.0,
6
+ 'b_spec.rb' => 1.5,
7
+ 'c_spec.rb' => 2.0,
8
+ 'd_spec.rb' => 2.5,
9
+ }
10
+ end
11
+
12
+ let(:distributor) { described_class.new(args) }
13
+
14
+ before do
15
+ allow(Knapsack).to receive(:report) {
16
+ instance_double(Knapsack::Report, open: default_report)
17
+ }
18
+ end
19
+
20
+ describe '#report_specs' do
21
+ subject { distributor.report_specs }
22
+ it { should eql ['a_spec.rb', 'b_spec.rb', 'c_spec.rb', 'd_spec.rb'] }
23
+ end
24
+
25
+ describe '#all_specs' do
26
+ subject { distributor.all_specs }
27
+
28
+ context 'when default spec pattern' do
29
+ it { should_not be_empty }
30
+ it { should include 'spec/knapsack/tracker_spec.rb' }
31
+ it { should include 'spec/knapsack/adapters/rspec_adapter_spec.rb' }
32
+ end
33
+
34
+ context 'when fake spec pattern' do
35
+ let(:args) { { spec_pattern: 'fake_pattern' } }
36
+ it { should be_empty }
37
+ end
38
+ end
39
+
40
+ describe '#leftover_specs' do
41
+ subject { distributor.leftover_specs }
42
+
43
+ before do
44
+ expect(distributor).to receive(:all_specs).and_return([
45
+ 'a_spec.rb',
46
+ 'b_spec.rb',
47
+ 'c_spec.rb',
48
+ 'd_spec.rb',
49
+ 'e_spec.rb',
50
+ 'f_spec.rb',
51
+ ])
52
+ end
53
+
54
+ it { should eql ['e_spec.rb', 'f_spec.rb'] }
55
+ end
56
+
57
+ context do
58
+ let(:args) { { ci_node_total: 3 } }
59
+ let(:leftover_specs) {[
60
+ 'a_spec.rb',
61
+ 'b_spec.rb',
62
+ 'c_spec.rb',
63
+ 'd_spec.rb',
64
+ 'e_spec.rb',
65
+ 'f_spec.rb',
66
+ 'g_spec.rb',
67
+ ]}
68
+
69
+ before do
70
+ expect(distributor).to receive(:leftover_specs).and_return(leftover_specs)
71
+ end
72
+
73
+ describe '#assign_spec_files_to_node' do
74
+ before do
75
+ distributor.assign_spec_files_to_node
76
+ end
77
+
78
+ it do
79
+ expect(distributor.node_specs[0]).to eql([
80
+ 'a_spec.rb',
81
+ 'd_spec.rb',
82
+ 'g_spec.rb',
83
+ ])
84
+ end
85
+
86
+ it do
87
+ expect(distributor.node_specs[1]).to eql([
88
+ 'b_spec.rb',
89
+ 'e_spec.rb',
90
+ ])
91
+ end
92
+
93
+ it do
94
+ expect(distributor.node_specs[2]).to eql([
95
+ 'c_spec.rb',
96
+ 'f_spec.rb',
97
+ ])
98
+ end
99
+ end
100
+
101
+ describe '#specs_for_node' do
102
+ it do
103
+ expect(distributor.specs_for_node(1)).to eql([
104
+ 'b_spec.rb',
105
+ 'e_spec.rb',
106
+ ])
107
+ end
108
+ end
109
+ end
110
+ end
@@ -0,0 +1,152 @@
1
+ describe Knapsack::Distributors::ReportDistributor do
2
+ let(:args) { {} }
3
+ let(:default_report) { { 'default_report_spec.rb' => 1.0 } }
4
+
5
+ let(:distributor) { described_class.new(args) }
6
+
7
+ before do
8
+ allow(Knapsack).to receive(:report) {
9
+ instance_double(Knapsack::Report, open: default_report)
10
+ }
11
+ end
12
+
13
+ describe '#sorted_report' do
14
+ subject { distributor.sorted_report }
15
+
16
+ let(:report) do
17
+ {
18
+ 'e_spec.rb' => 3.0,
19
+ 'f_spec.rb' => 3.5,
20
+ 'c_spec.rb' => 2.0,
21
+ 'd_spec.rb' => 2.5,
22
+ 'a_spec.rb' => 1.0,
23
+ 'b_spec.rb' => 1.5,
24
+ }
25
+ end
26
+ let(:args) { { report: report } }
27
+
28
+ it do
29
+ should eql([
30
+ ["f_spec.rb", 3.5],
31
+ ["e_spec.rb", 3.0],
32
+ ["d_spec.rb", 2.5],
33
+ ["c_spec.rb", 2.0],
34
+ ["b_spec.rb", 1.5],
35
+ ["a_spec.rb", 1.0],
36
+ ])
37
+ end
38
+ end
39
+
40
+ context do
41
+ let(:report) do
42
+ {
43
+ 'a_spec.rb' => 3.0,
44
+ 'b_spec.rb' => 1.0,
45
+ 'c_spec.rb' => 1.5,
46
+ }
47
+ end
48
+ let(:args) { { report: report } }
49
+
50
+ describe '#total_time_execution' do
51
+ subject { distributor.total_time_execution }
52
+
53
+ context 'when time is float' do
54
+ it { should eql 5.5 }
55
+ end
56
+
57
+ context 'when time is not float' do
58
+ let(:report) do
59
+ {
60
+ 'a_spec.rb' => 3,
61
+ 'b_spec.rb' => 1,
62
+ }
63
+ end
64
+
65
+ it { should eql 4.0 }
66
+ end
67
+ end
68
+
69
+ describe '#node_time_execution' do
70
+ subject { distributor.node_time_execution }
71
+ let(:args) { { report: report, ci_node_total: 4 } }
72
+ it { should eql 1.375 }
73
+ end
74
+ end
75
+
76
+ context do
77
+ let(:report) do
78
+ {
79
+ 'g_spec.rb' => 9.0,
80
+ 'h_spec.rb' => 3.0,
81
+ 'i_spec.rb' => 3.0,
82
+ 'f_spec.rb' => 3.5,
83
+ 'c_spec.rb' => 2.0,
84
+ 'd_spec.rb' => 2.5,
85
+ 'a_spec.rb' => 1.0,
86
+ 'b_spec.rb' => 1.5,
87
+ }
88
+ end
89
+ let(:args) do
90
+ {
91
+ report: report,
92
+ ci_node_total: 3,
93
+ }
94
+ end
95
+
96
+ describe '#assign_spec_files_to_node' do
97
+ before { distributor.assign_spec_files_to_node }
98
+
99
+ it do
100
+ expect(distributor.node_specs[0]).to eql({
101
+ :node_index => 0,
102
+ :time_left => -0.5,
103
+ :spec_files_with_time => [
104
+ ["g_spec.rb", 9.0]
105
+ ]
106
+ })
107
+ end
108
+
109
+ it do
110
+ expect(distributor.node_specs[1]).to eql({
111
+ :node_index => 1,
112
+ :time_left => 0.0,
113
+ :spec_files_with_time => [
114
+ ["f_spec.rb", 3.5],
115
+ ["d_spec.rb", 2.5],
116
+ ["a_spec.rb", 1.0],
117
+ ["b_spec.rb", 1.5]
118
+ ]
119
+ })
120
+ end
121
+
122
+ it do
123
+ expect(distributor.node_specs[2]).to eql({
124
+ :node_index => 2,
125
+ :time_left => 0.5,
126
+ :spec_files_with_time => [
127
+ ["h_spec.rb", 3.0],
128
+ ["c_spec.rb", 2.0],
129
+ ["i_spec.rb", 3.0]
130
+ ]
131
+ })
132
+ end
133
+ end
134
+
135
+ describe '#specs_for_node' do
136
+ context 'when node exists' do
137
+ it do
138
+ expect(distributor.specs_for_node(1)).to eql([
139
+ 'f_spec.rb',
140
+ 'd_spec.rb',
141
+ 'a_spec.rb',
142
+ 'b_spec.rb'
143
+ ])
144
+ end
145
+ end
146
+
147
+ context "when node doesn't exist" do
148
+ it { expect(distributor.specs_for_node(42)).to be_nil }
149
+ end
150
+ end
151
+ end
152
+ end
@@ -0,0 +1,83 @@
1
+ describe Knapsack::Presenter do
2
+ let(:tracker) { instance_double(Knapsack::Tracker) }
3
+ let(:spec_files_with_time) do
4
+ {
5
+ 'a_spec.rb' => 1.0,
6
+ 'b_spec.rb' => 0.4
7
+ }
8
+ end
9
+
10
+ describe 'report methods' do
11
+ before do
12
+ expect(Knapsack).to receive(:tracker) { tracker }
13
+ expect(tracker).to receive(:spec_files_with_time).and_return(spec_files_with_time)
14
+ end
15
+
16
+ describe '.report_yml' do
17
+ subject { described_class.report_yml }
18
+ it { should eql spec_files_with_time.to_yaml }
19
+ end
20
+
21
+ describe '.report_json' do
22
+ subject { described_class.report_json }
23
+ it { should eql JSON.pretty_generate(spec_files_with_time) }
24
+ end
25
+ end
26
+
27
+ describe '.global_time' do
28
+ subject { described_class.global_time }
29
+
30
+ before do
31
+ expect(Knapsack).to receive(:tracker) { tracker }
32
+ expect(tracker).to receive(:global_time).and_return(4.2)
33
+ end
34
+
35
+ it { should eql "\nKnapsack global time execution for specs: 4.2s" }
36
+ end
37
+
38
+ describe '.report_details' do
39
+ subject { described_class.report_details }
40
+
41
+ before do
42
+ expect(described_class).to receive(:report_json).and_return('{}')
43
+ end
44
+
45
+ it { should eql "Knapsack report was generated. Preview:\n{}" }
46
+ end
47
+
48
+ describe '.time_offset_warning' do
49
+ let(:time_offset_in_seconds) { 30 }
50
+ let(:max_node_time_execution) { 60 }
51
+ let(:exceeded_time) { 3 }
52
+
53
+ subject { described_class.time_offset_warning }
54
+
55
+ before do
56
+ allow(Knapsack).to receive(:tracker) { tracker }
57
+ expect(tracker).to receive(:config).and_return({time_offset_in_seconds: time_offset_in_seconds})
58
+ expect(tracker).to receive(:max_node_time_execution).and_return(max_node_time_execution)
59
+ expect(tracker).to receive(:exceeded_time).and_return(exceeded_time)
60
+ expect(tracker).to receive(:time_exceeded?).and_return(time_exceeded?)
61
+ end
62
+
63
+ shared_examples 'knapsack time offset warning' do
64
+ it { should include 'Time offset: 30s' }
65
+ it { should include 'Max allowed node time execution: 60s' }
66
+ it { should include 'Exceeded time: 3s' }
67
+ end
68
+
69
+ context 'when time exceeded' do
70
+ let(:time_exceeded?) { true }
71
+
72
+ it_behaves_like 'knapsack time offset warning'
73
+ it { should include 'Please regenerate your knapsack report.' }
74
+ end
75
+
76
+ context "when time did not exceed" do
77
+ let(:time_exceeded?) { false }
78
+
79
+ it_behaves_like 'knapsack time offset warning'
80
+ it { should include 'Global time execution for this CI node is fine.' }
81
+ end
82
+ end
83
+ end