knapsack_pro 0.0.1 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (100) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +3 -3
  3. data/CHANGELOG.md +4 -0
  4. data/README.md +368 -4
  5. data/bin/knapsack_pro +20 -0
  6. data/circle.yml +2 -0
  7. data/knapsack_pro.gemspec +6 -1
  8. data/lib/knapsack_pro.rb +74 -0
  9. data/lib/knapsack_pro/adapters/base_adapter.rb +30 -0
  10. data/lib/knapsack_pro/adapters/cucumber_adapter.rb +40 -0
  11. data/lib/knapsack_pro/adapters/minitest_adapter.rb +52 -0
  12. data/lib/knapsack_pro/adapters/rspec_adapter.rb +48 -0
  13. data/lib/knapsack_pro/allocator.rb +40 -0
  14. data/lib/knapsack_pro/allocator_builder.rb +40 -0
  15. data/lib/knapsack_pro/client/api/action.rb +17 -0
  16. data/lib/knapsack_pro/client/api/v1/base.rb +15 -0
  17. data/lib/knapsack_pro/client/api/v1/build_distributions.rb +29 -0
  18. data/lib/knapsack_pro/client/api/v1/build_subsets.rb +29 -0
  19. data/lib/knapsack_pro/client/connection.rb +94 -0
  20. data/lib/knapsack_pro/config/ci/base.rb +22 -0
  21. data/lib/knapsack_pro/config/ci/buildkite.rb +27 -0
  22. data/lib/knapsack_pro/config/ci/circle.rb +29 -0
  23. data/lib/knapsack_pro/config/ci/semaphore.rb +28 -0
  24. data/lib/knapsack_pro/config/env.rb +111 -0
  25. data/lib/knapsack_pro/logger_wrapper.rb +15 -0
  26. data/lib/knapsack_pro/presenter.rb +25 -0
  27. data/lib/knapsack_pro/report.rb +20 -0
  28. data/lib/knapsack_pro/repository_adapter_initiator.rb +12 -0
  29. data/lib/knapsack_pro/repository_adapters/base_adapter.rb +14 -0
  30. data/lib/knapsack_pro/repository_adapters/env_adapter.rb +13 -0
  31. data/lib/knapsack_pro/repository_adapters/git_adapter.rb +19 -0
  32. data/lib/knapsack_pro/runners/base_runner.rb +31 -0
  33. data/lib/knapsack_pro/runners/cucumber_runner.rb +16 -0
  34. data/lib/knapsack_pro/runners/minitest_runner.rb +26 -0
  35. data/lib/knapsack_pro/runners/rspec_runner.rb +16 -0
  36. data/lib/knapsack_pro/task_loader.rb +11 -0
  37. data/lib/knapsack_pro/test_file_cleaner.rb +7 -0
  38. data/lib/knapsack_pro/test_file_finder.rb +33 -0
  39. data/lib/knapsack_pro/test_file_pattern.rb +7 -0
  40. data/lib/knapsack_pro/test_file_presenter.rb +11 -0
  41. data/lib/knapsack_pro/test_flat_distributor.rb +84 -0
  42. data/lib/knapsack_pro/tracker.rb +64 -0
  43. data/lib/knapsack_pro/version.rb +1 -1
  44. data/lib/tasks/cucumber.rake +7 -0
  45. data/lib/tasks/minitest.rake +7 -0
  46. data/lib/tasks/rspec.rake +7 -0
  47. data/spec/fixtures/vcr_cassettes/api/v1/build_distributions/subset/invalid_test_suite_token.yml +50 -0
  48. data/spec/fixtures/vcr_cassettes/api/v1/build_distributions/subset/success.yml +52 -0
  49. data/spec/fixtures/vcr_cassettes/api/v1/build_subsets/create/invalid_test_suite_token.yml +50 -0
  50. data/spec/fixtures/vcr_cassettes/api/v1/build_subsets/create/success.yml +50 -0
  51. data/spec/integration/api/build_distributions_subset_spec.rb +74 -0
  52. data/spec/integration/api/build_subsets_create_spec.rb +76 -0
  53. data/spec/knapsack_pro/adapters/base_adapter_spec.rb +63 -0
  54. data/spec/knapsack_pro/adapters/cucumber_adapter_spec.rb +71 -0
  55. data/spec/knapsack_pro/adapters/minitest_adapter_spec.rb +107 -0
  56. data/spec/knapsack_pro/adapters/rspec_adapter_spec.rb +94 -0
  57. data/spec/knapsack_pro/allocator_builder_spec.rb +45 -0
  58. data/spec/knapsack_pro/allocator_spec.rb +80 -0
  59. data/spec/knapsack_pro/client/api/action_spec.rb +17 -0
  60. data/spec/knapsack_pro/client/api/v1/base_spec.rb +2 -0
  61. data/spec/knapsack_pro/client/api/v1/build_distributions_spec.rb +35 -0
  62. data/spec/knapsack_pro/client/api/v1/build_subsets_spec.rb +35 -0
  63. data/spec/knapsack_pro/client/connection_spec.rb +138 -0
  64. data/spec/knapsack_pro/config/ci/base_spec.rb +7 -0
  65. data/spec/knapsack_pro/config/ci/buildkite_spec.rb +74 -0
  66. data/spec/knapsack_pro/config/ci/circle_spec.rb +74 -0
  67. data/spec/knapsack_pro/config/ci/semaphore_spec.rb +74 -0
  68. data/spec/knapsack_pro/config/env_spec.rb +368 -0
  69. data/spec/knapsack_pro/logger_wrapper_spec.rb +15 -0
  70. data/spec/knapsack_pro/presenter_spec.rb +57 -0
  71. data/spec/knapsack_pro/report_spec.rb +68 -0
  72. data/spec/knapsack_pro/repository_adapter_initiator_spec.rb +21 -0
  73. data/spec/knapsack_pro/repository_adapters/base_adapter_spec.rb +13 -0
  74. data/spec/knapsack_pro/repository_adapters/env_adapter_spec.rb +27 -0
  75. data/spec/knapsack_pro/repository_adapters/git_adapter_spec.rb +27 -0
  76. data/spec/knapsack_pro/runners/base_runner_spec.rb +61 -0
  77. data/spec/knapsack_pro/runners/cucumber_runner_spec.rb +47 -0
  78. data/spec/knapsack_pro/runners/minitest_runner_spec.rb +34 -0
  79. data/spec/knapsack_pro/runners/rspec_runner_spec.rb +49 -0
  80. data/spec/knapsack_pro/task_loader_spec.rb +14 -0
  81. data/spec/knapsack_pro/test_file_cleaner_spec.rb +11 -0
  82. data/spec/knapsack_pro/test_file_finder_spec.rb +35 -0
  83. data/spec/knapsack_pro/test_file_pattern_spec.rb +23 -0
  84. data/spec/knapsack_pro/test_file_presenter_spec.rb +22 -0
  85. data/spec/knapsack_pro/test_flat_distributor_spec.rb +60 -0
  86. data/spec/knapsack_pro/tracker_spec.rb +102 -0
  87. data/spec/knapsack_pro_spec.rb +56 -0
  88. data/spec/spec_helper.rb +11 -1
  89. data/spec/support/fakes/cucumber.rb +12 -0
  90. data/spec/support/fakes/minitest.rb +12 -0
  91. data/spec/support/shared_examples/adapter.rb +17 -0
  92. data/spec_fake/controllers/users_controller_spec.rb +0 -0
  93. data/spec_fake/models/admin_spec.rb +0 -0
  94. data/spec_fake/models/user_spec.rb +0 -0
  95. data/spec_fake/spec_helper.rb +0 -0
  96. data/test_fake/a_test.rb +0 -0
  97. data/test_fake/b_test.rb +0 -0
  98. metadata +212 -12
  99. data/Gemfile.lock +0 -69
  100. data/spec/knapsack_spec.rb +0 -3
