bryanl-gherkin 2.11.1.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 (187) hide show
  1. data/.gitattributes +2 -0
  2. data/.mailmap +2 -0
  3. data/.rbenv-gemsets +1 -0
  4. data/.rspec +1 -0
  5. data/.rvmrc +1 -0
  6. data/.travis.yml +16 -0
  7. data/.yardopts +5 -0
  8. data/Gemfile +5 -0
  9. data/History.md +788 -0
  10. data/LICENSE +20 -0
  11. data/README.md +272 -0
  12. data/Rakefile +26 -0
  13. data/build_native_gems.sh +7 -0
  14. data/cucumber.yml +4 -0
  15. data/examples/parse_and_output_json.rb +19 -0
  16. data/features/.cucumber/stepdefs.json +244 -0
  17. data/features/escaped_pipes.feature +8 -0
  18. data/features/feature_parser.feature +237 -0
  19. data/features/json_formatter.feature +498 -0
  20. data/features/json_parser.feature +331 -0
  21. data/features/native_lexer.feature +19 -0
  22. data/features/parser_with_native_lexer.feature +205 -0
  23. data/features/pretty_formatter.feature +16 -0
  24. data/features/step_definitions/eyeball_steps.rb +3 -0
  25. data/features/step_definitions/gherkin_steps.rb +29 -0
  26. data/features/step_definitions/json_formatter_steps.rb +30 -0
  27. data/features/step_definitions/json_parser_steps.rb +20 -0
  28. data/features/step_definitions/pretty_formatter_steps.rb +85 -0
  29. data/features/steps_parser.feature +46 -0
  30. data/features/support/env.rb +42 -0
  31. data/gherkin.gemspec +77 -0
  32. data/install_mingw_os_x.sh +7 -0
  33. data/js/.npmignore +1 -0
  34. data/js/lib/gherkin/lexer/.npmignore +0 -0
  35. data/lib/gherkin.rb +2 -0
  36. data/lib/gherkin/c_lexer.rb +17 -0
  37. data/lib/gherkin/formatter/ansi_escapes.rb +97 -0
  38. data/lib/gherkin/formatter/argument.rb +16 -0
  39. data/lib/gherkin/formatter/escaping.rb +15 -0
  40. data/lib/gherkin/formatter/filter_formatter.rb +146 -0
  41. data/lib/gherkin/formatter/hashable.rb +19 -0
  42. data/lib/gherkin/formatter/json_formatter.rb +122 -0
  43. data/lib/gherkin/formatter/line_filter.rb +26 -0
  44. data/lib/gherkin/formatter/model.rb +281 -0
  45. data/lib/gherkin/formatter/pretty_formatter.rb +244 -0
  46. data/lib/gherkin/formatter/regexp_filter.rb +21 -0
  47. data/lib/gherkin/formatter/step_printer.rb +21 -0
  48. data/lib/gherkin/formatter/tag_count_formatter.rb +47 -0
  49. data/lib/gherkin/formatter/tag_filter.rb +19 -0
  50. data/lib/gherkin/i18n.rb +180 -0
  51. data/lib/gherkin/i18n.yml +613 -0
  52. data/lib/gherkin/js_lexer.rb +20 -0
  53. data/lib/gherkin/json_parser.rb +177 -0
  54. data/lib/gherkin/lexer/i18n_lexer.rb +46 -0
  55. data/lib/gherkin/listener/event.rb +45 -0
  56. data/lib/gherkin/listener/formatter_listener.rb +143 -0
  57. data/lib/gherkin/native.rb +7 -0
  58. data/lib/gherkin/native/java.rb +72 -0
  59. data/lib/gherkin/native/null.rb +5 -0
  60. data/lib/gherkin/native/therubyracer.rb +39 -0
  61. data/lib/gherkin/parser/meta.txt +5 -0
  62. data/lib/gherkin/parser/parser.rb +164 -0
  63. data/lib/gherkin/parser/root.txt +11 -0
  64. data/lib/gherkin/parser/steps.txt +4 -0
  65. data/lib/gherkin/rb_lexer.rb +8 -0
  66. data/lib/gherkin/rb_lexer/README.rdoc +8 -0
  67. data/lib/gherkin/rb_lexer/ar.rb +1165 -0
  68. data/lib/gherkin/rb_lexer/bg.rb +1377 -0
  69. data/lib/gherkin/rb_lexer/bm.rb +1081 -0
  70. data/lib/gherkin/rb_lexer/ca.rb +1305 -0
  71. data/lib/gherkin/rb_lexer/cs.rb +1157 -0
  72. data/lib/gherkin/rb_lexer/cy_gb.rb +1027 -0
  73. data/lib/gherkin/rb_lexer/da.rb +1043 -0
  74. data/lib/gherkin/rb_lexer/de.rb +1151 -0
  75. data/lib/gherkin/rb_lexer/en.rb +1151 -0
  76. data/lib/gherkin/rb_lexer/en_au.rb +971 -0
  77. data/lib/gherkin/rb_lexer/en_lol.rb +929 -0
  78. data/lib/gherkin/rb_lexer/en_pirate.rb +1205 -0
  79. data/lib/gherkin/rb_lexer/en_scouse.rb +1357 -0
  80. data/lib/gherkin/rb_lexer/en_tx.rb +1011 -0
  81. data/lib/gherkin/rb_lexer/eo.rb +990 -0
  82. data/lib/gherkin/rb_lexer/es.rb +1135 -0
  83. data/lib/gherkin/rb_lexer/et.rb +985 -0
  84. data/lib/gherkin/rb_lexer/fi.rb +964 -0
  85. data/lib/gherkin/rb_lexer/fr.rb +1223 -0
  86. data/lib/gherkin/rb_lexer/he.rb +1113 -0
  87. data/lib/gherkin/rb_lexer/hr.rb +1061 -0
  88. data/lib/gherkin/rb_lexer/hu.rb +1113 -0
  89. data/lib/gherkin/rb_lexer/id.rb +958 -0
  90. data/lib/gherkin/rb_lexer/is.rb +1115 -0
  91. data/lib/gherkin/rb_lexer/it.rb +1081 -0
  92. data/lib/gherkin/rb_lexer/ja.rb +1413 -0
  93. data/lib/gherkin/rb_lexer/ko.rb +1097 -0
  94. data/lib/gherkin/rb_lexer/lt.rb +1040 -0
  95. data/lib/gherkin/rb_lexer/lu.rb +1127 -0
  96. data/lib/gherkin/rb_lexer/lv.rb +1161 -0
  97. data/lib/gherkin/rb_lexer/nl.rb +1110 -0
  98. data/lib/gherkin/rb_lexer/no.rb +1055 -0
  99. data/lib/gherkin/rb_lexer/pl.rb +1452 -0
  100. data/lib/gherkin/rb_lexer/pt.rb +1425 -0
  101. data/lib/gherkin/rb_lexer/ro.rb +1159 -0
  102. data/lib/gherkin/rb_lexer/ru.rb +1749 -0
  103. data/lib/gherkin/rb_lexer/sk.rb +1041 -0
  104. data/lib/gherkin/rb_lexer/sr_cyrl.rb +1798 -0
  105. data/lib/gherkin/rb_lexer/sr_latn.rb +1289 -0
  106. data/lib/gherkin/rb_lexer/sv.rb +1065 -0
  107. data/lib/gherkin/rb_lexer/tr.rb +1087 -0
  108. data/lib/gherkin/rb_lexer/uk.rb +1641 -0
  109. data/lib/gherkin/rb_lexer/uz.rb +1371 -0
  110. data/lib/gherkin/rb_lexer/vi.rb +1193 -0
  111. data/lib/gherkin/rb_lexer/zh_cn.rb +1053 -0
  112. data/lib/gherkin/rb_lexer/zh_tw.rb +1047 -0
  113. data/lib/gherkin/rubify.rb +24 -0
  114. data/lib/gherkin/tag_expression.rb +62 -0
  115. data/ragel/lexer.c.rl.erb +454 -0
  116. data/ragel/lexer.java.rl.erb +219 -0
  117. data/ragel/lexer.js.rl.erb +227 -0
  118. data/ragel/lexer.rb.rl.erb +174 -0
  119. data/ragel/lexer_common.rl.erb +50 -0
  120. data/spec/gherkin/c_lexer_spec.rb +22 -0
  121. data/spec/gherkin/fixtures/1.feature +8 -0
  122. data/spec/gherkin/fixtures/comments_in_table.feature +9 -0
  123. data/spec/gherkin/fixtures/complex.feature +45 -0
  124. data/spec/gherkin/fixtures/complex.json +139 -0
  125. data/spec/gherkin/fixtures/complex_for_filtering.feature +60 -0
  126. data/spec/gherkin/fixtures/complex_with_tags.feature +61 -0
  127. data/spec/gherkin/fixtures/dos_line_endings.feature +45 -0
  128. data/spec/gherkin/fixtures/examples_with_only_header.feature +14 -0
  129. data/spec/gherkin/fixtures/hantu_pisang.feature +35 -0
  130. data/spec/gherkin/fixtures/i18n_fr.feature +14 -0
  131. data/spec/gherkin/fixtures/i18n_fr2.feature +8 -0
  132. data/spec/gherkin/fixtures/i18n_no.feature +7 -0
  133. data/spec/gherkin/fixtures/i18n_pt1.feature +44 -0
  134. data/spec/gherkin/fixtures/i18n_pt2.feature +4 -0
  135. data/spec/gherkin/fixtures/i18n_pt3.feature +4 -0
  136. data/spec/gherkin/fixtures/i18n_pt4.feature +4 -0
  137. data/spec/gherkin/fixtures/i18n_zh-CN.feature +9 -0
  138. data/spec/gherkin/fixtures/issue_145.feature +22 -0
  139. data/spec/gherkin/fixtures/scenario_outline_with_tags.feature +13 -0
  140. data/spec/gherkin/fixtures/scenario_without_steps.feature +5 -0
  141. data/spec/gherkin/fixtures/simple_with_comments.feature +7 -0
  142. data/spec/gherkin/fixtures/simple_with_tags.feature +11 -0
  143. data/spec/gherkin/fixtures/with_bom.feature +3 -0
  144. data/spec/gherkin/formatter/ansi_escapes_spec.rb +32 -0
  145. data/spec/gherkin/formatter/filter_formatter_spec.rb +204 -0
  146. data/spec/gherkin/formatter/json_formatter_spec.rb +92 -0
  147. data/spec/gherkin/formatter/model_spec.rb +28 -0
  148. data/spec/gherkin/formatter/pretty_formatter_spec.rb +177 -0
  149. data/spec/gherkin/formatter/spaces.feature +9 -0
  150. data/spec/gherkin/formatter/step_printer_spec.rb +55 -0
  151. data/spec/gherkin/formatter/tabs.feature +9 -0
  152. data/spec/gherkin/formatter/tag_count_formatter_spec.rb +30 -0
  153. data/spec/gherkin/i18n_spec.rb +241 -0
  154. data/spec/gherkin/java_lexer_spec.rb +20 -0
  155. data/spec/gherkin/js_lexer_spec.rb +23 -0
  156. data/spec/gherkin/json_parser_spec.rb +176 -0
  157. data/spec/gherkin/lexer/i18n_lexer_spec.rb +43 -0
  158. data/spec/gherkin/output_stream_string_io.rb +20 -0
  159. data/spec/gherkin/parser/parser_spec.rb +16 -0
  160. data/spec/gherkin/rb_lexer_spec.rb +20 -0
  161. data/spec/gherkin/sexp_recorder.rb +59 -0
  162. data/spec/gherkin/shared/bom_group.rb +20 -0
  163. data/spec/gherkin/shared/doc_string_group.rb +163 -0
  164. data/spec/gherkin/shared/lexer_group.rb +591 -0
  165. data/spec/gherkin/shared/row_group.rb +125 -0
  166. data/spec/gherkin/shared/tags_group.rb +54 -0
  167. data/spec/gherkin/tag_expression_spec.rb +142 -0
  168. data/spec/spec_helper.rb +75 -0
  169. data/tasks/bench.rake +184 -0
  170. data/tasks/bench/feature_builder.rb +49 -0
  171. data/tasks/bench/null_listener.rb +4 -0
  172. data/tasks/compile.rake +120 -0
  173. data/tasks/cucumber.rake +22 -0
  174. data/tasks/gems.rake +31 -0
  175. data/tasks/ikvm.rake +124 -0
  176. data/tasks/ragel_task.rb +100 -0
  177. data/tasks/release.rake +49 -0
  178. data/tasks/rspec.rake +8 -0
  179. data/tasks/yard.rake +7 -0
  180. data/tasks/yard/default/layout/html/bubble_32x32.png +0 -0
  181. data/tasks/yard/default/layout/html/bubble_48x48.png +0 -0
  182. data/tasks/yard/default/layout/html/footer.erb +5 -0
  183. data/tasks/yard/default/layout/html/index.erb +1 -0
  184. data/tasks/yard/default/layout/html/layout.erb +25 -0
  185. data/tasks/yard/default/layout/html/logo.erb +1 -0
  186. data/tasks/yard/default/layout/html/setup.rb +4 -0
  187. metadata +473 -0
