summaryrefslogtreecommitdiff
path: root/spec/lib/gitlab/ci/pipeline/expression
diff options
context:
space:
mode:
Diffstat (limited to 'spec/lib/gitlab/ci/pipeline/expression')
-rw-r--r--spec/lib/gitlab/ci/pipeline/expression/lexeme/and_spec.rb2
-rw-r--r--spec/lib/gitlab/ci/pipeline/expression/lexeme/equals_spec.rb2
-rw-r--r--spec/lib/gitlab/ci/pipeline/expression/lexeme/matches_spec.rb2
-rw-r--r--spec/lib/gitlab/ci/pipeline/expression/lexeme/not_equals_spec.rb2
-rw-r--r--spec/lib/gitlab/ci/pipeline/expression/lexeme/not_matches_spec.rb2
-rw-r--r--spec/lib/gitlab/ci/pipeline/expression/lexeme/or_spec.rb2
-rw-r--r--spec/lib/gitlab/ci/pipeline/expression/lexeme/pattern_spec.rb2
-rw-r--r--spec/lib/gitlab/ci/pipeline/expression/lexer_spec.rb29
-rw-r--r--spec/lib/gitlab/ci/pipeline/expression/parser_spec.rb117
-rw-r--r--spec/lib/gitlab/ci/pipeline/expression/statement_spec.rb14
10 files changed, 139 insertions, 35 deletions
diff --git a/spec/lib/gitlab/ci/pipeline/expression/lexeme/and_spec.rb b/spec/lib/gitlab/ci/pipeline/expression/lexeme/and_spec.rb
index 6601537a2d3..1448b045b18 100644
--- a/spec/lib/gitlab/ci/pipeline/expression/lexeme/and_spec.rb
+++ b/spec/lib/gitlab/ci/pipeline/expression/lexeme/and_spec.rb
@@ -24,7 +24,7 @@ RSpec.describe Gitlab::Ci::Pipeline::Expression::Lexeme::And do
describe '.type' do
it 'is an operator' do
- expect(described_class.type).to eq :operator
+ expect(described_class.type).to eq :logical_operator
end
end
diff --git a/spec/lib/gitlab/ci/pipeline/expression/lexeme/equals_spec.rb b/spec/lib/gitlab/ci/pipeline/expression/lexeme/equals_spec.rb
index 2bed47f0a87..ab223ae41fa 100644
--- a/spec/lib/gitlab/ci/pipeline/expression/lexeme/equals_spec.rb
+++ b/spec/lib/gitlab/ci/pipeline/expression/lexeme/equals_spec.rb
@@ -27,7 +27,7 @@ RSpec.describe Gitlab::Ci::Pipeline::Expression::Lexeme::Equals do
describe '.type' do
it 'is an operator' do
- expect(described_class.type).to eq :operator
+ expect(described_class.type).to eq :logical_operator
end
end
diff --git a/spec/lib/gitlab/ci/pipeline/expression/lexeme/matches_spec.rb b/spec/lib/gitlab/ci/pipeline/expression/lexeme/matches_spec.rb
index efcea0b0e09..0da04d8dcf7 100644
--- a/spec/lib/gitlab/ci/pipeline/expression/lexeme/matches_spec.rb
+++ b/spec/lib/gitlab/ci/pipeline/expression/lexeme/matches_spec.rb
@@ -28,7 +28,7 @@ RSpec.describe Gitlab::Ci::Pipeline::Expression::Lexeme::Matches do
describe '.type' do
it 'is an operator' do
- expect(described_class.type).to eq :operator
+ expect(described_class.type).to eq :logical_operator
end
end
diff --git a/spec/lib/gitlab/ci/pipeline/expression/lexeme/not_equals_spec.rb b/spec/lib/gitlab/ci/pipeline/expression/lexeme/not_equals_spec.rb
index a81e1713ef0..3cde4c5d9dc 100644
--- a/spec/lib/gitlab/ci/pipeline/expression/lexeme/not_equals_spec.rb
+++ b/spec/lib/gitlab/ci/pipeline/expression/lexeme/not_equals_spec.rb
@@ -27,7 +27,7 @@ RSpec.describe Gitlab::Ci::Pipeline::Expression::Lexeme::NotEquals do
describe '.type' do
it 'is an operator' do
- expect(described_class.type).to eq :operator
+ expect(described_class.type).to eq :logical_operator
end
end
diff --git a/spec/lib/gitlab/ci/pipeline/expression/lexeme/not_matches_spec.rb b/spec/lib/gitlab/ci/pipeline/expression/lexeme/not_matches_spec.rb
index f44fe19f86d..9bff2355d58 100644
--- a/spec/lib/gitlab/ci/pipeline/expression/lexeme/not_matches_spec.rb
+++ b/spec/lib/gitlab/ci/pipeline/expression/lexeme/not_matches_spec.rb
@@ -28,7 +28,7 @@ RSpec.describe Gitlab::Ci::Pipeline::Expression::Lexeme::NotMatches do
describe '.type' do
it 'is an operator' do
- expect(described_class.type).to eq :operator
+ expect(described_class.type).to eq :logical_operator
end
end
diff --git a/spec/lib/gitlab/ci/pipeline/expression/lexeme/or_spec.rb b/spec/lib/gitlab/ci/pipeline/expression/lexeme/or_spec.rb
index 7fe445975eb..c7d89c4e1e9 100644
--- a/spec/lib/gitlab/ci/pipeline/expression/lexeme/or_spec.rb
+++ b/spec/lib/gitlab/ci/pipeline/expression/lexeme/or_spec.rb
@@ -24,7 +24,7 @@ RSpec.describe Gitlab::Ci::Pipeline::Expression::Lexeme::Or do
describe '.type' do
it 'is an operator' do
- expect(described_class.type).to eq :operator
+ expect(described_class.type).to eq :logical_operator
end
end
diff --git a/spec/lib/gitlab/ci/pipeline/expression/lexeme/pattern_spec.rb b/spec/lib/gitlab/ci/pipeline/expression/lexeme/pattern_spec.rb
index 1a56a91c471..fa4f8a20984 100644
--- a/spec/lib/gitlab/ci/pipeline/expression/lexeme/pattern_spec.rb
+++ b/spec/lib/gitlab/ci/pipeline/expression/lexeme/pattern_spec.rb
@@ -70,7 +70,7 @@ RSpec.describe Gitlab::Ci::Pipeline::Expression::Lexeme::Pattern do
.to eq Gitlab::UntrustedRegexp.new('pattern')
end
- it 'is a eager scanner for regexp boundaries' do
+ it 'is an eager scanner for regexp boundaries' do
scanner = StringScanner.new('/some .* / pattern/')
token = described_class.scan(scanner)
diff --git a/spec/lib/gitlab/ci/pipeline/expression/lexer_spec.rb b/spec/lib/gitlab/ci/pipeline/expression/lexer_spec.rb
index 61c6ced4dac..6e242faa885 100644
--- a/spec/lib/gitlab/ci/pipeline/expression/lexer_spec.rb
+++ b/spec/lib/gitlab/ci/pipeline/expression/lexer_spec.rb
@@ -81,6 +81,35 @@ RSpec.describe Gitlab::Ci::Pipeline::Expression::Lexer do
with_them do
it { is_expected.to eq(tokens) }
end
+
+ context 'with parentheses are used' do
+ where(:expression, :tokens) do
+ '($PRESENT_VARIABLE =~ /my var/) && $EMPTY_VARIABLE =~ /nope/' | ['(', '$PRESENT_VARIABLE', '=~', '/my var/', ')', '&&', '$EMPTY_VARIABLE', '=~', '/nope/']
+ '$PRESENT_VARIABLE =~ /my var/ || ($EMPTY_VARIABLE =~ /nope/)' | ['$PRESENT_VARIABLE', '=~', '/my var/', '||', '(', '$EMPTY_VARIABLE', '=~', '/nope/', ')']
+ '($PRESENT_VARIABLE && (null || $EMPTY_VARIABLE == ""))' | ['(', '$PRESENT_VARIABLE', '&&', '(', 'null', '||', '$EMPTY_VARIABLE', '==', '""', ')', ')']
+ end
+
+ with_them do
+ context 'when ci_if_parenthesis_enabled is enabled' do
+ before do
+ stub_feature_flags(ci_if_parenthesis_enabled: true)
+ end
+
+ it { is_expected.to eq(tokens) }
+ end
+
+ context 'when ci_if_parenthesis_enabled is disabled' do
+ before do
+ stub_feature_flags(ci_if_parenthesis_enabled: false)
+ end
+
+ it do
+ expect { subject }
+ .to raise_error described_class::SyntaxError
+ end
+ end
+ end
+ end
end
end
diff --git a/spec/lib/gitlab/ci/pipeline/expression/parser_spec.rb b/spec/lib/gitlab/ci/pipeline/expression/parser_spec.rb
index 1704cabfd2e..3394a75ac0a 100644
--- a/spec/lib/gitlab/ci/pipeline/expression/parser_spec.rb
+++ b/spec/lib/gitlab/ci/pipeline/expression/parser_spec.rb
@@ -1,51 +1,79 @@
# frozen_string_literal: true
-require 'fast_spec_helper'
+require 'spec_helper'
RSpec.describe Gitlab::Ci::Pipeline::Expression::Parser do
+ before do
+ stub_feature_flags(ci_if_parenthesis_enabled: true)
+ end
+
describe '#tree' do
- context 'when using two operators' do
- it 'returns a reverse descent parse tree' do
- expect(described_class.seed('$VAR1 == "123"').tree)
- .to be_a Gitlab::Ci::Pipeline::Expression::Lexeme::Equals
+ context 'validates simple operators' do
+ using RSpec::Parameterized::TableSyntax
+
+ where(:expression, :result_tree) do
+ '$VAR1 == "123"' | 'equals($VAR1, "123")'
+ '$VAR1 == "123" == $VAR2' | 'equals(equals($VAR1, "123"), $VAR2)'
+ '$VAR' | '$VAR'
+ '"some value"' | '"some value"'
+ 'null' | 'null'
+ '$VAR1 || $VAR2 && $VAR3' | 'or($VAR1, and($VAR2, $VAR3))'
+ '$VAR1 && $VAR2 || $VAR3' | 'or(and($VAR1, $VAR2), $VAR3)'
+ '$VAR1 && $VAR2 || $VAR3 && $VAR4' | 'or(and($VAR1, $VAR2), and($VAR3, $VAR4))'
+ '$VAR1 && ($VAR2 || $VAR3) && $VAR4' | 'and(and($VAR1, or($VAR2, $VAR3)), $VAR4)'
end
- end
- context 'when using three operators' do
- it 'returns a reverse descent parse tree' do
- expect(described_class.seed('$VAR1 == "123" == $VAR2').tree)
- .to be_a Gitlab::Ci::Pipeline::Expression::Lexeme::Equals
+ with_them do
+ it { expect(described_class.seed(expression).tree.inspect).to eq(result_tree) }
end
end
- context 'when using a single variable token' do
- it 'returns a single token instance' do
- expect(described_class.seed('$VAR').tree)
- .to be_a Gitlab::Ci::Pipeline::Expression::Lexeme::Variable
+ context 'when combining && and OR operators' do
+ subject { described_class.seed('$VAR1 == "a" || $VAR2 == "b" && $VAR3 == "c" || $VAR4 == "d" && $VAR5 == "e"').tree }
+
+ context 'when parenthesis engine is enabled' do
+ before do
+ stub_feature_flags(ci_if_parenthesis_enabled: true)
+ end
+
+ it 'returns operations in a correct order' do
+ expect(subject.inspect)
+ .to eq('or(or(equals($VAR1, "a"), and(equals($VAR2, "b"), equals($VAR3, "c"))), and(equals($VAR4, "d"), equals($VAR5, "e")))')
+ end
+ end
+
+ context 'when parenthesis engine is disabled (legacy)' do
+ before do
+ stub_feature_flags(ci_if_parenthesis_enabled: false)
+ end
+
+ it 'returns operations in a invalid order' do
+ expect(subject.inspect)
+ .to eq('or(equals($VAR1, "a"), and(equals($VAR2, "b"), or(equals($VAR3, "c"), and(equals($VAR4, "d"), equals($VAR5, "e")))))')
+ end
end
end
- context 'when using a single string token' do
- it 'returns a single token instance' do
- expect(described_class.seed('"some value"').tree)
- .to be_a Gitlab::Ci::Pipeline::Expression::Lexeme::String
+ context 'when using parenthesis' do
+ subject { described_class.seed('(($VAR1 == "a" || $VAR2 == "b") && $VAR3 == "c" || $VAR4 == "d") && $VAR5 == "e"').tree }
+
+ before do
+ stub_feature_flags(ci_if_parenthesis_enabled: true)
+ end
+
+ it 'returns operations in a correct order' do
+ expect(subject.inspect)
+ .to eq('and(or(and(or(equals($VAR1, "a"), equals($VAR2, "b")), equals($VAR3, "c")), equals($VAR4, "d")), equals($VAR5, "e"))')
end
end
context 'when expression is empty' do
- it 'returns a null token' do
+ it 'raises a parsing error' do
expect { described_class.seed('').tree }
.to raise_error Gitlab::Ci::Pipeline::Expression::Parser::ParseError
end
end
- context 'when expression is null' do
- it 'returns a null token' do
- expect(described_class.seed('null').tree)
- .to be_a Gitlab::Ci::Pipeline::Expression::Lexeme::Null
- end
- end
-
context 'when two value tokens have no operator' do
it 'raises a parsing error' do
expect { described_class.seed('$VAR "text"').tree }
@@ -66,5 +94,42 @@ RSpec.describe Gitlab::Ci::Pipeline::Expression::Parser do
.to raise_error Gitlab::Ci::Pipeline::Expression::Lexeme::Operator::OperatorError
end
end
+
+ context 'when parenthesis are unmatched' do
+ context 'when parenthesis engine is enabled' do
+ before do
+ stub_feature_flags(ci_if_parenthesis_enabled: true)
+ end
+
+ where(:expression) do
+ [
+ '$VAR == (',
+ '$VAR2 == ("aa"',
+ '$VAR2 == ("aa"))',
+ '$VAR2 == "aa")',
+ '(($VAR2 == "aa")',
+ '($VAR2 == "aa"))'
+ ]
+ end
+
+ with_them do
+ it 'raises a ParseError' do
+ expect { described_class.seed(expression).tree }
+ .to raise_error Gitlab::Ci::Pipeline::Expression::Parser::ParseError
+ end
+ end
+ end
+
+ context 'when parenthesis engine is disabled' do
+ before do
+ stub_feature_flags(ci_if_parenthesis_enabled: false)
+ end
+
+ it 'raises an SyntaxError' do
+ expect { described_class.seed('$VAR == (').tree }
+ .to raise_error Gitlab::Ci::Pipeline::Expression::Lexer::SyntaxError
+ end
+ end
+ end
end
end
diff --git a/spec/lib/gitlab/ci/pipeline/expression/statement_spec.rb b/spec/lib/gitlab/ci/pipeline/expression/statement_spec.rb
index 642d6816030..cf3644c9ad5 100644
--- a/spec/lib/gitlab/ci/pipeline/expression/statement_spec.rb
+++ b/spec/lib/gitlab/ci/pipeline/expression/statement_spec.rb
@@ -1,7 +1,6 @@
# frozen_string_literal: true
-require 'fast_spec_helper'
-require 'rspec-parameterized'
+require 'spec_helper'
RSpec.describe Gitlab::Ci::Pipeline::Expression::Statement do
subject do
@@ -109,6 +108,17 @@ RSpec.describe Gitlab::Ci::Pipeline::Expression::Statement do
'$UNDEFINED_VARIABLE || $PRESENT_VARIABLE' | 'my variable'
'$UNDEFINED_VARIABLE == null || $PRESENT_VARIABLE' | true
'$PRESENT_VARIABLE || $UNDEFINED_VARIABLE == null' | 'my variable'
+
+ '($PRESENT_VARIABLE)' | 'my variable'
+ '(($PRESENT_VARIABLE))' | 'my variable'
+ '(($PRESENT_VARIABLE && null) || $EMPTY_VARIABLE == "")' | true
+ '($PRESENT_VARIABLE) && (null || $EMPTY_VARIABLE == "")' | true
+ '("string" || "test") == "string"' | true
+ '(null || ("test" == "string"))' | false
+ '("string" == ("test" && "string"))' | true
+ '("string" == ("test" || "string"))' | false
+ '("string" == "test" || "string")' | "string"
+ '("string" == ("string" || (("1" == "1") && ("2" == "3"))))' | true
end
with_them do