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.
- checksums.yaml +4 -4
- data/.gitignore +3 -3
- data/CHANGELOG.md +4 -0
- data/README.md +368 -4
- data/bin/knapsack_pro +20 -0
- data/circle.yml +2 -0
- data/knapsack_pro.gemspec +6 -1
- data/lib/knapsack_pro.rb +74 -0
- data/lib/knapsack_pro/adapters/base_adapter.rb +30 -0
- data/lib/knapsack_pro/adapters/cucumber_adapter.rb +40 -0
- data/lib/knapsack_pro/adapters/minitest_adapter.rb +52 -0
- data/lib/knapsack_pro/adapters/rspec_adapter.rb +48 -0
- data/lib/knapsack_pro/allocator.rb +40 -0
- data/lib/knapsack_pro/allocator_builder.rb +40 -0
- data/lib/knapsack_pro/client/api/action.rb +17 -0
- data/lib/knapsack_pro/client/api/v1/base.rb +15 -0
- data/lib/knapsack_pro/client/api/v1/build_distributions.rb +29 -0
- data/lib/knapsack_pro/client/api/v1/build_subsets.rb +29 -0
- data/lib/knapsack_pro/client/connection.rb +94 -0
- data/lib/knapsack_pro/config/ci/base.rb +22 -0
- data/lib/knapsack_pro/config/ci/buildkite.rb +27 -0
- data/lib/knapsack_pro/config/ci/circle.rb +29 -0
- data/lib/knapsack_pro/config/ci/semaphore.rb +28 -0
- data/lib/knapsack_pro/config/env.rb +111 -0
- data/lib/knapsack_pro/logger_wrapper.rb +15 -0
- data/lib/knapsack_pro/presenter.rb +25 -0
- data/lib/knapsack_pro/report.rb +20 -0
- data/lib/knapsack_pro/repository_adapter_initiator.rb +12 -0
- data/lib/knapsack_pro/repository_adapters/base_adapter.rb +14 -0
- data/lib/knapsack_pro/repository_adapters/env_adapter.rb +13 -0
- data/lib/knapsack_pro/repository_adapters/git_adapter.rb +19 -0
- data/lib/knapsack_pro/runners/base_runner.rb +31 -0
- data/lib/knapsack_pro/runners/cucumber_runner.rb +16 -0
- data/lib/knapsack_pro/runners/minitest_runner.rb +26 -0
- data/lib/knapsack_pro/runners/rspec_runner.rb +16 -0
- data/lib/knapsack_pro/task_loader.rb +11 -0
- data/lib/knapsack_pro/test_file_cleaner.rb +7 -0
- data/lib/knapsack_pro/test_file_finder.rb +33 -0
- data/lib/knapsack_pro/test_file_pattern.rb +7 -0
- data/lib/knapsack_pro/test_file_presenter.rb +11 -0
- data/lib/knapsack_pro/test_flat_distributor.rb +84 -0
- data/lib/knapsack_pro/tracker.rb +64 -0
- data/lib/knapsack_pro/version.rb +1 -1
- data/lib/tasks/cucumber.rake +7 -0
- data/lib/tasks/minitest.rake +7 -0
- data/lib/tasks/rspec.rake +7 -0
- data/spec/fixtures/vcr_cassettes/api/v1/build_distributions/subset/invalid_test_suite_token.yml +50 -0
- data/spec/fixtures/vcr_cassettes/api/v1/build_distributions/subset/success.yml +52 -0
- data/spec/fixtures/vcr_cassettes/api/v1/build_subsets/create/invalid_test_suite_token.yml +50 -0
- data/spec/fixtures/vcr_cassettes/api/v1/build_subsets/create/success.yml +50 -0
- data/spec/integration/api/build_distributions_subset_spec.rb +74 -0
- data/spec/integration/api/build_subsets_create_spec.rb +76 -0
- data/spec/knapsack_pro/adapters/base_adapter_spec.rb +63 -0
- data/spec/knapsack_pro/adapters/cucumber_adapter_spec.rb +71 -0
- data/spec/knapsack_pro/adapters/minitest_adapter_spec.rb +107 -0
- data/spec/knapsack_pro/adapters/rspec_adapter_spec.rb +94 -0
- data/spec/knapsack_pro/allocator_builder_spec.rb +45 -0
- data/spec/knapsack_pro/allocator_spec.rb +80 -0
- data/spec/knapsack_pro/client/api/action_spec.rb +17 -0
- data/spec/knapsack_pro/client/api/v1/base_spec.rb +2 -0
- data/spec/knapsack_pro/client/api/v1/build_distributions_spec.rb +35 -0
- data/spec/knapsack_pro/client/api/v1/build_subsets_spec.rb +35 -0
- data/spec/knapsack_pro/client/connection_spec.rb +138 -0
- data/spec/knapsack_pro/config/ci/base_spec.rb +7 -0
- data/spec/knapsack_pro/config/ci/buildkite_spec.rb +74 -0
- data/spec/knapsack_pro/config/ci/circle_spec.rb +74 -0
- data/spec/knapsack_pro/config/ci/semaphore_spec.rb +74 -0
- data/spec/knapsack_pro/config/env_spec.rb +368 -0
- data/spec/knapsack_pro/logger_wrapper_spec.rb +15 -0
- data/spec/knapsack_pro/presenter_spec.rb +57 -0
- data/spec/knapsack_pro/report_spec.rb +68 -0
- data/spec/knapsack_pro/repository_adapter_initiator_spec.rb +21 -0
- data/spec/knapsack_pro/repository_adapters/base_adapter_spec.rb +13 -0
- data/spec/knapsack_pro/repository_adapters/env_adapter_spec.rb +27 -0
- data/spec/knapsack_pro/repository_adapters/git_adapter_spec.rb +27 -0
- data/spec/knapsack_pro/runners/base_runner_spec.rb +61 -0
- data/spec/knapsack_pro/runners/cucumber_runner_spec.rb +47 -0
- data/spec/knapsack_pro/runners/minitest_runner_spec.rb +34 -0
- data/spec/knapsack_pro/runners/rspec_runner_spec.rb +49 -0
- data/spec/knapsack_pro/task_loader_spec.rb +14 -0
- data/spec/knapsack_pro/test_file_cleaner_spec.rb +11 -0
- data/spec/knapsack_pro/test_file_finder_spec.rb +35 -0
- data/spec/knapsack_pro/test_file_pattern_spec.rb +23 -0
- data/spec/knapsack_pro/test_file_presenter_spec.rb +22 -0
- data/spec/knapsack_pro/test_flat_distributor_spec.rb +60 -0
- data/spec/knapsack_pro/tracker_spec.rb +102 -0
- data/spec/knapsack_pro_spec.rb +56 -0
- data/spec/spec_helper.rb +11 -1
- data/spec/support/fakes/cucumber.rb +12 -0
- data/spec/support/fakes/minitest.rb +12 -0
- data/spec/support/shared_examples/adapter.rb +17 -0
- data/spec_fake/controllers/users_controller_spec.rb +0 -0
- data/spec_fake/models/admin_spec.rb +0 -0
- data/spec_fake/models/user_spec.rb +0 -0
- data/spec_fake/spec_helper.rb +0 -0
- data/test_fake/a_test.rb +0 -0
- data/test_fake/b_test.rb +0 -0
- metadata +212 -12
- data/Gemfile.lock +0 -69
- 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,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,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
|
data/lib/knapsack_pro/version.rb
CHANGED
data/spec/fixtures/vcr_cassettes/api/v1/build_distributions/subset/invalid_test_suite_token.yml
ADDED
@@ -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
|