Skip to content

Commit 66caac8

Browse files
pekkaklarckyanne
authored andcommitted
Handle various inline IF error situations.
Issue #4093 is about to be ready. Documentation still missing, though.
1 parent 09e5fd3 commit 66caac8

File tree

8 files changed

+350
-35
lines changed

8 files changed

+350
-35
lines changed

atest/robot/running/if/inline_if_else.robot

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,16 @@ Resource if.resource
55

66
*** Test Cases ***
77
IF passing
8-
PASS else=False
8+
PASS
99

1010
IF failing
11-
FAIL else=False
11+
FAIL
12+
13+
IF erroring
14+
FAIL
1215

1316
Not executed
14-
NOT RUN else=False
17+
NOT RUN
1518

1619
Not executed after failure
1720
NOT RUN NOT RUN NOT RUN index=1 run=False
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
*** Settings ***
2+
Suite Setup Run Tests ${EMPTY} running/if/invalid_inline_if.robot
3+
Resource atest_resource.robot
4+
5+
*** Test Cases ***
6+
Invalid condition
7+
Check Test Case ${TESTNAME}
8+
9+
Empty IF
10+
Check Test Case ${TESTNAME}
11+
12+
IF without branch
13+
Check Test Case ${TESTNAME}
14+
15+
IF without branch with ELSE IF
16+
Check Test Case ${TESTNAME}
17+
18+
IF without branch with ELSE
19+
Check Test Case ${TESTNAME}
20+
21+
IF follewed by ELSE IF
22+
Check Test Case ${TESTNAME}
23+
24+
IF follewed by ELSE
25+
Check Test Case ${TESTNAME}
26+
27+
Empty ELSE IF
28+
Check Test Case ${TESTNAME} 1
29+
Check Test Case ${TESTNAME} 2
30+
31+
ELSE IF without branch
32+
Check Test Case ${TESTNAME} 1
33+
Check Test Case ${TESTNAME} 2
34+
35+
Empty ELSE
36+
Check Test Case ${TESTNAME}
37+
38+
ELSE IF after ELSE
39+
Check Test Case ${TESTNAME} 1
40+
Check Test Case ${TESTNAME} 2
41+
42+
Multiple ELSEs
43+
Check Test Case ${TESTNAME} 1
44+
Check Test Case ${TESTNAME} 2
45+
46+
Nested IF
47+
Check Test Case ${TESTNAME} 1
48+
Check Test Case ${TESTNAME} 2
49+
Check Test Case ${TESTNAME} 3
50+
51+
Unnecessary END
52+
Check Test Case ${TESTNAME}
53+
54+
Assign in IF branch
55+
Check Test Case ${TESTNAME}
56+
57+
Assign in ELSE IF branch
58+
Check Test Case ${TESTNAME}
59+
60+
Assign in ELSE branch
61+
Check Test Case ${TESTNAME}
62+
63+
Invalid assing mark usage
64+
Check Test Case ${TESTNAME}
65+
66+
Too many list variables in assign
67+
Check Test Case ${TESTNAME}
68+
69+
Invalid number of variables in assign
70+
Check Test Case ${TESTNAME}
71+
72+
Invalid value for list assign
73+
Check Test Case ${TESTNAME}
74+
75+
Invalid value for dict assign
76+
Check Test Case ${TESTNAME}

atest/testdata/running/if/inline_if_else.robot

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,10 @@ IF failing
66
[Documentation] FAIL Inside IF
77
IF '1' == '1' Fail Inside IF
88

