cucumber-core 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (69) hide show
  1. checksums.yaml +7 -0
  2. data/.coveralls.yml +1 -0
  3. data/.rspec +1 -0
  4. data/.ruby-gemset +1 -0
  5. data/.travis.yml +16 -0
  6. data/Gemfile +2 -0
  7. data/LICENSE +20 -0
  8. data/README.md +9 -0
  9. data/Rakefile +24 -0
  10. data/cucumber-core.gemspec +32 -0
  11. data/lib/cucumber/core.rb +37 -0
  12. data/lib/cucumber/core/ast.rb +13 -0
  13. data/lib/cucumber/core/ast/background.rb +33 -0
  14. data/lib/cucumber/core/ast/comment.rb +17 -0
  15. data/lib/cucumber/core/ast/data_table.rb +326 -0
  16. data/lib/cucumber/core/ast/describes_itself.rb +16 -0
  17. data/lib/cucumber/core/ast/doc_string.rb +83 -0
  18. data/lib/cucumber/core/ast/empty_background.rb +12 -0
  19. data/lib/cucumber/core/ast/examples_table.rb +95 -0
  20. data/lib/cucumber/core/ast/feature.rb +62 -0
  21. data/lib/cucumber/core/ast/location.rb +140 -0
  22. data/lib/cucumber/core/ast/multiline_argument.rb +33 -0
  23. data/lib/cucumber/core/ast/names.rb +19 -0
  24. data/lib/cucumber/core/ast/outline_step.rb +51 -0
  25. data/lib/cucumber/core/ast/scenario.rb +43 -0
  26. data/lib/cucumber/core/ast/scenario_outline.rb +44 -0
  27. data/lib/cucumber/core/ast/step.rb +38 -0
  28. data/lib/cucumber/core/ast/tag.rb +14 -0
  29. data/lib/cucumber/core/compiler.rb +136 -0
  30. data/lib/cucumber/core/gherkin/ast_builder.rb +315 -0
  31. data/lib/cucumber/core/gherkin/document.rb +20 -0
  32. data/lib/cucumber/core/gherkin/parser.rb +45 -0
  33. data/lib/cucumber/core/gherkin/writer.rb +220 -0
  34. data/lib/cucumber/core/gherkin/writer/helpers.rb +178 -0
  35. data/lib/cucumber/core/platform.rb +30 -0
  36. data/lib/cucumber/core/test/case.rb +143 -0
  37. data/lib/cucumber/core/test/filters.rb +48 -0
  38. data/lib/cucumber/core/test/filters/tag_filter.rb +110 -0
  39. data/lib/cucumber/core/test/hook_compiler.rb +109 -0
  40. data/lib/cucumber/core/test/mapper.rb +56 -0
  41. data/lib/cucumber/core/test/mapping.rb +67 -0
  42. data/lib/cucumber/core/test/result.rb +191 -0
  43. data/lib/cucumber/core/test/runner.rb +149 -0
  44. data/lib/cucumber/core/test/step.rb +69 -0
  45. data/lib/cucumber/core/test/timer.rb +31 -0
  46. data/lib/cucumber/core/version.rb +9 -0
  47. data/lib/cucumber/initializer.rb +18 -0
  48. data/spec/capture_warnings.rb +68 -0
  49. data/spec/coverage.rb +10 -0
  50. data/spec/cucumber/core/ast/data_table_spec.rb +139 -0
  51. data/spec/cucumber/core/ast/doc_string_spec.rb +77 -0
  52. data/spec/cucumber/core/ast/examples_table_spec.rb +87 -0
  53. data/spec/cucumber/core/ast/location_spec.rb +105 -0
  54. data/spec/cucumber/core/ast/outline_step_spec.rb +77 -0
  55. data/spec/cucumber/core/ast/step_spec.rb +44 -0
  56. data/spec/cucumber/core/compiler_spec.rb +249 -0
  57. data/spec/cucumber/core/gherkin/parser_spec.rb +182 -0
  58. data/spec/cucumber/core/gherkin/writer_spec.rb +332 -0
  59. data/spec/cucumber/core/test/case_spec.rb +416 -0
  60. data/spec/cucumber/core/test/hook_compiler_spec.rb +78 -0
  61. data/spec/cucumber/core/test/mapper_spec.rb +68 -0
  62. data/spec/cucumber/core/test/mapping_spec.rb +103 -0
  63. data/spec/cucumber/core/test/result_spec.rb +178 -0
  64. data/spec/cucumber/core/test/runner_spec.rb +265 -0
  65. data/spec/cucumber/core/test/step_spec.rb +58 -0
  66. data/spec/cucumber/core/test/timer_spec.rb +13 -0
  67. data/spec/cucumber/core_spec.rb +419 -0
  68. data/spec/cucumber/initializer_spec.rb +49 -0
  69. metadata +221 -0