@@ -0,0 +1,125 @@
1
+ # encoding: utf-8
2
+ require 'spec_helper'
3
+
4
+ module Gherkin
5
+ module Lexer
6
+ shared_examples_for "a Gherkin lexer lexing rows" do
7
+ def scan(gherkin)
8
+ @lexer.scan(gherkin)
9
+ end
10
+
11
+ rows = {
12
+ "|a|b|\n" => %w{a b},
13
+ "|a|b|c|\n" => %w{a b c},
14
+ }
15
+
16
+ rows.each do |text, expected|
17
+ it "should parse #{text}" do
18
+ @listener.should_receive(:row).with(r(expected), 1)
19
+ scan(text.dup)
20
+ end
21
+ end
22
+
23
+ it "should parse a row with many cells" do
24
+ @listener.should_receive(:row).with(r(%w{a b c d e f g h i j k l m n o p}), 1)
25
+ scan("|a|b|c|d|e|f|g|h|i|j|k|l|m|n|o|p|\n")
26
+ end
27
+
28
+ it "should parse multicharacter cell content" do
29
+ @listener.should_receive(:row).with(r(%w{foo bar}), 1)
30
+ scan("| foo | bar |\n")
31
+ end
32
+
33
+ it "should escape backslashed pipes" do
34
+ @listener.should_receive(:row).with(r(['|', 'the', '\a', '\\', '|\\|']), 1)
35
+ scan('| \| | the | \a | \\ | \|\\\| |' + "\n")
36
+ end
37
+
38
+ it "should parse cells with newlines" do
39
+ @listener.should_receive(:row).with(r(["\n"]), 1)
40
+ scan("|\\n|" + "\n")
41
+ end
42
+
43
+ it "should parse cells with spaces within the content" do
44
+ @listener.should_receive(:row).with(r(["Dill pickle", "Valencia orange"]), 1)
45
+ scan("| Dill pickle | Valencia orange |\n")
46
+ end
47
+
48
+ it "should allow utf-8" do
49
+ scan(" | ůﻚ | 2 | \n")
50
+ @listener.to_sexp.should == [
51
+ [:row, ["ůﻚ", "2"], 1],
52
+ [:eof]
53
+ ]
54
+ end
55
+
56
+ it "should allow utf-8 using should_receive" do
57
+ @listener.should_receive(:row).with(r(['繁體中文 而且','並且','繁體中文 而且','並且']), 1)
58
+ scan("| 繁體中文 而且|並且| 繁體中文 而且|並且|\n")
59
+ end
60
+
61
+ it "should parse a 2x2 table" do
62
+ @listener.should_receive(:row).with(r(%w{1 2}), 1)
63
+ @listener.should_receive(:row).with(r(%w{3 4}), 2)
64
+ scan("| 1 | 2 |\n| 3 | 4 |\n")
65
+ end
66
+
67
+ it "should parse a 2x2 table with empty cells" do
68
+ @listener.should_receive(:row).with(r(['1', '']), 1)
69
+ @listener.should_receive(:row).with(r(['', '4']), 2)
70
+ scan("| 1 | |\n|| 4 |\n")
71
+ end
72
+
73
+ it "should parse a row with empty cells" do
74
+ @listener.should_receive(:row).with(r(['1', '']), 1).twice
75
+ scan("| 1 | |\n")
76
+ scan("|1||\n")
77
+ end
78
+
79
+ it "should parse a 1x2 table that does not end in a newline" do
80
+ @listener.should_receive(:row).with(r(%w{1 2}), 1)
81
+ scan("| 1 | 2 |")
82
+ end
83
+
84
+ it "should parse a row without spaces and with a newline" do
85
+ @listener.should_receive(:row).with(r(%w{1 2}), 1)
86
+ scan("|1|2|\n")
87
+ end
88
+
89
+ it "should parse a row with whitespace after the rows" do
90
+ @listener.should_receive(:row).with(r(%w{1 2}), 1)
91
+ scan("| 1 | 2 | \n ")
92
+ end
93
+
94
+ it "should parse a row with lots of whitespace" do
95
+ @listener.should_receive(:row).with(r(["abc", "123"]), 1)
96
+ scan(" \t| \t abc\t| \t123\t \t\t| \t\t \t \t\n ")
97
+ end
98
+
99
+ it "should parse a table with a commented-out row" do
100
+ @listener.should_receive(:row).with(r(["abc"]), 1)
101
+ @listener.should_receive(:comment).with("#|123|", 2)
102
+ @listener.should_receive(:row).with(r(["def"]), 3)
103
+ scan("|abc|\n#|123|\n|def|\n")
104
+ end
105
+
106
+ it "should raise LexingError for rows that aren't closed" do
107
+ lambda {
108
+ scan("|| oh hello \n")
109
+ }.should raise_error(/Lexing error on line 1: '\|\| oh hello/)
110
+ end
111
+
112
+ it "should raise LexingError for rows that are followed by a comment" do
113
+ lambda {
114
+ scan("|hi| # oh hello \n")
115
+ }.should raise_error(/Lexing error on line 1: '\|hi\| # oh hello/)
116
+ end
117
+
118
+ it "should raise LexingError for rows that aren't closed" do
119
+ lambda {
120
+ scan("|| oh hello \n |Shoudn't Get|Here|")
121
+ }.should raise_error(/Lexing error on line 1: '\|\| oh hello/)
122
+ end
123
+ end
124
+ end
125
+ end
@@ -0,0 +1,54 @@
1
+ # encoding: utf-8
2
+ require 'spec_helper'
3
+
4
+ module Gherkin
5
+ module Lexer
6
+ shared_examples_for "a Gherkin lexer lexing tags" do
7
+ def scan(gherkin)
8
+ @lexer.scan(gherkin)
9
+ end
10
+
11
+ it "should lex a single tag" do
12
+ @listener.should_receive(:tag).with("@dog", 1)
13
+ scan("@dog\n")
14
+ end
15
+
16
+ it "should lex multiple tags" do
17
+ @listener.should_receive(:tag).twice
18
+ scan("@dog @cat\n")
19
+ end
20
+
21
+ it "should lex UTF-8 tags" do
22
+ @listener.should_receive(:tag).with("@シナリオテンプレート", 1)
23
+ scan("@シナリオテンプレート\n")
24
+ end
25
+
26
+ it "should lex mixed tags" do
27
+ @listener.should_receive(:tag).with("@wip", 1).ordered
28
+ @listener.should_receive(:tag).with("@Значения", 1).ordered
29
+ scan("@wip @Значения\n")
30
+ end
31
+
32
+ it "should lex wacky identifiers" do
33
+ @listener.should_receive(:tag).exactly(4).times
34
+ scan("@BJ-x98.77 @BJ-z12.33 @O_o" "@#not_a_comment\n")
35
+ end
36
+
37
+ # TODO: Ask on ML for opinions about this one
38
+ it "should lex tags without spaces between them?" do
39
+ @listener.should_receive(:tag).twice
40
+ scan("@one@two\n")
41
+ end
42
+
43
+ it "should not lex tags beginning with two @@ signs" do
44
+ @listener.should_not_receive(:tag)
45
+ lambda { scan("@@test\n") }.should raise_error(/Lexing error on line 1/)
46
+ end
47
+
48
+ it "should not lex a lone @ sign" do
49
+ @listener.should_not_receive(:tag)
50
+ lambda { scan("@\n") }.should raise_error(/Lexing error on line 1/)
51
+ end
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,142 @@
1
+ require 'spec_helper'
2
+ require 'gherkin/tag_expression'
3
+ require 'gherkin/formatter/model'
4
+
5
+ module Gherkin
6
+ describe TagExpression do
7
+ def tag(name)
8
+ Formatter::Model::Tag.new(name, 0)
9
+ end
10
+
11
+ context "no tags" do
12
+ before(:each) do
13
+ @e = Gherkin::TagExpression.new([])
14
+ end
15
+
16
+ it "should match @foo" do
17
+ @e.eval([tag('@foo')]).should == true
18
+ end
19
+
20
+ it "should match empty tags" do
21
+ @e.eval([]).should == true
22
+ end
23
+ end
24
+
25
+ context "@foo" do
26
+ before(:each) do
27
+ @e = Gherkin::TagExpression.new(['@foo'])
28
+ end
29
+
30
+ it "should match @foo" do
31
+ @e.eval([tag('@foo')]).should == true
32
+ end
33
+
34
+ it "should not match @bar" do
35
+ @e.eval([tag('@bar')]).should == false
36
+ end
37
+
38
+ it "should not match no tags" do
39
+ @e.eval([]).should == false
40
+ end
41
+ end
42
+
43
+ context "!@foo" do
44
+ before(:each) do
45
+ @e = Gherkin::TagExpression.new(['~@foo'])
46
+ end
47
+
48
+ it "should match @bar" do
49
+ @e.eval([tag('@bar')]).should == true
50
+ end
51
+
52
+ it "should not match @foo" do
53
+ @e.eval([tag('@foo')]).should == false
54
+ end
55
+ end
56
+
57
+ context "@foo || @bar" do
58
+ before(:each) do
59
+ @e = Gherkin::TagExpression.new(['@foo,@bar'])
60
+ end
61
+
62
+ it "should match @foo" do
63
+ @e.eval([tag('@foo')]).should == true
64
+ end
65
+
66
+ it "should match @bar" do
67
+ @e.eval([tag('@bar')]).should == true
68
+ end
69
+
70
+ it "should not match @zap" do
71
+ @e.eval([tag('@zap')]).should == false
72
+ end
73
+ end
74
+
75
+ context "(@foo || @bar) && !@zap" do
76
+ before(:each) do
77
+ @e = Gherkin::TagExpression.new(['@foo,@bar', '~@zap'])
78
+ end
79
+
80
+ it "should match @foo" do
81
+ @e.eval([tag('@foo')]).should == true
82
+ end
83
+
84
+ it "should not match @foo @zap" do
85
+ @e.eval([tag('@foo'), tag('@zap')]).should == false
86
+ end
87
+ end
88
+
89
+ context "(@foo:3 || !@bar:4) && @zap:5" do
90
+ before(:each) do
91
+ @e = Gherkin::TagExpression.new(['@foo:3,~@bar','@zap:5'])
92
+ end
93
+
94
+ it "should count tags for positive tags" do
95
+ rubify_hash(@e.limits).should == {'@foo' => 3, '@zap' => 5}
96
+ end
97
+
98
+ it "should match @foo @zap" do
99
+ @e.eval([tag('@foo'), tag('@zap')]).should == true
100
+ end
101
+ end
102
+
103
+ context "Parsing '@foo:3,~@bar', '@zap:5'" do
104
+ before(:each) do
105
+ @e = Gherkin::TagExpression.new([' @foo:3 , ~@bar ', ' @zap:5 '])
106
+ end
107
+
108
+ unless defined?(JRUBY_VERSION)
109
+ it "should split and trim (ruby implementation detail)" do
110
+ @e.__send__(:ruby_expression).should == "(!vars['@bar']||vars['@foo'])&&(vars['@zap'])"
111
+ end
112
+ end
113
+
114
+ it "should have limits" do
115
+ rubify_hash(@e.limits).should == {"@zap"=>5, "@foo"=>3}
116
+ end
117
+ end
118
+
119
+ context "Tag limits" do
120
+ it "should be counted for negative tags" do
121
+ @e = Gherkin::TagExpression.new(['~@todo:3'])
122
+ rubify_hash(@e.limits).should == {"@todo"=>3}
123
+ end
124
+
125
+ it "should be counted for positive tags" do
126
+ @e = Gherkin::TagExpression.new(['@todo:3'])
127
+ rubify_hash(@e.limits).should == {"@todo"=>3}
128
+ end
129
+
130
+ it "should raise an error for inconsistent limits" do
131
+ lambda do
132
+ @e = Gherkin::TagExpression.new(['@todo:3', '~@todo:4'])
133
+ end.should raise_error(/Inconsistent tag limits for @todo: 3 and 4/)
134
+ end
135
+
136
+ it "should allow duplicate consistent limits" do
137
+ @e = Gherkin::TagExpression.new(['@todo:3', '~@todo:3'])
138
+ rubify_hash(@e.limits).should == {"@todo"=>3}
139
+ end
140
+ end
141
+ end
142
+ end
@@ -0,0 +1,75 @@
1
+ if RUBY_VERSION =~ /1.9/
2
+ Encoding.default_external = Encoding::UTF_8
3
+ Encoding.default_internal = Encoding::UTF_8
4
+ end
5
+
6
+ require 'rubygems'
7
+ require 'bundler'
8
+ Bundler.setup
9
+
10
+ require 'rspec'
11
+ require 'gherkin'
12
+ require 'stringio'
13
+ require 'gherkin/sexp_recorder'
14
+ require 'gherkin/output_stream_string_io'
15
+ require 'gherkin/shared/bom_group'
16
+ require 'gherkin/shared/lexer_group'
17
+ require 'gherkin/shared/tags_group'
18
+ require 'gherkin/shared/doc_string_group'
19
+ require 'gherkin/shared/row_group'
20
+ $:.unshift(File.dirname(__FILE__))
21
+
22
+ module GherkinSpecHelper
23
+ def scan_file(file)
24
+ @lexer.scan(fixture(file))
25
+ end
26
+
27
+ def fixture(file)
28
+ File.new(File.dirname(__FILE__) + "/gherkin/fixtures/" + file).read
29
+ end
30
+
31
+ def rubify_hash(hash)
32
+ if defined?(JRUBY_VERSION)
33
+ h = {}
34
+ hash.keySet.each{|key| h[key] = hash[key]}
35
+ h
36
+ else
37
+ hash
38
+ end
39
+ end
40
+ end
41
+
42
+ RSpec.configure do |c|
43
+ c.include(GherkinSpecHelper)
44
+ end
45
+
46
+ # Allows comparison of Java List with Ruby Array (rows)
47
+ RSpec::Matchers.define :r do |expected|
48
+ match do |row|
49
+ def row.inspect
50
+ "r " + self.map{|cell| cell}.inspect
51
+ end
52
+ row.map{|cell| cell}.should == expected
53
+ end
54
+ end
55
+
56
+ RSpec::Matchers.define :a do |expected|
57
+ match do |array|
58
+ def array.inspect
59
+ "a " + self.map{|e| e.to_sym}.inspect
60
+ end
61
+ array.map{|e| e.to_sym}.should == expected
62
+ end
63
+ end
64
+
65
+ RSpec::Matchers.define :sym do |expected|
66
+ match do |actual|
67
+ expected.to_s == actual.to_s
68
+ end
69
+ end
70
+
71
+ RSpec::Matchers.define :allow do |event|
72
+ match do |parser|
73
+ parser.expected.index(event)
74
+ end
75
+ end
@@ -0,0 +1,184 @@
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("native_gherkin:") { run_native_gherkin }
77
+ x.report("native_gherkin_no_parser:") { run_native_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 'null_formatter'
110
+ parser = Gherkin::Parser::Parser.new(NullFormatter.new, true, "root", true)
111
+ @features.each do |feature|
112
+ parser.parse(File.read(feature), feature, 0)
113
+ end
114
+ end
115
+
116
+ def run_native_gherkin
117
+ require 'gherkin'
118
+ require 'null_listener'
119
+ parser = Gherkin::Parser::Parser.new(NullFormatter.new, true, "root", false)
120
+ @features.each do |feature|
121
+ parser.parse(File.read(feature), feature, 0)
122
+ end
123
+ end
124
+
125
+ def run_native_gherkin_no_parser
126
+ require 'gherkin'
127
+ require 'gherkin/lexer/i18n_lexer'
128
+ require 'null_listener'
129
+ lexer = Gherkin::Lexer::I18nLexer.new(NullListener.new, false)
130
+ @features.each do |feature|
131
+ lexer.scan(File.read(feature), feature, 0)
132
+ end
133
+ end
134
+ end
135
+
136
+ desc "Generate 500 random features and benchmark Cucumber, Treetop and Gherkin with them"
137
+ task :bench => ["bench:clean", "bench:gen"] do
138
+ benchmarker = Benchmarker.new
139
+ benchmarker.report_all
140
+ end
141
+
142
+ namespace :bench do
143
+ desc "Generate [number] features with random content, or 500 features if number is not provided"
144
+ task :gen, :number do |t, args|
145
+ args.with_defaults(:number => 500)
146
+ generator = RandomFeatureGenerator.new(args.number.to_i)
147
+ generator.generate
148
+ end
149
+
150
+ desc "Benchmark Cucumber AST building from the features in tasks/bench/generated"
151
+ task :cucumber do
152
+ benchmarker = Benchmarker.new
153
+ benchmarker.report("cucumber")
154
+ end
155
+
156
+ desc "Benchmark the Treetop parser with the features in tasks/bench/generated"
157
+ task :tt do
158
+ benchmarker = Benchmarker.new
159
+ benchmarker.report("tt")
160
+ end
161
+
162
+ desc "Benchmark the Ruby Gherkin lexer+parser with the features in tasks/bench/generated"
163
+ task :rb_gherkin do
164
+ benchmarker = Benchmarker.new
165
+ benchmarker.report("rb_gherkin")
166
+ end
167
+
168
+ desc "Benchmark the ntive Gherkin lexer+parser with the features in tasks/bench/generated"
169
+ task :native_gherkin do
170
+ benchmarker = Benchmarker.new
171
+ benchmarker.report("native_gherkin")
172
+ end
173
+
174
+ desc "Benchmark the native Gherkin lexer (no parser) with the features in tasks/bench/generated"
175
+ task :native_gherkin_no_parser do
176
+ benchmarker = Benchmarker.new
177
+ benchmarker.report("native_gherkin_no_parser")
178
+ end
179
+
180
+ desc "Remove all generated features in tasks/bench/generated"
181
+ task :clean do
182
+ rm_f FileList[GENERATED_FEATURES + "/**/*feature"]
183
+ end
184
+ end