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
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