mattwynne-cucover 0.0.4 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (176) hide show
  1. data/.gitignore +6 -0
  2. data/Licence.txt +22 -0
  3. data/README.markdown +23 -7
  4. data/Rakefile +86 -0
  5. data/VERSION +1 -0
  6. data/bin/cucover +1 -1
  7. data/cucover.gemspec +305 -0
  8. data/examples/self_test/rails/.gitignore +2 -0
  9. data/examples/self_test/rails/README +256 -0
  10. data/examples/self_test/rails/Rakefile +10 -0
  11. data/examples/self_test/rails/app/controllers/application.rb +2 -0
  12. data/examples/self_test/rails/app/controllers/widgets_controller.rb +2 -0
  13. data/examples/self_test/rails/app/views/widgets/index.html.erb +1 -0
  14. data/examples/self_test/rails/config/boot.rb +109 -0
  15. data/examples/self_test/rails/config/database.yml +19 -0
  16. data/examples/self_test/rails/config/environment.rb +68 -0
  17. data/examples/self_test/rails/config/environments/development.rb +17 -0
  18. data/examples/self_test/rails/config/environments/production.rb +22 -0
  19. data/examples/self_test/rails/config/environments/test.rb +22 -0
  20. data/examples/self_test/rails/config/routes.rb +43 -0
  21. data/examples/self_test/rails/db/test.sqlite3 +0 -0
  22. data/examples/self_test/rails/features/see_widgets.feature +7 -0
  23. data/examples/self_test/rails/features/step_definitions/env.rb +0 -0
  24. data/examples/self_test/rails/features/step_definitions/webrat_steps.rb +7 -0
  25. data/examples/self_test/rails/features/support/env.rb +17 -0
  26. data/examples/self_test/rails/lib/tasks/cucumber.rake +15 -0
  27. data/examples/self_test/rails/public/404.html +30 -0
  28. data/examples/self_test/rails/public/422.html +30 -0
  29. data/examples/self_test/rails/public/500.html +30 -0
  30. data/examples/self_test/rails/public/dispatch.cgi +10 -0
  31. data/examples/self_test/rails/public/dispatch.fcgi +24 -0
  32. data/examples/self_test/rails/public/dispatch.rb +10 -0
  33. data/examples/self_test/rails/public/favicon.ico +0 -0
  34. data/examples/self_test/rails/public/images/rails.png +0 -0
  35. data/examples/self_test/rails/public/index.html +274 -0
  36. data/examples/self_test/rails/public/javascripts/application.js +2 -0
  37. data/examples/self_test/rails/public/javascripts/controls.js +963 -0
  38. data/examples/self_test/rails/public/javascripts/dragdrop.js +972 -0
  39. data/examples/self_test/rails/public/javascripts/effects.js +1120 -0
  40. data/examples/self_test/rails/public/javascripts/prototype.js +4225 -0
  41. data/examples/self_test/rails/public/robots.txt +5 -0
  42. data/examples/self_test/rails/script/about +4 -0
  43. data/examples/self_test/rails/script/console +3 -0
  44. data/examples/self_test/rails/script/cucumber +8 -0
  45. data/examples/self_test/rails/script/dbconsole +3 -0
  46. data/examples/self_test/rails/script/destroy +3 -0
  47. data/examples/self_test/rails/script/generate +3 -0
  48. data/examples/self_test/rails/script/performance/benchmarker +3 -0
  49. data/examples/self_test/rails/script/performance/profiler +3 -0
  50. data/examples/self_test/rails/script/performance/request +3 -0
  51. data/examples/self_test/rails/script/plugin +3 -0
  52. data/examples/self_test/rails/script/process/inspector +3 -0
  53. data/examples/self_test/rails/script/process/reaper +3 -0
  54. data/examples/self_test/rails/script/process/spawner +3 -0
  55. data/examples/self_test/rails/script/runner +3 -0
  56. data/examples/self_test/rails/script/server +3 -0
  57. data/examples/self_test/rails/vendor/gems/webrat-0.4.4/.specification +148 -0
  58. data/examples/self_test/rails/vendor/gems/webrat-0.4.4/History.txt +324 -0
  59. data/examples/self_test/rails/vendor/gems/webrat-0.4.4/MIT-LICENSE.txt +19 -0
  60. data/examples/self_test/rails/vendor/gems/webrat-0.4.4/README.rdoc +85 -0
  61. data/examples/self_test/rails/vendor/gems/webrat-0.4.4/Rakefile +151 -0
  62. data/examples/self_test/rails/vendor/gems/webrat-0.4.4/install.rb +1 -0
  63. data/examples/self_test/rails/vendor/gems/webrat-0.4.4/lib/webrat/core/configuration.rb +98 -0
  64. data/examples/self_test/rails/vendor/gems/webrat-0.4.4/lib/webrat/core/elements/area.rb +31 -0
  65. data/examples/self_test/rails/vendor/gems/webrat-0.4.4/lib/webrat/core/elements/element.rb +33 -0
  66. data/examples/self_test/rails/vendor/gems/webrat-0.4.4/lib/webrat/core/elements/field.rb +403 -0
  67. data/examples/self_test/rails/vendor/gems/webrat-0.4.4/lib/webrat/core/elements/form.rb +103 -0
  68. data/examples/self_test/rails/vendor/gems/webrat-0.4.4/lib/webrat/core/elements/label.rb +31 -0
  69. data/examples/self_test/rails/vendor/gems/webrat-0.4.4/lib/webrat/core/elements/link.rb +92 -0
  70. data/examples/self_test/rails/vendor/gems/webrat-0.4.4/lib/webrat/core/elements/select_option.rb +35 -0
  71. data/examples/self_test/rails/vendor/gems/webrat-0.4.4/lib/webrat/core/locators/area_locator.rb +38 -0
  72. data/examples/self_test/rails/vendor/gems/webrat-0.4.4/lib/webrat/core/locators/button_locator.rb +54 -0
  73. data/examples/self_test/rails/vendor/gems/webrat-0.4.4/lib/webrat/core/locators/field_by_id_locator.rb +37 -0
  74. data/examples/self_test/rails/vendor/gems/webrat-0.4.4/lib/webrat/core/locators/field_labeled_locator.rb +56 -0
  75. data/examples/self_test/rails/vendor/gems/webrat-0.4.4/lib/webrat/core/locators/field_locator.rb +25 -0
  76. data/examples/self_test/rails/vendor/gems/webrat-0.4.4/lib/webrat/core/locators/field_named_locator.rb +41 -0
  77. data/examples/self_test/rails/vendor/gems/webrat-0.4.4/lib/webrat/core/locators/form_locator.rb +19 -0
  78. data/examples/self_test/rails/vendor/gems/webrat-0.4.4/lib/webrat/core/locators/label_locator.rb +34 -0
  79. data/examples/self_test/rails/vendor/gems/webrat-0.4.4/lib/webrat/core/locators/link_locator.rb +66 -0
  80. data/examples/self_test/rails/vendor/gems/webrat-0.4.4/lib/webrat/core/locators/locator.rb +20 -0
  81. data/examples/self_test/rails/vendor/gems/webrat-0.4.4/lib/webrat/core/locators/select_option_locator.rb +59 -0
  82. data/examples/self_test/rails/vendor/gems/webrat-0.4.4/lib/webrat/core/locators.rb +20 -0
  83. data/examples/self_test/rails/vendor/gems/webrat-0.4.4/lib/webrat/core/logging.rb +21 -0
  84. data/examples/self_test/rails/vendor/gems/webrat-0.4.4/lib/webrat/core/matchers/have_content.rb +73 -0
  85. data/examples/self_test/rails/vendor/gems/webrat-0.4.4/lib/webrat/core/matchers/have_selector.rb +74 -0
  86. data/examples/self_test/rails/vendor/gems/webrat-0.4.4/lib/webrat/core/matchers/have_tag.rb +21 -0
  87. data/examples/self_test/rails/vendor/gems/webrat-0.4.4/lib/webrat/core/matchers/have_xpath.rb +147 -0
  88. data/examples/self_test/rails/vendor/gems/webrat-0.4.4/lib/webrat/core/matchers.rb +4 -0
  89. data/examples/self_test/rails/vendor/gems/webrat-0.4.4/lib/webrat/core/methods.rb +61 -0
  90. data/examples/self_test/rails/vendor/gems/webrat-0.4.4/lib/webrat/core/mime.rb +29 -0
  91. data/examples/self_test/rails/vendor/gems/webrat-0.4.4/lib/webrat/core/save_and_open_page.rb +50 -0
  92. data/examples/self_test/rails/vendor/gems/webrat-0.4.4/lib/webrat/core/scope.rb +350 -0
  93. data/examples/self_test/rails/vendor/gems/webrat-0.4.4/lib/webrat/core/session.rb +281 -0
  94. data/examples/self_test/rails/vendor/gems/webrat-0.4.4/lib/webrat/core/xml/hpricot.rb +19 -0
  95. data/examples/self_test/rails/vendor/gems/webrat-0.4.4/lib/webrat/core/xml/nokogiri.rb +76 -0
  96. data/examples/self_test/rails/vendor/gems/webrat-0.4.4/lib/webrat/core/xml/rexml.rb +24 -0
  97. data/examples/self_test/rails/vendor/gems/webrat-0.4.4/lib/webrat/core/xml.rb +115 -0
  98. data/examples/self_test/rails/vendor/gems/webrat-0.4.4/lib/webrat/core.rb +14 -0
  99. data/examples/self_test/rails/vendor/gems/webrat-0.4.4/lib/webrat/core_extensions/blank.rb +58 -0
  100. data/examples/self_test/rails/vendor/gems/webrat-0.4.4/lib/webrat/core_extensions/deprecate.rb +8 -0
  101. data/examples/self_test/rails/vendor/gems/webrat-0.4.4/lib/webrat/core_extensions/detect_mapped.rb +12 -0
  102. data/examples/self_test/rails/vendor/gems/webrat-0.4.4/lib/webrat/core_extensions/meta_class.rb +6 -0
  103. data/examples/self_test/rails/vendor/gems/webrat-0.4.4/lib/webrat/core_extensions/nil_to_param.rb +5 -0
  104. data/examples/self_test/rails/vendor/gems/webrat-0.4.4/lib/webrat/core_extensions/tcp_socket.rb +27 -0
  105. data/examples/self_test/rails/vendor/gems/webrat-0.4.4/lib/webrat/mechanize.rb +74 -0
  106. data/examples/self_test/rails/vendor/gems/webrat-0.4.4/lib/webrat/merb.rb +9 -0
  107. data/examples/self_test/rails/vendor/gems/webrat-0.4.4/lib/webrat/merb_session.rb +65 -0
  108. data/examples/self_test/rails/vendor/gems/webrat-0.4.4/lib/webrat/rack.rb +24 -0
  109. data/examples/self_test/rails/vendor/gems/webrat-0.4.4/lib/webrat/rails.rb +105 -0
  110. data/examples/self_test/rails/vendor/gems/webrat-0.4.4/lib/webrat/rspec-rails.rb +13 -0
  111. data/examples/self_test/rails/vendor/gems/webrat-0.4.4/lib/webrat/selenium/application_server.rb +71 -0
  112. data/examples/self_test/rails/vendor/gems/webrat-0.4.4/lib/webrat/selenium/location_strategy_javascript/button.js +12 -0
  113. data/examples/self_test/rails/vendor/gems/webrat-0.4.4/lib/webrat/selenium/location_strategy_javascript/label.js +16 -0
  114. data/examples/self_test/rails/vendor/gems/webrat-0.4.4/lib/webrat/selenium/location_strategy_javascript/webrat.js +5 -0
  115. data/examples/self_test/rails/vendor/gems/webrat-0.4.4/lib/webrat/selenium/location_strategy_javascript/webratlink.js +9 -0
  116. data/examples/self_test/rails/vendor/gems/webrat-0.4.4/lib/webrat/selenium/location_strategy_javascript/webratlinkwithin.js +15 -0
  117. data/examples/self_test/rails/vendor/gems/webrat-0.4.4/lib/webrat/selenium/location_strategy_javascript/webratselectwithoption.js +5 -0
  118. data/examples/self_test/rails/vendor/gems/webrat-0.4.4/lib/webrat/selenium/matchers/have_content.rb +66 -0
  119. data/examples/self_test/rails/vendor/gems/webrat-0.4.4/lib/webrat/selenium/matchers/have_selector.rb +49 -0
  120. data/examples/self_test/rails/vendor/gems/webrat-0.4.4/lib/webrat/selenium/matchers/have_tag.rb +72 -0
  121. data/examples/self_test/rails/vendor/gems/webrat-0.4.4/lib/webrat/selenium/matchers/have_xpath.rb +45 -0
  122. data/examples/self_test/rails/vendor/gems/webrat-0.4.4/lib/webrat/selenium/matchers.rb +4 -0
  123. data/examples/self_test/rails/vendor/gems/webrat-0.4.4/lib/webrat/selenium/merb_application_server.rb +48 -0
  124. data/examples/self_test/rails/vendor/gems/webrat-0.4.4/lib/webrat/selenium/rails_application_server.rb +42 -0
  125. data/examples/self_test/rails/vendor/gems/webrat-0.4.4/lib/webrat/selenium/selenium_extensions.js +6 -0
  126. data/examples/self_test/rails/vendor/gems/webrat-0.4.4/lib/webrat/selenium/selenium_rc_server.rb +80 -0
  127. data/examples/self_test/rails/vendor/gems/webrat-0.4.4/lib/webrat/selenium/selenium_session.rb +241 -0
  128. data/examples/self_test/rails/vendor/gems/webrat-0.4.4/lib/webrat/selenium/sinatra_application_server.rb +35 -0
  129. data/examples/self_test/rails/vendor/gems/webrat-0.4.4/lib/webrat/selenium.rb +80 -0
  130. data/examples/self_test/rails/vendor/gems/webrat-0.4.4/lib/webrat/sinatra.rb +44 -0
  131. data/examples/self_test/rails/vendor/gems/webrat-0.4.4/lib/webrat.rb +34 -0
  132. data/examples/self_test/rails/vendor/gems/webrat-0.4.4/vendor/selenium-server.jar +0 -0
  133. data/examples/self_test/simple/features/call_foo.feature +4 -0
  134. data/examples/self_test/simple/features/call_foo_and_bar_together.feature +5 -0
  135. data/examples/self_test/simple/features/call_foo_from_background_then_bar.feature +7 -0
  136. data/examples/self_test/simple/features/call_foo_from_background_then_bar_then_baz.feature +10 -0
  137. data/examples/self_test/simple/features/call_foo_then_bar.feature +7 -0
  138. data/examples/self_test/simple/features/call_foo_then_bar_from_scenario_outline_examples.feature +9 -0
  139. data/examples/self_test/simple/features/fail.feature +10 -0
  140. data/examples/self_test/simple/features/step_definitions/main_steps.rb +15 -0
  141. data/examples/self_test/simple/lib/bar.rb +5 -0
  142. data/examples/self_test/simple/lib/baz.rb +5 -0
  143. data/examples/self_test/simple/lib/foo.rb +9 -0
  144. data/features/call_foo.feature +0 -0
  145. data/features/coverage_of.feature +44 -0
  146. data/features/fail.feature +54 -0
  147. data/features/lazy_run.feature +79 -0
  148. data/features/lazy_run_per_scenario.feature +113 -0
  149. data/features/lazy_run_per_scenario_outline_example.feature +31 -0
  150. data/features/lazy_run_triggered_by_rails_view_change.feature +42 -0
  151. data/features/run.feature +25 -0
  152. data/features/show_recordings.feature +28 -0
  153. data/features/step_definitions/main_steps.rb +36 -0
  154. data/features/support/env.rb +48 -0
  155. data/lib/cucover/cli.rb +26 -0
  156. data/lib/cucover/cli_commands/coverage_of.rb +36 -0
  157. data/lib/cucover/cli_commands/cucumber.rb +25 -0
  158. data/lib/cucover/cli_commands/show_recordings.rb +29 -0
  159. data/lib/cucover/controller.rb +28 -0
  160. data/lib/cucover/cucumber_hooks.rb +29 -0
  161. data/lib/cucover/logging_config.rb +16 -0
  162. data/lib/cucover/monkey.rb +14 -0
  163. data/lib/cucover/rails.rb +20 -0
  164. data/lib/cucover/recorder.rb +37 -0
  165. data/lib/cucover/recording/covered_file.rb +53 -0
  166. data/lib/cucover/recording.rb +64 -0
  167. data/lib/cucover/store.rb +70 -0
  168. data/lib/cucover.rb +35 -212
  169. data/lib/dependencies.rb +10 -0
  170. data/spec/cucover/cli_spec.rb +31 -0
  171. data/spec/cucover/controller_spec.rb +16 -0
  172. data/spec/cucover/recording/covered_file_spec.rb +20 -0
  173. data/spec/cucover/store_spec.rb +28 -0
  174. data/spec/spec_helper.rb +7 -0
  175. data/tmp/.gitignore +0 -0
  176. metadata +304 -21