9+
IF erroring
10+
[Documentation] FAIL No keyword with name 'Oooops, I don't exist!' found.
11+
IF '1' == '1' Oooops, I don't exist!
12+
913
Not executed
1014
[Documentation] FAIL After IF
1115
IF False Not run
Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
*** Test Cases ***
2+
Invalid condition
3+
[Documentation] FAIL Evaluating expression 'ooops' failed: NameError: name 'ooops' is not defined nor importable as module
4+
IF ooops Not run ELSE Not run either
5+
6+
Empty IF
7+
[Documentation] FAIL Multiple errors:
8+
... - IF has no condition.
9+
... - IF branch cannot be empty.
10+
... - IF has no closing END.
11+
IF
12+
13+
IF without branch
14+
[Documentation] FAIL Multiple errors:
15+
... - IF branch cannot be empty.
16+
... - IF has no closing END.
17+
IF True
18+
19+
IF without branch with ELSE IF
20+
[Documentation] FAIL IF branch cannot be empty.
21+
IF True ELSE IF True Not run
22+
23+
IF without branch with ELSE
24+
[Documentation] FAIL IF branch cannot be empty.
25+
IF True ELSE Not run
26+
27+
IF follewed by ELSE IF
28+
[Documentation] FAIL STARTS: Evaluating expression 'ELSE IF' failed:
29+
IF ELSE IF False Not run
30+
31+
IF follewed by ELSE
32+
[Documentation] FAIL Evaluating expression 'ELSE' failed: NameError: name 'ELSE' is not defined nor importable as module
33+
IF ELSE Not run
34+
35+
Empty ELSE IF 1
36+
[Documentation] FAIL Multiple errors:
37+
... - ELSE IF has no condition.
38+
... - ELSE IF branch cannot be empty.
39+
IF False Not run ELSE IF
40+
41+
Empty ELSE IF 2
42+
[Documentation] FAIL Evaluating expression 'ELSE' failed: NameError: name 'ELSE' is not defined nor importable as module
43+
IF False Not run ELSE IF ELSE Not run
44+
45+
ELSE IF without branch 1
46+
[Documentation] FAIL ELSE IF branch cannot be empty.
47+
IF False Not run ELSE IF False
48+
49+
ELSE IF without branch 2
50+
[Documentation] FAIL ELSE IF branch cannot be empty.
51+
IF False Not run ELSE IF False ELSE Not run
52+
53+
Empty ELSE
54+
[Documentation] FAIL ELSE branch cannot be empty.
55+
IF True Not run ELSE IF True Not run ELSE
56+
57+
ELSE IF after ELSE 1
58+
[Documentation] FAIL ELSE IF after ELSE.
59+
IF True Not run ELSE Not run ELSE IF True Not run
60+
61+
ELSE IF after ELSE 2
62+
[Documentation] FAIL ELSE IF after ELSE.
63+
IF True Not run ELSE Not run ELSE IF True Not run ELSE IF True Not run
64+
65+
Multiple ELSEs 1
66+
[Documentation] FAIL Multiple ELSE branches.
67+
IF True Not run ELSE Not run ELSE Not run
68+
69+
Multiple ELSEs 2
70+
[Documentation] FAIL Multiple ELSE branches.
71+
IF True Not run ELSE Not run ELSE Not run ELSE Not run
72+
73+
Nested IF 1
74+
[Documentation] FAIL Inline IF cannot be nested.
75+
IF True IF True Not run
76+
77+
Nested IF 2
78+
[Documentation] FAIL Inline IF cannot be nested.
79+
IF True Not run ELSE IF True Not run
80+
81+
Nested IF 3
82+
[Documentation] FAIL Inline IF cannot be nested.
83+
IF True IF True Not run
84+
... ELSE IF True IF True Not run
85+
... ELSE IF True Not run
86+
87+
Unnecessary END
88+
[Documentation] FAIL Keyword 'BuiltIn.No Operation' expected 0 arguments, got 1.
89+
IF False Not run ELSE No operation END
90+
91+
Assign in IF branch
92+
[Documentation] FAIL Inline IF branch cannot have an assignment.
93+
IF False ${x} = Whatever
94+
95+
Assign in ELSE IF branch
96+
[Documentation] FAIL Inline ELSE IF branch cannot have an assignment.
97+
IF False Keyword ELSE IF False ${x} = Whatever
98+
99+
Assign in ELSE branch
100+
[Documentation] FAIL Inline ELSE branch cannot have an assignment.
101+
IF False Keyword ELSE ${x} = Whatever
102+
103+
Invalid assing mark usage
104+
[Documentation] FAIL Assign mark '=' can be used only with the last variable.
105+
${x} = ${y} IF True Create list x y
106+
107+
Too many list variables in assign
108+
[Documentation] FAIL Assignment can contain only one list variable.
109+
@{x} @{y} = IF True Create list x y
110+
111+
Invalid number of variables in assign
112+
[Documentation] FAIL Cannot set variables: Expected 2 return values, got 3.
113+
${x} ${y} = IF False Create list x y ELSE Create list x y z
114+
115+
Invalid value for list assign
116+
[Documentation] FAIL Cannot set variable '\@{x}': Expected list-like value, got string.
117+
@{x} = IF True Set variable String is not list
118+
119+
Invalid value for dict assign
120+
[Documentation] FAIL Cannot set variable '\&{x}': Expected dictionary-like value, got string.
121+
&{x} = IF False Not run ELSE Set variable String is not dict either

