gherkin 0.0.4-i386-mswin32

Sign up to get free protection for your applications and to get access to all the features.
Files changed (79) hide show
  1. data/.gitignore +7 -0
  2. data/LICENSE +20 -0
  3. data/README.rdoc +66 -0
  4. data/Rakefile +49 -0
  5. data/VERSION.yml +4 -0
  6. data/bin/gherkin +10 -0
  7. data/cucumber.yml +3 -0
  8. data/features/feature_parser.feature +206 -0
  9. data/features/native_lexer.feature +19 -0
  10. data/features/parser_with_native_lexer.feature +205 -0
  11. data/features/pretty_printer.feature +14 -0
  12. data/features/step_definitions/gherkin_steps.rb +34 -0
  13. data/features/step_definitions/pretty_printer_steps.rb +56 -0
  14. data/features/steps_parser.feature +46 -0
  15. data/features/support/env.rb +33 -0
  16. data/gherkin.gemspec +180 -0
  17. data/java/.gitignore +2 -0
  18. data/java/Gherkin.iml +24 -0
  19. data/java/build.xml +13 -0
  20. data/java/src/gherkin/FixJava.java +34 -0
  21. data/java/src/gherkin/Lexer.java +5 -0
  22. data/java/src/gherkin/LexingError.java +7 -0
  23. data/java/src/gherkin/Listener.java +27 -0
  24. data/java/src/gherkin/ParseError.java +22 -0
  25. data/java/src/gherkin/Parser.java +185 -0
  26. data/java/src/gherkin/lexer/.gitignore +1 -0
  27. data/java/src/gherkin/parser/StateMachineReader.java +62 -0
  28. data/lib/.gitignore +4 -0
  29. data/lib/gherkin.rb +2 -0
  30. data/lib/gherkin/c_lexer.rb +10 -0
  31. data/lib/gherkin/core_ext/array.rb +5 -0
  32. data/lib/gherkin/i18n.yml +535 -0
  33. data/lib/gherkin/i18n_lexer.rb +29 -0
  34. data/lib/gherkin/java_lexer.rb +10 -0
  35. data/lib/gherkin/lexer.rb +43 -0
  36. data/lib/gherkin/parser.rb +19 -0
  37. data/lib/gherkin/parser/meta.txt +4 -0
  38. data/lib/gherkin/parser/root.txt +9 -0
  39. data/lib/gherkin/parser/steps.txt +3 -0
  40. data/lib/gherkin/rb_lexer.rb +10 -0
  41. data/lib/gherkin/rb_lexer/.gitignore +1 -0
  42. data/lib/gherkin/rb_lexer/README.rdoc +8 -0
  43. data/lib/gherkin/rb_parser.rb +117 -0
  44. data/lib/gherkin/tools/pretty_printer.rb +83 -0
  45. data/nativegems.sh +5 -0
  46. data/ragel/i18n/.gitignore +1 -0
  47. data/ragel/lexer.c.rl.erb +401 -0
  48. data/ragel/lexer.java.rl.erb +200 -0
  49. data/ragel/lexer.rb.rl.erb +171 -0
  50. data/ragel/lexer_common.rl.erb +46 -0
  51. data/spec/gherkin/c_lexer_spec.rb +21 -0
  52. data/spec/gherkin/fixtures/1.feature +8 -0
  53. data/spec/gherkin/fixtures/complex.feature +43 -0
  54. data/spec/gherkin/fixtures/i18n_fr.feature +13 -0
  55. data/spec/gherkin/fixtures/i18n_no.feature +6 -0
  56. data/spec/gherkin/fixtures/i18n_zh-CN.feature +8 -0
  57. data/spec/gherkin/fixtures/simple.feature +3 -0
  58. data/spec/gherkin/fixtures/simple_with_comments.feature +7 -0
  59. data/spec/gherkin/fixtures/simple_with_tags.feature +11 -0
  60. data/spec/gherkin/i18n_spec.rb +57 -0
  61. data/spec/gherkin/java_lexer_spec.rb +20 -0
  62. data/spec/gherkin/parser_spec.rb +28 -0
  63. data/spec/gherkin/rb_lexer_spec.rb +18 -0
  64. data/spec/gherkin/sexp_recorder.rb +29 -0
  65. data/spec/gherkin/shared/lexer_spec.rb +433 -0
  66. data/spec/gherkin/shared/py_string_spec.rb +124 -0
  67. data/spec/gherkin/shared/table_spec.rb +97 -0
  68. data/spec/gherkin/shared/tags_spec.rb +50 -0
  69. data/spec/spec_helper.rb +53 -0
  70. data/tasks/bench.rake +193 -0
  71. data/tasks/bench/feature_builder.rb +49 -0
  72. data/tasks/bench/generated/.gitignore +1 -0
  73. data/tasks/bench/null_listener.rb +4 -0
  74. data/tasks/compile.rake +70 -0
  75. data/tasks/cucumber.rake +20 -0
  76. data/tasks/ragel_task.rb +83 -0
  77. data/tasks/rdoc.rake +12 -0
  78. data/tasks/rspec.rake +15 -0
  79. metadata +214 -0
