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,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