cucumber_fm-core 0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/LICENCE +20 -0
- data/lib/cucumber_f_m/aggregator.rb +65 -0
- data/lib/cucumber_f_m/comment_module/comment.rb +7 -0
- data/lib/cucumber_f_m/config.rb +37 -0
- data/lib/cucumber_f_m/cvs/git.rb +7 -0
- data/lib/cucumber_f_m/feature.rb +141 -0
- data/lib/cucumber_f_m/feature_element/background.rb +10 -0
- data/lib/cucumber_f_m/feature_element/comment.rb +13 -0
- data/lib/cucumber_f_m/feature_element/component/comments.rb +21 -0
- data/lib/cucumber_f_m/feature_element/component/information/component.rb +16 -0
- data/lib/cucumber_f_m/feature_element/component/tags.rb +108 -0
- data/lib/cucumber_f_m/feature_element/component/title.rb +26 -0
- data/lib/cucumber_f_m/feature_element/component/total_estimation.rb +42 -0
- data/lib/cucumber_f_m/feature_element/example.rb +7 -0
- data/lib/cucumber_f_m/feature_element/info.rb +13 -0
- data/lib/cucumber_f_m/feature_element/narrative.rb +7 -0
- data/lib/cucumber_f_m/feature_element/scenario.rb +15 -0
- data/lib/cucumber_f_m/feature_element/scenario_outline.rb +23 -0
- data/lib/cucumber_f_m/feature_element/step.rb +7 -0
- data/lib/cucumber_f_m/statistic.rb +31 -0
- data/lib/cucumber_f_m/tag_filter.rb +85 -0
- data/lib/cucumber_feature_manager.rb +164 -0
- data/lib/grit/lib/grit.rb +75 -0
- data/lib/grit/lib/grit/actor.rb +36 -0
- data/lib/grit/lib/grit/blame.rb +61 -0
- data/lib/grit/lib/grit/blob.rb +126 -0
- data/lib/grit/lib/grit/commit.rb +247 -0
- data/lib/grit/lib/grit/commit_stats.rb +128 -0
- data/lib/grit/lib/grit/config.rb +44 -0
- data/lib/grit/lib/grit/diff.rb +70 -0
- data/lib/grit/lib/grit/errors.rb +7 -0
- data/lib/grit/lib/grit/git-ruby.rb +267 -0
- data/lib/grit/lib/grit/git-ruby/commit_db.rb +52 -0
- data/lib/grit/lib/grit/git-ruby/file_index.rb +193 -0
- data/lib/grit/lib/grit/git-ruby/git_object.rb +350 -0
- data/lib/grit/lib/grit/git-ruby/internal/file_window.rb +58 -0
- data/lib/grit/lib/grit/git-ruby/internal/loose.rb +137 -0
- data/lib/grit/lib/grit/git-ruby/internal/pack.rb +384 -0
- data/lib/grit/lib/grit/git-ruby/internal/raw_object.rb +37 -0
- data/lib/grit/lib/grit/git-ruby/object.rb +325 -0
- data/lib/grit/lib/grit/git-ruby/repository.rb +767 -0
- data/lib/grit/lib/grit/git.rb +323 -0
- data/lib/grit/lib/grit/index.rb +122 -0
- data/lib/grit/lib/grit/lazy.rb +33 -0
- data/lib/grit/lib/grit/merge.rb +45 -0
- data/lib/grit/lib/grit/ref.rb +74 -0
- data/lib/grit/lib/grit/repo.rb +482 -0
- data/lib/grit/lib/grit/ruby1.9.rb +7 -0
- data/lib/grit/lib/grit/status.rb +151 -0
- data/lib/grit/lib/grit/submodule.rb +88 -0
- data/lib/grit/lib/grit/tag.rb +16 -0
- data/lib/grit/lib/grit/tree.rb +123 -0
- data/lib/grit/lib/open3_detach.rb +46 -0
- data/spec/cucumber_f_m/aggregator_spec.rb +210 -0
- data/spec/cucumber_f_m/comment_module/comment_spec.rb +4 -0
- data/spec/cucumber_f_m/config_spec.rb +27 -0
- data/spec/cucumber_f_m/cvs/git_spec.rb +40 -0
- data/spec/cucumber_f_m/feature_all_tags_to_scenario_spec.rb +7 -0
- data/spec/cucumber_f_m/feature_file_manipulation_spec.rb +29 -0
- data/spec/cucumber_f_m/feature_module/background_spec.rb +30 -0
- data/spec/cucumber_f_m/feature_module/comment_spec.rb +23 -0
- data/spec/cucumber_f_m/feature_module/example_spec.rb +5 -0
- data/spec/cucumber_f_m/feature_module/info_spec.rb +30 -0
- data/spec/cucumber_f_m/feature_module/narrative_spec.rb +7 -0
- data/spec/cucumber_f_m/feature_module/scenario_outline_spec.rb +39 -0
- data/spec/cucumber_f_m/feature_module/scenario_spec.rb +33 -0
- data/spec/cucumber_f_m/feature_module/step_spec.rb +7 -0
- data/spec/cucumber_f_m/feature_module/tag_spec.rb +96 -0
- data/spec/cucumber_f_m/feature_spec.rb +229 -0
- data/spec/cucumber_f_m/tag_filter_spec.rb +191 -0
- data/spec/cucumber_feature_manager_spec.rb +59 -0
- data/spec/data/feature_manager/some_ruby_file.rb +0 -0
- data/spec/spec_helper.rb +1 -0
- metadata +141 -0
@@ -0,0 +1,151 @@
|
|
1
|
+
module Grit
|
2
|
+
|
3
|
+
class Status
|
4
|
+
include Enumerable
|
5
|
+
|
6
|
+
@base = nil
|
7
|
+
@files = nil
|
8
|
+
|
9
|
+
def initialize(base)
|
10
|
+
@base = base
|
11
|
+
construct_status
|
12
|
+
end
|
13
|
+
|
14
|
+
def changed
|
15
|
+
@files.select { |k, f| f.type == 'M' }
|
16
|
+
end
|
17
|
+
|
18
|
+
def added
|
19
|
+
@files.select { |k, f| f.type == 'A' }
|
20
|
+
end
|
21
|
+
|
22
|
+
def deleted
|
23
|
+
@files.select { |k, f| f.type == 'D' }
|
24
|
+
end
|
25
|
+
|
26
|
+
def untracked
|
27
|
+
@files.select { |k, f| f.untracked }
|
28
|
+
end
|
29
|
+
|
30
|
+
def pretty
|
31
|
+
out = ''
|
32
|
+
self.each do |file|
|
33
|
+
out << file.path
|
34
|
+
out << "\n\tsha(r) " + file.sha_repo.to_s + ' ' + file.mode_repo.to_s
|
35
|
+
out << "\n\tsha(i) " + file.sha_index.to_s + ' ' + file.mode_index.to_s
|
36
|
+
out << "\n\ttype " + file.type.to_s
|
37
|
+
out << "\n\tstage " + file.stage.to_s
|
38
|
+
out << "\n\tuntrac " + file.untracked.to_s
|
39
|
+
out << "\n"
|
40
|
+
end
|
41
|
+
out << "\n"
|
42
|
+
out
|
43
|
+
end
|
44
|
+
|
45
|
+
# enumerable method
|
46
|
+
|
47
|
+
def [](file)
|
48
|
+
@files[file]
|
49
|
+
end
|
50
|
+
|
51
|
+
def each
|
52
|
+
@files.each do |k, file|
|
53
|
+
yield file
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
class StatusFile
|
58
|
+
attr_accessor :path, :type, :stage, :untracked
|
59
|
+
attr_accessor :mode_index, :mode_repo
|
60
|
+
attr_accessor :sha_index, :sha_repo
|
61
|
+
|
62
|
+
@base = nil
|
63
|
+
|
64
|
+
def initialize(base, hash)
|
65
|
+
@base = base
|
66
|
+
@path = hash[:path]
|
67
|
+
@type = hash[:type]
|
68
|
+
@stage = hash[:stage]
|
69
|
+
@mode_index = hash[:mode_index]
|
70
|
+
@mode_repo = hash[:mode_repo]
|
71
|
+
@sha_index = hash[:sha_index]
|
72
|
+
@sha_repo = hash[:sha_repo]
|
73
|
+
@untracked = hash[:untracked]
|
74
|
+
end
|
75
|
+
|
76
|
+
def blob(type = :index)
|
77
|
+
if type == :repo
|
78
|
+
@base.object(@sha_repo)
|
79
|
+
else
|
80
|
+
@base.object(@sha_index) rescue @base.object(@sha_repo)
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
end
|
85
|
+
|
86
|
+
private
|
87
|
+
|
88
|
+
def construct_status
|
89
|
+
@files = ls_files
|
90
|
+
|
91
|
+
Dir.chdir(@base.working_dir) do
|
92
|
+
# find untracked in working dir
|
93
|
+
Dir.glob('**/*') do |file|
|
94
|
+
if !@files[file]
|
95
|
+
@files[file] = {:path => file, :untracked => true} if !File.directory?(file)
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
# find modified in tree
|
100
|
+
diff_files.each do |path, data|
|
101
|
+
@files[path] ? @files[path].merge!(data) : @files[path] = data
|
102
|
+
end
|
103
|
+
|
104
|
+
# find added but not committed - new files
|
105
|
+
diff_index('HEAD').each do |path, data|
|
106
|
+
@files[path] ? @files[path].merge!(data) : @files[path] = data
|
107
|
+
end
|
108
|
+
|
109
|
+
@files.each do |k, file_hash|
|
110
|
+
@files[k] = StatusFile.new(@base, file_hash)
|
111
|
+
end
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
# compares the index and the working directory
|
116
|
+
def diff_files
|
117
|
+
hsh = {}
|
118
|
+
@base.git.diff_files.split("\n").each do |line|
|
119
|
+
(info, file) = line.split("\t")
|
120
|
+
(mode_src, mode_dest, sha_src, sha_dest, type) = info.split
|
121
|
+
hsh[file] = {:path => file, :mode_file => mode_src.to_s[1, 7], :mode_index => mode_dest,
|
122
|
+
:sha_file => sha_src, :sha_index => sha_dest, :type => type}
|
123
|
+
end
|
124
|
+
hsh
|
125
|
+
end
|
126
|
+
|
127
|
+
# compares the index and the repository
|
128
|
+
def diff_index(treeish)
|
129
|
+
hsh = {}
|
130
|
+
@base.git.diff_index({}, treeish).split("\n").each do |line|
|
131
|
+
(info, file) = line.split("\t")
|
132
|
+
(mode_src, mode_dest, sha_src, sha_dest, type) = info.split
|
133
|
+
hsh[file] = {:path => file, :mode_repo => mode_src.to_s[1, 7], :mode_index => mode_dest,
|
134
|
+
:sha_repo => sha_src, :sha_index => sha_dest, :type => type}
|
135
|
+
end
|
136
|
+
hsh
|
137
|
+
end
|
138
|
+
|
139
|
+
def ls_files
|
140
|
+
hsh = {}
|
141
|
+
lines = @base.git.ls_files({:stage => true})
|
142
|
+
lines.split("\n").each do |line|
|
143
|
+
(info, file) = line.split("\t")
|
144
|
+
(mode, sha, stage) = info.split
|
145
|
+
hsh[file] = {:path => file, :mode_index => mode, :sha_index => sha, :stage => stage}
|
146
|
+
end
|
147
|
+
hsh
|
148
|
+
end
|
149
|
+
end
|
150
|
+
|
151
|
+
end
|
@@ -0,0 +1,88 @@
|
|
1
|
+
module Grit
|
2
|
+
|
3
|
+
class Submodule
|
4
|
+
attr_reader :id
|
5
|
+
attr_reader :mode
|
6
|
+
attr_reader :name
|
7
|
+
|
8
|
+
# Create a Submodule containing just the specified attributes
|
9
|
+
# +repo+ is the Repo
|
10
|
+
# +atts+ is a Hash of instance variable data
|
11
|
+
#
|
12
|
+
# Returns Grit::Submodule (unbaked)
|
13
|
+
def self.create(repo, atts)
|
14
|
+
self.allocate.create_initialize(repo, atts)
|
15
|
+
end
|
16
|
+
|
17
|
+
# Initializer for Submodule.create
|
18
|
+
# +repo+ is the Repo
|
19
|
+
# +atts+ is a Hash of instance variable data
|
20
|
+
#
|
21
|
+
# Returns Grit::Submodule
|
22
|
+
def create_initialize(repo, atts)
|
23
|
+
@repo = repo
|
24
|
+
atts.each do |k, v|
|
25
|
+
instance_variable_set("@#{k}".to_sym, v)
|
26
|
+
end
|
27
|
+
self
|
28
|
+
end
|
29
|
+
|
30
|
+
# The url of this submodule
|
31
|
+
# +ref+ is the committish that should be used to look up the url
|
32
|
+
#
|
33
|
+
# Returns String
|
34
|
+
def url(ref)
|
35
|
+
config = self.class.config(@repo, ref)
|
36
|
+
|
37
|
+
lookup = config.keys.inject({}) do |acc, key|
|
38
|
+
id = config[key]['id']
|
39
|
+
acc[id] = config[key]['url']
|
40
|
+
acc
|
41
|
+
end
|
42
|
+
|
43
|
+
lookup[@id]
|
44
|
+
end
|
45
|
+
|
46
|
+
# The configuration information for the given +repo+
|
47
|
+
# +repo+ is the Repo
|
48
|
+
# +ref+ is the committish (defaults to 'master')
|
49
|
+
#
|
50
|
+
# Returns a Hash of { <path:String> => { 'url' => <url:String>, 'id' => <id:String> } }
|
51
|
+
# Returns {} if no .gitmodules file was found
|
52
|
+
def self.config(repo, ref = "master")
|
53
|
+
commit = repo.commit(ref)
|
54
|
+
blob = commit.tree/'.gitmodules'
|
55
|
+
return {} unless blob
|
56
|
+
|
57
|
+
lines = blob.data.gsub(/\r\n?/, "\n" ).split("\n")
|
58
|
+
|
59
|
+
config = {}
|
60
|
+
current = nil
|
61
|
+
|
62
|
+
lines.each do |line|
|
63
|
+
if line =~ /^\[submodule "(.+)"\]$/
|
64
|
+
current = $1
|
65
|
+
config[current] = {}
|
66
|
+
config[current]['id'] = (commit.tree/current).id
|
67
|
+
elsif line =~ /^\t(\w+) = (.+)$/
|
68
|
+
config[current][$1] = $2
|
69
|
+
config[current]['id'] = (commit.tree/$2).id if $1 == 'path'
|
70
|
+
else
|
71
|
+
# ignore
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
config
|
76
|
+
end
|
77
|
+
|
78
|
+
def basename
|
79
|
+
File.basename(name)
|
80
|
+
end
|
81
|
+
|
82
|
+
# Pretty object inspection
|
83
|
+
def inspect
|
84
|
+
%Q{#<Grit::Submodule "#{@id}">}
|
85
|
+
end
|
86
|
+
end # Submodule
|
87
|
+
|
88
|
+
end # Grit
|
@@ -0,0 +1,16 @@
|
|
1
|
+
module Grit
|
2
|
+
|
3
|
+
class Tag < Ref
|
4
|
+
def self.find_all(repo, options = {})
|
5
|
+
refs = repo.git.refs(options, prefix)
|
6
|
+
refs.split("\n").map do |ref|
|
7
|
+
name, id = *ref.split(' ')
|
8
|
+
cid = repo.git.commit_from_sha(id)
|
9
|
+
raise "Unknown object type." if cid == ''
|
10
|
+
commit = Commit.create(repo, :id => cid)
|
11
|
+
self.new(name, commit)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
end
|
@@ -0,0 +1,123 @@
|
|
1
|
+
module Grit
|
2
|
+
|
3
|
+
class Tree
|
4
|
+
lazy_reader :contents
|
5
|
+
attr_reader :id
|
6
|
+
attr_reader :mode
|
7
|
+
attr_reader :name
|
8
|
+
|
9
|
+
# Construct the contents of the tree
|
10
|
+
# +repo+ is the Repo
|
11
|
+
# +treeish+ is the reference
|
12
|
+
# +paths+ is an optional Array of directory paths to restrict the tree
|
13
|
+
#
|
14
|
+
# Returns Grit::Tree (baked)
|
15
|
+
def self.construct(repo, treeish, paths = [])
|
16
|
+
output = repo.git.ls_tree({}, treeish, *paths)
|
17
|
+
self.allocate.construct_initialize(repo, treeish, output)
|
18
|
+
end
|
19
|
+
|
20
|
+
def construct_initialize(repo, id, text)
|
21
|
+
@repo = repo
|
22
|
+
@id = id
|
23
|
+
@contents = []
|
24
|
+
|
25
|
+
text.split("\n").each do |line|
|
26
|
+
@contents << content_from_string(repo, line)
|
27
|
+
end
|
28
|
+
@contents.compact!
|
29
|
+
|
30
|
+
self
|
31
|
+
end
|
32
|
+
|
33
|
+
def lazy_source
|
34
|
+
Tree.construct(@repo, @id, [])
|
35
|
+
end
|
36
|
+
|
37
|
+
# Create an unbaked Tree containing just the specified attributes
|
38
|
+
# +repo+ is the Repo
|
39
|
+
# +atts+ is a Hash of instance variable data
|
40
|
+
#
|
41
|
+
# Returns Grit::Tree (unbaked)
|
42
|
+
def self.create(repo, atts)
|
43
|
+
self.allocate.create_initialize(repo, atts)
|
44
|
+
end
|
45
|
+
|
46
|
+
# Initializer for Tree.create
|
47
|
+
# +repo+ is the Repo
|
48
|
+
# +atts+ is a Hash of instance variable data
|
49
|
+
#
|
50
|
+
# Returns Grit::Tree (unbaked)
|
51
|
+
def create_initialize(repo, atts)
|
52
|
+
@repo = repo
|
53
|
+
|
54
|
+
atts.each do |k, v|
|
55
|
+
instance_variable_set("@#{k}", v)
|
56
|
+
end
|
57
|
+
self
|
58
|
+
end
|
59
|
+
|
60
|
+
# Parse a content item and create the appropriate object
|
61
|
+
# +repo+ is the Repo
|
62
|
+
# +text+ is the single line containing the items data in `git ls-tree` format
|
63
|
+
#
|
64
|
+
# Returns Grit::Blob or Grit::Tree
|
65
|
+
def content_from_string(repo, text)
|
66
|
+
mode, type, id, name = text.split(" ", 4)
|
67
|
+
case type
|
68
|
+
when "tree"
|
69
|
+
Tree.create(repo, :id => id, :mode => mode, :name => name)
|
70
|
+
when "blob"
|
71
|
+
Blob.create(repo, :id => id, :mode => mode, :name => name)
|
72
|
+
when "link"
|
73
|
+
Blob.create(repo, :id => id, :mode => mode, :name => name)
|
74
|
+
when "commit"
|
75
|
+
Submodule.create(repo, :id => id, :mode => mode, :name => name)
|
76
|
+
else
|
77
|
+
raise "Invalid type: #{type}"
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
# Find the named object in this tree's contents
|
82
|
+
#
|
83
|
+
# Examples
|
84
|
+
# Repo.new('/path/to/grit').tree/'lib'
|
85
|
+
# # => #<Grit::Tree "6cc23ee138be09ff8c28b07162720018b244e95e">
|
86
|
+
# Repo.new('/path/to/grit').tree/'README.txt'
|
87
|
+
# # => #<Grit::Blob "8b1e02c0fb554eed2ce2ef737a68bb369d7527df">
|
88
|
+
#
|
89
|
+
# Returns Grit::Blob or Grit::Tree or nil if not found
|
90
|
+
def /(file)
|
91
|
+
if file =~ /\//
|
92
|
+
file.split("/").inject(self) { |acc, x| acc/x } rescue nil
|
93
|
+
else
|
94
|
+
self.contents.find { |c| c.name == file }
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
def basename
|
99
|
+
File.basename(name)
|
100
|
+
end
|
101
|
+
|
102
|
+
# Pretty object inspection
|
103
|
+
def inspect
|
104
|
+
%Q{#<Grit::Tree "#{@id}">}
|
105
|
+
end
|
106
|
+
|
107
|
+
# Find only Tree objects from contents
|
108
|
+
def trees
|
109
|
+
contents.select {|v| v.kind_of? Tree}
|
110
|
+
end
|
111
|
+
|
112
|
+
# Find only Blob objects from contents
|
113
|
+
def blobs
|
114
|
+
contents.select {|v| v.kind_of? Blob}
|
115
|
+
end
|
116
|
+
|
117
|
+
# Compares trees by name
|
118
|
+
def <=>(other)
|
119
|
+
name <=> other.name
|
120
|
+
end
|
121
|
+
end # Tree
|
122
|
+
|
123
|
+
end # Grit
|
@@ -0,0 +1,46 @@
|
|
1
|
+
module Open3
|
2
|
+
extend self
|
3
|
+
|
4
|
+
def popen3(*cmd)
|
5
|
+
pw = IO::pipe # pipe[0] for read, pipe[1] for write
|
6
|
+
pr = IO::pipe
|
7
|
+
pe = IO::pipe
|
8
|
+
|
9
|
+
pid = fork{
|
10
|
+
# child
|
11
|
+
fork{
|
12
|
+
# grandchild
|
13
|
+
pw[1].close
|
14
|
+
STDIN.reopen(pw[0])
|
15
|
+
pw[0].close
|
16
|
+
|
17
|
+
pr[0].close
|
18
|
+
STDOUT.reopen(pr[1])
|
19
|
+
pr[1].close
|
20
|
+
|
21
|
+
pe[0].close
|
22
|
+
STDERR.reopen(pe[1])
|
23
|
+
pe[1].close
|
24
|
+
|
25
|
+
exec(*cmd)
|
26
|
+
}
|
27
|
+
exit!(0)
|
28
|
+
}
|
29
|
+
|
30
|
+
pw[0].close
|
31
|
+
pr[1].close
|
32
|
+
pe[1].close
|
33
|
+
Process.waitpid(pid)
|
34
|
+
pi = [pw[1], pr[0], pe[0]]
|
35
|
+
pw[1].sync = true
|
36
|
+
if defined? yield
|
37
|
+
begin
|
38
|
+
return yield(*pi)
|
39
|
+
ensure
|
40
|
+
Process.detach(pid) if pid
|
41
|
+
pi.each { |p| p.close unless p.closed? }
|
42
|
+
end
|
43
|
+
end
|
44
|
+
pi
|
45
|
+
end
|
46
|
+
end
|
@@ -0,0 +1,210 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
# TODO understand why subject { Klass.new(a,b) } doesn't work in rspec as it should
|
4
|
+
|
5
|
+
describe CucumberFM::Aggregator do
|
6
|
+
before(:each) do
|
7
|
+
@f1 = mock('feature1')
|
8
|
+
@f2 = mock('feature2')
|
9
|
+
@s11 = mock('scenario1', :feature => @f1, :tags => ['@m1', '@mc', '@i1'],
|
10
|
+
:tags_without_technical => ['@m1', '@mc', '@i1'], :estimation => 1.5)
|
11
|
+
@s12 = mock('scenario2', :feature => @f1, :tags => ['@m2', '@ak', '@i1'],
|
12
|
+
:tags_without_technical => ['@m2', '@ak', '@i1'], :estimation => 1.75)
|
13
|
+
@s13 = mock('scenario5', :feature => @f1, :tags => ['@m2', '@ak', '@i1'],
|
14
|
+
:tags_without_technical => ['@m2', '@ak', '@i1'], :estimation => 1)
|
15
|
+
@s21 = mock('scenario3', :feature => @f2, :tags => ['@m3', '@mc', '@i1'],
|
16
|
+
:tags_without_technical => ['@m3', '@mc', '@i1'], :estimation => 2)
|
17
|
+
@s22 = mock('scenario4', :feature => @f2, :tags => ['@m2', '@tb', '@i2'],
|
18
|
+
:tags_without_technical => ['@m2', '@tb', '@i2'], :estimation => 1)
|
19
|
+
@cfm = mock('cfm', :scenarios => [@s11, @s12, @s13, @s21, @s22])
|
20
|
+
@aggregator1 = CucumberFM::FeatureElement::Component::Tags::PATTERN[:milestone]
|
21
|
+
@aggregator2 = CucumberFM::FeatureElement::Component::Tags::PATTERN[:iteration]
|
22
|
+
end
|
23
|
+
|
24
|
+
it "should return estimation value for scenarios array" do
|
25
|
+
@aggregator = CucumberFM::Aggregator.new(@cfm, [@aggregator1])
|
26
|
+
@aggregator.collection['@m2'][@f1].estimation.should == 2.75
|
27
|
+
end
|
28
|
+
|
29
|
+
context "totals values should be correct" do
|
30
|
+
before(:each) do
|
31
|
+
@aggregator = CucumberFM::Aggregator.new(@cfm, [@aggregator1])
|
32
|
+
@collection = @aggregator.collection
|
33
|
+
end
|
34
|
+
it "for features" do
|
35
|
+
@collection.should have(2).features
|
36
|
+
end
|
37
|
+
it "for scenarios" do
|
38
|
+
@collection.should have(5).scenarios
|
39
|
+
end
|
40
|
+
it "for estimation" do
|
41
|
+
@collection.estimation.should == 7.25
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
context "single dimension" do
|
46
|
+
before(:each) do
|
47
|
+
@aggregator = CucumberFM::Aggregator.new(@cfm, [@aggregator1])
|
48
|
+
@collection = @aggregator.collection
|
49
|
+
end
|
50
|
+
|
51
|
+
it "should aggregate correctly" do
|
52
|
+
@collection.should ==
|
53
|
+
{
|
54
|
+
'@m1' => {
|
55
|
+
@f1 => [@s11]
|
56
|
+
},
|
57
|
+
'@m2' => {
|
58
|
+
@f1 => [@s12, @s13],
|
59
|
+
@f2 => [@s22]
|
60
|
+
},
|
61
|
+
'@m3' => {
|
62
|
+
@f2 => [@s21]
|
63
|
+
}
|
64
|
+
}
|
65
|
+
end
|
66
|
+
|
67
|
+
{'@m1' =>[1, 1, 1.5], '@m2' => [2, 3, 3.75], '@m3' => [1, 1, 2]}.each_pair do |key, value|
|
68
|
+
context "should correct count" do
|
69
|
+
context key do
|
70
|
+
it "features" do
|
71
|
+
@collection[key].should have(value[0]).features
|
72
|
+
end
|
73
|
+
it "scenarios" do
|
74
|
+
@collection[key].should have(value[1]).scenarios
|
75
|
+
end
|
76
|
+
it "estimation" do
|
77
|
+
@collection[key].estimation.should == value[2]
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
context "double dimension" do
|
85
|
+
before(:each) do
|
86
|
+
@aggregator = CucumberFM::Aggregator.new(@cfm, [@aggregator1, @aggregator2])
|
87
|
+
@collection = @aggregator.collection
|
88
|
+
end
|
89
|
+
it "should aggregate correctly" do
|
90
|
+
@collection.should ==
|
91
|
+
{
|
92
|
+
'@m1' => {
|
93
|
+
'@i1' => { @f1 => [@s11] }
|
94
|
+
},
|
95
|
+
'@m2' => {
|
96
|
+
'@i1' => { @f1 => [@s12, @s13]},
|
97
|
+
'@i2' => { @f2 => [@s22]}
|
98
|
+
},
|
99
|
+
'@m3' => {
|
100
|
+
'@i1' => { @f2 => [@s21]}
|
101
|
+
}
|
102
|
+
}
|
103
|
+
end
|
104
|
+
|
105
|
+
{'@m1' =>[1, 1, 1.5], '@m2' => [2, 3, 3.75], '@m3' => [1, 1, 2]}.each_pair do |key, value|
|
106
|
+
context "should count correctly at first level" do
|
107
|
+
context key do
|
108
|
+
it "features " do
|
109
|
+
@collection[key].should have(value[0]).features
|
110
|
+
end
|
111
|
+
it "scenarios" do
|
112
|
+
@collection[key].should have(value[1]).scenarios
|
113
|
+
end
|
114
|
+
it "estimation" do
|
115
|
+
@collection[key].estimation.should == value[2]
|
116
|
+
end
|
117
|
+
end
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
{['@m2', '@i1'] =>[1, 2, 2.75], ['@m2', '@i2'] =>[1, 1, 1], ['@m3', '@i1'] => [1, 1, 2]}.each_pair do |key, value|
|
122
|
+
context "should count correctly at second level" do
|
123
|
+
context key do
|
124
|
+
it "features " do
|
125
|
+
@collection[key.first][key.last].should have(value[0]).features
|
126
|
+
end
|
127
|
+
it "scenarios" do
|
128
|
+
@collection[key.first][key.last].should have(value[1]).scenarios
|
129
|
+
end
|
130
|
+
it "estimation" do
|
131
|
+
@collection[key.first][key.last].estimation.should == value[2]
|
132
|
+
end
|
133
|
+
end
|
134
|
+
end
|
135
|
+
end
|
136
|
+
|
137
|
+
end
|
138
|
+
|
139
|
+
context "when tag for pattern not found is should be labelled as 'undefined' " do
|
140
|
+
before(:each) do
|
141
|
+
@s22 = mock('scenario4', :feature => @f2, :tags => ['@tb'], :tags_without_technical => ['@tb'],
|
142
|
+
:estimation => 1)
|
143
|
+
@cfm = mock('cfm', :scenarios => [@s11, @s12, @s13, @s21, @s22])
|
144
|
+
@aggregator = CucumberFM::Aggregator.new(@cfm, [@aggregator1])
|
145
|
+
@collection = @aggregator.collection
|
146
|
+
end
|
147
|
+
|
148
|
+
it "should aggregate correctly" do
|
149
|
+
@collection.should ==
|
150
|
+
{
|
151
|
+
'@m1' => {
|
152
|
+
@f1 => [@s11]
|
153
|
+
},
|
154
|
+
'@m2' => {
|
155
|
+
@f1 => [@s12, @s13]
|
156
|
+
},
|
157
|
+
'_undefined_' => {
|
158
|
+
@f2 => [@s22]
|
159
|
+
},
|
160
|
+
'@m3' => {
|
161
|
+
@f2 => [@s21]
|
162
|
+
}
|
163
|
+
}
|
164
|
+
end
|
165
|
+
end
|
166
|
+
|
167
|
+
context "multiple tags (one scenario can be aggregated many times" do
|
168
|
+
before(:each) do
|
169
|
+
@s22 = mock('scenario4', :feature => @f2, :tags => ['@mc', '@tb'],
|
170
|
+
:tags_without_technical => ['@mc', '@tb'], :estimation => 1)
|
171
|
+
@cfm = mock('cfm', :scenarios => [@s11, @s12, @s13, @s21, @s22])
|
172
|
+
@aggregator = CucumberFM::Aggregator.new(@cfm, nil, ['@mc', '@tb', '@ak'])
|
173
|
+
@collection = @aggregator.collection
|
174
|
+
end
|
175
|
+
|
176
|
+
it "should aggregate correctly" do
|
177
|
+
@collection.should ==
|
178
|
+
{
|
179
|
+
'@mc' => [@s11, @s21, @s22],
|
180
|
+
'@tb' => [@s22],
|
181
|
+
'@ak' => [@s12, @s13]
|
182
|
+
}
|
183
|
+
end
|
184
|
+
|
185
|
+
end
|
186
|
+
|
187
|
+
context "results should not be aggregated by technical tags on second level" do
|
188
|
+
before(:each) do
|
189
|
+
@aggregator_component = CucumberFM::FeatureElement::Component::Tags::PATTERN[:component]
|
190
|
+
@aggregator_developer = CucumberFM::FeatureElement::Component::Tags::PATTERN[:developer]
|
191
|
+
@s22 = mock('scenario4', :feature => @f2, :tags => ['@selenium', '@aggregator', '@ao'],
|
192
|
+
:tags_without_technical => ['@aggregator','@ao'], :estimation => 1)
|
193
|
+
@cfm = mock('cfm', :scenarios => [@s22])
|
194
|
+
@aggregator = CucumberFM::Aggregator.new(@cfm, [@aggregator_developer, @aggregator_component])
|
195
|
+
@collection = @aggregator.collection
|
196
|
+
end
|
197
|
+
|
198
|
+
it "should aggregate correctly" do
|
199
|
+
@collection.should ==
|
200
|
+
{
|
201
|
+
'@ao' => {
|
202
|
+
'@aggregator' => {
|
203
|
+
@f2 => [@s22]
|
204
|
+
}
|
205
|
+
}
|
206
|
+
}
|
207
|
+
end
|
208
|
+
|
209
|
+
end
|
210
|
+
end
|