snowy_owl 0.2.2 → 0.3.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: faa64162b2877381d0681c26c743202388059960
4
- data.tar.gz: da3867a1189f65550a733022b4ee955f25d01d30
3
+ metadata.gz: 8a2cda9d116073fa7f2a3077f04b15601f8e1de8
4
+ data.tar.gz: 2f7c8fd431635eab7f2a814b3cf2a60859067005
5
5
  SHA512:
6
- metadata.gz: 55ab8558c1b6d20aa49948c367fe9c683102e807b0c822d9ec066331a940ab6edaf801ced75bfa310ef8b0a9f8a5720c4d9d942d3a66c21fc2609661dc582b1d
7
- data.tar.gz: 6de88a50bf2ad51f50716d72603a840b2c74cb77cabc241f813c7622962a0dcccd017dc7856e409c793b50358d76c25208404e260e3271120b1ade32d597d363
6
+ metadata.gz: 72893bc9063fdd463e0927f6952aa13caaefeb368c6c7d27f9f4b735653c53fc0cce16cf8b98eb9d92e9a8ff4f490de67070b33fd638af28188e718ac2bcdb19
7
+ data.tar.gz: d2174104a82ef6cd780cd9b69e2053b7a17672c7ca18c1869322ad762b288c94478e61f08b1b363a2720bb1071a8438ebd73bc2ab9e80e271ba54ef3ed5aad21
data/.rubocop.yml ADDED
@@ -0,0 +1,10 @@
1
+ AllCops:
2
+ Include:
3
+ - '**/Rakefile'
4
+ Exclude:
5
+ - 'bin/**/*'
6
+ - 'spec/**/*'
7
+ - '*.gemspec'
8
+ - 'Gemfile'
9
+ Documentation:
10
+ Enabled: false
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- snowy_owl (0.2.2)
4
+ snowy_owl (0.3.0)
5
5
 
6
6
  GEM
7
7
  remote: https://rubygems.org/
