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,31 @@
1
+ module KnapsackPro
2
+ module Runners
3
+ class BaseRunner
4
+ def self.run(args)
5
+ raise NotImplementedError
6
+ end
7
+
8
+ def initialize(adapter_class)
9
+ @allocator_builder = KnapsackPro::AllocatorBuilder.new(adapter_class)
10
+ @allocator = allocator_builder.allocator
11
+ end
12
+
13
+ def test_file_paths
14
+ allocator.test_file_paths
15
+ end
16
+
17
+ def stringify_test_file_paths
18
+ KnapsackPro::TestFilePresenter.stringify_paths(test_file_paths)
19
+ end
20
+
21
+ def test_dir
22
+ allocator_builder.test_dir
23
+ end
24
+
25
+ private
26
+
27
+ attr_reader :allocator_builder,
28
+ :allocator
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,16 @@
1
+ module KnapsackPro
2
+ module Runners
3
+ class CucumberRunner < BaseRunner
4
+ def self.run(args)
5
+ ENV['KNAPSACK_PRO_TEST_SUITE_TOKEN'] = KnapsackPro::Config::Env.test_suite_token_cucumber
6
+
7
+ runner = new(KnapsackPro::Adapters::CucumberAdapter)
8
+
9
+ cmd = %Q[KNAPSACK_PRO_RECORDING_ENABLED=true KNAPSACK_PRO_TEST_SUITE_TOKEN=#{ENV['KNAPSACK_PRO_TEST_SUITE_TOKEN']} bundle exec cucumber #{args} -- #{runner.stringify_test_file_paths}]
10
+
11
+ Kernel.system(cmd)
12
+ Kernel.exit($?.exitstatus) unless $?.exitstatus.zero?
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,26 @@
1
+ module KnapsackPro
2
+ module Runners
3
+ class MinitestRunner < BaseRunner
4
+ def self.run(args)
5
+ ENV['KNAPSACK_PRO_TEST_SUITE_TOKEN'] = KnapsackPro::Config::Env.test_suite_token_minitest
6
+ ENV['KNAPSACK_PRO_RECORDING_ENABLED'] = 'true'
7
+
8
+ runner = new(KnapsackPro::Adapters::MinitestAdapter)
9
+
10
+ task_name = 'knapsack_pro:minitest_run'
11
+
12
+ if Rake::Task.task_defined?(task_name)
13
+ Rake::Task[task_name].clear
14
+ end
15
+
16
+ Rake::TestTask.new(task_name) do |t|
17
+ t.libs << runner.test_dir
18
+ t.test_files = runner.test_file_paths
19
+ t.options = args
20
+ end
21
+
22
+ Rake::Task[task_name].invoke
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,16 @@
1
+ module KnapsackPro
2
+ module Runners
3
+ class RSpecRunner < BaseRunner
4
+ def self.run(args)
5
+ ENV['KNAPSACK_PRO_TEST_SUITE_TOKEN'] = KnapsackPro::Config::Env.test_suite_token_rspec
6
+
7
+ runner = new(KnapsackPro::Adapters::RSpecAdapter)
8
+
9
+ cmd = %Q[KNAPSACK_PRO_RECORDING_ENABLED=true KNAPSACK_PRO_TEST_SUITE_TOKEN=#{ENV['KNAPSACK_PRO_TEST_SUITE_TOKEN']} bundle exec rspec #{args} --default-path #{runner.test_dir} -- #{runner.stringify_test_file_paths}]
10
+
11
+ Kernel.system(cmd)
12
+ Kernel.exit($?.exitstatus) unless $?.exitstatus.zero?
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,11 @@
1
+ require 'rake'
2
+
3
+ module KnapsackPro
4
+ class TaskLoader
5
+ include ::Rake::DSL
6
+
7
+ def load_tasks
8
+ Dir.glob("#{KnapsackPro.root}/lib/tasks/*.rake").each { |r| import r }
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,7 @@
1
+ module KnapsackPro
2
+ class TestFileCleaner
3
+ def self.clean(test_file_path)
4
+ test_file_path.sub(/^\.\//, '')
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,33 @@
1
+ module KnapsackPro
2
+ class TestFileFinder
3
+ def self.call(test_file_pattern)
4
+ new(test_file_pattern).call
5
+ end
6
+
7
+ def initialize(test_file_pattern)
8
+ @test_file_pattern = test_file_pattern
9
+ end
10
+
11
+ def call
12
+ test_file_hashes = []
13
+ test_files.each do |test_file_path|
14
+ test_file_hashes << test_file_hash_for(test_file_path)
15
+ end
16
+ test_file_hashes
17
+ end
18
+
19
+ private
20
+
21
+ attr_reader :test_file_pattern
22
+
23
+ def test_files
24
+ Dir.glob(test_file_pattern).sort
25
+ end
26
+
27
+ def test_file_hash_for(test_file_path)
28
+ {
29
+ 'path' => TestFileCleaner.clean(test_file_path)
30
+ }
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,7 @@
1
+ module KnapsackPro
2
+ class TestFilePattern
3
+ def self.call(adapter_class)
4
+ KnapsackPro::Config::Env.test_file_pattern || adapter_class::TEST_DIR_PATTERN
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,11 @@
1
+ module KnapsackPro
2
+ class TestFilePresenter
3
+ def self.stringify_paths(test_file_paths)
4
+ test_file_paths.join(' ')
5
+ end
6
+
7
+ def self.paths(test_files)
8
+ test_files.map { |t| t['path'] }
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,84 @@
1
+ module KnapsackPro
2
+ class TestFlatDistributor
3
+ DIR_TYPES = [
4
+ :feature,
5
+ :integration,
6
+ :e2e,
7
+ :models,
8
+ :views,
9
+ :controllers,
10
+ ]
11
+
12
+ def initialize(test_files, node_total)
13
+ @test_files = test_files
14
+ @node_total = node_total
15
+ set_default_nodes_hash
16
+ set_grouped_test_files
17
+ end
18
+
19
+ def nodes
20
+ group_test_files_by_directory
21
+ generate_nodes
22
+ end
23
+
24
+ def test_files_for_node(node_index)
25
+ nodes[node_index]
26
+ end
27
+
28
+ private
29
+
30
+ attr_reader :test_files,
31
+ :node_total,
32
+ :nodes_hash,
33
+ :grouped_test_files
34
+
35
+ def set_default_nodes_hash
36
+ @nodes_hash = {}
37
+ node_total.times do |index|
38
+ nodes_hash[index] = []
39
+ end
40
+ end
41
+
42
+ def set_grouped_test_files
43
+ @grouped_test_files = {}
44
+ DIR_TYPES.each do |type|
45
+ grouped_test_files[type] = []
46
+ end
47
+ grouped_test_files[:other] = []
48
+ end
49
+
50
+ def sorted_test_files
51
+ test_files.sort_by { |t| t['path'] }
52
+ end
53
+
54
+ def group_test_files_by_directory
55
+ sorted_test_files.each do |test_file|
56
+ found = false
57
+ DIR_TYPES.each do |type|
58
+ if test_file['path'].match(/#{type}/)
59
+ grouped_test_files[type] << test_file
60
+ found = true
61
+ break
62
+ end
63
+ end
64
+
65
+ unless found
66
+ grouped_test_files[:other] << test_file
67
+ end
68
+ end
69
+ end
70
+
71
+ def generate_nodes
72
+ node_index = 0
73
+ grouped_test_files.each do |_type, test_files|
74
+ test_files.each do |test_file|
75
+ nodes_hash[node_index] << test_file
76
+
77
+ node_index += 1
78
+ node_index %= node_total
79
+ end
80
+ end
81
+ nodes_hash
82
+ end
83
+ end
84
+ end
@@ -0,0 +1,64 @@
1
+ module KnapsackPro
2
+ class Tracker
3
+ include Singleton
4
+
5
+ attr_reader :global_time, :test_files_with_time
6
+ attr_writer :current_test_path
7
+
8
+ def initialize
9
+ set_defaults
10
+ end
11
+
12
+ def reset!
13
+ set_defaults
14
+ end
15
+
16
+ def start_timer
17
+ @start_time = now_without_mock_time.to_f
18
+ end
19
+
20
+ def stop_timer
21
+ @execution_time = now_without_mock_time.to_f - @start_time
22
+ update_global_time
23
+ update_test_file_time
24
+ @execution_time
25
+ end
26
+
27
+ def current_test_path
28
+ raise("current_test_path needs to be set by Knapsack Pro Adapter's bind method") unless @current_test_path
29
+ @current_test_path.sub(/^\.\//, '')
30
+ end
31
+
32
+ def to_a
33
+ test_files = []
34
+ @test_files_with_time.each do |path, time_execution|
35
+ test_files << {
36
+ path: path,
37
+ time_execution: time_execution
38
+ }
39
+ end
40
+ test_files
41
+ end
42
+
43
+ private
44
+
45
+ def set_defaults
46
+ @global_time = 0
47
+ @test_files_with_time = {}
48
+ @test_path = nil
49
+ end
50
+
51
+ def update_global_time
52
+ @global_time += @execution_time
53
+ end
54
+
55
+ def update_test_file_time
56
+ @test_files_with_time[current_test_path] ||= 0
57
+ @test_files_with_time[current_test_path] += @execution_time
58
+ end
59
+
60
+ def now_without_mock_time
61
+ Time.now_without_mock_time
62
+ end
63
+ end
64
+ end
@@ -1,3 +1,3 @@
1
1
  module KnapsackPro
2
- VERSION = '0.0.1'
2
+ VERSION = '0.1.0'
3
3
  end
@@ -0,0 +1,7 @@
1
+ require 'knapsack_pro'
2
+
3
+ namespace :knapsack_pro do
4
+ task :cucumber, [:cucumber_args] do |_, args|
5
+ KnapsackPro::Runners::CucumberRunner.run(args[:cucumber_args])
6
+ end
7
+ end
@@ -0,0 +1,7 @@
1
+ require 'knapsack_pro'
2
+
3
+ namespace :knapsack_pro do
4
+ task :minitest, [:minitest_args] do |_, args|
5
+ KnapsackPro::Runners::MinitestRunner.run(args[:minitest_args])
6
+ end
7
+ end
@@ -0,0 +1,7 @@
1
+ require 'knapsack_pro'
2
+
3
+ namespace :knapsack_pro do
4
+ task :rspec, [:rspec_args] do |_, args|
5
+ KnapsackPro::Runners::RSpecRunner.run(args[:rspec_args])
6
+ end
7
+ end
@@ -0,0 +1,50 @@
1
+ ---
2
+ http_interactions:
3
+ - request:
4
+ method: post
5
+ uri: http://api.knapsackpro.dev:3000/v1/build_distributions/subset
6
+ body:
7
+ encoding: UTF-8
8
+ string: '{"commit_hash":"abcdefg","branch":"master","node_total":"2","node_index":"1","test_files":[{"path":"a_spec.rb"},{"path":"b_spec.rb"}],"test_suite_token":"fake"}'
9
+ headers:
10
+ Content-Type:
11
+ - application/json
12
+ Accept:
13
+ - application/json
14
+ Accept-Encoding:
15
+ - gzip;q=1.0,deflate;q=0.6,identity;q=0.3
16
+ User-Agent:
17
+ - Ruby
18
+ response:
19
+ status:
20
+ code: 403
21
+ message: 'Forbidden '
22
+ headers:
23
+ X-Frame-Options:
24
+ - SAMEORIGIN
25
+ X-Xss-Protection:
26
+ - 1; mode=block
27
+ X-Content-Type-Options:
28
+ - nosniff
29
+ Content-Type:
30
+ - application/json; charset=utf-8
31
+ Cache-Control:
32
+ - no-cache
33
+ X-Request-Id:
34
+ - 6d40fe71-7fad-449a-812f-9773429777ce
35
+ X-Runtime:
36
+ - '0.054890'
37
+ Server:
38
+ - WEBrick/1.3.1 (Ruby/2.2.2/2015-04-13)
39
+ Date:
40
+ - Fri, 31 Jul 2015 16:09:00 GMT
41
+ Content-Length:
42
+ - '39'
43
+ Connection:
44
+ - Keep-Alive
45
+ body:
46
+ encoding: UTF-8
47
+ string: '{"errors":["invalid test suite token"]}'
48
+ http_version:
49
+ recorded_at: Fri, 31 Jul 2015 16:09:00 GMT
50
+ recorded_with: VCR 2.9.3
@@ -0,0 +1,52 @@
1
+ ---
2
+ http_interactions:
3
+ - request:
4
+ method: post
5
+ uri: http://api.knapsackpro.dev:3000/v1/build_distributions/subset
6
+ body:
7
+ encoding: UTF-8
8
+ string: '{"commit_hash":"abcdefg","branch":"master","node_total":"2","node_index":"1","test_files":[{"path":"a_spec.rb"},{"path":"b_spec.rb"}],"test_suite_token":"3fa64859337f6e56409d49f865d13fd7"}'
9
+ headers:
10
+ Content-Type:
11
+ - application/json
12
+ Accept:
13
+ - application/json
14
+ Accept-Encoding:
15
+ - gzip;q=1.0,deflate;q=0.6,identity;q=0.3
16
+ User-Agent:
17
+ - Ruby
18
+ response:
19
+ status:
20
+ code: 200
21
+ message: 'OK '
22
+ headers:
23
+ X-Frame-Options:
24
+ - SAMEORIGIN
25
+ X-Xss-Protection:
26
+ - 1; mode=block
27
+ X-Content-Type-Options:
28
+ - nosniff
29
+ Content-Type:
30
+ - application/json; charset=utf-8
31
+ Etag:
32
+ - W/"2348448eb68a360747d21fe1bb304d5b"
33
+ Cache-Control:
34
+ - max-age=0, private, must-revalidate
35
+ X-Request-Id:
36
+ - 66e07e14-0dfd-43f9-b331-2a930141dfef
37
+ X-Runtime:
38
+ - '0.158064'
39
+ Server:
40
+ - WEBrick/1.3.1 (Ruby/2.2.3/2015-08-18)
41
+ Date:
42
+ - Fri, 23 Oct 2015 20:12:23 GMT
43
+ Content-Length:
44
+ - '74'
45
+ Connection:
46
+ - Keep-Alive
47
+ body:
48
+ encoding: UTF-8
49
+ string: '{"node_index":1,"test_files":[{"path":"b_spec.rb","time_execution":null}]}'
50
+ http_version:
51
+ recorded_at: Fri, 23 Oct 2015 20:12:23 GMT
52
+ recorded_with: VCR 2.9.3