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
data/knapsack_pro.gemspec CHANGED
@@ -19,11 +19,16 @@ Gem::Specification.new do |spec|
19
19
  spec.require_paths = ["lib"]
20
20
 
21
21
  spec.add_dependency 'rake', '>= 0'
22
+ spec.add_dependency 'activesupport', '>= 3.2.8'
23
+ spec.add_dependency 'timecop', '>= 0.1.0'
22
24
 
23
25
  spec.add_development_dependency 'bundler', '~> 1.6'
24
26
  spec.add_development_dependency 'rspec', '~> 3.0', '>= 2.0.0'
27
+ spec.add_development_dependency 'rspec-its', '~> 1.2'
25
28
  spec.add_development_dependency 'cucumber', '>= 1.3'
26
- spec.add_development_dependency 'timecop', '~> 0'
29
+ spec.add_development_dependency 'minitest', '>= 5.0.0'
27
30
  spec.add_development_dependency 'codeclimate-test-reporter', '~> 0'
28
31
  spec.add_development_dependency 'pry', '~> 0'
32
+ spec.add_development_dependency 'vcr', '~> 2.9'
33
+ spec.add_development_dependency 'webmock', '~> 1.21'
29
34
  end
data/lib/knapsack_pro.rb CHANGED
@@ -1,9 +1,83 @@
1
+ require 'logger'
2
+ require 'singleton'
3
+ require 'net/http'
4
+ require 'json'
5
+ require 'uri'
6
+ require 'rake/testtask'
7
+ require 'active_support/tagged_logging'
8
+ require 'timecop'
1
9
  require_relative 'knapsack_pro/version'
10
+ require_relative 'knapsack_pro/logger_wrapper'
11
+ require_relative 'knapsack_pro/config/ci/base'
12
+ require_relative 'knapsack_pro/config/ci/circle'
13
+ require_relative 'knapsack_pro/config/ci/semaphore'
14
+ require_relative 'knapsack_pro/config/ci/buildkite'
15
+ require_relative 'knapsack_pro/config/env'
16
+ require_relative 'knapsack_pro/client/api/action'
17
+ require_relative 'knapsack_pro/client/api/v1/base'
18
+ require_relative 'knapsack_pro/client/api/v1/build_distributions'
19
+ require_relative 'knapsack_pro/client/api/v1/build_subsets'
20
+ require_relative 'knapsack_pro/client/connection'
21
+ require_relative 'knapsack_pro/repository_adapters/base_adapter'
22
+ require_relative 'knapsack_pro/repository_adapters/env_adapter'
23
+ require_relative 'knapsack_pro/repository_adapters/git_adapter'
24
+ require_relative 'knapsack_pro/repository_adapter_initiator'
25
+ require_relative 'knapsack_pro/report'
26
+ require_relative 'knapsack_pro/presenter'
27
+ require_relative 'knapsack_pro/test_file_cleaner'
28
+ require_relative 'knapsack_pro/test_file_presenter'
29
+ require_relative 'knapsack_pro/test_file_finder'
30
+ require_relative 'knapsack_pro/test_file_pattern'
31
+ require_relative 'knapsack_pro/test_flat_distributor'
32
+ require_relative 'knapsack_pro/task_loader'
33
+ require_relative 'knapsack_pro/tracker'
34
+ require_relative 'knapsack_pro/allocator'
35
+ require_relative 'knapsack_pro/allocator_builder'
36
+ require_relative 'knapsack_pro/adapters/base_adapter'
37
+ require_relative 'knapsack_pro/adapters/rspec_adapter'
38
+ require_relative 'knapsack_pro/adapters/cucumber_adapter'
39
+ require_relative 'knapsack_pro/adapters/minitest_adapter'
40
+ require_relative 'knapsack_pro/runners/base_runner'
41
+ require_relative 'knapsack_pro/runners/rspec_runner'
42
+ require_relative 'knapsack_pro/runners/cucumber_runner'
43
+ require_relative 'knapsack_pro/runners/minitest_runner'
2
44
 