@@ -0,0 +1,30 @@
1
+ # Detect the platform we're running on so we can tweak behaviour
2
+ # in various places.
3
+ require 'rbconfig'
4
+
5
+ module Cucumber
6
+ unless defined?(Cucumber::VERSION)
7
+ VERSION = '2.0'
8
+ BINARY = File.expand_path(File.dirname(__FILE__) + '/../../bin/cucumber')
9
+ LIBDIR = File.expand_path(File.dirname(__FILE__) + '/../../lib')
10
+ JRUBY = defined?(JRUBY_VERSION)
11
+ IRONRUBY = defined?(RUBY_ENGINE) && RUBY_ENGINE == "ironruby"
12
+ WINDOWS = RbConfig::CONFIG['host_os'] =~ /mswin|mingw/
13
+ OS_X = RbConfig::CONFIG['host_os'] =~ /darwin/
14
+ WINDOWS_MRI = WINDOWS && !JRUBY && !IRONRUBY
15
+ RAILS = defined?(Rails)
16
+ RUBY_BINARY = File.join(RbConfig::CONFIG['bindir'], RbConfig::CONFIG['ruby_install_name'])
17
+ RUBY_2_0 = RUBY_VERSION =~ /^2\.0/
18
+ RUBY_1_9 = RUBY_VERSION =~ /^1\.9/
19
+
20
+ class << self
21
+ attr_accessor :use_full_backtrace
22
+
23
+ # @private
24
+ def file_mode(m, encoding="UTF-8")
25
+ "#{m}:#{encoding}"
26
+ end
27
+ end
28
+ self.use_full_backtrace = false
29
+ end
30
+ end
@@ -0,0 +1,143 @@
1
+ require 'cucumber/initializer'
2
+ require 'cucumber/core/test/result'
3
+
4
+ module Cucumber
5
+ module Core
6
+ module Test
7
+ class Case
8
+ include Cucumber.initializer(:test_steps, :source, :around_hooks)
9
+ attr_reader :source
10
+
11
+ def initialize(test_steps, source, around_hooks = [])
12
+ super(test_steps, source, around_hooks)
13
+ end
14
+
15
+ def describe_to(visitor, *args)
16
+ visitor.test_case(self, *args) do |child_visitor=visitor|
17
+ compose_around_hooks(child_visitor, *args) do
18
+ test_steps.each do |test_step|
19
+ test_step.describe_to(child_visitor, *args)
20
+ end
21
+ end
22
+ end
23
+ self
24
+ end
25
+
26
+ def describe_source_to(visitor, *args)
27
+ source.each do |node|
28
+ node.describe_to(visitor, *args)
29
+ end
30
+ self
31
+ end
32
+
33
+ def with_steps(test_steps)
34
+ self.class.new(test_steps, source, around_hooks)
35
+ end
36
+
37
+ def with_around_hooks(around_hooks)
38
+ self.class.new(test_steps, source, around_hooks)
39
+ end
40
+
41
+ def name
42
+ @name ||= NameBuilder.new(self).result
43
+ end
44
+
45
+ def tags
46
+ @tags ||= TagCollector.new(self).result
47
+ end
48
+
49
+ require 'gherkin/tag_expression'
50
+ def match_tags?(*expressions)
51
+ ::Gherkin::TagExpression.new(expressions.flatten).evaluate(tags.map {|t| ::Gherkin::Formatter::Model::Tag.new(t.name, t.line) })
52
+ end
53
+
54
+ def match_name?(name_regexp)
55
+ source.any? { |node| node.respond_to?(:name) && node.name =~ name_regexp }
56
+ end
57
+
58
+ def language
59
+ feature.language
60
+ end
61
+
62
+ def location
63
+ source.last.location
64
+ end
65
+
66
+ def match_locations?(queried_locations)
67
+ return true if source.any? { |s| s.match_locations?(queried_locations) }
68
+ test_steps.any? { |node| node.match_locations? queried_locations }
69
+ end
70
+
71
+ def inspect
72
+ "<#{self.class}: #{location}>"
73
+ end
74
+
75
+ private
76
+
77
+ def compose_around_hooks(visitor, *args, &block)
78
+ around_hooks.reverse.reduce(block) do |continue, hook|
79
+ -> { hook.describe_to(visitor, *args, &continue) }
80
+ end.call
81
+ end
82
+
83
+ def feature
84
+ source.first
85
+ end
86
+
87
+ class NameBuilder
88
+ attr_reader :result
89
+
90
+ def initialize(test_case)
91
+ test_case.describe_source_to self
92
+ end
93
+
94
+ def feature(*)
95
+ self
96
+ end
97
+
98
+ def scenario(scenario)
99
+ @result = "#{scenario.keyword}: #{scenario.name}"
100
+ self
101
+ end
102
+
103
+ def scenario_outline(outline)
104
+ @result = "#{outline.keyword}: #{outline.name}"
105
+ self
106
+ end
107
+
108
+ def examples_table(table)
109
+ name = table.name.strip
110
+ name = table.keyword if name.length == 0
111
+ @result << ", #{name}"
112
+ self
113
+ end
114
+
115
+ def examples_table_row(row)
116
+ @result << " (row #{row.number})"
117
+ self
118
+ end
119
+ end
120
+
121
+ class TagCollector
122
+ attr_reader :result
123
+
124
+ def initialize(test_case)
125
+ @result = []
126
+ test_case.describe_source_to self
127
+ end
128
+
129
+ [:feature, :scenario, :scenario_outline, :examples_table].each do |node_name|
130
+ define_method(node_name) do |node|
131
+ @result += node.tags
132
+ self
133
+ end
134
+ end
135
+
136
+ def examples_table_row(*)
137
+ end
138
+ end
139
+
140
+ end
141
+ end
142
+ end
143
+ end
@@ -0,0 +1,48 @@
1
+ require 'cucumber/core/test/filters/tag_filter'
2
+ module Cucumber
3
+ module Core
4
+ module Test
5
+
6
+ class LocationsFilter
7
+ def initialize(locations, receiver)
8
+ @receiver = receiver
9
+ @locations = locations
10
+ end
11
+
12
+ def test_case(test_case)
13
+ if test_case.match_locations?(@locations)
14
+ test_case.describe_to @receiver
15
+ end
16
+ self
17
+ end
18
+
19
+ def done
20
+ @receiver.done
21
+ self
22
+ end
23
+ end
24
+
25
+ class NameFilter
26
+ include Cucumber.initializer(:name_regexps, :receiver)
27
+
28
+ def test_case(test_case)
29
+ if accept?(test_case)
30
+ test_case.describe_to(receiver)
31
+ end
32
+ self
33
+ end
34
+
35
+ def done
36
+ @receiver.done
37
+ self
38
+ end
39
+
40
+ private
41
+
42
+ def accept?(test_case)
43
+ name_regexps.empty? || name_regexps.any? { |name_regexp| test_case.match_name?(name_regexp) }
44
+ end
45
+ end
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,110 @@
1
+ module Cucumber
2
+ module Core
3
+ module Test
4
+ class TagFilter
5
+ include Cucumber.initializer(:filter_expressions, :receiver)
6
+
7
+ def test_case(test_case)
8
+ test_cases << test_case
9
+ if test_case.match_tags?(filter_expressions)
10
+ test_case.describe_to(receiver)
11
+ end
12
+ self
13
+ end
14
+
15
+ def done
16
+ tag_limits.enforce(test_cases)
17
+ receiver.done
18
+ self
19
+ end
20
+
21
+ private
22
+ def test_cases
23
+ @test_cases ||= TestCases.new
24
+ end
25
+
26
+ def tag_limits
27
+ @tag_limits ||= TagLimits.new(filter_expressions)
28
+ end
29
+
30
+ class TestCases
31
+ attr_reader :test_cases_by_tag_name
32
+ private :test_cases_by_tag_name
33
+ def initialize
34
+ @test_cases_by_tag_name = Hash.new { [] }
35
+ end
36
+
37
+ def <<(test_case)
38
+ test_case.tags.each do |tag|
39
+ test_cases_by_tag_name[tag.name] += [test_case]
40
+ end
41
+ self
42
+ end
43
+
44
+ def with_tag_name(tag_name)
45
+ test_cases_by_tag_name[tag_name]
46
+ end
47
+ end
48
+
49
+ class TagLimits
50
+ TAG_MATCHER = /^
51
+ (?:~)? #The tag negation symbol "~". This is optional and not captured.
52
+ (?<tag_name>\@[\w\d]+) #Captures the tag name including the "@" symbol.
53
+ \: #The seperator, ":", between the tag name and the limit.
54
+ (?<limit>\d+) #Caputres the limit number.
55
+ $/x
56
+
57
+ attr_reader :limit_list
58
+ private :limit_list
59
+ def initialize(filter_expressions)
60
+ @limit_list = Array(filter_expressions).flat_map do |raw_expression|
61
+ raw_expression.split(/\s*,\s*/)
62
+ end.map do |filter_expression|
63
+ TAG_MATCHER.match(filter_expression)
64
+ end.compact.each_with_object({}) do |matchdata, limit_list|
65
+ limit_list[matchdata[:tag_name]] = Integer(matchdata[:limit])
66
+ end
67
+ end
68
+
69
+ def enforce(test_cases)
70
+ limit_breaches = limit_list.reduce([]) do |breaches, (tag_name, limit)|
71
+ tag_count = test_cases.with_tag_name(tag_name).count
72
+ if tag_count > limit
73
+ tag_locations = test_cases.with_tag_name(tag_name).map(&:location)
74
+ breaches << TagLimitBreach.new(
75
+ tag_count,
76
+ limit,
77
+ tag_name,
78
+ tag_locations
79
+ )
80
+ end
81
+ breaches
82
+ end
83
+ raise TagExcess.new(limit_breaches) if limit_breaches.any?
84
+ self
85
+ end
86
+ end
87
+
88
+ TagLimitBreach = Struct.new(
89
+ :tag_count,
90
+ :tag_limit,
91
+ :tag_name,
92
+ :tag_locations
93
+ ) do
94
+
95
+ def message
96
+ "#{tag_name} occurred #{tag_count} times, but the limit was set to #{tag_limit}\n " +
97
+ tag_locations.map(&:to_s).join("\n ")
98
+ end
99
+ alias :to_s :message
100
+ end
101
+
102
+ class TagExcess < StandardError
103
+ def initialize(limit_breaches)
104
+ super(limit_breaches.map(&:to_s).join("\n"))
105
+ end
106
+ end
107
+ end
108
+ end
109
+ end
110
+ end
@@ -0,0 +1,109 @@
1
+ require 'cucumber/initializer'
2
+
3
+ module Cucumber
4
+ module Core
5
+ module Test
6
+ class HookStep
7
+ def initialize(source, &block)
8
+ @mapping = Test::Mapping.new(&block)
9
+ @source = source
10
+ end
11
+
12
+ def describe_to(visitor, *args)
13
+ visitor.test_step(self, *args)
14
+ end
15
+
16
+ def describe_source_to(visitor, *args)
17
+ visitor.hook(*args)
18
+ end
19
+
20
+ def execute
21
+ @mapping.execute
22
+ end
23
+
24
+ def skip
25
+ execute
26
+ end
27
+
28
+ def map
29
+ self
30
+ end
31
+
32
+ def match_locations?(locations)
33
+ false
34
+ end
35
+
36
+ def inspect
37
+ "#{self.class}"
38
+ end
39
+ end
40
+
41
+ class HookCompiler
42
+ include Cucumber.initializer(:mappings, :receiver)
43
+
44
+ def done
45
+ receiver.done
46
+ self
47
+ end
48
+
49
+ def test_case(test_case, &descend)
50
+ @before_hooks, @after_hooks, @around_hooks, @steps = [], [], [], []
51
+ mapper = HookMapper.new(self, test_case.source)
52
+ test_case.describe_to mappings, mapper
53
+ descend.call
54
+ test_case.
55
+ with_steps(@before_hooks + @steps + @after_hooks).
56
+ with_around_hooks(@around_hooks).
57
+ describe_to(receiver)
58
+ end
59
+
60
+ def before_hook(hook)
61
+ @before_hooks << hook
62
+ end
63
+
64
+ def after_hook(hook)
65
+ @after_hooks << hook
66
+ end
67
+
68
+ def around_hook(hook)
69
+ @around_hooks << hook
70
+ end
71
+
72
+ def test_step(step)
73
+ @steps << step
74
+ end
75
+
76
+ class HookMapper
77
+ include Cucumber.initializer(:compiler, :source)
78
+
79
+ def before(&block)
80
+ compiler.before_hook HookStep.new(source, &block)
81
+ end
82
+
83
+ def after(&block)
84
+ compiler.after_hook HookStep.new(source, &block)
85
+ end
86
+
87
+ def around(&block)
88
+ compiler.around_hook AroundHook.new(source, &block)
89
+ end
90
+
91
+ class AroundHook
92
+ def initialize(source, &block)
93
+ @source = source
94
+ @block = block
95
+ end
96
+
97
+ def call(continue)
98
+ @block.call(continue)
99
+ end
100
+
101
+ def describe_to(visitor, *args, &continue)
102
+ visitor.around_hook(self, *args, &continue)
103
+ end
104
+ end
105
+ end
106
+ end
107
+ end
108
+ end
109
+ end
@@ -0,0 +1,56 @@
1
+ require 'cucumber/initializer'
2
+ module Cucumber
3
+ module Core
4
+ module Test
5
+ class Mapper
6
+ include Cucumber.initializer(:mappings, :runner)
7
+
8
+ def test_case(test_case, &descend)
9
+ mapper = CaseMapper.new(mappings)
10
+ descend.call(mapper)
11
+ test_case.with_steps(mapper.test_steps).describe_to(runner)
12
+ self
13
+ end
14
+
15
+ def done
16
+ runner.done
17
+ self
18
+ end
19
+
20
+ class CaseMapper
21
+ include Cucumber.initializer(:mappings)
22
+
23
+ attr_reader :test_steps
24
+
25
+ def initialize(*)
26
+ super
27
+ @test_steps = []
28
+ end
29
+
30
+ def test_step(test_step)
31
+ mapper = StepMapper.new(test_step)
32
+ test_step.describe_to(mappings, mapper)
33
+ test_steps << mapper.mapped_test_step
34
+ end
35
+
36
+ end
37
+
38
+ class StepMapper
39
+ include Cucumber.initializer(:test_step)
40
+
41
+ attr_reader :mapped_test_step
42
+
43
+ def initialize(*)
44
+ super
45
+ @mapped_test_step = test_step
46
+ end
47
+
48
+ def map(&block)
49
+ @mapped_test_step = test_step.map(&block)
50
+ end
51
+ end
52
+
53
+ end
54
+ end
55
+ end
56
+ end