@@ -0,0 +1,124 @@
1
+ # encoding: utf-8
2
+ require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper')
3
+
4
+ module Gherkin
5
+ module Lexer
6
+ shared_examples_for "a Gherkin lexer lexing py_strings" do
7
+
8
+ def ps(content)
9
+ '"""%s"""' % ("\n" + content + "\n")
10
+ end
11
+
12
+ it "should provide the amount of indentation of the triple quotes to the listener" do
13
+ str = <<EOS
14
+ Feature: some feature
15
+ Scenario: some scenario
16
+ Given foo
17
+ """
18
+ Hello
19
+ Goodbye
20
+ """
21
+ Then bar
22
+ EOS
23
+ @listener.should_receive(:py_string).with(" Hello\nGoodbye", 4)
24
+ @lexer.scan(str)
25
+ end
26
+
27
+ it "should parse a simple py_string" do
28
+ @listener.should_receive(:py_string).with("I am a py_string", 1)
29
+ @lexer.scan ps("I am a py_string")
30
+ end
31
+
32
+ it "should parse an empty py_string" do
33
+ @listener.should_receive(:py_string).with("", 4)
34
+ @lexer.scan("Feature: Hi\nScenario: Hi\nGiven a step\n\"\"\"\n\"\"\"")
35
+ end
36
+
37
+ it "should treat a string containing only newlines as only newlines" do
38
+ py_string = <<EOS
39
+ """
40
+
41
+
42
+
43
+ """
44
+ EOS
45
+ @listener.should_receive(:py_string).with("\n\n", 1)
46
+ @lexer.scan(py_string)
47
+ end
48
+
49
+ it "should parse content separated by two newlines" do
50
+ @lexer.scan ps("A\n\nB")
51
+ @listener.to_sexp.should == [
52
+ [:py_string, "A\n\nB", 1],
53
+ ]
54
+ end
55
+
56
+ it "should parse a multiline string" do
57
+ @listener.should_receive(:py_string).with("A\nB\nC\nD", 1)
58
+ @lexer.scan ps("A\nB\nC\nD")
59
+ end
60
+
61
+ it "should ignore unescaped quotes inside the string delimeters" do
62
+ @listener.should_receive(:py_string).with("What does \"this\" mean?", 1)
63
+ @lexer.scan ps('What does "this" mean?')
64
+ end
65
+
66
+ it "should preserve whitespace within the triple quotes" do
67
+ str = <<EOS
68
+ """
69
+ Line one
70
+ Line two
71
+ """
72
+ EOS
73
+ @listener.should_receive(:py_string).with(" Line one\nLine two", 1)
74
+ @lexer.scan(str)
75
+ end
76
+
77
+ it "should preserve tabs within the content" do
78
+ @listener.should_receive(:py_string).with("I have\tsome tabs\nInside\t\tthe content", 1)
79
+ @lexer.scan ps("I have\tsome tabs\nInside\t\tthe content")
80
+ end
81
+
82
+ it "should handle complex py_strings" do
83
+ py_string = <<EOS
84
+ # Feature comment
85
+ @one
86
+ Feature: Sample
87
+
88
+ @two @three
89
+ Scenario: Missing
90
+ Given missing
91
+
92
+ 1 scenario (1 passed)
93
+ 1 step (1 passed)
94
+
95
+ EOS
96
+
97
+ @listener.should_receive(:py_string).with(py_string, 1)
98
+ @lexer.scan ps(py_string)
99
+ end
100
+
101
+ it "should allow whitespace after the closing py_string delimiter" do
102
+ str = <<EOS
103
+ """
104
+ Line one
105
+ """
106
+ EOS
107
+ @listener.should_receive(:py_string).with(" Line one", 1)
108
+ @lexer.scan(str)
109
+ end
110
+
111
+ it "should preserve the last newline(s) at the end of a py_string" do
112
+ str = <<EOS
113
+ """
114
+ PyString text
115
+
116
+
117
+ """
118
+ EOS
119
+ @listener.should_receive(:py_string).with("PyString text\n\n",1)
120
+ @lexer.scan(str)
121
+ end
122
+ end
123
+ end
124
+ end
@@ -0,0 +1,97 @@
1
+ #encoding: utf-8
2
+ require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper')
3
+
4
+ module Gherkin
5
+ module Lexer
6
+ shared_examples_for "a Gherkin lexer lexing tables" do
7
+ tables = {
8
+ "|a|b|\n" => [%w{a b}],
9
+ "|a|b|c|\n" => [%w{a b c}],
10
+ "|c|d|\n|e|f|\n" => [%w{c d}, %w{e f}]
11
+ }
12
+
13
+ tables.each do |text, expected|
14
+ it "should parse #{text}" do
15
+ @listener.should_receive(:table).with(t(expected), 1)
16
+ @lexer.scan(text.dup)
17
+ end
18
+ end
19
+
20
+ it "should parse a table with many columns" do
21
+ @listener.should_receive(:table).with(t([%w{a b c d e f g h i j k l m n o p}]), 1)
22
+ @lexer.scan("|a|b|c|d|e|f|g|h|i|j|k|l|m|n|o|p|\n")
23
+ end
24
+
25
+ it "should parse a multicharacter cell content" do
26
+ @listener.should_receive(:table).with(t([%w{foo bar}]), 1)
27
+ @lexer.scan("| foo | bar |\n")
28
+ end
29
+
30
+ it "should parse cells with spaces within the content" do
31
+ @listener.should_receive(:table).with(t([["Dill pickle", "Valencia orange"], ["Ruby red grapefruit", "Tire iron"]]), 1)
32
+ @lexer.scan("| Dill pickle | Valencia orange |\n| Ruby red grapefruit | Tire iron |\n")
33
+ end
34
+
35
+ it "should allow utf-8" do
36
+ # Fails in 1.9.1!
37
+ # 'Gherkin::Lexer::Table should allow utf-8 with using == to evaluate' FAILED
38
+ # expected: [[:table, [["ůﻚ", "2"]], 1]],
39
+ # got: [[:table, [["\xC5\xAF\xEF\xBB\x9A", "2"]], 1]] (using ==)
40
+ # BUT, simply running:
41
+ # [[:table, [["ůﻚ", "2"]], 1]].should == [[:table, [["\xC5\xAF\xEF\xBB\x9A", "2"]], 1]]
42
+ # passes
43
+ #
44
+ @lexer.scan(" | ůﻚ | 2 | \n")
45
+ @listener.to_sexp.should == [
46
+ [:table, [["ůﻚ", "2"]], 1]
47
+ ]
48
+ end
49
+
50
+ it "should allow utf-8 using should_receive" do
51
+ @listener.should_receive(:table).with(t([['繁體中文 而且','並且','繁體中文 而且','並且']]), 1)
52
+ @lexer.scan("| 繁體中文 而且|並且| 繁體中文 而且|並且|\n")
53
+ end
54
+
55
+ it "should parse a 2x2 table" do
56
+ @listener.should_receive(:table).with(t([%w{1 2}, %w{3 4}]), 1)
57
+ @lexer.scan("| 1 | 2 |\n| 3 | 4 |\n")
58
+ end
59
+
60
+ it "should parse a 2x2 table with several newlines" do
61
+ @listener.should_receive(:table).with(t([%w{1 2}, %w{3 4}]), 1)
62
+ @lexer.scan("| 1 | 2 |\n| 3 | 4 |\n\n\n")
63
+ end
64
+
65
+ it "should parse a 2x2 table with empty cells" do
66
+ @listener.should_receive(:table).with(t([['1', ''], ['', '4']]), 1)
67
+ @lexer.scan("| 1 | |\n|| 4 |\n")
68
+ end
69
+
70
+ it "should parse a 1x2 table that does not end in a newline" do
71
+ @listener.should_receive(:table).with(t([%w{1 2}]), 1)
72
+ @lexer.scan("| 1 | 2 |")
73
+ end
74
+
75
+ it "should parse a 1x2 table without spaces and newline" do
76
+ @listener.should_receive(:table).with(t([%w{1 2}]), 1)
77
+ @lexer.scan("|1|2|\n")
78
+ end
79
+
80
+ it "should parse a row with whitespace after the rows" do
81
+ @listener.should_receive(:table).with(t([%w{1 2}, %w{a b}]), 1)
82
+ @lexer.scan("| 1 | 2 | \n | a | b | \n")
83
+ end
84
+
85
+ it "should parse a table with lots of whitespace" do
86
+ @listener.should_receive(:table).with(t([["abc", "123"]]), 1)
87
+ @lexer.scan(" \t| \t abc\t| \t123\t \t\t| \t\t \t \t\n ")
88
+ end
89
+
90
+ it "should raise LexingError for rows that aren't closed" do
91
+ lambda {
92
+ @lexer.scan("|| oh hello \n")
93
+ }.should raise_error(/Parsing error on line 1: '|| oh hello/)
94
+ end
95
+ end
96
+ end
97
+ end
@@ -0,0 +1,50 @@
1
+ #encoding: utf-8
2
+ require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper')
3
+
4
+ module Gherkin
5
+ module Lexer
6
+ shared_examples_for "a Gherkin lexer lexing tags" do
7
+ it "should lex a single tag" do
8
+ @listener.should_receive(:tag).with("dog", 1)
9
+ @lexer.scan("@dog\n")
10
+ end
11
+
12
+ it "should lex multiple tags" do
13
+ @listener.should_receive(:tag).twice
14
+ @lexer.scan("@dog @cat\n")
15
+ end
16
+
17
+ it "should lex UTF-8 tags" do
18
+ @listener.should_receive(:tag).with("シナリオテンプレート", 1)
19
+ @lexer.scan("@シナリオテンプレート\n")
20
+ end
21
+
22
+ it "should lex mixed tags" do
23
+ @listener.should_receive(:tag).with("wip", 1).ordered
24
+ @listener.should_receive(:tag).with("Значения", 1).ordered
25
+ @lexer.scan("@wip @Значения\n")
26
+ end
27
+
28
+ it "should lex wacky identifiers" do
29
+ @listener.should_receive(:tag).exactly(4).times
30
+ @lexer.scan("@BJ-x98.77 @BJ-z12.33 @O_o" "@#not_a_comment\n")
31
+ end
32
+
33
+ # TODO: Ask on ML for opinions about this one
34
+ it "should lex tags without spaces between them?" do
35
+ @listener.should_receive(:tag).twice
36
+ @lexer.scan("@one@two\n")
37
+ end
38
+
39
+ it "should not lex tags beginning with two @@ signs" do
40
+ @listener.should_not_receive(:tag)
41
+ lambda { @lexer.scan("@@test\n") }.should raise_error(/Lexing error on line 1/)
42
+ end
43
+
44
+ it "should not lex a lone @ sign" do
45
+ @listener.should_not_receive(:tag)
46
+ lambda { @lexer.scan("@\n") }.should raise_error(/Lexing error on line 1/)
47
+ end
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,53 @@
1
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
2
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
3
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'spec/gherkin'))
4
+ require 'gherkin'
5
+ require 'gherkin/sexp_recorder'
6
+ require 'rubygems'
7
+ require 'spec'
8
+ require 'spec/autorun'
9
+ require 'shared/lexer_spec'
10
+ require 'shared/tags_spec'
11
+ require 'shared/py_string_spec'
12
+ require 'shared/table_spec'
13
+
14
+ module GherkinSpecHelper
15
+ def scan_file(file)
16
+ @lexer.scan(File.new(File.dirname(__FILE__) + "/gherkin/fixtures/" + file).read)
17
+ end
18
+ end
19
+
20
+ Spec::Runner.configure do |c|
21
+ c.include(GherkinSpecHelper)
22
+ end
23
+
24
+ # Allows comparison of Java List with Ruby Array (tables)
25
+ Spec::Matchers.define :t do |expected|
26
+ match do |table|
27
+ def table.inspect
28
+ "t " + self.map{|row| row.map{|cell| cell}}.inspect
29
+ end
30
+ table.map{|row| row.map{|cell| cell}}.should == expected
31
+ end
32
+ end
33
+
34
+ Spec::Matchers.define :a do |expected|
35
+ match do |array|
36
+ def array.inspect
37
+ "a " + self.map{|e| e.to_sym}.inspect
38
+ end
39
+ array.map{|e| e.to_sym}.should == expected
40
+ end
41
+ end
42
+
43
+ Spec::Matchers.define :sym do |expected|
44
+ match do |actual|
45
+ expected.to_s == actual.to_s
46
+ end
47
+ end
48
+
49
+ Spec::Matchers.define :allow do |event|
50
+ match do |parser|
51
+ parser.expected.index(event)
52
+ end
53
+ end
@@ -0,0 +1,193 @@
1
+ %w{/../lib /bench}.each do |l|
2
+ $LOAD_PATH << File.expand_path(File.dirname(__FILE__) + l)
3
+ end
4
+
5
+ require 'benchmark'
6
+
7
+ GENERATED_FEATURES = File.expand_path(File.dirname(__FILE__) + "/bench/generated")
8
+
9
+ class RandomFeatureGenerator
10
+ def initialize(number)
11
+ require 'faker'
12
+ require 'feature_builder'
13
+
14
+ @number = number
15
+ end
16
+
17
+ def generate
18
+ @number.times do
19
+ name = catch_phrase
20
+ feature = FeatureBuilder.new(name) do |f|
21
+ num_scenarios = rand_in(1..10)
22
+ num_scenarios.times do
23
+ f.scenario(bs) do |steps|
24
+ num_steps = rand_in(3..10)
25
+ num_steps.times do
26
+ steps.step(sentence, self)
27
+ end
28
+ end
29
+ end
30
+ end
31
+ write feature.to_s, name
32
+ end
33
+ end
34
+
35
+ def write(content, name)
36
+ File.open(GENERATED_FEATURES + "/#{name.downcase.gsub(/[\s\-\/]/, '_')}.feature", "w+") do |file|
37
+ file << content
38
+ end
39
+ end
40
+
41
+ def rand_in(range)
42
+ ary = range.to_a
43
+ ary[rand(ary.length - 1)]
44
+ end
45
+
46
+ def catch_phrase
47
+ Faker::Company.catch_phrase
48
+ end
49
+
50
+ def bs
51
+ Faker::Company.bs.capitalize
52
+ end
53
+
54
+ def sentence
55
+ Faker::Lorem.sentence
56
+ end
57
+
58
+ def table_cell
59
+ Faker::Lorem.words(rand(2)+1).join(" ")
60
+ end
61
+ end
62
+
63
+ class Benchmarker
64
+ def initialize
65
+ @features = Dir[GENERATED_FEATURES + "/**/*feature"]
66
+ end
67
+
68
+ def report(lexer)
69
+ Benchmark.bm do |x|
70
+ x.report("#{lexer}:") { send :"run_#{lexer}" }
71
+ end
72
+ end
73
+
74
+ def report_all
75
+ Benchmark.bmbm do |x|
76
+ x.report("c_gherkin:") { run_c_gherkin }
77
+ x.report("c_gherkin_no_parser:") { run_c_gherkin_no_parser }
78
+ x.report("rb_gherkin:") { run_rb_gherkin }
79
+ x.report("cucumber:") { run_cucumber }
80
+ x.report("tt:") { run_tt }
81
+ end
82
+ end
83
+
84
+ def run_cucumber
85
+ require 'cucumber'
86
+ require 'logger'
87
+ step_mother = Cucumber::StepMother.new
88
+ logger = Logger.new(STDOUT)
89
+ logger.level = Logger::INFO
90
+ step_mother.log = logger
91
+ step_mother.load_plain_text_features(@features)
92
+ end
93
+
94
+ def run_tt
95
+ require 'cucumber'
96
+ # Using Cucumber's Treetop lexer, but never calling #build to build the AST
97
+ lexer = Cucumber::Parser::NaturalLanguage.new(nil, 'en').parser
98
+ @features.each do |file|
99
+ source = IO.read(file)
100
+ parse_tree = lexer.parse(source)
101
+ if parse_tree.nil?
102
+ raise Cucumber::Parser::SyntaxError.new(lexer, file, 0)
103
+ end
104
+ end
105
+ end
106
+
107
+ def run_rb_gherkin
108
+ require 'gherkin'
109
+ require 'gherkin/rb_lexer'
110
+ require 'null_listener'
111
+ @features.each do |feature|
112
+ parser = Gherkin::Parser.new(NullListener.new, true, "root")
113
+ lexer = Gherkin::RbLexer['en'].new(parser)
114
+ lexer.scan(File.read(feature))
115
+ end
116
+ end
117
+
118
+ def run_c_gherkin
119
+ require 'gherkin'
120
+ require 'null_listener'
121
+ @features.each do |feature|
122
+ parser = Gherkin::Parser.new(NullListener.new, true, "root")
123
+ lexer = Gherkin::CLexer['en'].new(parser)
124
+ lexer.scan(File.read(feature))
125
+ end
126
+ end
127
+
128
+ def run_c_gherkin_no_parser
129
+ require 'gherkin'
130
+ require 'null_listener'
131
+ @features.each do |feature|
132
+ lexer = Gherkin::CLexer['en'].new(NullListener.new)
133
+ lexer.scan(File.read(feature))
134
+ end
135
+ end
136
+ end
137
+
138
+ desc "Generate 500 random features and benchmark Cucumber, Treetop and Gherkin with them"
139
+ task :bench => ["bench:clean", "bench:gen"] do
140
+ benchmarker = Benchmarker.new
141
+ benchmarker.report_all
142
+ end
143
+
144
+ namespace :bench do
145
+ desc "Generate [number] features with random content, or 500 features if number is not provided"
146
+ task :gen, :number do |t, args|
147
+ args.with_defaults(:number => 500)
148
+ generator = RandomFeatureGenerator.new(args.number.to_i)
149
+ generator.generate
150
+ end
151
+
152
+ desc "Benchmark Cucumber AST building from the features in tasks/bench/generated"
153
+ task :cucumber do
154
+ benchmarker = Benchmarker.new
155
+ benchmarker.report("cucumber")
156
+ end
157
+
158
+ desc "Benchmark the Treetop parser with the features in tasks/bench/generated"
159
+ task :tt do
160
+ benchmarker = Benchmarker.new
161
+ benchmarker.report("tt")
162
+ end
163
+
164
+ desc "Benchmark the Ruby Gherkin lexer+parser with the features in tasks/bench/generated"
165
+ task :rb_gherkin do
166
+ benchmarker = Benchmarker.new
167
+ benchmarker.report("rb_gherkin")
168
+ end
169
+
170
+ desc "Benchmark the C Gherkin lexer+parser with the features in tasks/bench/generated"
171
+ task :c_gherkin do
172
+ benchmarker = Benchmarker.new
173
+ benchmarker.report("c_gherkin")
174
+ end
175
+
176
+ desc "Benchmark the C Gherkin lexer (no parser) with the features in tasks/bench/generated"
177
+ task :c_gherkin_no_parser do
178
+ benchmarker = Benchmarker.new
179
+ benchmarker.report("c_gherkin_no_parser")
180
+ end
181
+
182
+ desc "Show basic statistics about the features in tasks/bench/generated"
183
+ task :stats do
184
+ ["Feature", "Scenario", "Given"].each do |kw|
185
+ sh "grep #{kw} #{GENERATED_FEATURES}/* | wc -l"
186
+ end
187
+ end
188
+
189
+ desc "Remove all generated features in tasks/bench/generated"
190
+ task :clean do
191
+ rm_f FileList[GENERATED_FEATURES + "/**/*feature"]
192
+ end
193
+ end