cucumber-tag-expressions 2.0.3

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.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 6ace30f7929f56e9f7fb15e914f1cd2a505f7d8d45c938c26e4bbf02e21fbd5a
4
+ data.tar.gz: 39eeb518cb6cba26529321912bb3f81a889ef33387ee4e0bb4212615f5931872
5
+ SHA512:
6
+ metadata.gz: db6b0596392018944363bd639303c1148ac1063e080f89bbb1d81c99b251a2637f1ef79d16b7c74f949069f974854a71ae75d03f95346497c15e4a5013677f84
7
+ data.tar.gz: e0e5c54580a07129712df9a3f1ae23314640576a34529046d942d6da84de71df73973410b07fc18d64e5cf1b0bfaaa766cf6137485338fdece12101a37617d17
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) Cucumber Ltd
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,3 @@
1
+ # Cucumber Tag Expressions for Ruby
2
+
3
+ [The docs are here](https://cucumber.io/docs/cucumber/api/#tag-expressions).
@@ -0,0 +1 @@
1
+ require 'cucumber/tag_expressions/parser'
@@ -0,0 +1,65 @@
1
+ module Cucumber
2
+ module TagExpressions
3
+ # Literal expression node
4
+ class Literal
5
+ def initialize(value)
6
+ @value = value.gsub(/\\\(/, '(').gsub(/\\\)/, ')')
7
+ end
8
+
9
+ def evaluate(variables)
10
+ variables.include?(@value)
11
+ end
12
+
13
+ def to_s
14
+ @value.gsub(/\(/, '\\(').gsub(/\)/, '\\)')
15
+ end
16
+ end
17
+
18
+ # Not expression node
19
+ class Not
20
+ def initialize(expression)
21
+ @expression = expression
22
+ end
23
+
24
+ def evaluate(variables)
25
+ !@expression.evaluate(variables)
26
+ end
27
+
28
+ def to_s
29
+ "not ( #{@expression} )"
30
+ end
31
+ end
32
+
33
+ # Or expression node
34
+ class Or
35
+ def initialize(left, right)
36
+ @left = left
37
+ @right = right
38
+ end
39
+
40
+ def evaluate(variables)
41
+ @left.evaluate(variables) || @right.evaluate(variables)
42
+ end
43
+
44
+ def to_s
45
+ "( #{@left} or #{@right} )"
46
+ end
47
+ end
48
+
49
+ # And expression node
50
+ class And
51
+ def initialize(left, right)
52
+ @left = left
53
+ @right = right
54
+ end
55
+
56
+ def evaluate(variables)
57
+ @left.evaluate(variables) && @right.evaluate(variables)
58
+ end
59
+
60
+ def to_s
61
+ "( #{@left} and #{@right} )"
62
+ end
63
+ end
64
+ end
65
+ end
@@ -0,0 +1,137 @@
1
+ require 'cucumber/tag_expressions/expressions.rb'
2
+
3
+ module Cucumber
4
+ module TagExpressions
5
+ # Ruby tag expression parser
6
+ class Parser
7
+ def initialize
8
+ @expressions = []
9
+ @operators = []
10
+
11
+ @operator_types = {
12
+ 'or' => { type: :binary_operator, precedence: 0, assoc: :left },
13
+ 'and' => { type: :binary_operator, precedence: 1, assoc: :left },
14
+ 'not' => { type: :unary_operator, precedence: 2, assoc: :right },
15
+ ')' => { type: :close_paren, precedence: -1 },
16
+ '(' => { type: :open_paren, precedence: 1 }
17
+ }
18
+ end
19
+
20
+ def parse(infix_expression)
21
+ process_tokens!(infix_expression)
22
+ while @operators.any?
23
+ raise 'Syntax error. Unmatched (' if @operators.last == '('
24
+ push_expression(pop(@operators))
25
+ end
26
+ expression = pop(@expressions)
27
+ @expressions.empty? ? expression : raise('Not empty')
28
+ end
29
+
30
+ private
31
+
32
+ ############################################################################
33
+ # Helpers
34
+ #
35
+ def assoc_of(token, value)
36
+ @operator_types[token][:assoc] == value
37
+ end
38
+
39
+ def lower_precedence?(operation)
40
+ (assoc_of(operation, :left) &&
41
+ precedence(operation) <= precedence(@operators.last)) ||
42
+ (assoc_of(operation, :right) &&
43
+ precedence(operation) < precedence(@operators.last))
44
+ end
45
+
46
+ def operator?(token)
47
+ @operator_types[token][:type] == :unary_operator ||
48
+ @operator_types[token][:type] == :binary_operator
49
+ end
50
+
51
+ def precedence(token)
52
+ @operator_types[token][:precedence]
53
+ end
54
+
55
+ def tokens(infix_expression)
56
+ infix_expression.gsub(/(?<!\\)\(/, ' ( ').gsub(/(?<!\\)\)/, ' ) ').strip.split(/\s+/)
57
+ end
58
+
59
+ def process_tokens!(infix_expression)
60
+ expected_token_type = :operand
61
+ tokens(infix_expression).each do |token|
62
+ if @operator_types[token]
63
+ expected_token_type = send("handle_#{@operator_types[token][:type]}", token, expected_token_type)
64
+ else
65
+ expected_token_type = handle_literal(token, expected_token_type)
66
+ end
67
+ end
68
+ end
69
+
70
+ def push_expression(token)
71
+ case token
72
+ when 'and'
73
+ @expressions.push(And.new(*pop(@expressions, 2)))
74
+ when 'or'
75
+ @expressions.push(Or.new(*pop(@expressions, 2)))
76
+ when 'not'
77
+ @expressions.push(Not.new(pop(@expressions)))
78
+ else
79
+ @expressions.push(Literal.new(token))
80
+ end
81
+ end
82
+
83
+ ############################################################################
84
+ # Handlers
85
+ #
86
+ def handle_unary_operator(token, expected_token_type)
87
+ check(expected_token_type, :operand)
88
+ @operators.push(token)
89
+ :operand
90
+ end
91
+
92
+ def handle_binary_operator(token, expected_token_type)
93
+ check(expected_token_type, :operator)
94
+ while @operators.any? && operator?(@operators.last) &&
95
+ lower_precedence?(token)
96
+ push_expression(pop(@operators))
97
+ end
98
+ @operators.push(token)
99
+ :operand
100
+ end
101
+
102
+ def handle_open_paren(token, expected_token_type)
103
+ check(expected_token_type, :operand)
104
+ @operators.push(token)
105
+ :operand
106
+ end
107
+
108
+ def handle_close_paren(_token, expected_token_type)
109
+ check(expected_token_type, :operator)
110
+ while @operators.any? && @operators.last != '('
111
+ push_expression(pop(@operators))
112
+ end
113
+ raise 'Syntax error. Unmatched )' if @operators.empty?
114
+ pop(@operators) if @operators.last == '('
115
+ :operator
116
+ end
117
+
118
+ def handle_literal(token, expected_token_type)
119
+ check(expected_token_type, :operand)
120
+ push_expression(token)
121
+ :operator
122
+ end
123
+
124
+ def check(expected_token_type, token_type)
125
+ if expected_token_type != token_type
126
+ raise "Syntax error. Expected #{expected_token_type}"
127
+ end
128
+ end
129
+
130
+ def pop(array, n = 1)
131
+ result = array.pop(n)
132
+ raise('Empty stack') if result.size != n
133
+ n == 1 ? result.first : result
134
+ end
135
+ end
136
+ end
137
+ end
@@ -0,0 +1,74 @@
1
+ # frozen_string_literal: true
2
+ # With thanks to @myronmarston
3
+ # https://github.com/vcr/vcr/blob/master/spec/capture_warnings.rb
4
+
5
+ module CaptureWarnings
6
+ def report_warnings(&block)
7
+ current_dir = Dir.pwd
8
+ warnings, errors = capture_error(&block).partition { |line| line.include?('warning') }
9
+ project_warnings, other_warnings = warnings.uniq.partition { |line| line.include?(current_dir) }
10
+
11
+ if errors.any?
12
+ puts errors.join("\n")
13
+ end
14
+
15
+ if other_warnings.any?
16
+ puts "#{ other_warnings.count } warnings detected, set VIEW_OTHER_WARNINGS=true to see them."
17
+ print_warnings('other', other_warnings) if ENV['VIEW_OTHER_WARNINGS']
18
+ end
19
+
20
+ # Until they fix https://bugs.ruby-lang.org/issues/10661
21
+ if RUBY_VERSION == "2.2.0"
22
+ project_warnings = project_warnings.reject { |w| w =~ /warning: possible reference to past scope/ }
23
+ end
24
+
25
+ if project_warnings.any?
26
+ puts "#{ project_warnings.count } warnings detected"
27
+ print_warnings('cucumber-expressions', project_warnings)
28
+ fail "Please remove all cucumber-expressions warnings."
29
+ end
30
+
31
+ ensure_system_exit_if_required
32
+ end
33
+
34
+ def capture_error(&block)
35
+ old_stderr = STDERR.clone
36
+ pipe_r, pipe_w = IO.pipe
37
+ pipe_r.sync = true
38
+ error = String.new
39
+ reader = Thread.new do
40
+ begin
41
+ loop do
42
+ error << pipe_r.readpartial(1024)
43
+ end
44
+ rescue EOFError
45
+ end
46
+ end
47
+ STDERR.reopen(pipe_w)
48
+ block.call
49
+ ensure
50
+ capture_system_exit
51
+ STDERR.reopen(old_stderr)
52
+ pipe_w.close
53
+ reader.join
54
+ return error.split("\n")
55
+ end
56
+
57
+ def print_warnings(type, warnings)
58
+ puts
59
+ puts "-" * 30 + " #{type} warnings: " + "-" * 30
60
+ puts
61
+ puts warnings.join("\n")
62
+ puts
63
+ puts "-" * 75
64
+ puts
65
+ end
66
+
67
+ def ensure_system_exit_if_required
68
+ raise @system_exit if @system_exit
69
+ end
70
+
71
+ def capture_system_exit
72
+ @system_exit = $!
73
+ end
74
+ end
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+ require 'simplecov'
3
+ formatters = [ SimpleCov::Formatter::HTMLFormatter ]
4
+
5
+ SimpleCov.formatter = SimpleCov::Formatter::MultiFormatter.new(*formatters)
6
+ SimpleCov.add_filter 'spec/'
7
+ SimpleCov.start
@@ -0,0 +1,55 @@
1
+ require 'cucumber/tag_expressions/parser'
2
+
3
+ describe 'Expression node' do
4
+ shared_examples 'expression node' do |infix_expression, data|
5
+ data.each do |tags, result|
6
+ it "#{infix_expression.inspect} with variables: #{tags.inspect}'" do
7
+ expression = Cucumber::TagExpressions::Parser.new.parse(infix_expression)
8
+ expect(expression.evaluate(tags)).to eq(result)
9
+ end
10
+ end
11
+ end
12
+
13
+ describe Cucumber::TagExpressions::Not do
14
+ context '#evaluate' do
15
+ infix_expression = 'not x'
16
+ data = [[%w(x), false],
17
+ [%w(y), true]]
18
+ include_examples 'expression node', infix_expression, data
19
+ end
20
+ end
21
+
22
+ describe Cucumber::TagExpressions::And do
23
+ context '#evaluate' do
24
+ infix_expression = 'x and y'
25
+ data = [[%w(x y), true],
26
+ [%w(x), false],
27
+ [%w(y), false]]
28
+ include_examples 'expression node', infix_expression, data
29
+ end
30
+ end
31
+
32
+ describe Cucumber::TagExpressions::Or do
33
+ context '#evaluate' do
34
+ infix_expression = 'x or y'
35
+ data = [[%w(), false],
36
+ [%w(x), true],
37
+ [%w(y), true],
38
+ [%w(x q), true],
39
+ [%w(x y), true]]
40
+ include_examples 'expression node', infix_expression, data
41
+ end
42
+ end
43
+
44
+ describe Cucumber::TagExpressions::Or do
45
+ context '#evaluate' do
46
+ infix_expression = 'x\\(1\\) or (y\\(2\\))'
47
+ data = [[%w(), false],
48
+ [%w(x), false],
49
+ [%w(y), false],
50
+ [%w(x(1)), true],
51
+ [%w(y(2)), true]]
52
+ include_examples 'expression node', infix_expression, data
53
+ end
54
+ end
55
+ end
@@ -0,0 +1,46 @@
1
+ require 'cucumber/tag_expressions/parser'
2
+
3
+ describe Cucumber::TagExpressions::Parser do
4
+ correct_test_data = [
5
+ ['a and b', '( a and b )'],
6
+ ['a or (b)', '( a or b )'],
7
+ ['not a', 'not ( a )'],
8
+ ['( a and b ) or ( c and d )', '( ( a and b ) or ( c and d ) )'],
9
+ ['not a or b and not c or not d or e and f',
10
+ '( ( ( not ( a ) or ( b and not ( c ) ) ) or not ( d ) ) or ( e and f ) )'],
11
+ ['not a\\(\\) or b and not c or not d or e and f',
12
+ '( ( ( not ( a\\(\\) ) or ( b and not ( c ) ) ) or not ( d ) ) or ( e and f ) )']
13
+ ]
14
+
15
+ error_test_data = [
16
+ ['@a @b or', 'Syntax error. Expected operator'],
17
+ ['@a and (@b not)', 'Syntax error. Expected operator'],
18
+ ['@a and (@b @c) or', 'Syntax error. Expected operator'],
19
+ ['@a and or', 'Syntax error. Expected operand'],
20
+ ['or or', 'Syntax error. Expected operand'],
21
+ ['a b', 'Syntax error. Expected operator'],
22
+ ['( a and b ) )', 'Syntax error. Unmatched )'],
23
+ ['( ( a and b )', 'Syntax error. Unmatched ('],
24
+ ]
25
+
26
+ context '#parse' do
27
+ context 'with correct test data' do
28
+ correct_test_data.each do |infix_expression, to_string|
29
+ parser = Cucumber::TagExpressions::Parser.new
30
+ it "parses correctly #{infix_expression.inspect}" do
31
+ expect(parser.parse(infix_expression).to_s).to eq(to_string)
32
+ end
33
+ end
34
+ end
35
+
36
+ context 'with error test data' do
37
+ error_test_data.each do |infix_expression, message|
38
+ parser = Cucumber::TagExpressions::Parser.new
39
+ it "raises an error parsing #{infix_expression.inspect}" do
40
+ expect { parser.parse(infix_expression) }
41
+ .to raise_error(RuntimeError, message)
42
+ end
43
+ end
44
+ end
45
+ end
46
+ end
metadata ADDED
@@ -0,0 +1,123 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: cucumber-tag-expressions
3
+ version: !ruby/object:Gem::Version
4
+ version: 2.0.3
5
+ platform: ruby
6
+ authors:
7
+ - Andrea Nodari
8
+ - Aslak Hellesøy
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2019-12-11 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: rake
16
+ requirement: !ruby/object:Gem::Requirement
17
+ requirements:
18
+ - - "~>"
19
+ - !ruby/object:Gem::Version
20
+ version: '13.0'
21
+ - - ">="
22
+ - !ruby/object:Gem::Version
23
+ version: 13.0.1
24
+ type: :development
25
+ prerelease: false
26
+ version_requirements: !ruby/object:Gem::Requirement
27
+ requirements:
28
+ - - "~>"
29
+ - !ruby/object:Gem::Version
30
+ version: '13.0'
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: 13.0.1
34
+ - !ruby/object:Gem::Dependency
35
+ name: rspec
36
+ requirement: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '3.9'
41
+ - - ">="
42
+ - !ruby/object:Gem::Version
43
+ version: 3.9.0
44
+ type: :development
45
+ prerelease: false
46
+ version_requirements: !ruby/object:Gem::Requirement
47
+ requirements:
48
+ - - "~>"
49
+ - !ruby/object:Gem::Version
50
+ version: '3.9'
51
+ - - ">="
52
+ - !ruby/object:Gem::Version
53
+ version: 3.9.0
54
+ - !ruby/object:Gem::Dependency
55
+ name: coveralls
56
+ requirement: !ruby/object:Gem::Requirement
57
+ requirements:
58
+ - - "~>"
59
+ - !ruby/object:Gem::Version
60
+ version: '0.8'
61
+ - - ">="
62
+ - !ruby/object:Gem::Version
63
+ version: 0.8.23
64
+ type: :development
65
+ prerelease: false
66
+ version_requirements: !ruby/object:Gem::Requirement
67
+ requirements:
68
+ - - "~>"
69
+ - !ruby/object:Gem::Version
70
+ version: '0.8'
71
+ - - ">="
72
+ - !ruby/object:Gem::Version
73
+ version: 0.8.23
74
+ description: Cucumber tag expressions for ruby
75
+ email: cukes@googlegroups.com
76
+ executables: []
77
+ extensions: []
78
+ extra_rdoc_files: []
79
+ files:
80
+ - LICENSE
81
+ - README.md
82
+ - lib/cucumber/tag_expressions.rb
83
+ - lib/cucumber/tag_expressions/expressions.rb
84
+ - lib/cucumber/tag_expressions/parser.rb
85
+ - spec/capture_warnings.rb
86
+ - spec/coverage.rb
87
+ - spec/expressions_spec.rb
88
+ - spec/parser_spec.rb
89
+ homepage: https://cucumber.io/docs/cucumber/api/#tag-expressions
90
+ licenses:
91
+ - MIT
92
+ metadata:
93
+ bug_tracker_uri: https://github.com/cucumber/cucumber/issues
94
+ changelog_uri: https://github.com/cucumber/cucumber/blob/master/tag-expressions/CHANGELOG.md
95
+ documentation_uri: https://cucumber.io/docs/cucumber/api/#tag-expressions
96
+ mailing_list_uri: https://groups.google.com/forum/#!forum/cukes
97
+ source_code_uri: https://github.com/cucumber/cucumber/blob/master/tag-expressions/ruby
98
+ post_install_message:
99
+ rdoc_options:
100
+ - "--charset=UTF-8"
101
+ require_paths:
102
+ - lib
103
+ required_ruby_version: !ruby/object:Gem::Requirement
104
+ requirements:
105
+ - - ">="
106
+ - !ruby/object:Gem::Version
107
+ version: 1.9.3
108
+ required_rubygems_version: !ruby/object:Gem::Requirement
109
+ requirements:
110
+ - - ">="
111
+ - !ruby/object:Gem::Version
112
+ version: '0'
113
+ requirements: []
114
+ rubyforge_project:
115
+ rubygems_version: 2.7.6.2
116
+ signing_key:
117
+ specification_version: 4
118
+ summary: cucumber-tag-expressions-2.0.3
119
+ test_files:
120
+ - spec/capture_warnings.rb
121
+ - spec/parser_spec.rb
122
+ - spec/coverage.rb
123
+ - spec/expressions_spec.rb