Robot Framework Integrated Development Environment (RIDE)
runprofiles.py
Go to the documentation of this file.
1 # Copyright 2010 Orbitz WorldWide
2 #
3 # Ammended by Helio Guilherme <helioxentric@gmail.com>
4 # Copyright 2011-2015 Nokia Networks
5 # Copyright 2016- Robot Framework Foundation
6 #
7 # Licensed under the Apache License, Version 2.0 (the "License");
8 # you may not use this file except in compliance with the License.
9 # You may obtain a copy of the License at
10 #
11 # http://www.apache.org/licenses/LICENSE-2.0
12 #
13 # Unless required by applicable law or agreed to in writing, software
14 # distributed under the License is distributed on an "AS IS" BASIS,
15 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 # See the License for the specific language governing permissions and
17 # limitations under the License.
18 
19 
29 
30 import os
31 import re
32 import time
33 import wx
34 
35 from robotide import pluginapi
36 from robotide.context import IS_WINDOWS
37 from robotide.contrib.testrunner.usages import USAGE
38 from robotide.lib.robot.utils import format_time
39 from robotide.robotapi import DataError, Information
40 from robotide.utils import overrides, ArgumentParser
41 from robotide.widgets import ButtonWithHandler, Label, RIDEDialog
42 from sys import getfilesystemencoding
43 from wx.lib.filebrowsebutton import FileBrowseButton
44 
45 OUTPUT_ENCODING = getfilesystemencoding()
46 
47 
48 
59 class BaseProfile():
60 
61  # this will be set to the plugin instance at runtime
62  plugin = None
63  default_settings = {}
64 
65 
66  def __init__(self, plugin):
67  self.pluginplugin = plugin
68  self._panel_panel = None
69 
70 
71  def get_toolbar(self, parent):
72  if self._panel_panel is None:
73  self._panel_panel = wx.Panel(parent, wx.ID_ANY)
74  return self.panel
75 
76  def enable_toolbar(self):
77  if self._panel_panel is None:
78  return
79  self._panel_panel.Enable()
80 
81  def disable_toolbar(self):
82  if self._panel_panel is None:
83  return
84  self._panel_panel.Enable(False)
85 
86 
87  def delete_pressed(self):
88  pass
89 
90 
91  def get_command(self):
92  return 'robot'
93 
94 
99  def get_command_args(self):
100  return []
101 
102 
106  def get_settings(self):
107  return []
108 
109 
114  def set_setting(self, name, value):
115  self.pluginplugin.save_setting(self._get_setting_name_get_setting_name(name), value, delay=2)
116 
117  def format_error(self, error, returncode):
118  return error, self._create_error_log_message_create_error_log_message(error, returncode)
119 
120  def _create_error_log_message(self, error, returncode):
121  return None
122 
123 
129  def __getattr__(self, name):
130  try:
131  return getattr(self.pluginplugin, self._get_setting_name_get_setting_name(name))
132  except AttributeError:
133  try:
134  return getattr(self.pluginplugin, name)
135  except AttributeError:
136  if name in self.default_settingsdefault_settings:
137  return self.default_settingsdefault_settings[name]
138  raise
139 
140 
143  def _get_setting_name(self, name):
144  return "%s_%s" % (self.name.replace(' ', '_'), name)
145 
146 
147 RF_INSTALLATION_NOT_FOUND = """Robot Framework installation not found.<br>
148 To run tests, you need to install Robot Framework separately.<br>
149 See <a href="http://robotframework.org">http://robotframework.org</a> for
150 installation instructions.
151 """
152 
153 
154 
159 
162  _quotes_re = re.compile('(.*)(\".*\")(.*)?')
163 
164  name = "robot"
165  default_settings = {"arguments": "",
166  "output_directory": "",
167  "include_tags": "",
168  "exclude_tags": "",
169  "are_log_names_with_suite_name": False,
170  "are_log_names_with_timestamp": False,
171  "are_saving_logs": False,
172  "apply_include_tags": False,
173  "apply_exclude_tags": False}
174 
175  def __init__(self, plugin):
176  BaseProfile.__init__(self, plugin)
177  self._defined_arguments_defined_arguments = self.arguments
178  self._toolbar_toolbar = None
179 
180  def get_toolbar(self, parent):
181  if self._toolbar_toolbar is None:
182  self._toolbar_toolbar = wx.Panel(parent, wx.ID_ANY)
183  self._mysettings_mysettings = RIDEDialog(parent=self._toolbar_toolbar)
184  self._toolbar_toolbar.SetBackgroundColour(self._mysettings_mysettings.color_background)
185  self._toolbar_toolbar.SetForegroundColour(self._mysettings_mysettings.color_foreground)
186  sizer = wx.BoxSizer(wx.VERTICAL)
187  for item in self.get_toolbar_itemsget_toolbar_items(self._toolbar_toolbar):
188  sizer.Add(item, 0, wx.EXPAND)
189  self._toolbar_toolbar.SetSizer(sizer)
190  return self._toolbar_toolbar
191 
192  def get_toolbar_items(self, parent):
193  return [self._get_arguments_panel_get_arguments_panel(parent),
194  self._get_tags_panel_get_tags_panel(parent),
195  self._get_log_options_panel_get_log_options_panel(parent)]
196 
197  def enable_toolbar(self):
198  if self._toolbar_toolbar is None:
199  return
200  self._enable_toolbar_enable_toolbar()
201 
202  def disable_toolbar(self):
203  if self._toolbar_toolbar is None:
204  return
205  self._enable_toolbar_enable_toolbar(False)
206 
207  def _enable_toolbar(self, enable=True):
208  for panel in self._toolbar_toolbar.GetChildren():
209  if isinstance(panel, wx.CollapsiblePane):
210  panel = panel.GetPane()
211  panel.Enable(enable)
212 
213  @overrides(BaseProfile)
214  def delete_pressed(self):
215  focused = wx.Window.FindFocus()
216  if focused not in [self._arguments, self._include_tags,
217  self._exclude_tags]:
218  return
219  start, end = focused.GetSelection()
220  focused.Remove(start, max(end, start + 1))
221 
222  def get_command(self):
223  from subprocess import call
224  from tempfile import TemporaryFile
225  try:
226  with TemporaryFile(mode="at") as out:
227  result = call(["robot", "--version"], stdout=out)
228  if result == 251:
229  return "robot"
230 
231  with TemporaryFile(mode="at") as out:
232  result = call(["robot.bat" if os.name == "nt" else "robot",
233  "--version"], stdout=out)
234  if result == 251:
235  return "robot.bat" if os.name == "nt" else "robot"
236  except OSError:
237  try:
238  with TemporaryFile(mode="at") as out:
239  result = call(["pybot.bat" if os.name == "nt" else "pybot",
240  "--version"], stdout=out)
241  if result == 251:
242  return "pybot.bat" if os.name == "nt" else "pybot"
243  except OSError:
244  result = "no pybot"
245  return result
246 
247  def get_command_args(self):
248  args = self._get_arguments_get_arguments()
249  if self.output_directory and \
250  '--outputdir' not in args and \
251  '-d' not in args:
252  args.extend(['-d', os.path.abspath(self.output_directory)])
253 
254  log_name_format = '%s'
255  if self.are_log_names_with_suite_name:
256  log_name_format = f'{self.plugin.model.suite.name}-%s'
257  if '--log' not in args and '-l' not in args:
258  args.extend(['-l', log_name_format % 'Log.html'])
259  if '--report' not in args and '-r' not in args:
260  args.extend(['-r', log_name_format % 'Report.html'])
261  if '--output' not in args and '-o' not in args:
262  args.extend(['-o', log_name_format % 'Output.xml'])
263 
264  if self.are_saving_logs and \
265  '--debugfile' not in args and \
266  '-b' not in args:
267  args.extend(['-b', log_name_format % 'Message.log'])
268 
269  if self.are_log_names_with_timestamp and \
270  '--timestampoutputs' not in args and \
271  '-T' not in args:
272  args.append('-T')
273 
274  if self.apply_include_tags and self.include_tags:
275  for include in self._get_tags_from_string_get_tags_from_string(self.include_tags):
276  args.append('--include=%s' % include)
277 
278  if self.apply_exclude_tags and self.exclude_tags:
279  for exclude in self._get_tags_from_string_get_tags_from_string(self.exclude_tags):
280  args.append('--exclude=%s' % exclude)
281  return args
282 
283  def _get_arguments(self):
284  if IS_WINDOWS:
285  self._parse_windows_command_parse_windows_command()
286  else:
287  self._parse_posix_command_parse_posix_command()
288  return self._save_filenames_save_filenames()
289 
290  def _save_filenames(self):
291  # print(f"DEBUG: Run Profiles _save_filenames enter before parsing self._defined_arguments {self._defined_arguments}")
292  args = self._defined_arguments_defined_arguments.replace('\\"', '"')
293  # print(f"DEBUG: Run Profiles _save_filenames enter before detecting quotes args {args}")
294  res = self._quotes_re_quotes_re.match(args)
295  if not res:
296  return args.strip().strip().split()
297  clean = []
298  # DEBUG: example args
299  # --xunit "another output file.xml" --variablefile "a test file for variables.py" -v abc:new
300  # --debugfile "debug file.log"
301  # print(f"DEBUG: Run Profiles _save_filenames res.groups {res.groups()}")
302  for gr in res.groups():
303  line = []
304  if gr is not None and gr != '':
305  second_m = re.split('"', gr)
306  # print(f"DEBUG: Run Profiles _save_filenames second_m = {second_m}")
307  m = len(second_m)
308  if m > 2: # the middle element is the content
309  m = len(second_m)
310  for idx in range(0, m):
311  if second_m[idx]:
312  if idx % 2 == 0:
313  line.extend(second_m[idx].strip().strip().split())
314  elif idx % 2 != 0:
315  line.append(f"{second_m[idx]}")
316  else:
317  for idx in range(0, m):
318  if second_m[idx]:
319  line.extend(second_m[idx].strip().strip().split())
320  clean.extend(line)
321  # Fix variables
322  # print(f"DEBUG: Run Profiles _save_filenames DEFORE FIX VARIABLES clean= {clean}")
323  for idx, value in enumerate(clean):
324  if value[-1] == ':' and idx + 1 < len(clean):
325  clean[idx] = ''.join([value, clean[idx+1]])
326  clean.pop(idx+1)
327  # print(f"DEBUG: Run Profiles _save_filenames returnin clean= {clean}")
328  return clean
329 
331  # print(f"DEBUG: run_profiles _parse_windows_command: ENTER self.arguments={self.arguments}")
332  # wx.MessageBox(f"DEBUG: run_profiles _parse_windows_command: ENTER self.arguments={self.arguments}", "Debug")
333  from subprocess import Popen, PIPE
334  try:
335  p = Popen(['echo', self.arguments], stdin=PIPE, stdout=PIPE,
336  stderr=PIPE, shell=True)
337  output, _ = p.communicate()
338  from ctypes import cdll
339 
340  code_page = cdll.kernel32.GetConsoleCP()
341  if code_page == 0:
342  os_encoding = os.getenv('RIDE_ENCODING', OUTPUT_ENCODING)
343  else:
344  os_encoding = 'cp' + str(code_page)
345  # print(f"DEBUG: run_profiles _parse_windows_command: RAW output ={output} codepage={code_page} {os_encoding}")
346  try:
347  output = output.decode(os_encoding)
348  except UnicodeDecodeError:
349  wx.MessageBox(f"An UnicodeDecodeError occurred when processing the Arguments."
350  f" The encoding used was '{os_encoding}'. You may try to define the environment variable"
351  f" RIDE_ENCODING with a proper value. Other possibility, is to replace 'pythonw.exe' by 'python.exe'"
352  f" in the Desktop Shortcut.", "UnicodeDecodeError")
353  # print(f"DEBUG: run_profiles _parse_windows_command: RAW_decoded output ={output.decode(sys.getfilesystemencoding())}")
354  output = str(output).lstrip("b\'").lstrip('"').replace('\\r\\n', '').replace('\'', '').replace('\\""', '\"').strip()
355  # print(f"DEBUG: run_profiles _parse_windows_command: output ={output}")
356  even = True
357  counter = 0
358  for idx in range(0, len(output)):
359  if output[idx] == '"':
360  counter += 1
361  even = counter % 2 == 0
362  # print(f"DEBUG: run_profiles loop({idx} counter:{counter}")
363  self._defined_arguments_defined_arguments = output.replace('\'', '')\
364  .replace('\\\\', '\\').replace('\\r\\n', '')
365  if not even:
366  self._defined_arguments_defined_arguments = self._defined_arguments_defined_arguments.rstrip('"')
367  # print(f"DEBUG: run_profiles _parse_windows_command: success EVEN? {even} self._defined_arguments={self._defined_arguments}")
368  except IOError as e:
369  # print(f"DEBUG: run_profiles _parse_windows_command IOError: {e}")
370  pass
371 
373  # print(f"DEBUG: run_profiles _parse_posix_command: ENTER self.arguments={self.arguments}")
374  from subprocess import Popen, PIPE
375  try:
376  p = Popen(['echo ' + self.arguments.replace('"', '\\"')], stdin=PIPE, stdout=PIPE,
377  stderr=PIPE, shell=True)
378  output, _ = p.communicate()
379  # print(f"DEBUG: run_profiles _parse_posix_command: RAW output ={output}")
380  output = str(output).lstrip("b\'").replace('\\n', '').rstrip("\'").strip()
381  # print(f"DEBUG: run_profiles _parse_posix_command: output ={output}")
382  even = True
383  counter = 0
384  for idx in range(0, len(output)):
385  if output[idx] == '"':
386  counter += 1
387  even = counter % 2 == 0
388  # print(f"DEBUG: run_profiles loop({idx} counter:{counter}")
389  self._defined_arguments_defined_arguments = output.replace('\'', '')\
390  .replace('\\\\', '\\').replace('\\n', '')
391  if not even:
392  self._defined_arguments_defined_arguments = self._defined_arguments_defined_arguments.rstrip('"')
393  # print(f"DEBUG: run_profiles _parse_posix_command: success EVEN? {even} self._defined_arguments={self._defined_arguments}")
394  except IOError as e:
395  # print(f"DEBUG: run_profiles _parse_posix_command IOError: {e}")
396  pass
397 
398  @staticmethod
399  def _get_tags_from_string(tag_string):
400  tags = []
401  for tag in tag_string.split(","):
402  tag = tag.strip().replace(' ', '')
403  if len(tag) > 0:
404  tags.append(tag)
405  return tags
406 
407  def get_settings(self):
408  settings = []
409  if self.are_saving_logs:
410  name = 'Console.txt'
411  if self.are_log_names_with_timestamp:
412  start_timestamp = format_time(time.time(), '', '-', '')
413  base, ext = os.path.splitext(name)
414  base = f'{base}-{start_timestamp}'
415  name = base + ext
416 
417  if self.are_log_names_with_suite_name:
418  name = f'{self.plugin.model.suite.name}-{name}'
419  settings.extend(['console_log_name', name])
420  return settings
421 
422  def _create_error_log_message(self, error, returncode):
423  # bash and zsh use return code 127 and the text `command not found`
424  # In Windows, the error is `The system cannot file the file specified`
425  if b'not found' in error \
426  or returncode == 127 or \
427  b'system cannot find the file specified' in error:
428  return pluginapi.RideLogMessage(
429  RF_INSTALLATION_NOT_FOUND, notify_user=True)
430  return None
431 
432  def _get_log_options_panel(self, parent):
433  collapsible_pane = wx.CollapsiblePane(
434  parent, wx.ID_ANY, 'Log options',
435  style=wx.CP_DEFAULT_STYLE | wx.CP_NO_TLW_RESIZE)
436  collapsible_pane.Bind(wx.EVT_COLLAPSIBLEPANE_CHANGED,
437  self.OnCollapsiblePaneChangedOnCollapsiblePaneChanged,
438  collapsible_pane)
439  pane = collapsible_pane.GetPane()
440  pane.SetBackgroundColour(self._mysettings_mysettings.color_background)
441  pane.SetForegroundColour(self._mysettings_mysettings.color_foreground)
442  label = Label(pane, label="Output directory: ")
443  self._output_directory_text_ctrl_output_directory_text_ctrl = \
444  self._create_text_ctrl_create_text_ctrl(pane, self.output_directory,
445  "removed due unicode_error (delete this)",
446  self.OnOutputDirectoryChangedOnOutputDirectoryChanged)
447  self._output_directory_text_ctrl_output_directory_text_ctrl.SetBackgroundColour(self._mysettings_mysettings.color_secondary_background)
448  self._output_directory_text_ctrl_output_directory_text_ctrl.SetForegroundColour(self._mysettings_mysettings.color_secondary_foreground)
449  button = ButtonWithHandler(pane, "...", self._handle_select_directory_handle_select_directory)
450  button.SetBackgroundColour(self._mysettings_mysettings.color_secondary_background)
451  button.SetForegroundColour(self._mysettings_mysettings.color_secondary_foreground)
452  horizontal_sizer = wx.BoxSizer(wx.HORIZONTAL)
453  horizontal_sizer.Add(label, 0,
454  wx.ALIGN_CENTER_VERTICAL | wx.LEFT, 10)
455  horizontal_sizer.Add(self._output_directory_text_ctrl_output_directory_text_ctrl, 1, wx.EXPAND)
456  horizontal_sizer.Add(button, 0, wx.LEFT | wx.RIGHT, 10)
457 
458  suite_name_outputs_cb = self._create_checkbox_create_checkbox(
459  pane, self.are_log_names_with_suite_name,
460  "Add suite name to log names", self.OnSuiteNameOutputsCheckBoxOnSuiteNameOutputsCheckBox)
461  timestamp_outputs_cb = self._create_checkbox_create_checkbox(
462  pane, self.are_log_names_with_timestamp,
463  "Add timestamp to log names", self.OnTimestampOutputsCheckboxOnTimestampOutputsCheckbox)
464  save_logs_cb = self._create_checkbox_create_checkbox(
465  pane, self.are_saving_logs,
466  "Save Console and Message logs", self.OnSaveLogsCheckboxOnSaveLogsCheckbox)
467 
468  vertical_sizer = wx.BoxSizer(wx.VERTICAL)
469  vertical_sizer.Add(horizontal_sizer, 0, wx.EXPAND)
470  vertical_sizer.Add(suite_name_outputs_cb, 0, wx.LEFT | wx.TOP, 10)
471  vertical_sizer.Add(timestamp_outputs_cb, 0, wx.LEFT | wx.TOP, 10)
472  vertical_sizer.Add(save_logs_cb, 0, wx.LEFT | wx.TOP | wx.BOTTOM, 10)
473  pane.SetSizer(vertical_sizer)
474  return collapsible_pane
475 
476  def OnOutputDirectoryChanged(self, evt):
477  value = self._output_directory_text_ctrl_output_directory_text_ctrl.GetValue()
478  self.set_settingset_setting("output_directory", value)
479 
480  def _handle_select_directory(self, event):
481  path = self._output_directory_text_ctrl_output_directory_text_ctrl.GetValue()
482  dlg = wx.DirDialog(None, "Select Logs Directory",
483  path, wx.DD_DEFAULT_STYLE)
484  dlg.SetBackgroundColour(self._mysettings_mysettings.color_background)
485  dlg.SetForegroundColour(self._mysettings_mysettings.color_foreground)
486  for item in dlg.GetChildren(): # DEBUG This is not working
487  item.SetBackgroundColour(self._mysettings_mysettings.color_secondary_background)
488  item.SetForegroundColour(self._mysettings_mysettings.color_secondary_foreground)
489  if dlg.ShowModal() == wx.ID_OK and dlg.Path:
490  self._output_directory_text_ctrl_output_directory_text_ctrl.SetValue(dlg.Path)
491  dlg.Destroy()
492 
494  self.set_settingset_setting("are_log_names_with_suite_name", evt.IsChecked())
495 
497  self.set_settingset_setting("are_log_names_with_timestamp", evt.IsChecked())
498 
499  def OnSaveLogsCheckbox(self, evt):
500  self.set_settingset_setting("are_saving_logs", evt.IsChecked())
501 
502  def _get_arguments_panel(self, parent):
503  collapsible_pane = wx.CollapsiblePane(
504  parent, wx.ID_ANY, 'Arguments',
505  style=wx.CP_DEFAULT_STYLE | wx.CP_NO_TLW_RESIZE)
506  collapsible_pane.Bind(wx.EVT_COLLAPSIBLEPANE_CHANGED,
507  self.OnCollapsiblePaneChangedOnCollapsiblePaneChanged,
508  collapsible_pane)
509  pane = collapsible_pane.GetPane()
510  pane.SetBackgroundColour(self._mysettings_mysettings.color_background)
511  pane.SetForegroundColour(self._mysettings_mysettings.color_foreground)
512  self._args_text_ctrl_args_text_ctrl = \
513  self._create_text_ctrl_create_text_ctrl(pane, self.arguments,
514  "removed due unicode_error (delete this)",
515  self.OnArgumentsChangedOnArgumentsChanged)
516  self._args_text_ctrl_args_text_ctrl.SetToolTip("Arguments for the test run. "
517  "Arguments are space separated list.")
518  self._args_text_ctrl_args_text_ctrl.SetBackgroundColour(self._mysettings_mysettings.color_secondary_background)
519  self._args_text_ctrl_args_text_ctrl.SetForegroundColour(self._mysettings_mysettings.color_secondary_foreground)
520  horizontal_sizer = wx.BoxSizer(wx.HORIZONTAL)
521  horizontal_sizer.Add(self._args_text_ctrl_args_text_ctrl, 1,
522  wx.EXPAND | wx.LEFT | wx.RIGHT | wx.BOTTOM, 10)
523 
524  pane.SetSizer(horizontal_sizer)
525  self._validate_arguments_validate_arguments(self.arguments or u'')
526  return collapsible_pane
527 
528  def OnArgumentsChanged(self, evt):
529  args = self._args_text_ctrl_args_text_ctrl.GetValue()
530  self._validate_arguments_validate_arguments(args or u'')
531  self.set_settingset_setting("arguments", args)
532  self._defined_arguments_defined_arguments = args
533 
534  def _validate_arguments(self, args):
535  invalid_message = self._get_invalid_message_get_invalid_message(args)
536  self._args_text_ctrl_args_text_ctrl.SetBackgroundColour(
537  'red' if invalid_message else self._mysettings_mysettings.color_secondary_background)
538  self._args_text_ctrl_args_text_ctrl.SetForegroundColour(
539  'white' if invalid_message else self._mysettings_mysettings.color_secondary_foreground)
540  if not bool(invalid_message):
541  invalid_message = "Arguments for the test run. " \
542  "Arguments are space separated list."
543  self._args_text_ctrl_args_text_ctrl.SetToolTip(invalid_message)
544 
545  @staticmethod
547  if not args:
548  return None
549  try:
550  clean_args = args.split("`") # Shell commands
551  # print(f"DEBUG: run_profiles _get_invalid_message ENTER clean_args= {clean_args}")
552  for idx, item in enumerate(clean_args):
553  clean_args[idx] = item.strip()
554  if clean_args[idx] and clean_args[idx][0] != '-': # Not option, then is argument
555  clean_args[idx] = 'arg'
556  args = " ".join(clean_args)
557  # print(f"DEBUG: run_profiles _get_invalid_message: Check invalid args={args}")
558  _, invalid = ArgumentParser(USAGE).parse_args(args) # DEBUG .split())
559  except Information:
560  return 'Does not execute - help or version option given'
561  except (DataError, Exception) as e:
562  if e.message:
563  return e.message
564  # raise DataError(e.message)
565  # print(f"DEBUG: Exception at run_profiles _get_invalid_messagee (Not DataError?) {e}")
566  if bool(invalid):
567  return f'Unknown option(s): {invalid}'
568  return None
569 
570 
573  def _get_tags_panel(self, parent):
574  collapsible_pane = wx.CollapsiblePane(
575  parent, wx.ID_ANY, 'Tests filters',
576  style=wx.CP_DEFAULT_STYLE | wx.CP_NO_TLW_RESIZE)
577  collapsible_pane.Bind(wx.EVT_COLLAPSIBLEPANE_CHANGED,
578  self.OnCollapsiblePaneChangedOnCollapsiblePaneChanged,
579  collapsible_pane)
580  pane = collapsible_pane.GetPane()
581  pane.SetBackgroundColour(self._mysettings_mysettings.color_background)
582  pane.SetForegroundColour(self._mysettings_mysettings.color_foreground)
583  include_cb = self._create_checkbox_create_checkbox(pane, self.apply_include_tags,
584  "Only run tests with these tags:",
585  self.OnIncludeCheckboxOnIncludeCheckbox)
586  exclude_cb = self._create_checkbox_create_checkbox(pane, self.apply_exclude_tags,
587  "Skip tests with these tags:",
588  self.OnExcludeCheckboxOnExcludeCheckbox)
589  self._include_tags_text_ctrl_include_tags_text_ctrl = \
590  self._create_text_ctrl_create_text_ctrl(pane, self.include_tags, "unicode_error",
591  self.OnIncludeTagsChangedOnIncludeTagsChanged,
592  self.apply_include_tags)
593  self._exclude_tags_text_ctrl_exclude_tags_text_ctrl = \
594  self._create_text_ctrl_create_text_ctrl(pane, self.exclude_tags, "unicode error",
595  self.OnExcludeTagsChangedOnExcludeTagsChanged,
596  self.apply_exclude_tags)
597  self._include_tags_text_ctrl_include_tags_text_ctrl.SetBackgroundColour(self._mysettings_mysettings.color_secondary_background)
598  self._include_tags_text_ctrl_include_tags_text_ctrl.SetForegroundColour(self._mysettings_mysettings.color_secondary_foreground)
599  self._exclude_tags_text_ctrl_exclude_tags_text_ctrl.SetBackgroundColour(self._mysettings_mysettings.color_secondary_background)
600  self._exclude_tags_text_ctrl_exclude_tags_text_ctrl.SetForegroundColour(self._mysettings_mysettings.color_secondary_foreground)
601  horizontal_sizer = wx.BoxSizer(wx.HORIZONTAL)
602  horizontal_sizer.Add(include_cb, 0, wx.ALIGN_CENTER_VERTICAL)
603  horizontal_sizer.Add(self._include_tags_text_ctrl_include_tags_text_ctrl, 1, wx.EXPAND)
604  horizontal_sizer.Add(exclude_cb, 0, wx.ALIGN_CENTER_VERTICAL | wx.LEFT, 10)
605  horizontal_sizer.Add(self._exclude_tags_text_ctrl_exclude_tags_text_ctrl, 1, wx.EXPAND)
606  # Set Left, Right and Bottom content margins
607  sizer = wx.BoxSizer(wx.HORIZONTAL)
608  sizer.Add(horizontal_sizer, 1, wx.LEFT | wx.RIGHT | wx.BOTTOM, 10)
609  pane.SetSizer(sizer)
610 
611  return collapsible_pane
612 
613  def OnCollapsiblePaneChanged(self, evt=None):
614  parent = self._toolbar_toolbar.GetParent().GetParent()
615  parent.Layout()
616 
617  def OnIncludeCheckbox(self, evt):
618  self.set_settingset_setting("apply_include_tags", evt.IsChecked())
619  self._include_tags_text_ctrl_include_tags_text_ctrl.Enable(evt.IsChecked())
620 
621  def OnExcludeCheckbox(self, evt):
622  self.set_settingset_setting("apply_exclude_tags", evt.IsChecked())
623  self._exclude_tags_text_ctrl_exclude_tags_text_ctrl.Enable(evt.IsChecked())
624 
625  def OnIncludeTagsChanged(self, evt):
626  self.set_settingset_setting("include_tags", self._include_tags_text_ctrl_include_tags_text_ctrl.GetValue())
627 
628  def OnExcludeTagsChanged(self, evt):
629  self.set_settingset_setting("exclude_tags", self._exclude_tags_text_ctrl_exclude_tags_text_ctrl.GetValue())
630 
631  @staticmethod
632  def _create_checkbox(parent, value, title, handler):
633  checkbox = wx.CheckBox(parent, wx.ID_ANY, title)
634  checkbox.SetValue(value)
635  parent.Bind(wx.EVT_CHECKBOX, handler, checkbox)
636  return checkbox
637 
638  @staticmethod
639  def _create_text_ctrl(parent, value, value_for_error,
640  text_change_handler, enable=True):
641  try:
642  text_ctrl = wx.TextCtrl(parent, wx.ID_ANY, value=value)
643  except UnicodeError:
644  text_ctrl = wx.TextCtrl(parent, wx.ID_ANY, value=value_for_error)
645  text_ctrl.Bind(wx.EVT_TEXT, text_change_handler)
646  text_ctrl.Enable(enable)
647  return text_ctrl
648 
649 
650 
652 
653  name = "custom script"
654  default_settings = dict(PybotProfile.default_settings, runner_script="")
655 
656  def get_command(self):
657  # strip the starting and ending spaces to ensure
658  # /bin/sh finding the executable file
659  return self.runner_script.strip()
660 
661  def get_cwd(self):
662  return os.path.dirname(self.runner_script)
663 
664  @overrides(PybotProfile)
665  def get_toolbar_items(self, parent):
666  return [self._get_run_script_panel_get_run_script_panel(parent),
667  self._get_arguments_panel_get_arguments_panel(parent),
668  self._get_tags_panel_get_tags_panel(parent),
669  self._get_log_options_panel_get_log_options_panel(parent)]
670 
671  def _validate_arguments(self, args):
672  # Can't say anything about custom script argument validity
673  pass
674 
675  def _create_error_log_message(self, error, returncode):
676  return None
677 
678  def _get_run_script_panel(self, parent):
679  panel = wx.Panel(parent, wx.ID_ANY)
680  self._script_ctrl_script_ctrl = FileBrowseButton(
681  panel, labelText="Script to run tests:", size=(-1, -1),
682  fileMask="*", changeCallback=self.OnCustomScriptChangedOnCustomScriptChanged)
683  self._script_ctrl_script_ctrl.SetValue(self.runner_script)
684 
685  sizer = wx.BoxSizer(wx.VERTICAL)
686  sizer.Add(self._script_ctrl_script_ctrl, 0, wx.EXPAND | wx.LEFT | wx.RIGHT, 5)
687 
688  panel.SetSizerAndFit(sizer)
689  return panel
690 
691  def OnCustomScriptChanged(self, evt):
692  self.set_settingset_setting("runner_script", self._script_ctrl_script_ctrl.GetValue())
Base class for all test runner profiles.
Definition: runprofiles.py:59
def get_toolbar(self, parent)
Returns a panel with toolbar controls for this profile.
Definition: runprofiles.py:71
def __init__(self, plugin)
plugin is required so that the profiles can save their settings
Definition: runprofiles.py:66
def __getattr__(self, name)
Provides attribute access to profile's settings.
Definition: runprofiles.py:129
def _create_error_log_message(self, error, returncode)
Definition: runprofiles.py:120
def delete_pressed(self)
Handle delete key pressing.
Definition: runprofiles.py:87
def set_setting(self, name, value)
Sets a plugin setting.
Definition: runprofiles.py:114
def _get_setting_name(self, name)
Adds profile's name to the setting.
Definition: runprofiles.py:143
def get_settings(self)
Return a list of settings unique to this profile.
Definition: runprofiles.py:106
def get_command(self)
Returns a command for this profile.
Definition: runprofiles.py:91
def get_command_args(self)
Return a list of command arguments unique to this profile.
Definition: runprofiles.py:99
A runner profile which uses script given by the use.
Definition: runprofiles.py:651
def get_command(self)
Returns a command for this profile.
Definition: runprofiles.py:656
def _create_text_ctrl(parent, value, value_for_error, text_change_handler, enable=True)
Definition: runprofiles.py:640
def get_toolbar(self, parent)
Returns a panel with toolbar controls for this profile.
Definition: runprofiles.py:180
def get_command_args(self)
Return a list of command arguments unique to this profile.
Definition: runprofiles.py:247
def _create_checkbox(parent, value, title, handler)
Definition: runprofiles.py:632
def get_command(self)
Returns a command for this profile.
Definition: runprofiles.py:222
def get_settings(self)
Return a list of settings unique to this profile.
Definition: runprofiles.py:407
def delete_pressed(self)
Handle delete key pressing.
Definition: runprofiles.py:214
def _get_tags_panel(self, parent)
Create a panel to input include/exclude tags.
Definition: runprofiles.py:573
def _create_error_log_message(self, error, returncode)
Definition: runprofiles.py:422
def __init__(self, plugin)
plugin is required so that the profiles can save their settings
Definition: runprofiles.py:175
def format_time(timetuple_or_epochsecs, daysep='', daytimesep=' ', timesep=':', millissep=None)
Returns a timestamp formatted from given time using separators.
Definition: robottime.py:183