3
45
  module KnapsackPro
4
46
  class << self
5
47
  def root
6
48
  File.expand_path('../..', __FILE__)
7
49
  end
50
+
51
+ def logger
52
+ unless @logger
53
+ default_logger = ::Logger.new(STDOUT)
54
+ default_logger.level = ::Logger::INFO
55
+ self.logger = default_logger
56
+ end
57
+ @logger
58
+ end
59
+
60
+ def logger=(logger)
61
+ @logger = KnapsackPro::LoggerWrapper.new(logger)
62
+ end
63
+
64
+ def reset_logger!
65
+ @logger = nil
66
+ end
67
+
68
+ def tracker
69
+ KnapsackPro::Tracker.instance
70
+ end
71
+
72
+ def load_tasks
73
+ task_loader = KnapsackPro::TaskLoader.new
74
+ task_loader.load_tasks
75
+ end
76
+
77
+ private
78
+
79
+ def set_progname(logger)
80
+ logger.progname = '[knapsack_pro]' if logger
81
+ end
8
82
  end
9
83
  end
@@ -0,0 +1,30 @@
1
+ module KnapsackPro
2
+ module Adapters
3
+ class BaseAdapter
4
+ # Just example, please overwrite constant in subclass
5
+ TEST_DIR_PATTERN = 'test/**/*_test.rb'
6
+
7
+ def self.bind
8
+ adapter = new
9
+ adapter.bind
10
+ adapter
11
+ end
12
+
13
+ def bind
14
+ if KnapsackPro::Config::Env.recording_enabled?
15
+ KnapsackPro.logger.info('Test suite time execution recording enabled.')
16
+ bind_time_tracker
17
+ bind_save_report
18
+ end
19
+ end
20
+
21
+ def bind_time_tracker
22
+ raise NotImplementedError
23
+ end
24
+
25
+ def bind_save_report
26
+ raise NotImplementedError
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,40 @@
1
+ module KnapsackPro
2
+ module Adapters
3
+ class CucumberAdapter < BaseAdapter
4
+ TEST_DIR_PATTERN = 'features/**/*.feature'
5
+
6
+ def self.test_path(scenario_or_outline_table)
7
+ if scenario_or_outline_table.respond_to?(:file)
8
+ scenario_or_outline_table.file
9
+ else
10
+ scenario_or_outline_table.scenario_outline.file
11
+ end
12
+ end
13
+
14
+ def bind_time_tracker
15
+ Around do |scenario_or_outline_table, block|
16
+ KnapsackPro.tracker.current_test_path = KnapsackPro::Adapters::CucumberAdapter.test_path(scenario_or_outline_table)
17
+ KnapsackPro.tracker.start_timer
18
+ block.call
19
+ KnapsackPro.tracker.stop_timer
20
+ end
21
+
22
+ ::Kernel.at_exit do
23
+ KnapsackPro.logger.info(KnapsackPro::Presenter.global_time)
24
+ end
25
+ end
26
+
27
+ def bind_save_report
28
+ ::Kernel.at_exit do
29
+ KnapsackPro::Report.save
30
+ end
31
+ end
32
+
33
+ private
34
+
35
+ def Around(*tag_expressions, &proc)
36
+ ::Cucumber::RbSupport::RbDsl.register_rb_hook('around', tag_expressions, proc)
37
+ end
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,52 @@
1
+ module KnapsackPro
2
+ module Adapters
3
+ class MinitestAdapter < BaseAdapter
4
+ TEST_DIR_PATTERN = 'test/**/*_test.rb'
5
+ @@parent_of_test_dir = nil
6
+
7
+ def self.test_path(obj)
8
+ test_method_name = obj.name
9
+ method_object = obj.method(test_method_name)
10
+ full_test_path = method_object.source_location.first
11
+ parent_of_test_dir_regexp = Regexp.new("^#{@@parent_of_test_dir}")
12
+ test_path = full_test_path.gsub(parent_of_test_dir_regexp, '.')
13
+ # test_path will look like ./test/dir/unit_test.rb
14
+ test_path
15
+ end
16
+
17
+ # See how to write hooks and plugins
18
+ # https://github.com/seattlerb/minitest/blob/master/lib/minitest/test.rb
19
+ module BindTimeTrackerMinitestPlugin
20
+ def before_setup
21
+ super
22
+ KnapsackPro.tracker.current_test_path = KnapsackPro::Adapters::MinitestAdapter.test_path(self)
23
+ KnapsackPro.tracker.start_timer
24
+ end
25
+
26
+ def after_teardown
27
+ KnapsackPro.tracker.stop_timer
28
+ super
29
+ end
30
+ end
31
+
32
+ def bind_time_tracker
33
+ ::Minitest::Test.send(:include, BindTimeTrackerMinitestPlugin)
34
+
35
+ ::Minitest.after_run do
36
+ KnapsackPro.logger.info(KnapsackPro::Presenter.global_time)
37
+ end
38
+ end
39
+
40
+ def bind_save_report
41
+ Minitest.after_run do
42
+ KnapsackPro::Report.save
43
+ end
44
+ end
45
+
46
+ def set_test_helper_path(file_path)
47
+ test_dir_path = File.dirname(file_path)
48
+ @@parent_of_test_dir = File.expand_path('../', test_dir_path)
49
+ end
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,48 @@
1
+ module KnapsackPro
2
+ module Adapters
3
+ class RSpecAdapter < BaseAdapter
4
+ TEST_DIR_PATTERN = 'spec/**/*_spec.rb'
5
+
6
+ def self.test_path(example_group)
7
+ unless example_group[:turnip]
8
+ until example_group[:parent_example_group].nil?
9
+ example_group = example_group[:parent_example_group]
10
+ end
11
+ end
12
+
13
+ example_group[:file_path]
14
+ end
15
+
16
+ def bind_time_tracker
17
+ ::RSpec.configure do |config|
18
+ config.before(:each) do
19
+ current_example_group =
20
+ if ::RSpec.respond_to?(:current_example)
21
+ ::RSpec.current_example.metadata[:example_group]
22
+ else
23
+ example.metadata
24
+ end
25
+ KnapsackPro.tracker.current_test_path = KnapsackPro::Adapters::RSpecAdapter.test_path(current_example_group)
26
+ KnapsackPro.tracker.start_timer
27
+ end
28
+
29
+ config.after(:each) do
30
+ KnapsackPro.tracker.stop_timer
31
+ end
32
+
33
+ config.after(:suite) do
34
+ KnapsackPro.logger.info(KnapsackPro::Presenter.global_time)
35
+ end
36
+ end
37
+ end
38
+
39
+ def bind_save_report
40
+ ::RSpec.configure do |config|
41
+ config.after(:suite) do
42
+ KnapsackPro::Report.save
43
+ end
44
+ end
45
+ end
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,40 @@
1
+ module KnapsackPro
2
+ class Allocator
3
+ def initialize(test_files:,
4
+ ci_node_total:,
5
+ ci_node_index:,
6
+ repository_adapter:)
7
+ @test_files = test_files
8
+ @ci_node_total = ci_node_total
9
+ @ci_node_index = ci_node_index
10
+ @repository_adapter = repository_adapter
11
+ end
12
+
13
+ def test_file_paths
14
+ action = KnapsackPro::Client::API::V1::BuildDistributions.subset(
15
+ commit_hash: repository_adapter.commit_hash,
16
+ branch: repository_adapter.branch,
17
+ node_total: ci_node_total,
18
+ node_index: ci_node_index,
19
+ test_files: test_files,
20
+ )
21
+ connection = KnapsackPro::Client::Connection.new(action)
22
+ response = connection.call
23
+ if connection.success?
24
+ raise ArgumentError.new(response) if connection.errors?
25
+ KnapsackPro::TestFilePresenter.paths(response['test_files'])
26
+ else
27
+ test_flat_distributor = KnapsackPro::TestFlatDistributor.new(test_files, ci_node_total)
28
+ test_files_for_node_index = test_flat_distributor.test_files_for_node(ci_node_index)
29
+ KnapsackPro::TestFilePresenter.paths(test_files_for_node_index)
30
+ end
31
+ end
32
+
33
+ private
34
+
35
+ attr_reader :test_files,
36
+ :ci_node_total,
37
+ :ci_node_index,
38
+ :repository_adapter
39
+ end
40
+ end
@@ -0,0 +1,40 @@
1
+ module KnapsackPro
2
+ class AllocatorBuilder
3
+ def initialize(adapter_class)
4
+ @adapter_class = adapter_class
5
+ end
6
+
7
+ def allocator
8
+ KnapsackPro::Allocator.new(
9
+ test_files: test_files,
10
+ ci_node_total: env.ci_node_total,
11
+ ci_node_index: env.ci_node_index,
12
+ repository_adapter: repository_adapter,
13
+ )
14
+ end
15
+
16
+ def test_dir
17
+ test_file_pattern.split('/').first
18
+ end
19
+
20
+ private
21
+
22
+ attr_reader :adapter_class
23
+
24
+ def env
25
+ KnapsackPro::Config::Env
26
+ end
27
+
28
+ def repository_adapter
29
+ KnapsackPro::RepositoryAdapterInitiator.call
30
+ end
31
+
32
+ def test_file_pattern
33
+ TestFilePattern.call(adapter_class)
34
+ end
35
+
36
+ def test_files
37
+ KnapsackPro::TestFileFinder.call(test_file_pattern)
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,17 @@
1
+ module KnapsackPro
2
+ module Client
3
+ module API
4
+ class Action
5
+ attr_reader :endpoint_path, :http_method, :request_hash
6
+
7
+ def initialize(endpoint_path:,
8
+ http_method:,
9
+ request_hash:)
10
+ @endpoint_path = endpoint_path
11
+ @http_method = http_method
12
+ @request_hash = request_hash
13
+ end
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,15 @@
1
+ module KnapsackPro
2
+ module Client
3
+ module API
4
+ module V1
5
+ class Base
6
+ private
7
+
8
+ def self.action_class
9
+ KnapsackPro::Client::API::Action
10
+ end
11
+ end
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,29 @@
1
+ module KnapsackPro
2
+ module Client
3
+ module API
4
+ module V1
5
+ class BuildDistributions < Base
6
+ class << self
7
+ def subset(commit_hash:,
8
+ branch:,
9
+ node_total:,
10
+ node_index:,
11
+ test_files:)
12
+ action_class.new(
13
+ endpoint_path: '/v1/build_distributions/subset',
14
+ http_method: :post,
15
+ request_hash: {
16
+ :commit_hash => commit_hash,
17
+ :branch => branch,
18
+ :node_total => node_total,
19
+ :node_index => node_index,
20
+ :test_files => test_files
21
+ }
22
+ )
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,29 @@
1
+ module KnapsackPro
2
+ module Client
3
+ module API
4
+ module V1
5
+ class BuildSubsets < Base
6
+ class << self
7
+ def create(commit_hash:,
8
+ branch:,
9
+ node_total:,
10
+ node_index:,
11
+ test_files:)
12
+ action_class.new(
13
+ endpoint_path: '/v1/build_subsets',
14
+ http_method: :post,
15
+ request_hash: {
16
+ :commit_hash => commit_hash,
17
+ :branch => branch,
18
+ :node_total => node_total,
19
+ :node_index => node_index,
20
+ :test_files => test_files
21
+ }
22
+ )
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end