snowy_owl 0.2.2 → 0.3.0

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