src/robot/parsing/lexer/blocklexers.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -253,7 +253,8 @@ def lexer_classes(self):
253253

254254
def input(self, statement):
255255
for part in self._split_statements(statement):
256-
super().input(part)
256+
if part:
257+
super().input(part)
257258
return self
258259

259260
def _split_statements(self, statement):
@@ -268,9 +269,8 @@ def _split_statements(self, statement):
268269
expect_condition = False
269270
elif token.value in ('IF', 'ELSE IF'):
270271
token._add_eos_before = token.value == 'ELSE IF'
271-
if current:
272-
yield current
273-
current = []
272+
yield current
273+
current = []
274274
current.append(token)
275275
expect_condition = True
276276
elif token.value == 'ELSE':

src/robot/parsing/lexer/lexer.py

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -124,17 +124,15 @@ def _get_tokens(self, statements):
124124
token_type = token.type
125125
if token_type in ignored_types:
126126
continue
127-
if token._add_eos_before:
128-
token._add_eos_before = False
127+
if token._add_eos_before and not (last and last._add_eos_after):
129128
yield EOS.from_token(token, before=True)
130129
yield token
131130
if token._add_eos_after:
132-
token._add_eos_after = False
133131
yield EOS.from_token(token)
134132
if token_type == inline_if_type:
135133
inline_if = True
136134
last = token
137-
if last:
135+
if last and not last._add_eos_after:
138136
yield EOS.from_token(last)
139137
if inline_if:
140138
yield END.from_token(last, virtual=True)

src/robot/parsing/model/blocks.py

Lines changed: 20 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -159,46 +159,47 @@ def assign(self):
159159

160160
def validate(self):
161161
self._validate_body()
162-
if self.type in (Token.IF, Token.INLINE_IF):
162+
if self.type == Token.IF:
163163
self._validate_structure()
164164
self._validate_end()
165165
if self.type == Token.INLINE_IF:
166-
self._validate_branch_keyword_calls()
166+
self._validate_structure()
167+
self._validate_inline_if()
167168

168169
def _validate_body(self):
169170
if not self.body:
170-
self.errors += (f'{self.type} branch cannot be empty.',)
171+
type = self.type if self.type != Token.INLINE_IF else 'IF'
172+
self.errors += (f'{type} branch cannot be empty.',)
171173

172174
def _validate_structure(self):
173175
orelse = self.orelse
174176
else_seen = False
175177
while orelse:
176178
if else_seen:
177179
if orelse.type == Token.ELSE:
178-
self.errors += ('Multiple ELSE branches.',)
180+
error = 'Multiple ELSE branches.'
179181
else:
180-
self.errors += ('ELSE IF after ELSE.',)
182+
error = 'ELSE IF after ELSE.'
183+
if error not in self.errors:
184+
self.errors += (error,)
181185
else_seen = else_seen or orelse.type == Token.ELSE
182186
orelse = orelse.orelse
183187

184188
def _validate_end(self):
185189
if not self.end:
186190
self.errors += ('IF has no closing END.',)
187191

188-
def _validate_branch_keyword_calls(self):
189-
# TODO: validation messages
190-
def validate(body):
191-
if not body:
192-
self.errors += (f'{self.type} has empty body.' ,)
193-
if len(body) > 1:
194-
self.errors += (f'{self.type} branch has more than one keyword call.',)
195-
if body[0].assign:
196-
self.errors += (f'{self.type} branch cannot have an assignment.',)
197-
validate(self.body)
198-
orelse = self.orelse
199-
while orelse:
200-
validate(orelse.body)
201-
orelse = orelse.orelse
192+
def _validate_inline_if(self):
193+
branch = self
194+
while branch:
195+
if branch.body:
196+
item = branch.body[0]
197+
if getattr(item, 'assign', None):
198+
type = branch.type if branch.type != Token.INLINE_IF else 'IF'
199+
self.errors += (f'Inline {type} branch cannot have an assignment.',)
200+
if item.type == Token.INLINE_IF:
201+
self.errors += (f'Inline IF cannot be nested.',)
202+
branch = branch.orelse
202203

203204

204205
class For(Block):

0 commit comments

Comments
 (0)