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,26 @@
1
+ module CucumberFM
2
+ module FeatureElement
3
+ module Component
4
+ module Title
5
+
6
+ def title
7
+ @title ||= fetch_title
8
+ end
9
+
10
+ private
11
+
12
+ def title_line_pattern
13
+ /^\s*[A-Z][a-z]+:\s.*$/
14
+ end
15
+
16
+ def fetch_title
17
+ if tag_line = title_line_pattern.match(raw)
18
+ tag_line[0].gsub(/^[^:]*:/,'').strip
19
+ else
20
+ '--- no title found'
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,42 @@
1
+ module CucumberFM
2
+ module FeatureElement
3
+ module Component
4
+ module TotalEstimation
5
+ def estimation
6
+ scenarios.inject(0.0) { |sum, scenario| sum + scenario.estimation }
7
+ end
8
+
9
+ def estimation_done
10
+ scenarios.inject(0.0) do |sum, scenario|
11
+ estimation_done_filter.pass?(scenario.tags) ?
12
+ sum + scenario.estimation :
13
+ sum
14
+ end
15
+ end
16
+
17
+ def estimation_done_percentage
18
+ estimation > 0 ? ( estimation_done.to_f / estimation * 100 ).round : 0
19
+ end
20
+
21
+ def scenarios_done
22
+ scenarios.inject(0) do |sum, scenario|
23
+ estimation_done_filter.pass?(scenario.tags) ?
24
+ sum + 1 :
25
+ sum
26
+ end
27
+ end
28
+
29
+ def scenarios_done_percentage
30
+ !scenarios.empty? ? ( scenarios_done.to_f / scenarios.size * 100 ).round : 0
31
+ end
32
+
33
+ private
34
+
35
+ def estimation_done_filter
36
+ tags = CucumberFM::FeatureElement::Component::Tags::STATUS_COMPLETE
37
+ @estimation_done_filter = CucumberFM::TagFilter.new(tags.join(','))
38
+ end
39
+ end
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,7 @@
1
+ module CucumberFM
2
+ module FeatureElement
3
+ class Example
4
+ #Code here
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,13 @@
1
+ module CucumberFM
2
+ module FeatureElement
3
+ class Info < Struct.new(:feature, :raw)
4
+
5
+ PATTERN = /((^.*#+.*\n)+\n?)?(^.*@+.*\n)?^[ \t]*Feature:.*\n?(^.*\S+.*\n?)*/
6
+
7
+ include CucumberFM::FeatureElement::Component::Tags
8
+ include CucumberFM::FeatureElement::Component::Title
9
+ include CucumberFM::FeatureElement::Component::Comments
10
+
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,7 @@
1
+ module CucumberFM
2
+ module FeatureElement
3
+ class Narrative
4
+ #Code here
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,15 @@
1
+ module CucumberFM
2
+ module FeatureElement
3
+ class Scenario < Struct.new(:feature, :raw)
4
+ PATTERN = /((^.*#+.*\n)+\n?)?(^.*@+.*\n)?^[ \t]*Scenario:.*\n?(^.*\S+.*\n?)*/
5
+
6
+ include CucumberFM::FeatureElement::Component::Tags
7
+ include CucumberFM::FeatureElement::Component::Title
8
+ include CucumberFM::FeatureElement::Component::Comments
9
+
10
+ def second_tags_source
11
+ feature.info
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,23 @@
1
+ module CucumberFM
2
+ module FeatureElement
3
+ class ScenarioOutline < Struct.new(:feature, :raw)
4
+
5
+ # TODO requires more specs and some improvements
6
+ PATTERN = /((^.*#+.*\n)+\n?)?(^.*@+.*\n)?^[ \t]*Scenario Outline:.*\n?((^.*\S+.*\n?)*\n?)?(^[ \t]*Examples:.*\n(^.*\S+.*\n?)*)?/
7
+
8
+ include CucumberFM::FeatureElement::Component::Tags
9
+ include CucumberFM::FeatureElement::Component::Title
10
+ include CucumberFM::FeatureElement::Component::Comments
11
+
12
+ def second_tags_source
13
+ feature.info
14
+ end
15
+
16
+ private
17
+
18
+ def title_line_pattern
19
+ /^\s*Scenario Outline:\s.*$/
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,7 @@
1
+ module CucumberFM
2
+ module FeatureElement
3
+ class Step
4
+ #Code here
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,31 @@
1
+ module CucumberFM
2
+ class Statistic < Struct.new(:cfm)
3
+
4
+ def overal
5
+ cfm
6
+ end
7
+
8
+ def various
9
+ @various ||= CucumberFM::Aggregator.new(cfm, nil, CucumberFM::FeatureElement::Component::Tags::TECHNICAL).collection
10
+ end
11
+
12
+ private
13
+
14
+ def method_missing(m, * args, & block)
15
+ if CucumberFM::FeatureElement::Component::Tags::PATTERN.has_key?(m.to_sym)
16
+ report(m)
17
+ else
18
+ super
19
+ end
20
+ end
21
+
22
+ def report(aggregate_by)
23
+ CucumberFM::Aggregator.new(cfm, (aggregate_by.is_a?(Regexp) ? aggregate_by : patterns(aggregate_by))).collection
24
+ end
25
+
26
+ def patterns(label)
27
+ [CucumberFM::FeatureElement::Component::Tags::PATTERN[label]]
28
+ end
29
+
30
+ end
31
+ end
@@ -0,0 +1,85 @@
1
+ module CucumberFM
2
+ class TagFilter < Struct.new(:expression)
3
+
4
+ TAG_PATTERN = CucumberFM::FeatureElement::Component::Tags::TAG_PATTERN
5
+ AND_PATTERN = /\s+/
6
+ OR_PATTERN = /\s?,\s?/
7
+ NOT_PATTERN = /~/
8
+
9
+ TOKEN = Regexp.union(TAG_PATTERN,
10
+ OR_PATTERN,
11
+ AND_PATTERN,
12
+ NOT_PATTERN)
13
+
14
+ def pass?(tags)
15
+ if expression.nil? or expression.empty?
16
+ true
17
+ else
18
+ evaluate_expression(tags)
19
+ end
20
+ end
21
+
22
+ private
23
+
24
+
25
+ # TODO - refactoring
26
+
27
+ def evaluate_expression(tags)
28
+ buffer = nil
29
+ buffer_array = []
30
+ buffer_negation = nil
31
+
32
+ text = expression.gsub(/\s+/, ' ')
33
+ while token = text.match(TOKEN)
34
+ case token[0]
35
+ when TAG_PATTERN
36
+ throw "Error at #{expression} | token: #{token[0]} | last token: #{buffer}" unless buffer.nil?
37
+ buffer = token[0]
38
+ when NOT_PATTERN
39
+ buffer_negation = true
40
+ when OR_PATTERN
41
+ buffer_array.push buffer
42
+ buffer = nil
43
+ when AND_PATTERN
44
+ if buffer_array.empty? and buffer
45
+ return(false) unless (!buffer_negation == tags.include?(buffer))
46
+ buffer = nil
47
+ buffer_negation = nil
48
+ true
49
+ elsif !buffer_array.empty?
50
+ if buffer
51
+ buffer_array.push(buffer)
52
+ buffer = nil
53
+ end
54
+ return(false) unless (!buffer_negation == buffer_array.any? { |tag| tags.include? tag })
55
+ buffer_array = []
56
+ buffer_negation = nil
57
+ true
58
+ else
59
+ true
60
+ end
61
+ end
62
+
63
+ text = token.post_match
64
+ end
65
+
66
+ if buffer_array.empty? and buffer
67
+ return(false) unless (!buffer_negation == tags.include?(buffer))
68
+ buffer = nil
69
+ buffer_negation = nil
70
+ true
71
+ elsif !buffer_array.empty?
72
+ if buffer
73
+ buffer_array.push(buffer)
74
+ buffer = nil
75
+ end
76
+ return(false) unless (!buffer_negation == buffer_array.any? { |tag| tags.include? tag })
77
+ buffer_array = []
78
+ buffer_negation = nil
79
+ true
80
+ else
81
+ true
82
+ end
83
+ end
84
+ end
85
+ end
@@ -0,0 +1,164 @@
1
+ require 'cucumber_f_m/feature_element/component/tags'
2
+ require 'cucumber_f_m/feature_element/component/title'
3
+ require 'cucumber_f_m/feature_element/component/comments'
4
+ require 'cucumber_f_m/feature_element/component/total_estimation'
5
+
6
+ require 'cucumber_f_m/comment_module/comment'
7
+
8
+ require 'cucumber_f_m/feature_element/info'
9
+ require 'cucumber_f_m/feature_element/comment'
10
+ require 'cucumber_f_m/feature_element/narrative'
11
+ require 'cucumber_f_m/feature_element/background'
12
+ require 'cucumber_f_m/feature_element/scenario'
13
+ require 'cucumber_f_m/feature_element/scenario_outline'
14
+ require 'cucumber_f_m/feature_element/example'
15
+ require 'cucumber_f_m/feature_element/step'
16
+
17
+ require 'cucumber_f_m/feature'
18
+ require 'cucumber_f_m/tag_filter'
19
+ require 'cucumber_f_m/config'
20
+ require 'cucumber_f_m/aggregator'
21
+ require 'cucumber_f_m/statistic'
22
+
23
+ require 'cucumber_f_m/cvs/git'
24
+ require 'grit/lib/grit'
25
+
26
+ require 'base64'
27
+
28
+ # TODO refactor, use repo full_path and feature not full path
29
+ class CucumberFeatureManager < Struct.new(:path, :repo_path, :config_parameters)
30
+
31
+ include Grit
32
+ include CucumberFM::FeatureElement::Component::TotalEstimation
33
+
34
+ attr_reader :info
35
+
36
+ def features
37
+ @features ||= scan_features
38
+ end
39
+
40
+ def scenarios
41
+ (features.collect { |feature| feature.scenarios }).flatten
42
+ end
43
+
44
+ def config
45
+ @config ||= CucumberFM::Config.new((config_parameters || {}))
46
+ end
47
+
48
+ def filter
49
+ @filter ||= CucumberFM::TagFilter.new(config.tags)
50
+ end
51
+
52
+ def aggregate
53
+ unless patterns_for_aggregator.empty?
54
+ @raport ||= CucumberFM::Aggregator.new(self, patterns_for_aggregator).collection
55
+ end
56
+ end
57
+
58
+ def prefix
59
+ config.dir.empty? ? path : File.join(path, config.dir)
60
+ end
61
+
62
+ def commit_change_on(feature)
63
+ # use info to notify user
64
+ # @info = 'aaaa'
65
+ add_to_index(feature)
66
+ repo.commit_index("spec-update: #{feature.filename}")
67
+ end
68
+
69
+ def remove_file_from_repo(relative_path)
70
+ `cd #{repo_path} && git rm #{relative_path}`
71
+ end
72
+
73
+ def send_to_remote
74
+ push_to_remote
75
+ end
76
+
77
+ private
78
+
79
+ def add_to_index(feature)
80
+ # WTF - why this is not works
81
+ # repo.add(feature.path)
82
+ `cd #{repo_path} && git add #{feature.path}`
83
+ end
84
+
85
+ # TODO cleanup
86
+ def push_to_remote
87
+ # WTF - why this is not works
88
+ # git.push({}, repo_remote_name, "#{repo_current_branch}:#{repo_remote_branch_name}")
89
+ if capistrano_branch_name
90
+ `cd #{repo_path} && git push #{repo_remote_name} #{repo_current_branch}:#{capistrano_branch_name}`
91
+ elsif last_stories_branch_name
92
+ begin
93
+ response = `cd #{repo_path} && git push #{repo_remote_name} #{repo_current_branch}:#{last_stories_branch_name}`
94
+ throw :not_fast_forward if response =~ /non\-fast\-forward/
95
+ rescue => e
96
+ `cd #{repo_path} && git push #{repo_remote_name} #{repo_current_branch}:#{new_branch_name}`
97
+ end
98
+ else
99
+ `cd #{repo_path} && git push #{repo_remote_name} #{repo_current_branch}:#{new_branch_name}`
100
+ end
101
+ end
102
+
103
+ def scan_features
104
+ all_features.collect { |feature| feature if filter.pass?(feature.tags_all)}.compact
105
+ end
106
+
107
+ def all_features
108
+ @all_features ||= scan_all_features
109
+ end
110
+
111
+ def scan_all_features
112
+ features = []
113
+ Dir.glob("#{prefix}/**/*.feature").each do |full_path|
114
+ feature = CucumberFM::Feature.new(full_path, self)
115
+ features.push(feature)
116
+ end
117
+ features
118
+ end
119
+
120
+ def repo_relative_path(path)
121
+ path.gsub(repo_path, '').gsub(/^\//, '')
122
+ end
123
+
124
+ def repo
125
+ @repo ||= Repo.new(repo_path)
126
+ end
127
+
128
+ def git
129
+ repo.git
130
+ end
131
+
132
+ def repo_current_branch
133
+ repo.head.name
134
+ end
135
+
136
+ def repo_remote_name
137
+ repo.remote_list.first
138
+ end
139
+
140
+ def last_stories_branch_name
141
+ repo.remotes.map(& :name).collect { |name| /stories_\d+/.match(name) }.compact.map(& :to_s).sort.last
142
+ end
143
+
144
+ def capistrano_branch_name
145
+ "stories_#{timestamp_capistrano}" if timestamp_capistrano
146
+ end
147
+
148
+ def timestamp_capistrano
149
+ pattern = /\d{14}$/
150
+ if defined?(Rails)
151
+ pattern.match(Rails.root.to_s)
152
+ end
153
+ end
154
+
155
+ def new_branch_name
156
+ "stories_#{Time.now.strftime('%Y%m%d%H%M%S')}"
157
+ end
158
+
159
+ def patterns_for_aggregator
160
+ config.aggregate.map { |label|
161
+ CucumberFM::FeatureElement::Component::Tags::PATTERN[label.to_sym] unless label.blank?
162
+ }.compact
163
+ end
164
+ end
@@ -0,0 +1,75 @@
1
+ $:.unshift File.dirname(__FILE__) # For use/testing when no gem is installed
2
+
3
+ # core
4
+ require 'fileutils'
5
+ require 'time'
6
+
7
+ # stdlib
8
+ require 'timeout'
9
+ require 'logger'
10
+ require 'digest/sha1'
11
+
12
+
13
+ if defined? RUBY_ENGINE && RUBY_ENGINE == 'jruby'
14
+ require 'open3'
15
+ elsif RUBY_PLATFORM.downcase =~ /mswin(?!ce)|mingw|bccwin/
16
+ require 'win32/open3'
17
+ else
18
+ require 'open3_detach'
19
+ end
20
+
21
+ # third party
22
+ require 'rubygems'
23
+ begin
24
+ gem "mime-types", ">=0"
25
+ require 'mime/types'
26
+ rescue Gem::LoadError => e
27
+ puts "WARNING: Gem LoadError: #{e.message}"
28
+ end
29
+
30
+ # ruby 1.9 compatibility
31
+ require 'grit/ruby1.9'
32
+
33
+ # internal requires
34
+ require 'grit/lazy'
35
+ require 'grit/errors'
36
+ require 'grit/git-ruby'
37
+ require 'grit/git' unless defined? Grit::Git
38
+ require 'grit/ref'
39
+ require 'grit/tag'
40
+ require 'grit/commit'
41
+ require 'grit/commit_stats'
42
+ require 'grit/tree'
43
+ require 'grit/blob'
44
+ require 'grit/actor'
45
+ require 'grit/diff'
46
+ require 'grit/config'
47
+ require 'grit/repo'
48
+ require 'grit/index'
49
+ require 'grit/status'
50
+ require 'grit/submodule'
51
+ require 'grit/blame'
52
+ require 'grit/merge'
53
+
54
+
55
+ module Grit
56
+ class << self
57
+ # Set +debug+ to true to log all git calls and responses
58
+ attr_accessor :debug
59
+ attr_accessor :use_git_ruby
60
+ # The standard +logger+ for debugging git calls - this defaults to a plain STDOUT logger
61
+ attr_accessor :logger
62
+ def log(str)
63
+ logger.debug { str }
64
+ end
65
+ end
66
+ self.debug = false
67
+ self.use_git_ruby = true
68
+
69
+ @logger ||= ::Logger.new(STDOUT)
70
+
71
+ def self.version
72
+ yml = YAML.load(File.read(File.join(File.dirname(__FILE__), *%w[.. VERSION.yml])))
73
+ "#{yml[:major]}.#{yml[:minor]}.#{yml[:patch]}"
74
+ end
75
+ end