cucumber_fm-core 0.1

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.
Files changed (74) hide show
  1. data/LICENCE +20 -0
  2. data/lib/cucumber_f_m/aggregator.rb +65 -0
  3. data/lib/cucumber_f_m/comment_module/comment.rb +7 -0
  4. data/lib/cucumber_f_m/config.rb +37 -0
  5. data/lib/cucumber_f_m/cvs/git.rb +7 -0
  6. data/lib/cucumber_f_m/feature.rb +141 -0
  7. data/lib/cucumber_f_m/feature_element/background.rb +10 -0
  8. data/lib/cucumber_f_m/feature_element/comment.rb +13 -0
  9. data/lib/cucumber_f_m/feature_element/component/comments.rb +21 -0
  10. data/lib/cucumber_f_m/feature_element/component/information/component.rb +16 -0
  11. data/lib/cucumber_f_m/feature_element/component/tags.rb +108 -0
  12. data/lib/cucumber_f_m/feature_element/component/title.rb +26 -0
  13. data/lib/cucumber_f_m/feature_element/component/total_estimation.rb +42 -0
  14. data/lib/cucumber_f_m/feature_element/example.rb +7 -0
  15. data/lib/cucumber_f_m/feature_element/info.rb +13 -0
  16. data/lib/cucumber_f_m/feature_element/narrative.rb +7 -0
  17. data/lib/cucumber_f_m/feature_element/scenario.rb +15 -0
  18. data/lib/cucumber_f_m/feature_element/scenario_outline.rb +23 -0
  19. data/lib/cucumber_f_m/feature_element/step.rb +7 -0
  20. data/lib/cucumber_f_m/statistic.rb +31 -0
  21. data/lib/cucumber_f_m/tag_filter.rb +85 -0
  22. data/lib/cucumber_feature_manager.rb +164 -0
  23. data/lib/grit/lib/grit.rb +75 -0
  24. data/lib/grit/lib/grit/actor.rb +36 -0
  25. data/lib/grit/lib/grit/blame.rb +61 -0
  26. data/lib/grit/lib/grit/blob.rb +126 -0
  27. data/lib/grit/lib/grit/commit.rb +247 -0
  28. data/lib/grit/lib/grit/commit_stats.rb +128 -0
  29. data/lib/grit/lib/grit/config.rb +44 -0
  30. data/lib/grit/lib/grit/diff.rb +70 -0
  31. data/lib/grit/lib/grit/errors.rb +7 -0
  32. data/lib/grit/lib/grit/git-ruby.rb +267 -0
  33. data/lib/grit/lib/grit/git-ruby/commit_db.rb +52 -0
  34. data/lib/grit/lib/grit/git-ruby/file_index.rb +193 -0
  35. data/lib/grit/lib/grit/git-ruby/git_object.rb +350 -0
  36. data/lib/grit/lib/grit/git-ruby/internal/file_window.rb +58 -0
  37. data/lib/grit/lib/grit/git-ruby/internal/loose.rb +137 -0
  38. data/lib/grit/lib/grit/git-ruby/internal/pack.rb +384 -0
  39. data/lib/grit/lib/grit/git-ruby/internal/raw_object.rb +37 -0
  40. data/lib/grit/lib/grit/git-ruby/object.rb +325 -0
  41. data/lib/grit/lib/grit/git-ruby/repository.rb +767 -0
  42. data/lib/grit/lib/grit/git.rb +323 -0
  43. data/lib/grit/lib/grit/index.rb +122 -0
  44. data/lib/grit/lib/grit/lazy.rb +33 -0
  45. data/lib/grit/lib/grit/merge.rb +45 -0
  46. data/lib/grit/lib/grit/ref.rb +74 -0
  47. data/lib/grit/lib/grit/repo.rb +482 -0
  48. data/lib/grit/lib/grit/ruby1.9.rb +7 -0
  49. data/lib/grit/lib/grit/status.rb +151 -0
  50. data/lib/grit/lib/grit/submodule.rb +88 -0
  51. data/lib/grit/lib/grit/tag.rb +16 -0
  52. data/lib/grit/lib/grit/tree.rb +123 -0
  53. data/lib/grit/lib/open3_detach.rb +46 -0
  54. data/spec/cucumber_f_m/aggregator_spec.rb +210 -0
  55. data/spec/cucumber_f_m/comment_module/comment_spec.rb +4 -0
  56. data/spec/cucumber_f_m/config_spec.rb +27 -0
  57. data/spec/cucumber_f_m/cvs/git_spec.rb +40 -0
  58. data/spec/cucumber_f_m/feature_all_tags_to_scenario_spec.rb +7 -0
  59. data/spec/cucumber_f_m/feature_file_manipulation_spec.rb +29 -0
  60. data/spec/cucumber_f_m/feature_module/background_spec.rb +30 -0
  61. data/spec/cucumber_f_m/feature_module/comment_spec.rb +23 -0
  62. data/spec/cucumber_f_m/feature_module/example_spec.rb +5 -0
  63. data/spec/cucumber_f_m/feature_module/info_spec.rb +30 -0
  64. data/spec/cucumber_f_m/feature_module/narrative_spec.rb +7 -0
  65. data/spec/cucumber_f_m/feature_module/scenario_outline_spec.rb +39 -0
  66. data/spec/cucumber_f_m/feature_module/scenario_spec.rb +33 -0
  67. data/spec/cucumber_f_m/feature_module/step_spec.rb +7 -0
  68. data/spec/cucumber_f_m/feature_module/tag_spec.rb +96 -0
  69. data/spec/cucumber_f_m/feature_spec.rb +229 -0
  70. data/spec/cucumber_f_m/tag_filter_spec.rb +191 -0
  71. data/spec/cucumber_feature_manager_spec.rb +59 -0
  72. data/spec/data/feature_manager/some_ruby_file.rb +0 -0
  73. data/spec/spec_helper.rb +1 -0
  74. metadata +141 -0
@@ -0,0 +1,7 @@
1
+ class String
2
+ if ((defined? RUBY_VERSION) && (RUBY_VERSION[0..2] == "1.9"))
3
+ def getord(offset); self[offset].ord; end
4
+ else
5
+ alias :getord :[]
6
+ end
7
+ end
@@ -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