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 +4 -4
- data/.rubocop.yml +10 -0
- data/Gemfile.lock +1 -1
- data/README.md +5 -2
- data/Rakefile +3 -3
- data/lib/snowy_owl/config.rb +17 -18
- data/lib/snowy_owl/determinations.rb +1 -2
- data/lib/snowy_owl/digest.rb +18 -0
- data/lib/snowy_owl/owl_field.rb +17 -18
- data/lib/snowy_owl/persist.rb +8 -10
- data/lib/snowy_owl/plays.rb +73 -0
- data/lib/snowy_owl/plots.rb +2 -4
- data/lib/snowy_owl/props.rb +1 -2
- data/lib/snowy_owl/rspec/rerun.rb +1 -1
- data/lib/snowy_owl/rspec.rb +34 -13
- data/lib/snowy_owl/version.rb +1 -1
- data/lib/snowy_owl.rb +4 -2
- metadata +5 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 8a2cda9d116073fa7f2a3077f04b15601f8e1de8
|
4
|
+
data.tar.gz: 2f7c8fd431635eab7f2a814b3cf2a60859067005
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 72893bc9063fdd463e0927f6952aa13caaefeb368c6c7d27f9f4b735653c53fc0cce16cf8b98eb9d92e9a8ff4f490de67070b33fd638af28188e718ac2bcdb19
|
7
|
+
data.tar.gz: d2174104a82ef6cd780cd9b69e2053b7a17672c7ca18c1869322ad762b288c94478e61f08b1b363a2720bb1071a8438ebd73bc2ab9e80e271ba54ef3ed5aad21
|
data/.rubocop.yml
ADDED
data/Gemfile.lock
CHANGED
data/README.md
CHANGED
@@ -2,7 +2,7 @@
|
|
2
2
|
|
3
3
|
[](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
data/lib/snowy_owl/config.rb
CHANGED
@@ -1,25 +1,24 @@
|
|
1
1
|
module SnowyOwl
|
2
2
|
class Config
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
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
|
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
|
30
|
+
def persist_callback(&block)
|
32
31
|
@persist_callback = block || @persist_callback
|
33
32
|
end
|
34
|
-
|
35
|
-
def recover_callback
|
33
|
+
|
34
|
+
def recover_callback(&block)
|
36
35
|
@recover_callback = block || @recover_callback
|
37
36
|
end
|
38
37
|
end
|
@@ -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
|
data/lib/snowy_owl/owl_field.rb
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
module SnowyOwl
|
2
2
|
class OwlField
|
3
|
-
def play
|
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?
|
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
|
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.
|
24
|
+
candidate_plots.each_with_object([]) do |plot, acc|
|
25
25
|
plot_name = plot['plot_name']
|
26
|
-
|
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
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
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
|
data/lib/snowy_owl/persist.rb
CHANGED
@@ -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(
|
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
|
-
|
13
|
-
|
14
|
-
|
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(
|
19
|
-
|
20
|
-
|
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
|
data/lib/snowy_owl/plots.rb
CHANGED
@@ -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 =
|
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
|
14
|
+
def plot(plot_name)
|
17
15
|
@__plots__[plot_name]
|
18
16
|
end
|
19
17
|
end
|
data/lib/snowy_owl/props.rb
CHANGED
@@ -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
|
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'
|
data/lib/snowy_owl/rspec.rb
CHANGED
@@ -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
|
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
|
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
|
-
|
33
|
-
SnowyOwl::Persist.
|
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
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
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
|
data/lib/snowy_owl/version.rb
CHANGED
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}="
|
12
|
+
def_delegators :config, "#{method}=", "#{method}="
|
12
13
|
def_delegators :config, method, method
|
13
14
|
end
|
14
15
|
|
15
16
|
def configure
|
16
|
-
yield
|
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.
|
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-
|
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
|