data/README.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  [![Code Climate](https://codeclimate.com/github/hanystudy/snowy_owl/badges/gpa.svg)](https://codeclimate.com/github/hanystudy/snowy_owl)
4
4
 
5
- Owl exploratory testing. Go to [SnowyOwl Wiki](https://github.com/hanystudy/snowy_owl/wiki) for background story and concepts.
5
+ Owl exploratory testing. A demo project can be found [here](https://github.com/hanystudy/snowy_owl_demo). Go to [SnowyOwl Wiki](https://github.com/hanystudy/snowy_owl/wiki) for background story and concepts.
6
6
 
7
7
  ## Installation
8
8
 
@@ -41,11 +41,14 @@ require 'snowy_owl'
41
41
  SnowyOwl.play
42
42
  ```
43
43
 
44
- ### Used with RSpec
44
+ ### Used with RSpec and Capybara
45
+
46
+ Confirm you have rspec and rspec-rerun in your project.
45
47
 
46
48
  Add this to spec_helper.rb:
47
49
 
48
50
  ```ruby
51
+ require 'capybara/rspec'
49
52
  require 'snowy_owl'
50
53
  require 'snowy_owl/rspec'
51
54
  ```
data/Rakefile CHANGED
@@ -1,6 +1,6 @@
1
- require "bundler/gem_tasks"
2
- require "rspec/core/rake_task"
1
+ require 'bundler/gem_tasks'
2
+ require 'rspec/core/rake_task'
3
3
 
4
4
  RSpec::Core::RakeTask.new(:spec)
5
5
 
6
- task :default => :spec
6
+ task default: :spec
@@ -1,25 +1,24 @@
1
1
  module SnowyOwl
2
2
  class Config
3
-
4
- OPTIONS = [
5
- :props_path,
6
- :determine_context,
7
- :determinations_path,
8
- :play_books_path,
9
- :is_persisting,
10
- :is_recovering,
11
- :persist_path,
12
- :plots_path,
13
- :persist_callback,
14
- :recover_callback,
15
- :spec_file
16
- ]
3
+ OPTIONS = %i[
4
+ props_path
5
+ determine_context
6
+ determinations_path
7
+ play_books_path
8
+ is_persisting
9
+ is_recovering
10
+ persist_path
11
+ plots_path
12
+ persist_callback
13
+ recover_callback
14
+ spec_file
15
+ ].freeze
17
16
 
18
17
  attr_accessor :props_path, :determinations_path, :play_books_path
19
18
  attr_accessor :is_persisting, :is_recovering, :persist_path, :plots_path
20
19
  attr_accessor :spec_file
21
20
 
22
- def determine_context key, &determine_block
21
+ def determine_context(key, &determine_block)
23
22
  @determine_context ||= {}
24
23
  if block_given?
25
24
  @determine_context[key] = determine_block
@@ -28,11 +27,11 @@ module SnowyOwl
28
27
  end
29
28
  end
30
29
 
31
- def persist_callback &block
30
+ def persist_callback(&block)
32
31
  @persist_callback = block || @persist_callback
33
32
  end
34
-
35
- def recover_callback &block
33
+
34
+ def recover_callback(&block)
36
35
  @recover_callback = block || @recover_callback
37
36
  end
38
37
  end
@@ -3,8 +3,7 @@ require 'ostruct'
3
3
 
4
4
  module SnowyOwl
5
5
  module Determinations
6
-
7
- def self.determine name, &block
6
+ def self.determine(name, &block)
8
7
  SnowyOwl.determine_context SnowyOwl::Support.to_underscore(name).to_sym, &block
9
8
  end
10
9
 
@@ -0,0 +1,18 @@
1
+ require 'digest'
2
+
3
+ module SnowyOwl
4
+ module Digest
5
+ def self.generate_full_path_digest(candidate_plots)
6
+ pre_digest = nil
7
+ candidate_plots.each do |plot|
8
+ pre_digest = pre_digest.nil? ? digest(plot['plot_name']) : digest(pre_digest + plot['plot_name'])
9
+ plot['digest'] = pre_digest
10
+ end
11
+ candidate_plots
12
+ end
13
+
14
+ def self.digest string
15
+ ::Digest::SHA1.hexdigest string
16
+ end
17
+ end
18
+ end
@@ -1,6 +1,6 @@
1
1
  module SnowyOwl
2
2
  class OwlField
3
- def play *args, &block
3
+ def play(*args, &block)
4
4
  instance_exec *args, &block
5
5
  end
6
6
 
@@ -10,37 +10,36 @@ module SnowyOwl
10
10
  plots_range = expression.match /(.*)(\.{2})(.*)/
11
11
  if plots_range.nil?
12
12
  scope = expression.split("\n")
13
- candidate_plots.select { |plot| scope.include? plot['plot_name'] }
13
+ candidate_plots.select { |plot| scope.include?(plot['plot_name']) || scope.include?(plot['digest']) }
14
14
  else
15
15
  sequence_run_from_starting_point candidate_plots, plots_range
16
16
  end
17
17
  end
18
18
 
19
- def sequence_run_from_starting_point candidate_plots, plots_range
19
+ def sequence_run_from_starting_point(candidate_plots, plots_range)
20
20
  starting_point = plots_range[1]
21
21
  range_type = plots_range[2]
22
22
  ending_point = plots_range[3]
23
23
  in_scope = false
24
- candidate_plots.inject([]) do |acc, plot|
24
+ candidate_plots.each_with_object([]) do |plot, acc|
25
25
  plot_name = plot['plot_name']
26
- in_scope = true if plot_name == starting_point
26
+ plot_digest = plot['digest']
27
+ in_scope = true if plot_name == starting_point || plot_digest == starting_point
27
28
  acc << plot if in_scope
28
- in_scope = false if plot_name == ending_point
29
- acc
29
+ in_scope = false if plot_name == ending_point || plot_digest == ending_point
30
30
  end
31
31
  end
32
32
 
33
- def sequence_run candidate_play_books
34
- candidate_play_books.each do |play_book|
35
- candidate_plots = YAML.load_file(play_book)
36
- expression = ENV['PLOTS_SCOPE']
37
- candidate_plots = plots_scope(candidate_plots, expression)
38
- candidate_plots.each do |plot|
39
- plot_name = plot['plot_name']
40
- SnowyOwl::Persist.recover_state plot_name if SnowyOwl.is_recovering
41
- SnowyOwl::Persist.persist_state plot_name if SnowyOwl.is_persisting
42
- instance_exec plot_name, &SnowyOwl::Plots.plot(plot_name)
43
- end
33
+ def sequence_run(candidate_play_books)
34
+ candidate_plots = SnowyOwl::Plays.build_plays(candidate_play_books)
35
+ expression = ENV['PLOTS_SCOPE']
36
+ candidate_plots = plots_scope(candidate_plots, expression)
37
+ candidate_plots.each do |plot|
38
+ plot_name = plot['plot_name']
39
+ digest = plot['digest']
40
+ SnowyOwl::Persist.recover_state digest if SnowyOwl.is_recovering
41
+ SnowyOwl::Persist.persist_state digest if SnowyOwl.is_persisting
42
+ instance_exec plot_name, &SnowyOwl::Plots.plot(plot_name)
44
43
  end
45
44
  end
46
45
  end
@@ -4,21 +4,19 @@ require 'fileutils'
4
4
 
5
5
  module SnowyOwl
6
6
  module Persist
7
-
8
7
  class << self
9
- def persist_state(plot_name)
10
- plot_name_path = SnowyOwl::Support.to_underscore plot_name
8
+ def persist_state(digest)
11
9
  temp_persist_path = SnowyOwl.persist_path + '/.tmp/'
12
- path = temp_persist_path + plot_name_path
13
- FileUtils::mkdir_p path
14
- args = [plot_name_path, temp_persist_path]
10
+ sub_path = digest
11
+ path = temp_persist_path + sub_path
12
+ FileUtils.mkdir_p path
13
+ args = [sub_path, temp_persist_path]
15
14
  SnowyOwl.persist_callback.call(args)
16
15
  end
17
16
 
18
- def recover_state(plot_name)
19
- plot_name_path = SnowyOwl::Support.to_underscore plot_name
20
- path = SnowyOwl.persist_path + plot_name_path
21
- args = [plot_name_path, SnowyOwl.persist_path]
17
+ def recover_state(digest)
18
+ sub_path = digest
19
+ args = [sub_path, SnowyOwl.persist_path]
22
20
  SnowyOwl.recover_callback.call(args)
23
21
  end
24
22
  end
@@ -0,0 +1,73 @@
1
+ module SnowyOwl
2
+ module Plays
3
+ class PlotNodeHash
4
+ attr_accessor :children, :digest, :parent, :name
5
+
6
+ def initialize
7
+ @children = []
8
+ end
9
+
10
+ def append_child(digest)
11
+ @children << digest
12
+ end
13
+
14
+ def to_h
15
+ {'digest' => @digest,
16
+ 'parent' => @parent,
17
+ 'plot_name' => @name }
18
+ end
19
+ end
20
+
21
+ class << self
22
+ def build_plays(candidate_play_books)
23
+ @play_hash = {}
24
+ @starting_node = nil
25
+ candidate_play_books.each do |play_book|
26
+ candidate_plots = YAML.load_file(play_book)
27
+ generate_full_path_digest(candidate_plots)
28
+ end
29
+ generate_deep_first_plays
30
+ end
31
+
32
+ def generate_deep_first_plays
33
+ plays = []
34
+ deep_stack = [@starting_node.digest]
35
+ while !deep_stack.empty?
36
+ node = deep_stack.pop
37
+ deep_stack = deep_stack.concat @play_hash[node].children
38
+ plays << @play_hash[node].to_h
39
+ end
40
+ plays
41
+ end
42
+
43
+ def generate_full_path_digest(candidate_plots)
44
+ pre_digest = nil
45
+ candidate_plots.each do |plot|
46
+ parent_digest = plot['parent']
47
+ digest = plot['digest']
48
+ plot['digest'] = SnowyOwl::Digest.digest((parent_digest || pre_digest).to_s + plot['plot_name']) if digest.nil?
49
+ plot['parent'] = pre_digest if parent_digest.nil?
50
+ pre_digest = plot['digest']
51
+ node = create_node plot
52
+ @starting_node = node if node.parent.nil?
53
+ end
54
+ candidate_plots
55
+ end
56
+
57
+ def create_node plot
58
+ plot_node = @play_hash[plot['digest']] || PlotNodeHash.new
59
+ plot_node.name = plot['plot_name']
60
+ plot_node.parent = plot['parent']
61
+ plot_node.digest = plot['digest']
62
+ @play_hash[plot['digest']] = plot_node
63
+ if !plot['parent'].nil?
64
+ parent_node = @play_hash[plot['parent']] || PlotNodeHash.new
65
+ plot_node.parent = plot['parent']
66
+ parent_node.append_child plot['digest']
67
+ @play_hash[plot['parent']] = parent_node
68
+ end
69
+ @play_hash[plot['digest']]
70
+ end
71
+ end
72
+ end
73
+ end
@@ -1,10 +1,8 @@
1
1
  module SnowyOwl
2
2
  module Plots
3
-
4
3
  class << self
5
-
6
4
  def write(plot_name, &block)
7
- proc = Proc.new do |*args|
5
+ proc = proc do |*args|
8
6
  extend SnowyOwl::Props
9
7
  extend SnowyOwl::Determinations
10
8
  instance_exec(*args, &block)
@@ -13,7 +11,7 @@ module SnowyOwl
13
11
  @__plots__[plot_name] = proc
14
12
  end
15
13
 
16
- def plot plot_name
14
+ def plot(plot_name)
17
15
  @__plots__[plot_name]
18
16
  end
19
17
  end
@@ -4,10 +4,9 @@ require 'yaml'
4
4
 
5
5
  module SnowyOwl
6
6
  module Props
7
-
8
7
  def init_props
9
8
  props_path = SnowyOwl.props_path
10
- raise RuntimeError.new 'Invalid props path' if props_path.empty?
9
+ raise 'Invalid props path' if props_path.empty?
11
10
  props_hash = {}
12
11
  Dir[props_path].each do |f|
13
12
  field_name = File.basename f, '.yml'
@@ -4,7 +4,7 @@ module RSpec
4
4
  module Rerun
5
5
  class Formatter
6
6
  def retry_command(example)
7
- example.description
7
+ example.metadata[:digest]
8
8
  end
9
9
  end
10
10
 
@@ -2,7 +2,7 @@ require 'snowy_owl/rspec/rerun'
2
2
 
3
3
  module SnowyOwl
4
4
  class Config
5
- alias_options = OPTIONS
5
+ alias_options = OPTIONS.dup
6
6
  alias_options << :spec_file
7
7
  remove_const :OPTIONS
8
8
  const_set :OPTIONS, alias_options.uniq
@@ -12,39 +12,60 @@ module SnowyOwl
12
12
 
13
13
  module Plots
14
14
  class << self
15
- alias :old_write :write
15
+ alias old_write write
16
16
 
17
17
  def write(plot_name, &block)
18
18
  proc = old_write plot_name, &block
19
- RSpec.shared_examples plot_name do
19
+ RSpec.shared_examples plot_name do |args|
20
+ @metadata[:digest] = args[:digest]
20
21
  scenario plot_name, &proc
21
22
  end
22
23
  end
23
24
  end
24
25
  end
25
26
 
26
- def self.play *args
27
+ def self.play(*args)
27
28
  RSpec.feature *args do
28
29
  Dir[SnowyOwl.plots_path].each { |f| require f }
29
30
  Dir[SnowyOwl.determinations_path].each { |f| require f }
30
31
 
31
32
  before do |plot|
32
- SnowyOwl::Persist.recover_state plot.description if SnowyOwl.is_recovering
33
- SnowyOwl::Persist.persist_state plot.description if SnowyOwl.is_persisting
33
+ digest = plot.metadata[:digest]
34
+ SnowyOwl::Persist.recover_state digest if SnowyOwl.is_recovering
35
+ SnowyOwl::Persist.persist_state digest if SnowyOwl.is_persisting
34
36
  end
35
37
 
36
38
  candidate_play_books = Dir[SnowyOwl.play_books_path]
37
39
 
38
40
  owl_field = OwlField.new
39
41
 
40
- candidate_play_books.each do |play_book|
41
- candidate_plots = YAML.load_file(play_book)
42
- expression = ENV['PLOTS_SCOPE']
43
- candidate_plots = owl_field.plots_scope(candidate_plots, expression)
44
- candidate_plots.each do |plot|
45
- it_behaves_like plot['plot_name']
46
- end
42
+ candidate_plots = SnowyOwl::Plays.build_plays(candidate_play_books)
43
+ expression = ENV['PLOTS_SCOPE']
44
+ candidate_plots = owl_field.plots_scope(candidate_plots, expression)
45
+ candidate_plots.each do |plot|
46
+ plot_name = plot['plot_name']
47
+ digest = plot['digest']
48
+ it_behaves_like plot_name, digest: digest
47
49
  end
48
50
  end
49
51
  end
52
+
53
+ class StatusFormatter
54
+ RSpec::Core::Formatters.register self, :example_failed
55
+
56
+ def initialize(out)
57
+ @out = out
58
+ end
59
+
60
+ def example_failed(notification)
61
+ example = notification.example
62
+ @out.puts "\nplot name: #{example.description}"
63
+ @out.puts "plot digest: #{example.metadata[:digest]}"
64
+ end
65
+ end
66
+
67
+ RSpec.configure do |c|
68
+ c.add_formatter 'progress'
69
+ c.add_formatter StatusFormatter
70
+ end
50
71
  end
@@ -1,3 +1,3 @@
1
1
  module SnowyOwl
2
- VERSION = "0.2.2"
2
+ VERSION = '0.3.0'.freeze
3
3
  end
data/lib/snowy_owl.rb CHANGED
@@ -2,18 +2,19 @@ require 'forwardable'
2
2
  require 'yaml'
3
3
  require 'snowy_owl/support'
4
4
  require 'snowy_owl/config'
5
+ require 'snowy_owl/digest'
5
6
 
6
7
  module SnowyOwl
7
8
  class << self
8
9
  extend Forwardable
9
10
 
10
11
  SnowyOwl::Config::OPTIONS.each do |method|
11
- def_delegators :config, "#{method}=" , "#{method}="
12
+ def_delegators :config, "#{method}=", "#{method}="
12
13
  def_delegators :config, method, method
13
14
  end
14
15
 
15
16
  def configure
16
- yield self.config
17
+ yield config
17
18
  end
18
19
 
19
20
  def config
@@ -41,6 +42,7 @@ SnowyOwl.configure do |config|
41
42
  config.persist_path = ''
42
43
  end
43
44
 
45
+ require 'snowy_owl/plays'
44
46
  require 'snowy_owl/persist'
45
47
  require 'snowy_owl/plots'
46
48
  require 'snowy_owl/props'
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: snowy_owl
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.2
4
+ version: 0.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Yi Han
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2017-07-30 00:00:00.000000000 Z
11
+ date: 2017-08-18 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -89,6 +89,7 @@ extra_rdoc_files: []
89
89
  files:
90
90
  - ".gitignore"
91
91
  - ".rspec"
92
+ - ".rubocop.yml"
92
93
  - ".travis.yml"
93
94
  - CODE_OF_CONDUCT.md
94
95
  - Gemfile
@@ -101,8 +102,10 @@ files:
101
102
  - lib/snowy_owl.rb
102
103
  - lib/snowy_owl/config.rb
103
104
  - lib/snowy_owl/determinations.rb
105
+ - lib/snowy_owl/digest.rb
104
106
  - lib/snowy_owl/owl_field.rb
105
107
  - lib/snowy_owl/persist.rb
108
+ - lib/snowy_owl/plays.rb
106
109
  - lib/snowy_owl/plots.rb
107
110
  - lib/snowy_owl/props.rb
108
111
  - lib/snowy_owl/rspec.rb