@@ -0,0 +1,15 @@
1
+ describe KnapsackPro::LoggerWrapper do
2
+ let(:io) { StringIO.new }
3
+ let(:logger) { ::Logger.new(io) }
4
+ let(:logger_wrapper) { described_class.new(logger) }
5
+
6
+ subject { io.string }
7
+
8
+ [:debug, :info, :warn, :error, :fatal].each do |log_level|
9
+ describe "##{log_level}" do
10
+ before {logger_wrapper.public_send(log_level, 'Test message') }
11
+
12
+ it { should eq "[knapsack_pro] Test message\n" }
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,57 @@
1
+ describe KnapsackPro::Presenter do
2
+ describe '.global_time' do
3
+ let(:tracker) { instance_double(KnapsackPro::Tracker, global_time: 60*62+3) }
4
+
5
+ subject { described_class.global_time }
6
+
7
+ before do
8
+ expect(KnapsackPro).to receive(:tracker).and_return(tracker)
9
+ end
10
+
11
+ it { should eql "Global time execution for tests: 01h 02m 03s" }
12
+ end
13
+
14
+ describe '.pretty_seconds' do
15
+ subject { described_class.pretty_seconds(seconds) }
16
+
17
+ context 'when less then one second' do
18
+ let(:seconds) { 0.987 }
19
+ it { should eql '0.987s' }
20
+ end
21
+
22
+ context 'when one second' do
23
+ let(:seconds) { 1 }
24
+ it { should eql '01s' }
25
+ end
26
+
27
+ context 'when only seconds' do
28
+ let(:seconds) { 5 }
29
+ it { should eql '05s' }
30
+ end
31
+
32
+ context 'when only minutes' do
33
+ let(:seconds) { 120 }
34
+ it { should eql '02m' }
35
+ end
36
+
37
+ context 'when only hours' do
38
+ let(:seconds) { 60*60*3 }
39
+ it { should eql '03h' }
40
+ end
41
+
42
+ context 'when minutes and seconds' do
43
+ let(:seconds) { 180+9 }
44
+ it { should eql '03m 09s' }
45
+ end
46
+
47
+ context 'when all' do
48
+ let(:seconds) { 60*60*4+120+7 }
49
+ it { should eql '04h 02m 07s' }
50
+ end
51
+
52
+ context 'when negative seconds' do
53
+ let(:seconds) { -67 }
54
+ it { should eql '-01m 07s' }
55
+ end
56
+ end
57
+ end
@@ -0,0 +1,68 @@
1
+ describe KnapsackPro::Report do
2
+ describe '.save' do
3
+ subject { described_class.save }
4
+
5
+ before do
6
+ commit_hash = double
7
+ branch = double
8
+ repository_adapter = instance_double(KnapsackPro::RepositoryAdapters::EnvAdapter, commit_hash: commit_hash, branch: branch)
9
+ expect(KnapsackPro::RepositoryAdapterInitiator).to receive(:call).and_return(repository_adapter)
10
+
11
+ node_total = double
12
+ node_index = double
13
+ expect(KnapsackPro::Config::Env).to receive(:ci_node_total).and_return(node_total)
14
+ expect(KnapsackPro::Config::Env).to receive(:ci_node_index).and_return(node_index)
15
+
16
+ test_files = double
17
+ tracker = instance_double(KnapsackPro::Tracker, to_a: test_files)
18
+ expect(KnapsackPro).to receive(:tracker).and_return(tracker)
19
+
20
+ action = double
21
+ expect(KnapsackPro::Client::API::V1::BuildSubsets).to receive(:create).with({
22
+ commit_hash: commit_hash,
23
+ branch: branch,
24
+ node_total: node_total,
25
+ node_index: node_index,
26
+ test_files: test_files,
27
+ }).and_return(action)
28
+
29
+ connection = instance_double(KnapsackPro::Client::Connection, success?: success?, errors?: errors?)
30
+ expect(KnapsackPro::Client::Connection).to receive(:new).with(action).and_return(connection).and_return(connection)
31
+
32
+ response = double
33
+ expect(connection).to receive(:call).and_return(response)
34
+ end
35
+
36
+ context 'when success' do
37
+ let(:success?) { true }
38
+
39
+ context 'when response has errors' do
40
+ let(:errors?) { true }
41
+
42
+ it do
43
+ expect {
44
+ subject
45
+ }.to raise_error(ArgumentError)
46
+ end
47
+ end
48
+
49
+ context 'when response has no errors' do
50
+ let(:errors?) { false }
51
+
52
+ it do
53
+ logger = instance_double(Logger)
54
+ expect(KnapsackPro).to receive(:logger).and_return(logger)
55
+ expect(logger).to receive(:info).with('Saved time execution report on API server.')
56
+ subject
57
+ end
58
+ end
59
+ end
60
+
61
+ context 'when failure' do
62
+ let(:success?) { false }
63
+ let(:errors?) { nil }
64
+
65
+ it { subject }
66
+ end
67
+ end
68
+ end
@@ -0,0 +1,21 @@
1
+ describe KnapsackPro::RepositoryAdapterInitiator do
2
+ describe '.call' do
3
+ subject { described_class.call }
4
+
5
+ before do
6
+ expect(KnapsackPro::Config::Env).to receive(:repository_adapter).and_return(repository_adapter)
7
+ end
8
+
9
+ context 'when repository adapter is git' do
10
+ let(:repository_adapter) { 'git' }
11
+
12
+ it { should be_instance_of KnapsackPro::RepositoryAdapters::GitAdapter }
13
+ end
14
+
15
+ context 'when default repository adapter' do
16
+ let(:repository_adapter) { nil }
17
+
18
+ it { should be_instance_of KnapsackPro::RepositoryAdapters::EnvAdapter }
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,13 @@
1
+ describe KnapsackPro::RepositoryAdapters::BaseAdapter do
2
+ describe '#commit_hash' do
3
+ it do
4
+ expect { subject.commit_hash }.to raise_error NotImplementedError
5
+ end
6
+ end
7
+
8
+ describe '#branch' do
9
+ it do
10
+ expect { subject.branch }.to raise_error NotImplementedError
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,27 @@
1
+ describe KnapsackPro::RepositoryAdapters::EnvAdapter do
2
+ it { should be_kind_of KnapsackPro::RepositoryAdapters::BaseAdapter }
3
+
4
+ describe '#commit_hash' do
5
+ let(:commit_hash) { double }
6
+
7
+ subject { described_class.new.commit_hash }
8
+
9
+ before do
10
+ expect(KnapsackPro::Config::Env).to receive(:commit_hash).and_return(commit_hash)
11
+ end
12
+
13
+ it { should eq commit_hash }
14
+ end
15
+
16
+ describe '#branch' do
17
+ let(:branch) { double }
18
+
19
+ subject { described_class.new.branch }
20
+
21
+ before do
22
+ expect(KnapsackPro::Config::Env).to receive(:branch).and_return(branch)
23
+ end
24
+
25
+ it { should eq branch }
26
+ end
27
+ end
@@ -0,0 +1,27 @@
1
+ describe KnapsackPro::RepositoryAdapters::GitAdapter do
2
+ let!(:circle_sha1) { ENV['CIRCLE_SHA1'] }
3
+ let!(:circle_branch) { ENV['CIRCLE_BRANCH'] }
4
+
5
+ before do
6
+ stub_const('ENV', {
7
+ 'KNAPSACK_PRO_PROJECT_DIR' => KnapsackPro.root,
8
+ })
9
+ end
10
+
11
+ it { should be_kind_of KnapsackPro::RepositoryAdapters::BaseAdapter }
12
+
13
+ describe '#commit_hash' do
14
+ subject { described_class.new.commit_hash }
15
+
16
+ it { should_not be_nil }
17
+ its(:size) { should eq 40 }
18
+ it { should eq circle_sha1 } if ENV['CIRCLE_SHA1']
19
+ end
20
+
21
+ describe '#branch' do
22
+ subject { described_class.new.branch }
23
+
24
+ it { should_not be_nil }
25
+ it { should eq circle_branch } if ENV['CIRCLE_BRANCH']
26
+ end
27
+ end
@@ -0,0 +1,61 @@
1
+ describe KnapsackPro::Runners::BaseRunner do
2
+ describe '.run' do
3
+ it do
4
+ expect {
5
+ described_class.run(nil)
6
+ }.to raise_error NotImplementedError
7
+ end
8
+ end
9
+
10
+ describe 'instance methods' do
11
+ let(:adapter_class) { double }
12
+ let(:runner) do
13
+ described_class.new(adapter_class)
14
+ end
15
+ let(:allocator) { instance_double(KnapsackPro::Allocator) }
16
+ let(:allocator_builder) { instance_double(KnapsackPro::AllocatorBuilder) }
17
+
18
+ before do
19
+ expect(KnapsackPro::AllocatorBuilder).to receive(:new).with(adapter_class).and_return(allocator_builder)
20
+ expect(allocator_builder).to receive(:allocator).and_return(allocator)
21
+ end
22
+
23
+ describe '#test_file_paths' do
24
+ let(:test_file_paths) { double }
25
+
26
+ subject { runner.test_file_paths }
27
+
28
+ before do
29
+ expect(allocator).to receive(:test_file_paths).and_return(test_file_paths)
30
+ end
31
+
32
+ it { should eq test_file_paths }
33
+ end
34
+
35
+ describe '#stringify_test_file_paths' do
36
+ let(:stringify_test_file_paths) { double }
37
+
38
+ subject { runner.stringify_test_file_paths }
39
+
40
+ before do
41
+ test_file_paths = double
42
+ expect(runner).to receive(:test_file_paths).and_return(test_file_paths)
43
+ expect(KnapsackPro::TestFilePresenter).to receive(:stringify_paths).with(test_file_paths).and_return(stringify_test_file_paths)
44
+ end
45
+
46
+ it { should eq stringify_test_file_paths }
47
+ end
48
+
49
+ describe '#test_dir' do
50
+ let(:test_dir) { double }
51
+
52
+ subject { runner.test_dir }
53
+
54
+ before do
55
+ expect(allocator_builder).to receive(:test_dir).and_return(test_dir)
56
+ end
57
+
58
+ it { should eq test_dir }
59
+ end
60
+ end
61
+ end
@@ -0,0 +1,47 @@
1
+ describe KnapsackPro::Runners::CucumberRunner do
2
+ subject { described_class.new(KnapsackPro::Adapters::CucumberAdapter) }
3
+
4
+ it { should be_kind_of KnapsackPro::Runners::BaseRunner }
5
+
6
+ describe '.run' do
7
+ let(:args) { '--custom-arg' }
8
+
9
+ after { described_class.run(args) }
10
+
11
+ before do
12
+ stub_const("ENV", { 'KNAPSACK_PRO_TEST_SUITE_TOKEN_CUCUMBER' => 'cucumber-token' })
13
+
14
+ stringify_test_file_paths = 'features/a.feature features/b.feature'
15
+ runner = instance_double(described_class,
16
+ stringify_test_file_paths: stringify_test_file_paths)
17
+ expect(described_class).to receive(:new)
18
+ .with(KnapsackPro::Adapters::CucumberAdapter).and_return(runner)
19
+
20
+ expect(Kernel).to receive(:system).with('KNAPSACK_PRO_RECORDING_ENABLED=true KNAPSACK_PRO_TEST_SUITE_TOKEN=cucumber-token bundle exec cucumber --custom-arg -- features/a.feature features/b.feature')
21
+ end
22
+
23
+ context 'when command exit with success code' do
24
+ let(:exitstatus) { 0 }
25
+
26
+ before do
27
+ expect($?).to receive(:exitstatus).and_return(exitstatus)
28
+ end
29
+
30
+ it do
31
+ expect(Kernel).not_to receive(:exit)
32
+ end
33
+ end
34
+
35
+ context 'when command exit without success code' do
36
+ let(:exitstatus) { 1 }
37
+
38
+ before do
39
+ expect($?).to receive(:exitstatus).twice.and_return(exitstatus)
40
+ end
41
+
42
+ it do
43
+ expect(Kernel).to receive(:exit).with(exitstatus)
44
+ end
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,34 @@
1
+ describe KnapsackPro::Runners::MinitestRunner do
2
+ subject { described_class.new(KnapsackPro::Adapters::MinitestAdapter) }
3
+
4
+ it { should be_kind_of KnapsackPro::Runners::BaseRunner }
5
+
6
+ describe '.run' do
7
+ let(:args) { '--custom-arg' }
8
+ let(:task_name) { 'knapsack_pro:minitest_run' }
9
+
10
+ subject { described_class.run(args) }
11
+
12
+ before do
13
+ stub_const("ENV", { 'KNAPSACK_PRO_TEST_SUITE_TOKEN_MINITEST' => 'minitest-token' })
14
+ end
15
+
16
+ it do
17
+ test_file_paths = ['test_fake/a_test.rb', 'test_fake/b_test.rb']
18
+ runner = instance_double(described_class,
19
+ test_dir: 'test',
20
+ test_file_paths: test_file_paths)
21
+ expect(described_class).to receive(:new)
22
+ .with(KnapsackPro::Adapters::MinitestAdapter).and_return(runner)
23
+
24
+ expect(Rake::Task.task_defined?(task_name)).to be false
25
+
26
+ subject
27
+
28
+ expect(Rake::Task.task_defined?(task_name)).to be true
29
+
30
+ expect(ENV['KNAPSACK_PRO_TEST_SUITE_TOKEN']).to eq 'minitest-token'
31
+ expect(ENV['KNAPSACK_PRO_RECORDING_ENABLED']).to eq 'true'
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,49 @@
1
+ describe KnapsackPro::Runners::RSpecRunner do
2
+ subject { described_class.new(KnapsackPro::Adapters::RSpecAdapter) }
3
+
4
+ it { should be_kind_of KnapsackPro::Runners::BaseRunner }
5
+
6
+ describe '.run' do
7
+ let(:args) { '--profile --color' }
8
+
9
+ after { described_class.run(args) }
10
+
11
+ before do
12
+ stub_const("ENV", { 'KNAPSACK_PRO_TEST_SUITE_TOKEN_RSPEC' => 'rspec-token' })
13
+
14
+ stringify_test_file_paths = 'spec/a_spec.rb spec/b_spec.rb'
15
+ runner = instance_double(described_class,
16
+ test_dir: 'spec',
17
+ stringify_test_file_paths: stringify_test_file_paths)
18
+ expect(described_class).to receive(:new)
19
+ .with(KnapsackPro::Adapters::RSpecAdapter).and_return(runner)
20
+
21
+ expect(Kernel).to receive(:system)
22
+ .with('KNAPSACK_PRO_RECORDING_ENABLED=true KNAPSACK_PRO_TEST_SUITE_TOKEN=rspec-token bundle exec rspec --profile --color --default-path spec -- spec/a_spec.rb spec/b_spec.rb')
23
+ end
24
+
25
+ context 'when command exit with success code' do
26
+ let(:exitstatus) { 0 }
27
+
28
+ before do
29
+ expect($?).to receive(:exitstatus).and_return(exitstatus)
30
+ end
31
+
32
+ it do
33
+ expect(Kernel).not_to receive(:exit)
34
+ end
35
+ end
36
+
37
+ context 'when command exit without success code' do
38
+ let(:exitstatus) { 1 }
39
+
40
+ before do
41
+ expect($?).to receive(:exitstatus).twice.and_return(exitstatus)
42
+ end
43
+
44
+ it do
45
+ expect(Kernel).to receive(:exit).with(exitstatus)
46
+ end
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,14 @@
1
+ describe KnapsackPro::TaskLoader do
2
+ describe '#load_tasks' do
3
+ let(:rspec_rake_task_path) { "#{KnapsackPro.root}/lib/tasks/rspec.rake" }
4
+ let(:cucumber_rake_task_path) { "#{KnapsackPro.root}/lib/tasks/cucumber.rake" }
5
+ let(:minitest_rake_task_path) { "#{KnapsackPro.root}/lib/tasks/minitest.rake" }
6
+
7
+ before { allow(subject).to receive(:import) }
8
+ after { subject.load_tasks }
9
+
10
+ it { expect(subject).to receive(:import).with(rspec_rake_task_path) }
11
+ it { expect(subject).to receive(:import).with(cucumber_rake_task_path) }
12
+ it { expect(subject).to receive(:import).with(minitest_rake_task_path) }
13
+ end
14
+ end