knapsack 0.0.3 → 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 (49) hide show
  1. checksums.yaml +4 -4
  2. data/.travis.yml +12 -1
  3. data/CHANGELOG.md +11 -0
  4. data/LICENSE.txt +1 -1
  5. data/README.md +71 -24
  6. data/Rakefile +3 -1
  7. data/TODO.md +5 -0
  8. data/knapsack_report.json +9 -12
  9. data/lib/knapsack.rb +25 -6
  10. data/lib/knapsack/adapters/{base.rb → base_adapter.rb} +18 -8
  11. data/lib/knapsack/adapters/{rspec.rb → rspec_adapter.rb} +13 -6
  12. data/lib/knapsack/allocator.rb +65 -0
  13. data/lib/knapsack/distributors/base_distributor.rb +67 -0
  14. data/lib/knapsack/distributors/leftover_distributor.rb +49 -0
  15. data/lib/knapsack/distributors/report_distributor.rb +76 -0
  16. data/lib/knapsack/presenter.rb +38 -5
  17. data/lib/knapsack/report.rb +27 -12
  18. data/lib/knapsack/task_loader.rb +11 -0
  19. data/lib/knapsack/tracker.rb +30 -13
  20. data/lib/knapsack/version.rb +1 -1
  21. data/lib/tasks/knapsack.rake +21 -0
  22. data/spec/knapsack/adapters/base_adapter_spec.rb +91 -0
  23. data/spec/knapsack/adapters/rspec_adapter_spec.rb +29 -0
  24. data/spec/knapsack/allocator_spec.rb +57 -0
  25. data/spec/knapsack/distributors/base_distributor_spec.rb +85 -0
  26. data/spec/knapsack/distributors/leftover_distributor_spec.rb +110 -0
  27. data/spec/knapsack/distributors/report_distributor_spec.rb +152 -0
  28. data/spec/knapsack/presenter_spec.rb +83 -0
  29. data/spec/knapsack/report_spec.rb +73 -0
  30. data/spec/knapsack/task_loader_spec.rb +10 -0
  31. data/spec/knapsack/tracker_spec.rb +84 -20
  32. data/spec/knapsack_spec.rb +23 -2
  33. data/spec/spec_helper.rb +16 -0
  34. data/spec_examples/fast/1_spec.rb +1 -4
  35. data/spec_examples/fast/2_spec.rb +1 -4
  36. data/spec_examples/fast/3_spec.rb +1 -4
  37. data/spec_examples/fast/4_spec.rb +1 -4
  38. data/spec_examples/fast/5_spec.rb +1 -4
  39. data/spec_examples/fast/6_spec.rb +1 -4
  40. data/spec_examples/leftover/1_spec.rb +4 -0
  41. data/spec_examples/leftover/a_spec.rb +8 -0
  42. data/spec_examples/slow/a_spec.rb +1 -3
  43. data/spec_examples/slow/b_spec.rb +3 -5
  44. data/spec_examples/slow/c_spec.rb +1 -3
  45. data/spec_examples/spec_helper.rb +5 -2
  46. metadata +32 -7
  47. data/spec_examples/slow/d_spec.rb +0 -7
  48. data/spec_examples/slow/e_spec.rb +0 -7
  49. data/spec_examples/slow/f_spec.rb +0 -7
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: ed337f85241e8de0a55b8f40474e07c491ffa954
4
- data.tar.gz: 594c3f97f5076272f0883de5f093e6c6930e68f9
3
+ metadata.gz: 987e3a066be096679bd6378e1423d23b4235c9cf
4
+ data.tar.gz: 082b1df779eaf2db8f18f4dc4afb1c02539fda1d
5
5
  SHA512:
6
- metadata.gz: 18e3ba7b288ce4b91d174b121b7f731564bf39e720ef7fa37191bfd68387696e5bbc45c1bc06a28fb0bc503c875078748d673a1f851af321eb657c92bcea78e4
7
- data.tar.gz: 6eed013b760611599ce3bb8028cfc7bea32e6e853b5fc7a1366bdf26d8d2b41f443aed166777fd1d7dc7b6d2c467572f0a0cc7ab36dd4d94f26610030a2ef9f0
6
+ metadata.gz: aab5c000855a105e7b9691fa38390ce242eb768d59f8b5ff62eebba1e9fa015b8880015ab53cf70b1fb22f8f611deff7c594ad748952e883f12da4db88dfdfe7
7
+ data.tar.gz: 578b23a46f3e0d4f268d0a9761d9f1d40eb3104c041a6dd8130ab3bb39d8d566674598474e7ef124b19ebc4aaaa8f048e8d36cb15f26c437f4062caaf001b847
data/.travis.yml CHANGED
@@ -13,9 +13,20 @@ before_install:
13
13
  - "echo 'gem: --no-ri --no-rdoc' > ~/.gemrc"
14
14
  script:
15
15
  - bundle exec rspec spec
16
+
16
17
  # generate knapsack report
17
- - KNAPSACK_GENERATE_REPORT=true bundle exec rspec --default-path spec_examples
18
+ - KNAPSACK_GENERATE_REPORT=true bundle exec rspec --default-path spec_examples --tag focus
19
+
18
20
  # run specs with enabled time offset warning
19
21
  - bundle exec rspec --default-path spec_examples
22
+
23
+ # run rake task for the first CI node
24
+ - CI_NODE_TOTAL=2 CI_NODE_INDEX=0 KNAPSACK_SPEC_PATTERN="spec_examples/**/*_spec.rb" bundle exec rake knapsack:rspec
25
+ # run rake task for the second CI node
26
+ - CI_NODE_TOTAL=2 CI_NODE_INDEX=1 KNAPSACK_SPEC_PATTERN="spec_examples/**/*_spec.rb" bundle exec rake knapsack:rspec
27
+
28
+ # run specs for custom knapsack report path
29
+ - cp knapsack_report.json custom_knapsack_report.json
30
+ - KNAPSACK_SPEC_PATTERN="spec_examples/**/*_spec.rb" KNAPSACK_REPORT_PATH="custom_knapsack_report.json" bundle exec rake knapsack:rspec
20
31
  notifications:
21
32
  email: false
data/CHANGELOG.md ADDED
@@ -0,0 +1,11 @@
1
+ ### unreleased
2
+
3
+ * nothing yet
4
+
5
+ ### 0.1.0
6
+
7
+ * Gem ready to use it!
8
+
9
+ ### 0.0.3
10
+
11
+ * Test release. Not ready to use it.
data/LICENSE.txt CHANGED
@@ -1,4 +1,4 @@
1
- Copyright (c) 2014 ArturT
1
+ Copyright (c) 2014 Artur Trzop
2
2
 
3
3
  MIT License
4
4
 
data/README.md CHANGED
@@ -1,18 +1,20 @@
1
1
  # Knapsack
2
2
 