@@ -0,0 +1,53 @@
1
+ module Cucover
2
+ class Recording::CoveredFile
3
+ attr_reader :file
4
+
5
+ def initialize(full_filename, rcov_marked_info, recording)
6
+ @full_filename = full_filename
7
+ @rcov_marked_info = rcov_marked_info
8
+ @recording = recording
9
+ @file = File.expand_path(full_filename).gsub(/^#{Dir.pwd}\//, '')
10
+
11
+ extend HasLineNumberDetail if @rcov_marked_info
12
+ end
13
+
14
+ def dirty?
15
+ Cucover.logger.debug("#{file} last modified at #{File.mtime(@full_filename)}")
16
+ Cucover.logger.debug("#{file} recording started at #{@recording.start_time}")
17
+ result = File.mtime(@full_filename).to_i >= @recording.start_time.to_i
18
+ Cucover.logger.debug("verdict: #{(result ? "dirty" : "not dirty")}")
19
+ result
20
+ end
21
+
22
+ def covers_line?(line_number)
23
+ covered_lines.include?(line_number)
24
+ end
25
+
26
+ def ==(other)
27
+ other == file
28
+ end
29
+
30
+ def to_s
31
+ "#{file}:#{covered_lines.join(':')}"
32
+ end
33
+
34
+ private
35
+
36
+ def covered_lines
37
+ ['<unknown lines>']
38
+ end
39
+
40
+ module HasLineNumberDetail
41
+ def covered_lines
42
+ return @covered_lines if @covered_lines
43
+
44
+ @covered_lines = []
45
+ @rcov_marked_info.each_with_index do |covered, index|
46
+ line_number = index + 1
47
+ @covered_lines << line_number if covered
48
+ end
49
+ @covered_lines
50
+ end
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,64 @@
1
+ module Cucover
2
+ class Recording < Struct.new(
3
+ :file_colon_line,
4
+ :exception,
5
+ :additional_covered_files,
6
+ :analyzer,
7
+ :start_time, :end_time)
8
+
9
+ def feature_filename
10
+ file_colon_line.split(':').first
11
+ end
12
+
13
+ def covers_file?(source_file)
14
+ covered_files.include?(source_file)
15
+ end
16
+
17
+ def covers_line?(source_file, line_number)
18
+ covered_files.detect{ |f| f.file == source_file }.covers_line?(line_number)
19
+ end
20
+
21
+ def covered_files
22
+ @covered_files ||= analyzed_covered_files + additional_covered_files
23
+ end
24
+
25
+ def failed?
26
+ !!exception
27
+ end
28
+
29
+ private
30
+
31
+ def additional_covered_files
32
+ super.map do |filename|
33
+ CoveredFile.new(filename, nil, self)
34
+ end
35
+ end
36
+
37
+ def analyzed_covered_files
38
+ filtered_analyzed_files.map do |filename|
39
+ lines, marked_info, count_info = analyzer.data(filename)
40
+ CoveredFile.new(filename, marked_info, self)
41
+ end
42
+ end
43
+
44
+ def boring?(file)
45
+ [
46
+ /gem/,
47
+ /vendor/,
48
+ /lib\/ruby/,
49
+ /cucover/
50
+ ].any? do |expression|
51
+ file.match expression
52
+ end
53
+ end
54
+
55
+ def filtered_analyzed_files
56
+ analyzer.analyzed_files.reject{ |f| boring?(f) }
57
+ end
58
+
59
+ def normalized_files
60
+ cleaned_analyzed_files + additional_covered_files
61
+ end
62
+ end
63
+ end
64
+ require 'cucover/recording/covered_file'
@@ -0,0 +1,70 @@
1
+ module Cucover
2
+ class Store
3
+ def initialize(cache = DiskCache.new)
4
+ @cache = cache
5
+ end
6
+
7
+ def latest_recordings
8
+ recordings.keys.map{ |file_colon_line| latest_recording(file_colon_line) }
9
+ end
10
+
11
+ def latest_recording(file_colon_line)
12
+ recordings[file_colon_line].sort{ |x, y| x.end_time <=> y.end_time }.last
13
+ end
14
+
15
+ def keep!(recording_data)
16
+ Cucover.logger.debug("Storing recording of #{recording_data.file_colon_line}")
17
+ recordings[recording_data.file_colon_line] << recording_data
18
+ @cache.save(@recordings)
19
+ end
20
+
21
+ def recordings_covering(source_file)
22
+ latest_recordings.select { |r| r.covers_file?(source_file) }
23
+ end
24
+
25
+ def recordings
26
+ ensure_recordings_loaded!
27
+ @recordings
28
+ end
29
+
30
+ private
31
+
32
+ def ensure_recordings_loaded!
33
+ return if @recordings
34
+
35
+ if @recordings = @cache.load
36
+ Cucover.logger.debug("Loaded #{@recordings.length} recording(s)")
37
+ else
38
+ Cucover.logger.debug("Starting with no existing coverage data.")
39
+ @recordings = Recordings.new
40
+ end
41
+ end
42
+
43
+ class Recordings < Hash
44
+ def [](key)
45
+ self[key] = [] if super.nil?
46
+ super
47
+ end
48
+ end
49
+
50
+ class DiskCache
51
+ def save(recordings)
52
+ Cucover.logger.debug("Saving #{recordings.length} recording(s) to #{data_file}")
53
+ File.open(data_file, 'w') { |f| f.puts Marshal.dump(recordings) }
54
+ end
55
+
56
+ def load
57
+ return unless File.exists?(data_file)
58
+
59
+ Cucover.logger.debug("Reading existing coverage data from #{data_file}")
60
+ File.open(data_file) { |f| Marshal.load(f) }
61
+ end
62
+
63
+ private
64
+
65
+ def data_file
66
+ Dir.pwd + '/cucover.data'
67
+ end
68
+ end
69
+ end
70
+ end
data/lib/cucover.rb CHANGED
@@ -1,237 +1,60 @@
1
-
2
- require 'rubygems'
3
-
4
- gem 'cucumber', '>=0.3'
5
- require 'cucumber'
6
-
7
- gem 'spicycode-rcov', '>=0.8.1.5.0'
8
- require 'rcov'
9
- require 'spec'
10
-
11
1
  $:.unshift(File.dirname(__FILE__))
2
+ require 'dependencies'
3
+ require 'cucover/logging_config'
4
+ require 'cucover/cli_commands/coverage_of'
5
+ require 'cucover/cli_commands/cucumber'
6
+ require 'cucover/cli_commands/show_recordings'
7
+ require 'cucover/controller'
8
+ require 'cucover/cli'
12
9
  require 'cucover/monkey'
13
10
  require 'cucover/rails'
11
+ require 'cucover/recording'
12
+ require 'cucover/recorder'
13
+ require 'cucover/store'
14
14
 
15
15
  module Cucover
16
-
17
- class TestRun
18
- def initialize(feature_file, visitor)
19
- @feature_file, @visitor = feature_file, visitor
20
- end
21
-
22
- def record(source_file)
23
- additional_covered_files << source_file
24
- end
25
-
26
- def fail!
27
- @failed = true
28
- end
29
-
30
- def watch
31
- announce_skip unless may_execute?
32
-
33
- analyzer.run_hooked do
34
- yield
35
- end
36
-
37
- record(@feature_file)
38
- source_files_cache.save analyzed_files
39
- status_cache.record(status)
40
- end
41
-
42
- def may_execute?
43
- dirty? || failed_on_last_run?
44
- end
45
-
46
- private
47
-
48
- def status
49
- @failed ? :failed : :passed
50
- end
51
-
52
- def additional_covered_files
53
- @additional_covered_files ||= []
54
- end
55
-
56
- def announce_skip
57
- messages = []
58
- messages << "Cucover - Skipping clean feature"
59
- messages << "Last run status: #{status_cache.last_run_status}"
60
- @visitor.announce messages.flatten.map{ |m| "[ #{m.rstrip} ]"}.join("\n")
61
- end
62
-
63
- def failed_on_last_run?
64
- return false unless status_cache.exists?
65
- status_cache.last_run_status == "failed"
66
- end
67
-
68
- def dirty?
69
- return true unless source_files_cache.exists?
70
- source_files_cache.any_dirty_files?
16
+ class << self
17
+ def logger
18
+ Logging::Logger['Cucover']
71
19
  end
72
20
 
73
- def source_files_cache
74
- @source_files_cache ||= SourceFileCache.new(@feature_file)
21
+ def should_execute?(scenario_or_table_row)
22
+ controller(scenario_or_table_row).should_execute?
75
23
  end
76
24
 
77
- def status_cache
78
- @status_cache ||= StatusCache.new(@feature_file)
79
- end
25
+ def start_recording!(scenario_or_table_row)
26
+ raise("Already recording. Please call stop first.") if recording?
80
27
 
81
- def source_files
82
- analyzed_files
28
+ @current_recorder = Recorder.new(scenario_or_table_row)
29
+ @current_recorder.start!
30
+ record_file(scenario_or_table_row.file_colon_line.split(':').first) # TODO: clean this by extending the feature element
83
31
  end
84
-
85
- def analyzed_files
86
- normalized_files.reject{ |f| boring?(f) }.sort
87
- end
88
-
89
- def normalized_files
90
- (analyzer.analyzed_files + additional_covered_files.uniq).map{ |f| File.expand_path(f).gsub(/^#{Dir.pwd}\//, '') }
91
- end
92
-
93
- def boring?(file)
94
- return false
95
- (file.match /gem/) || (file.match /vendor/) || (file.match /lib\/ruby/)
96
- end
97
-
98
- def analyzer
99
- @analyzer ||= Rcov::CodeCoverageAnalyzer.new
100
- end
101
-
102
- end
103
-
104
- class << self
105
- def start_test(test, visitor)
106
- @current_test = TestRun.new(test.file, visitor)
107
32
 
108
- @current_test.watch do
109
- yield
110
- end
111
- end
112
-
113
- def fail_current_test!
114
- current_test.fail!
115
- end
116
-
117
- def record(source_file)
118
- current_test.record(source_file)
119
- end
120
-
121
- def should_skip?
122
- not current_test.may_execute?
123
- end
124
-
125
- private
126
-
127
- def current_test
128
- @current_test or raise("You need to start the a test first!")
129
- end
130
- end
131
-
132
- class Cache
133
- def initialize(feature_file)
134
- @feature_file = feature_file
135
- end
136
-
137
- def exists?
138
- File.exist?(cache_filename)
139
- end
140
-
141
- def cache_filename
142
- @feature_file.gsub /([^\/]*\.feature)/, '.coverage/\1'
143
- end
144
-
145
- def time
146
- File.mtime(cache_filename)
147
- end
33
+ def stop_recording!
34
+ return unless recording?
148
35
 
149
- def write_to_cache
150
- FileUtils.mkdir_p File.dirname(cache_filename)
151
- File.open(cache_filename, "w") do |file|
152
- yield file
153
- end
154
- end
155
- end
156
-
157
- class StatusCache < Cache
158
- def last_run_status
159
- File.open(cache_filename, "r") do |file|
160
- file.each_line do |line|
161
- return line.strip
162
- end
163
- end
36
+ @current_recorder.stop!
37
+ store.keep!(@current_recorder.recording)
38
+ @current_recorder = nil
164
39
  end
165
40
 
166
- def record(status)
167
- write_to_cache do |file|
168
- file.puts status
169
- end
170
- end
171
-
172
- private
173
-
174
- def cache_filename
175
- super + '.status'
41
+ def record_file(source_file)
42
+ Cucover.logger.debug("Recording extra source file #{source_file}")
43
+ @current_recorder.record_file!(source_file)
176
44
  end
177
- end
178
45
 
179
- class SourceFileCache < Cache
180
- def save(analyzed_files)
181
- write_to_cache do |file|
182
- file.puts analyzed_files
183
- end
184
- end
185
-
186
- def any_dirty_files?
187
- not dirty_files.empty?
188
- end
189
-
190
- def source_files
191
- result = []
192
- File.open(cache_filename, "r") do |file|
193
- file.each_line do |line|
194
- result.push line
195
- end
196
- end
197
- result
198
- end
199
-
200
46
  private
201
-
202
- def dirty_files
203
- source_files.select do |source_file|
204
- File.mtime(source_file.strip) >= time
205
- end
206
- end
207
47
 
208
- end
209
-
210
- module LazyStepInvocation
211
- def accept(visitor)
212
- skip_invoke! if Cucover.should_skip?
213
- super
48
+ def controller(scenario_or_table_row)
49
+ Controller.new(scenario_or_table_row.file_colon_line, store)
214
50
  end
215
51
 
216
- def failed(exception, clear_backtrace)
217
- Cucover.fail_current_test!
218
- super
52
+ def recording?
53
+ !!@current_recorder
219
54
  end
220
- end
221
55
 
222
- module LazyFeature
223
- def accept(visitor)
224
- Cucover.start_test(self, visitor) do
225
- super
226
- end
56
+ def store
57
+ @store ||= Store.new
227
58
  end
228
-
229
- end
230
- end
231
-
232
- Cucover::Monkey.extend_every Cucumber::Ast::Feature => Cucover::LazyFeature
233
- Cucover::Monkey.extend_every Cucumber::Ast::StepInvocation => Cucover::LazyStepInvocation
234
-
235
- Before do
236
- Cucover::Rails.patch_if_necessary
59
+ end
237
60
  end
@@ -0,0 +1,10 @@
1
+ require 'rubygems'
2
+
3
+ gem 'cucumber', '>=0.3.1'
4
+ require 'cucumber'
5
+
6
+ gem 'spicycode-rcov', '>=0.8.1.5.0'
7
+ require 'rcov'
8
+ require 'spec'
9
+
10
+ require 'logging'
@@ -0,0 +1,31 @@
1
+ require File.dirname(__FILE__) + '/../spec_helper'
2
+
3
+ describe Cucover::Cli do
4
+ describe "given a --coverage-of command" do
5
+ before(:each) do
6
+ @args = ['--coverage-of', 'lib/foo.rb']
7
+ end
8
+
9
+ it "should create a CoverageOf command object and execute it" do
10
+ Cucover::CliCommands::CoverageOf.should_receive(:new).with(['--coverage-of', 'lib/foo.rb']).and_return(command = mock('command'))
11
+ command.should_receive(:execute)
12
+ cli = Cucover::Cli.new(@args)
13
+ cli.start
14
+ end
15
+ end
16
+
17
+ describe "given arguments for Cucumber" do
18
+ before(:each) do
19
+ @args = ['--', 'c', 'd']
20
+ end
21
+ it "should pass the arguments after the -- to cucumber" do
22
+ cli = Cucover::Cli.new(@args)
23
+
24
+ Kernel.stub!(:load).with(Cucumber::BINARY) do
25
+ ARGV.should == ['c', 'd']
26
+ end
27
+
28
+ cli.start
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,16 @@
1
+ require File.dirname(__FILE__) + '/../spec_helper'
2
+
3
+ module Cucover
4
+ describe Controller do
5
+ describe "#should_execute?" do
6
+ before(:each) do
7
+ @store = mock(Store)
8
+ @example_id = 'foo.feature:123'
9
+ end
10
+ it "when no previous recording exists, it should always return true" do
11
+ @store.should_receive(:latest_recording).with(@example_id).and_return(nil)
12
+ Controller.new(@example_id, @store).should_execute?.should be_true
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,20 @@
1
+ require File.dirname(__FILE__) + '/../../spec_helper'
2
+
3
+ class Cucover::Recording
4
+ describe CoveredFile do
5
+ describe "with a marked info" do
6
+ before(:each) do
7
+ @recording = mock('Recording')
8
+ @covered_file = CoveredFile.new("foo.rb", [true, true, false], @recording)
9
+ end
10
+
11
+ it "should set the covered_lines correctly from the marked info" do
12
+ [1,2].each do |line_number|
13
+ @covered_file.covers_line?(line_number).should be_true
14
+ end
15
+
16
+ @covered_file.covers_line?(3).should be_false
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,28 @@
1
+ require File.dirname(__FILE__) + '/../spec_helper'
2
+
3
+ module Cucover
4
+ describe Store do
5
+ before(:each) do
6
+ @cache = mock('cache', :load => nil)
7
+ @store = Store.new(@cache)
8
+ end
9
+ describe "#latest_recording" do
10
+ it "should return nil if no recordings match the given identifier" do
11
+ @store.latest_recording('blah:234').should be_nil
12
+ end
13
+
14
+ describe "when multiple recordings match the given indentifier" do
15
+ before(:each) do
16
+ old_recording = mock('old data', :end_time => Time.now - 200)
17
+ @new_recording = mock('new data', :end_time => Time.now)
18
+ @cache.stub!(:load).and_return({
19
+ 'foo.feature:33' => [ old_recording, @new_recording ]
20
+ })
21
+ end
22
+ it "should return the one with the latest end_time" do
23
+ @store.latest_recording('foo.feature:33').should == @new_recording
24
+ end
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,7 @@
1
+ require 'rubygems'
2
+ gem 'rspec'
3
+ require 'spec'
4
+ require 'spec/mocks/framework'
5
+
6
+ $:.unshift(File.dirname(__FILE__) + '/../lib')
7
+ require 'cucover'
data/tmp/.gitignore ADDED
File without changes