Revision: 3301
Author: janne.t.harkonen
Date: Tue May 18 01:10:22 2010
Log: Support for FOR loops, cleanup
http://code.google.com/p/robotframework/source/detail?r=3301
Modified:
/trunk/src/robot/parsing/populator.py
/trunk/utest/parsing/test_populator.py
=======================================
--- /trunk/src/robot/parsing/populator.py Mon May 17 23:24:38 2010
+++ /trunk/src/robot/parsing/populator.py Tue May 18 01:10:22 2010
@@ -117,6 +117,40 @@
return UserKeywordPopulator(self._table.add)
+class ForLoopPopulator(Populator):
+ row_continuation_marker = '...'
+
+ def __init__(self, for_loop_creator):
+ self._for_loop_creator = for_loop_creator
+ self._loop = None
+ self._populator = NullPopulator()
+ self._declaration = []
+
+ def add(self, row):
+ dedented_row = row.dedent()
+ if not self._loop:
+ declaration_ready = self._populate_declaration(row)
+ if not declaration_ready:
+ return
+ self._loop = self._for_loop_creator(self._declaration)
+ if not (self._continues(row) or self._continues(dedented_row)):
+ self._populator.populate()
+ self._populator = StepPopulator(self._loop.add_step)
+ self._populator.add(dedented_row)
+
+ def _populate_declaration(self, row):
+ if row.starts_for_loop() or self._continues(row):
+ self._declaration.extend(row.tail())
+ return False
+ return True
+
+ def populate(self):
+ self._populator.populate()
+
+ def _continues(self, row):
+ return row.startswith(self.row_continuation_marker)
+
+
class _TestCaseUserKeywordPopulator(Populator):
row_continuation_marker = '...'
@@ -129,23 +163,24 @@
dedented_row = row.dedent()
if not self._test_or_uk:
self._test_or_uk = self._test_or_uk_creator(row.head())
- if not dedented_row.startswith(self.row_continuation_marker):
+ if not self._continues(dedented_row):
self._populator.populate()
self._populator = self._get_populator(dedented_row)
self._populator.add(dedented_row)
def populate(self):
- if self._populator:
- self._populator.populate()
+ self._populator.populate()
def _get_populator(self, row):
- first_cell = row.head()
- if self._is_setting(first_cell):
- return SettingPopulator(self._setting_setter(first_cell))
+ if row.starts_test_or_user_keyword_setting():
+ return SettingPopulator(self._setting_setter(row.head()))
+ if row.starts_for_loop():
+ return ForLoopPopulator(self._test_or_uk.add_for_loop)
return StepPopulator(self._test_or_uk.add_step)
- def _is_setting(self, cell):
- return cell and cell[0] == '[' and cell[-1] == ']'
+ def _continues(self, row):
+ return row.startswith(self.row_continuation_marker) or \
+ (isinstance(self._populator, ForLoopPopulator) and
row.startswith(''))
def _setting_setter(self, cell):
attr_name = self.attrs_by_name[self._setting_name(cell)]
@@ -268,6 +303,9 @@
def populate(self):
self._current_populator.populate()
+ def eof(self):
+ self.populate()
+
def add(self, row):
data = DataRow(row)
if data:
@@ -295,6 +333,15 @@
def startswith(self, value):
return self.head() == value
+ def starts_for_loop(self):
+ if not self.head().startswith(':'):
+ return False
+ return self.head().replace(':', '').upper().strip() == 'FOR'
+
+ def starts_test_or_user_keyword_setting(self):
+ head = self.head()
+ return head and head[0] == '[' and head[-1] == ']'
+
def _data_cells(self, row):
cells = [ self._collapse_whitespace(cell)
for cell in self._cells_without_comments(row) ]
=======================================
--- /trunk/utest/parsing/test_populator.py Mon May 17 23:24:38 2010
+++ /trunk/utest/parsing/test_populator.py Tue May 18 01:10:22 2010
@@ -87,6 +87,50 @@
assert_equals(len(self._first_test().steps), 1)
assert_equals(len(self._nth_test(2).steps), 2)
+ def test_for_loop(self):
+ self._create_table('Test cases', [['For loop test'],
+
['', ':FOR', '${i}', 'IN', '@{list}'],
+ ['', '', 'Log', '${i}']])
+ assert_equals(len(self._first_test().steps), 1)
+ for_loop = self._first_test().steps[0]
+ assert_equals(len(for_loop.steps), 1)
+ assert_true(not for_loop.range)
+ assert_equals(for_loop.vars, ['${i}'])
+ assert_equals(for_loop.values, ['@{list}'])
+
+ def test_in_range_for_loop(self):
+ self._create_table('Test cases', [['For loop test'],
+ ['', 'Log', 'Before FOR'],
+ ['', ':
for', '${i}', '${j}', 'IN RANGE', '10'],
+ ['', '', 'Log', '${i}'],
+ ['', '', 'Fail', '${j}'],
+ ['', 'Log', 'Outside FOR']])
+ assert_equals(len(self._first_test().steps), 3)
+ for_loop = self._first_test().steps[1]
+ assert_equals(len(for_loop.steps), 2)
+ assert_true(for_loop.range)
+ assert_equals(for_loop.vars, ['${i}', '${j}'])
+
+ def test_malicious_for_loop(self):
+ self._create_table('Test cases', [['Malicious for loop test'],
+ ['', 'Log', 'Before FOR'],
+ ['#', 'Log', 'Before FOR'],
+ ['', '::::
fOr', '${i}', 'IN', '10', '20'],
+ ['#', '...', 'No operation'],
+ ['', '...', '30', '40'],
+ ['', '...', '50', '60'],
+ ['', '', 'Log Many', '${i}'],
+ ['', '', '...', '${i}'],
+ ['', '...', '${i}'],
+ ['', 'Log', 'Outside FOR']])
+ assert_equals(len(self._first_test().steps), 3)
+ for_loop = self._first_test().steps[1]
+ assert_equals(len(for_loop.steps), 1)
+ assert_true(not for_loop.range)
+ assert_equals(for_loop.vars, ['${i}'])
+ assert_equals(for_loop.values,
['10', '20', '30', '40', '50', '60'])
+
+
def test_test_settings(self):
doc = 'This is domumentation for the test case'
self._create_table('Test cases', [['My test name'],
@@ -167,7 +211,7 @@
self._start_table(name)
for r in rows:
self._populator.add(r)
- self._populator.populate()
+ self._populator.eof()
def _nth_test(self, index):
return self._datafile.testcase_table.tests[index-1]