3
- [![Build Status](https://travis-ci.org/ArturT/knapsack.svg?branch=master)](travis)
4
- [![Code Climate](https://codeclimate.com/github/ArturT/knapsack.png)](codeclimate)
3
+ [![Gem Version](https://badge.fury.io/rb/knapsack.png)][gem_version]
4
+ [![Build Status](https://travis-ci.org/ArturT/knapsack.svg?branch=master)][travis]
5
+ [![Code Climate](https://codeclimate.com/github/ArturT/knapsack.png)][codeclimate]
5
6
  [![Coverage Status](https://codeclimate.com/github/ArturT/knapsack/coverage.png)][coverage]
6
7
 
8
+ [gem_version]: https://rubygems.org/gems/knapsack
7
9
  [travis]: http://travis-ci.org/ArturT/knapsack
8
10
  [codeclimate]: https://codeclimate.com/github/ArturT/knapsack
9
11
  [coverage]: https://codeclimate.com/github/ArturT/knapsack
10
12
 
11
- Parallel specs across CI server nodes based on each spec file's time execution.
13
+ Parallel specs across CI server nodes based on each spec file's time execution. It generates spec time execution report and uses it for further test runs.
12
14
 
13
15
  ## Installation
14
16
 
15
- Add this line to your application's Gemfile:
17
+ Add this line to your application's Gemfile in test group:
16
18
 
17
19
  gem 'knapsack'
18
20
 
@@ -20,35 +22,66 @@ And then execute:
20
22
 
21
23
  $ bundle
22
24
 
23
- Or install it yourself as:
24
-
25
- $ gem install knapsack
26
-
27
25
  ## Usage
28
26
 
29
27
  Add at the beginning of your `spec_helper.rb`:
30
28
 
31
29
  require 'knapsack'
32
- # default configuration, you can change it or omit
30
+
31
+ # default configuration, you can change it or omit completely
33
32
  Knapsack.tracker.config({
34
33
  enable_time_offset_warning: true,
35
- time_offset_warning: 30,
34
+ time_offset_in_seconds: 30
35
+ })
36
+
37
+ # default configuration for report, you can change it or omit completely
38
+ Knapsack.report.config({
39
+ report_path: 'knapsack_report.json'
36
40
  })
37
- Knapsack::Adapters::Rspec.bind
41
+
42
+ Knapsack::Adapters::RspecAdapter.bind
43
+
44
+ Add in your `Rakefile` this lines:
45
+
46
+ require 'knapsack'
47
+ Knapsack.load_tasks
38
48
 
39
49
  Generate time execution report for your spec files.
40
50
 
41
- $ KNAPSACK_GENERATE_REPORT=true rspec spec
51
+ $ KNAPSACK_GENERATE_REPORT=true bundle exec rspec spec
42
52
 
43
53
  Commit generated report `knapsack_report.json` into your repository.
44
54
 
45
- ## Contributing
55
+ ## Setup your CI server
46
56
 
47
- 1. Fork it ( https://github.com/[my-github-username]/knapsack/fork )
48
- 2. Create your feature branch (`git checkout -b my-new-feature`)
49
- 3. Commit your changes (`git commit -am 'Add some feature'`)
50
- 4. Push to the branch (`git push origin my-new-feature`)
51
- 5. Create a new Pull Request
57
+ On your CI server run this command for the first CI node. Update `CI_NODE_INDEX` for the next one.
58
+
59
+ $ CI_NODE_TOTAL=2 CI_NODE_INDEX=0 bundle exec rake knapsack:rspec
60
+
61
+ You can add `KNAPSACK_SPEC_PATTERN` if your specs are not in `spec` directory. For instance:
62
+
63
+ $ KNAPSACK_SPEC_PATTERN="directory_with_specs/**/*_spec.rb" CI_NODE_TOTAL=2 CI_NODE_INDEX=0 bundle exec rake knapsack:rspec
64
+
65
+ You can set `KNAPSACK_REPORT_PATH` if your knapsack report was saved in non default location. Example:
66
+
67
+ $ KNAPSACK_REPORT_PATH="custom_knapsack_report.json" CI_NODE_TOTAL=2 CI_NODE_INDEX=0 bundle exec rake knapsack:rspec
68
+
69
+ ### Info about ENV variables
70
+
71
+ `CI_NODE_TOTAL` - total number CI nodes you have.
72
+
73
+ `CI_NODE_INDEX` - index of current CI node starts from 0. Second CI node should have `CI_NODE_INDEX=1`.
74
+
75
+ ### Info for CircleCI users
76
+
77
+ If you are using circleci.com you can omit `CI_NODE_TOTAL` and `CI_NODE_INDEX`. Knapsack will use `CIRCLE_NODE_TOTAL` and `CIRCLE_NODE_INDEX` provided by CircleCI.
78
+
79
+ Here is example for test configuration in your `circleci.yml` file.
80
+
81
+ test:
82
+ override:
83
+ - bundle exec rake knapsack:rspec
84
+ parallel: true
52
85
 
53
86
  ## Tests
54
87
 
@@ -56,16 +89,30 @@ Commit generated report `knapsack_report.json` into your repository.
56
89
 
57
90
  To run specs for Knapsack gem type:
58
91
 
59
- $ rspec spec
92
+ $ bundle exec rspec spec
60
93
 
61
94
  ### Spec examples
62
95
 
63
- Directory `spec_examples` contains examples of fast and slow specs. There is a `spec_helper.rb` with binded Knapsack.
96
+ Directory `spec_examples` contains examples of fast and slow specs. There is a `spec_example/spec_helper.rb` with binded Knapsack.
97
+
98
+ To generate a new knapsack report for specs with `focus` tag (only specs in `spec_examples/leftover` directory have no `focus` tag), please type:
64
99
 
65
- Change one spec to make it sure it will take more than 5 seconds then run below to see Knapsack time offset warning.
100
+ $ KNAPSACK_GENERATE_REPORT=true bundle exec rspec --default-path spec_examples --tag focus
66
101
 
67
- $ rspec --default-path spec_examples
102
+ **Warning:** Current `knapsack_report.json` file was generated for `spec_examples` except `spec_examples/leftover` directory. Just for testing reason to see how leftover specs will be distribute in a dumb way across CI nodes.
68
103
 
69
- To generate a new knapsack report file please type:
104
+ To see specs distributed for the first CI node type:
70
105
 
71
- $ KNAPSACK_GENERATE_REPORT=true rspec --default-path spec_examples
106
+ $ CI_NODE_TOTAL=2 CI_NODE_INDEX=0 KNAPSACK_SPEC_PATTERN="spec_examples/**/*_spec.rb" bundle exec rake knapsack:rspec
107
+
108
+ Specs in `spec_examples/leftover` take more than 3 seconds. This should cause a Knapsack time offset warning because we set `time_offset_in_seconds` to 3 in `spec_examples/spec_helper.rb`. Type below to see warning:
109
+
110
+ $ bundle exec rspec --default-path spec_examples
111
+
112
+ ## Contributing
113
+
114
+ 1. Fork it ( https://github.com/ArturT/knapsack/fork )
115
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
116
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
117
+ 4. Push to the branch (`git push origin my-new-feature`)
118
+ 5. Create a new Pull Request
data/Rakefile CHANGED
@@ -1,2 +1,4 @@
1
- require "bundler/gem_tasks"
1
+ require 'bundler/gem_tasks'
2
+ require 'knapsack'
2
3
 
4
+ Knapsack.load_tasks
data/TODO.md ADDED
@@ -0,0 +1,5 @@
1
+ # TODO
2
+
3
+ * Add other adapters than RSpec.
4
+ * Add support for ENV CI node total and CI node index other than CircleCI.
5
+
data/knapsack_report.json CHANGED
@@ -1,14 +1,11 @@
1
1
  {
2
- "./spec_examples/fast/1_spec.rb": 9.584426879882812e-05,
3
- "./spec_examples/fast/2_spec.rb": 0.00014638900756835938,
4
- "./spec_examples/fast/3_spec.rb": 0.0002243518829345703,
5
- "./spec_examples/fast/4_spec.rb": 0.0002560615539550781,
6
- "./spec_examples/fast/5_spec.rb": 0.00023126602172851562,
7
- "./spec_examples/fast/6_spec.rb": 0.00020503997802734375,
8
- "./spec_examples/slow/a_spec.rb": 1.6018133163452148,
9
- "./spec_examples/slow/b_spec.rb": 3.202699661254883,
10
- "./spec_examples/slow/c_spec.rb": 1.0021936893463135,
11
- "./spec_examples/slow/d_spec.rb": 4.001989841461182,
12
- "./spec_examples/slow/e_spec.rb": 2.4013099670410156,
13
- "./spec_examples/slow/f_spec.rb": 0.60198974609375
2
+ "spec_examples/fast/6_spec.rb": 6.628036499023438e-05,
3
+ "spec_examples/fast/3_spec.rb": 2.8133392333984375e-05,
4
+ "spec_examples/fast/2_spec.rb": 2.002716064453125e-05,
5
+ "spec_examples/fast/1_spec.rb": 1.0013580322265625e-05,
6
+ "spec_examples/fast/5_spec.rb": 7.867813110351562e-05,
7
+ "spec_examples/slow/b_spec.rb": 0.9035449028015137,
8
+ "spec_examples/fast/4_spec.rb": 6.842613220214844e-05,
9
+ "spec_examples/slow/a_spec.rb": 1.6037836074829102,
10
+ "spec_examples/slow/c_spec.rb": 1.0028371810913086
14
11
  }
data/lib/knapsack.rb CHANGED
@@ -1,14 +1,33 @@
1
- require 'knapsack/version'
2
- require 'knapsack/tracker'
3
- require 'knapsack/presenter'
4
- require 'knapsack/report'
5
- require 'knapsack/adapters/base'
6
- require 'knapsack/adapters/rspec'
1
+ require 'singleton'
2
+ require_relative 'knapsack/version'
3
+ require_relative 'knapsack/tracker'
4
+ require_relative 'knapsack/presenter'
5
+ require_relative 'knapsack/report'
6
+ require_relative 'knapsack/allocator'
7
+ require_relative 'knapsack/task_loader'
8
+ require_relative 'knapsack/distributors/base_distributor'
9
+ require_relative 'knapsack/distributors/report_distributor'
10
+ require_relative 'knapsack/distributors/leftover_distributor'
11
+ require_relative 'knapsack/adapters/base_adapter'
12
+ require_relative 'knapsack/adapters/rspec_adapter'
7
13
 
8
14
  module Knapsack
9
15
  class << self
10
16
  def tracker
11
17
  Knapsack::Tracker.instance
12
18
  end
19
+
20
+ def report
21
+ Knapsack::Report.instance
22
+ end
23
+
24
+ def root
25
+ File.expand_path '../..', __FILE__
26
+ end
27
+
28
+ def load_tasks
29
+ task_loader = Knapsack::TaskLoader.new
30
+ task_loader.load_tasks
31
+ end
13
32
  end
14
33
  end
@@ -1,21 +1,21 @@
1
1
  module Knapsack
2
2
  module Adapters
3
- class Base
3
+ class BaseAdapter
4
4
  def self.bind
5
- new
5
+ adapter = new
6
+ adapter.bind
7
+ adapter
6
8
  end
7
9
 
8
- protected
9
-
10
- def initialize
11
- if Knapsack.tracker.generate_report?
10
+ def bind
11
+ if tracker.config[:generate_report]
12
12
  puts 'Knapsack report generator started!'
13
13
  bind_time_tracker
14
14
  bind_report_generator
15
- elsif Knapsack.tracker.config[:enable_time_offset_warning]
15
+ elsif tracker.config[:enable_time_offset_warning]
16
16
  puts 'Knapsack time offset warning enabled!'
17
17
  bind_time_tracker
18
- # TODO
18
+ bind_time_offset_warning
19
19
  else
20
20
  puts 'Knapsack is off!'
21
21
  end
@@ -28,6 +28,16 @@ module Knapsack
28
28
  def bind_report_generator
29
29
  raise NotImplementedError
30
30
  end
31
+
32
+ def bind_time_offset_warning
33
+ raise NotImplementedError
34
+ end
35
+
36
+ protected
37
+
38
+ def tracker
39
+ Knapsack.tracker
40
+ end
31
41
  end
32
42
  end
33
43
  end
@@ -1,12 +1,10 @@
1
1
  module Knapsack
2
2
  module Adapters
3
- class Rspec < Base
4
- protected
5
-
3
+ class RspecAdapter < BaseAdapter
6
4
  def bind_time_tracker
7
5
  ::RSpec.configure do |config|
8
6
  config.before(:each) do
9
- Knapsack.tracker.spec_path = Rspec.spec_path
7
+ Knapsack.tracker.spec_path = RspecAdapter.spec_path
10
8
  Knapsack.tracker.start_timer
11
9
  end
12
10
 
@@ -15,7 +13,6 @@ module Knapsack
15
13
  end
16
14
 
17
15
  config.after(:suite) do
18
- puts
19
16
  puts Presenter.global_time
20
17
  end
21
18
  end
@@ -24,12 +21,22 @@ module Knapsack
24
21
  def bind_report_generator
25
22
  ::RSpec.configure do |config|
26
23
  config.after(:suite) do
27
- Report.save
24
+ Knapsack.report.save
28
25
  puts Presenter.report_details
29
26
  end
30
27
  end
31
28
  end
32
29
 
30
+ def bind_time_offset_warning
31
+ ::RSpec.configure do |config|
32
+ config.after(:suite) do
33
+ puts Presenter.time_offset_warning
34
+ end
35
+ end
36
+ end
37
+
38
+ protected
39
+
33
40
  def self.spec_path
34
41
  ::RSpec.current_example.metadata[:example_group][:file_path]
35
42
  end
@@ -0,0 +1,65 @@
1
+ module Knapsack
2
+ class Allocator
3
+ def initialize(args={})
4
+ @config = default_args.merge(args)
5
+ @report_distributor = Knapsack::Distributors::ReportDistributor.new(@config)
6
+ @leftover_distributor = Knapsack::Distributors::LeftoverDistributor.new(@config)
7
+ end
8
+
9
+ def report_node_specs
10
+ @report_node_specs ||= @report_distributor.specs_for_current_node
11
+ end
12
+
13
+ def leftover_node_specs
14
+ @leftover_node_specs ||= @leftover_distributor.specs_for_current_node
15
+ end
16
+
17
+ def node_specs
18
+ @node_specs ||= report_node_specs + leftover_node_specs
19
+ end
20
+
21
+ def stringify_node_specs
22
+ node_specs.join(' ')
23
+ end
24
+
25
+ def custom_spec_dir
26
+ return unless @config[:spec_pattern]
27
+ @config[:spec_pattern].gsub(/^(.*?)\//).first
28
+ end
29
+
30
+ private
31
+
32
+ def default_args
33
+ {
34
+ ci_node_total: env_ci_node_total,
35
+ ci_node_index: env_ci_node_index,
36
+ spec_pattern: env_spec_pattern,
37
+ report: report
38
+ }
39
+ end
40
+
41
+ def env_ci_node_total
42
+ ENV['CI_NODE_TOTAL'] || ENV['CIRCLE_NODE_TOTAL']
43
+ end
44
+
45
+ def env_ci_node_index
46
+ ENV['CI_NODE_INDEX'] || ENV['CIRCLE_NODE_INDEX']
47
+ end
48
+
49
+ def env_spec_pattern
50
+ ENV['KNAPSACK_SPEC_PATTERN']
51
+ end
52
+
53
+ def env_report_path
54
+ ENV['KNAPSACK_REPORT_PATH']
55
+ end
56
+
57
+ def report
58
+ return unless env_report_path
59
+ Knapsack.report.config({
60
+ report_path: env_report_path
61
+ })
62
+ Knapsack.report.open
63
+ end
64
+ end
65
+ end
@@ -0,0 +1,67 @@
1
+ module Knapsack
2
+ module Distributors
3
+ class BaseDistributor
4
+ attr_reader :report, :node_specs
5
+
6
+ DEFAULT_CI_NODE_TOTAL = 1
7
+ DEFAULT_CI_NODE_INDEX = 0
8
+
9
+ def initialize(args={})
10
+ @report = args[:report] || default_report
11
+ @ci_node_total = args[:ci_node_total] || DEFAULT_CI_NODE_TOTAL
12
+ @ci_node_index = args[:ci_node_index] || DEFAULT_CI_NODE_INDEX
13
+ post_initialize(args)
14
+ end
15
+
16
+ def default_report
17
+ Knapsack.report.open
18
+ end
19
+
20
+ def ci_node_total
21
+ @ci_node_total.to_i
22
+ end
23
+
24
+ def ci_node_index
25
+ @ci_node_index.to_i
26
+ end
27
+
28
+ def specs_for_current_node
29
+ specs_for_node(ci_node_index)
30
+ end
31
+
32
+ def specs_for_node(node_index)
33
+ assign_spec_files_to_node
34
+ post_specs_for_node(node_index)
35
+ end
36
+
37
+ def assign_spec_files_to_node
38
+ default_node_specs
39
+ @node_index = 0
40
+ post_assign_spec_files_to_node
41
+ end
42
+
43
+ protected
44
+
45
+ def post_initialize(args)
46
+ nil
47
+ end
48
+
49
+ def post_specs_for_node(node_index)
50
+ raise NotImplementedError
51
+ end
52
+
53
+ def post_assign_spec_files_to_node
54
+ raise NotImplementedError
55
+ end
56
+
57
+ def default_node_specs
58
+ raise NotImplementedError
59
+ end
60
+
61
+ def update_node_index
62
+ @node_index += 1
63
+ @node_index = 0 if @node_index == ci_node_total
64
+ end
65
+ end
66
+ end
67
+ end