knapsack_pro 0.0.1 → 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 (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