knapsack 0.0.3 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
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