Robot Framework Integrated Development Environment (RIDE)
macrocontrollers.py
Go to the documentation of this file.
1 # Copyright 2008-2015 Nokia Networks
2 # Copyright 2016- Robot Framework Foundation
3 #
4 # Licensed under the Apache License, Version 2.0 (the "License");
5 # you may not use this file except in compliance with the License.
6 # You may obtain a copy of the License at
7 #
8 # http://www.apache.org/licenses/LICENSE-2.0
9 #
10 # Unless required by applicable law or agreed to in writing, software
11 # distributed under the License is distributed on an "AS IS" BASIS,
12 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 # See the License for the specific language governing permissions and
14 # limitations under the License.
15 
16 import os
17 from itertools import chain
18 
19 from .. import robotapi
20 from .arguments import parse_arguments_to_var_dict
21 from .basecontroller import ControllerWithParent, WithUndoRedoStacks
22 from .settingcontrollers import (DocumentationController, FixtureController, TagsController, TimeoutController,
23  TemplateController, ArgumentsController, ReturnValueController)
24 from .stepcontrollers import ForLoopStepController, StepController, IntendedStepController
25 from .tags import Tag
26 from ..namespace.local_namespace import LocalNamespace
27 from ..publish.messages import (RideItemStepsChanged, RideItemNameChanged, RideItemSettingsChanged,
28  RideUserKeywordRemoved)
29 from ..spec.iteminfo import ResourceUserKeywordInfo, TestCaseUserKeywordInfo
30 from ..utils import variablematcher
31 
32 KEYWORD_NAME_FIELD = 'Keyword Name'
33 TESTCASE_NAME_FIELD = 'Test Case Name'
34 
35 
37  return robotapi.Step([])
38 
39 
41 
42  def __init__(self, item):
43  self._item_item = item
44 
45  def contains_keyword(self, name):
46  if isinstance(name, str):
47  return self._item_item.name == name
48  return name.match(self._item_item.name)
49 
50  def contains_variable(self, name):
51  return variablematcher.value_contains_variable(self._item_item.name, name)
52 
53  def replace_keyword(self, new_name, old_value=None):
54  print(f"DEBUG: macrocontrollers.py replace_keyword new_name={new_name}")
55  self._item_item.rename(new_name)
56 
57  def rename(self, new_name):
58  self._item_item.rename(new_name)
59 
61  self._item_item.notify_name_changed()
62 
63  @property
64  parent = property
65 
66  def parent(self):
67  return self._item_item
68 
69 
71 
74  _name_field = KEYWORD_NAME_FIELD
75 
76 
78 
81  _name_field = TESTCASE_NAME_FIELD
82 
83 
85 
86  def __init__(self, parent_controller, data):
87  self._parent_parent_parent = parent_controller
88  self.datadata = data
89  self._init(data)
90  self._has_steps_changed_has_steps_changed = True
91  self._steps_cached_steps_cached = None
92  self.datafile_controllerdatafile_controllerdatafile_controller.register_for_namespace_updates(
93  self._clear_cached_steps_clear_cached_steps)
94 
95  @property
96  source = property
97 
98  def source(self):
99  return os.path.basename(self.datadata.source) if self.datadata.source else ''
100 
101  @property
102  name = property
103 
104  def name(self):
105  return self.datadata.name
106 
107  @property
108  steps = property
109 
110  def steps(self):
111  if self._has_steps_changed_has_steps_changed:
112  self._recreate_steps_recreate_steps()
113  return self._steps_cached_steps_cached
114 
115  def set_parent(self, new_parent):
116  self._clear_cached_steps_clear_cached_steps()
117  ControllerWithParent.set_parent(self, new_parent)
118 
119  def _recreate_steps(self):
120  flattened_steps = []
121  for step in self.datadata.steps:
122  if step.is_for_loop():
123  for_loop = ForLoopStepController(self, step)
124  flattened_steps.append(for_loop)
125  flattened_steps.extend(for_loop.steps)
126  else:
127  flattened_steps.append(StepController(self, step))
128  self._steps_cached_steps_cached = flattened_steps
129  self._has_steps_changed_has_steps_changed = False
130 
132  self._has_steps_changed_has_steps_changed = True
133  self._steps_cached_steps_cached = None
134 
135  @property
136  max_columns = property
137 
138  def max_columns(self):
139  return max(chain((len(step) for step in self.stepsstepssteps), [0]))
140 
141  def has_template(self):
142  return False
143 
144  def step(self, index):
145  return self.stepsstepssteps[index]
146 
147  def index_of_step(self, step):
148  return [s._step for s in self.stepsstepssteps].index(step)
149 
150  def replace_step(self, index, new_step):
151  corrected_index = index
152  for i in range(index):
153  if isinstance(self.stepstep(i), IntendedStepController):
154  corrected_index -= 1
155  self.datadata.steps[corrected_index] = new_step
156  self._has_steps_changed_has_steps_changed = True
157 
158  def move_step_up(self, index):
159  # print(f"DEBUG: macrocontrollers move_step_up index={index} step={self.step(index).as_list()}")
160  self.stepstep(index).move_up()
161  # print(f"DEBUG: macrocontrollers move_step_up AFTER index-1={index-1} step={self.step(index-1).as_list()}")
162  # print(f"DEBUG: macrocontrollers move_step_up AFTER index={index} step={self.step(index).as_list()}")
163  self._has_steps_changed_has_steps_changed = True
164 
165  def move_step_down(self, index):
166  self.stepstep(index).move_down()
167  self._has_steps_changed_has_steps_changed = True
168 
169  def set_steps(self, steps):
170  self.datadata.steps = steps
171  self._has_steps_changed_has_steps_changed = True
172 
173  def update_namespace(self):
175 
177  # print(f"DEBUG: local namespace controller._namespace {self.datafile_controller._namespace}")
178  return LocalNamespace(self, self.datafile_controllerdatafile_controllerdatafile_controller._namespace)
179 
181  # print(f"DEBUG: local namespace_for_row controller._namespace {self.datafile_controller._namespace} row {row}")
182  return LocalNamespace(self, self.datafile_controllerdatafile_controllerdatafile_controller._namespace, row)
183 
184  def get_cell_info(self, row, col):
185  steps = self.stepsstepssteps
186  if row < 0 or len(steps) <= row:
187  return None
188  return steps[row].get_cell_info(col)
189 
190  def get_keyword_info(self, kw_name):
191  return self.datafile_controllerdatafile_controllerdatafile_controller.keyword_info(kw_name)
192 
193  def is_user_keyword(self, value):
194  return self.datafile_controllerdatafile_controllerdatafile_controller.is_user_keyword(value)
195 
196  def is_library_keyword(self, value):
197  return self.datafile_controllerdatafile_controllerdatafile_controller.is_library_keyword(value)
198 
199  def delete(self):
200  self.datafile_controllerdatafile_controllerdatafile_controller.unregister_namespace_updates(
201  self._clear_cached_steps_clear_cached_steps)
202  self._parent_parent_parent.delete(self)
203  self.notify_keyword_removednotify_keyword_removed()
204 
205  def rename(self, new_name):
206  self.datadata.name = new_name.strip()
207  self.mark_dirtymark_dirty()
208 
209  def copy(self, name):
210  new = self._parent_parent_parent.new(name)
211  for orig, copied in zip(self.settings, new.settings):
212  copied.set_from(orig)
213  new.data.steps = [robotapi.Step(s.as_list()) for s in self.stepsstepssteps]
214  new.notify_steps_changed()
215  return new
216 
217  def get_empty_rows(self):
218  return [index for index, step in enumerate(self.stepsstepssteps)
219  if self._is_empty_step_is_empty_step(step)]
220 
221  def _is_empty_step(self, step):
222  return step.as_list() in [[], ['']]
223 
224  def remove_step(self, index):
225  self._remove_step_remove_step(self.stepsstepssteps[index])
226  self._has_steps_changed_has_steps_changed = True
227 
228  def recreate(self):
229  self._parent_parent_parent.add(self)
230 
231  def _remove_step(self, step):
232  step.remove()
233  self._has_steps_changed_has_steps_changed = True
234 
235  def add_step(self, index, step=None):
236  # print(f"\nDEBUG: _WithStepsController enter add_step step={step}")
237  if step is None:
238  step = _empty_step()
239  if index == len(self.stepsstepssteps):
240  self.datadata.steps.append(step)
241  else:
242  previous_step = self.stepstep(index)
243  previous_step.insert_before(step)
244  self._has_steps_changed_has_steps_changed = True
245 
246  def create_keyword(self, name, argstr):
247  name = self._remove_bdd_prefix_remove_bdd_prefix(name)
248  validation = self.datafile_controllerdatafile_controllerdatafile_controller.validate_keyword_name(name)
249  if validation.error_message:
250  raise ValueError(validation.error_message)
251  return self.datafile_controllerdatafile_controllerdatafile_controller.create_keyword(name, argstr)
252 
253  def _remove_bdd_prefix(self, name):
254  matcher = name.lower()
255  for match in ['given ', 'when ', 'then ', 'and ', 'but ']:
256  if matcher.startswith(match):
257  return name[len(match):]
258  return name
259 
260  def create_test(self, name):
261  return self.datafile_controllerdatafile_controllerdatafile_controller.create_test(name)
262 
263  def extract_keyword(self, name, argstr, step_range):
264  extracted_steps = self._extract_steps_extract_steps(step_range)
265  self._replace_steps_with_kw_replace_steps_with_kw(name, step_range)
266  self._create_extracted_kw_create_extracted_kw(name, argstr, extracted_steps)
267 
268  def get_raw_steps(self):
269  # Reveales inner state so can't be sure if cache is up to date
270  self._has_steps_changed_has_steps_changed = True
271  return self.datadata.steps
272 
273  def set_raw_steps(self, steps):
274  self.datadata.steps = steps
275  self._has_steps_changed_has_steps_changed = True
276 
277  def _extract_steps(self, step_range):
278  rem_start, rem_end = step_range
279  extracted_steps = self.stepsstepssteps[rem_start:rem_end + 1]
280  return self._convert_controller_to_steps_convert_controller_to_steps(extracted_steps)
281 
282  @staticmethod
283  def _convert_controller_to_steps(step_controllers):
284  return [robotapi.Step(s.as_list()) for s in step_controllers]
285 
286  def _replace_steps_with_kw(self, name, step_range):
287  steps_before_extraction_point = self._convert_controller_to_steps_convert_controller_to_steps(
288  self.stepsstepssteps[:step_range[0]])
289  extracted_kw_step = [robotapi.Step([name])]
290  steps_after_extraction_point = self._convert_controller_to_steps_convert_controller_to_steps(
291  self.stepsstepssteps[step_range[1] + 1:])
292  self.set_stepsset_steps(steps_before_extraction_point + extracted_kw_step +
293  steps_after_extraction_point)
294 
295  def _create_extracted_kw(self, name, argstr, extracted_steps):
296  controller = self.datafile_controllerdatafile_controllerdatafile_controller.create_keyword(name, argstr)
297  controller.set_steps(extracted_steps)
298  return controller
299 
300  def validate_name(self, name):
301  return self._parent_parent_parent.validate_name(name, self)
302 
303  def notify_name_changed(self, old_name=None):
304  self.update_namespaceupdate_namespace()
305  self.mark_dirtymark_dirty()
306  RideItemNameChanged(item=self, old_name=old_name).publish()
307 
309  self.update_namespaceupdate_namespace()
310  RideUserKeywordRemoved(datafile=self.datafiledatafiledatafile, name=self.namenamename, item=self).publish()
311  self.notify_steps_changednotify_steps_changed()
312 
314  self.update_namespaceupdate_namespace()
315  self._notify_notify(RideItemSettingsChanged)
316 
318  self._has_steps_changed_has_steps_changed = True
319  self._notify_notify(RideItemStepsChanged)
320 
321  def _notify(self, messageclass):
322  self.mark_dirtymark_dirty()
323  messageclass(item=self).publish()
324 
325 
327 
328 
332  filename = ""
333 
334  def _init(self, test):
335  self._test_test = test
336  self._run_passed_run_passed = None
337 
338  def __eq__(self, other):
339  if self is other:
340  return True
341  if other.__class__ != self.__class__:
342  return False
343  return self._test_test == other._test
344 
345  def __hash__(self):
346  return hash(repr(self))
347 
348  @property
349  longname = property
350 
351  def longname(self):
352  return self.parentparentparent.parent.longname + '.' + self.datadata.name
353 
354  @property
355  test_name = property
356 
357  def test_name(self):
358  return TestCaseNameController(self)
359 
360  @property
361  tags = property
362 
363  def tags(self):
364  return TagsController(self, self._test_test.tags)
365 
366  @property
367  force_tags = property
368 
369  def force_tags(self):
370  return self.datafile_controllerdatafile_controllerdatafile_controller.force_tags
371 
372  @property
373  default_tags = property
374 
375  def default_tags(self):
376  return self.datafile_controllerdatafile_controllerdatafile_controller.default_tags
377 
378  def add_tag(self, name):
379  self.tagstagstags.add(Tag(name))
380 
381  @property
382  settings = property
383 
384  def settings(self):
385  return [
386  self.documentationdocumentationdocumentation,
387  FixtureController(self, self._test_test.setup),
388  FixtureController(self, self._test_test.teardown),
389  TimeoutController(self, self._test_test.timeout),
390  TemplateController(self, self._test_test.template),
391  self.tagstagstags
392  ]
393 
394  @property
395  documentation = property
396 
397  def documentation(self):
398  return DocumentationController(self, self._test_test.doc)
399 
400  def move_up(self):
401  return self._parent_parent_parent.move_up(self._test_test)
402 
403  def move_down(self):
404  return self._parent_parent_parent.move_down(self._test_test)
405 
406  def validate_test_name(self, name):
407  return self._parent_parent_parent.validate_name(name)
408 
409  def validate_keyword_name(self, name):
410  return self.datafile_controllerdatafile_controllerdatafile_controller.validate_keyword_name(name)
411 
412  @staticmethod
414  return {}
415 
416  def has_template(self):
417  template = self._get_template_get_template()
418  if not template:
419  return False
420  return bool(template.value)
421 
422  def _get_template(self):
423  template = self._test_test.template
424  if template.value is not None:
425  return template
426  return self.datafile_controllerdatafile_controllerdatafile_controller.get_template()
427 
428  @property
429  run_passed = property
430 
431  def run_passed(self):
432  return self._run_passed_run_passed
433 
434  @run_passed.setter
435 
436  def run_passed(self, value):
437  if value == True:
438  self._run_passed_run_passed = True # Test execution passed
439  elif value == False:
440  self._run_passed_run_passed = False # Test execution failed
441  else:
442  self._run_passed_run_passed = None # Test did not run
443 
444 
446 
450 
453  _TEARDOWN_NOT_SET = object()
454 
457  _teardown = _TEARDOWN_NOT_SET
458 
459  def _init(self, kw):
460  self._kw_kw = kw
461  # Needed for API compatibility in tag search
462  self.force_tagsforce_tags = []
463  self.default_tagsdefault_tags = []
464 
465  def __eq__(self, other):
466  if self is other:
467  return True
468  if other.__class__ != self.__class__:
469  return False
470  return self._kw_kw == other._kw
471 
472  def __hash__(self):
473  return hash(repr(self))
474 
475  @property
476  info = property
477 
478  def info(self):
479  if isinstance(self.datafiledatafiledatafile, robotapi.ResourceFile):
480  return ResourceUserKeywordInfo(self.datadata)
481  return TestCaseUserKeywordInfo(self.datadata)
482 
483  @property
484  keyword_name = property
485 
486  def keyword_name(self):
487  return KeywordNameController(self)
488 
489  def move_up(self):
490  return self._parent_parent_parent.move_up(self._kw_kw)
491 
492  def move_down(self):
493  return self._parent_parent_parent.move_down(self._kw_kw)
494 
495  @property
496  settings = property
497 
498  def settings(self):
499  result = [
500  DocumentationController(self, self._kw_kw.doc),
501  ArgumentsController(self, self._kw_kw.args),
502  self.teardownteardownteardown,
503  ReturnValueController(self, self._kw_kw.return_),
504  TimeoutController(self, self._kw_kw.timeout),
505  TagsController(self, self._kw_kw.tags),
506  ]
507  return result
508 
509  @property
510  teardown = property
511 
512  def teardown(self):
513  if self._teardown_teardown_teardown == self._TEARDOWN_NOT_SET_TEARDOWN_NOT_SET:
514  self._teardown_teardown_teardown = FixtureController(self, self._kw_kw.teardown)
515  return self._teardown_teardown_teardown
516 
517  @property
518  arguments = property
519 
520  def arguments(self):
521  return ArgumentsController(self, self._kw_kw.args)
522 
523  def validate_keyword_name(self, name):
524  return self._parent_parent_parent.validate_name(name)
525 
527  return parse_arguments_to_var_dict(self._kw_kw.args.value, self._kw_kw.name)
def replace_keyword(self, new_name, old_value=None)
def _create_extracted_kw(self, name, argstr, extracted_steps)
The parsed resource file object.
Definition: model.py:254
Sent when a user keyword is removed from a suite or resource.
Definition: messages.py:398
def parse_arguments_to_var_dict(args, name)
Definition: arguments.py:24
def LocalNamespace(controller, namespace, row=None)