cucumber_fm-core 0.1

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