pymtt
 All Classes Namespaces Files Functions Variables Groups
TestDef.py
Go to the documentation of this file.
1 #!/usr/bin/env python
2 #
3 # Copyright (c) 2015-2018 Intel, Inc. All rights reserved.
4 # $COPYRIGHT$
5 #
6 # Additional copyrights may follow
7 #
8 # $HEADER$
9 #
10 
11 from __future__ import print_function
12 from future import standard_library
13 standard_library.install_aliases()
14 from builtins import range
15 from builtins import object
16 import os
17 import shutil
18 import sys
19 import configparser
20 import importlib
21 import logging
22 import imp
23 from yapsy.PluginManager import PluginManager
24 import datetime
25 from distutils.spawn import find_executable
26 from threading import Semaphore
27 from pathlib import Path
28 
29 is_py2 = sys.version[0] == '2'
30 
31 # The Test Definition class is mostly a storage construct
32 # to make it easier when passing values across stages and
33 # tools.
34 
35 def _mkdir_recursive(path):
36  sub_path = os.path.dirname(path)
37  if sub_path and not os.path.exists(sub_path):
38  _mkdir_recursive(sub_path)
39  if not os.path.exists(path):
40  os.mkdir(path)
41 
42 ## @mainpage
43 # See @ref Stages for details on the stages of test execution.\n
44 # See @ref Tools for details on the plugins required by Stages.\n
45 # See @ref Utilities for details on the plugins used by the MTT framework.\n
46 # @addtogroup Stages
47 # Stages of test execution
48 # @addtogroup Tools
49 # Plugins required by Stages
50 # @addtogroup Utilities
51 # Plugins used by the MTT framework
52 class TestDef(object):
53  def __init__(self):
54  # set aside storage for options and cmd line args
55  self.options = {}
56  self.args = []
57  # record if we have loaded the plugins or
58  # not - this is just a bozo check to ensure
59  # someone doesn't tell us to do it twice
60  self.loaded = False
61  # set aside a spot for a logger object, and
62  # note that it hasn't yet been defined
63  self.logger = None
64  self.modcmd = None
65  self.execmd = None
66  self.harasser = None
67  self.config = None
68  self.stages = None
69  self.tools = None
70  self.utilities = None
71  self.defaults = None
72  self.log = {}
73  self.watchdog = None
74  self.plugin_trans_sem = Semaphore()
75 
76  def setOptions(self, args):
77  self.options = vars(args)
78  self.args = args
79  # if they want us to clear the scratch, then do so
80  if self.options['clean'] and os.path.isdir(self.options['scratchdir']) :
81  shutil.rmtree(self.options['scratchdir'])
82  # setup the scratch directory
83  _mkdir_recursive(self.options['scratchdir'])
84 
85  # private function to convert values
86  def __convert_value(self, opt, inval):
87  if opt is None or type(opt) is str:
88  return 0, inval
89  elif type(opt) is bool:
90  if type(inval) is bool:
91  return 0, inval
92  elif type(inval) is str:
93  if inval.lower() in ['true', '1', 't', 'y', 'yes']:
94  return 0, True
95  else:
96  return 0, False
97  elif type(inval) is int:
98  if 0 == inval:
99  return 0, False
100  else:
101  return 0, True
102  elif is_py2 and type(inval) is unicode:
103  return 0, int(inval)
104  else:
105  # unknown conversion required
106  print("Unknown conversion required for " + inval)
107  return 1, None
108  elif type(opt) is int:
109  if type(inval) is int:
110  return 0, inval
111  elif type(inval) is str:
112  return 0, int(inval)
113  else:
114  # unknown conversion required
115  print("Unknown conversion required for " + inval)
116  return 1, None
117  elif type(opt) is float:
118  if type(inval) is float:
119  return 0, inval
120  elif type(inval) is str or type(inval) is int:
121  return 0, float(inval)
122  else:
123  # unknown conversion required
124  print("Unknown conversion required for " + inval)
125  return 1, None
126  else:
127  return 1, None
128 
129  # scan the key-value pairs obtained from the configuration
130  # parser and compare them with the options defined for a
131  # given plugin. Generate an output dictionary that contains
132  # the updated set of option values, the default value for
133  # any option that wasn't included in the configuration file,
134  # and return an error status plus output identifying any
135  # keys in the configuration file that are not supported
136  # by the list of options
137  #
138  # @log [INPUT]
139  # - a dictionary that will return the status plus
140  # stderr containing strings identifying any
141  # provided keyvals that don't have a corresponding
142  # supported option
143  # @options [INPUT]
144  # - a dictionary of tuples, each consisting of three
145  # entries:
146  # (a) the default value
147  # (b) data type
148  # (c) a help-like description
149  # @keyvals [INPUT]
150  # - a dictionary of key-value pairs obtained from
151  # the configuration parser
152  # @target [OUTPUT]
153  # - the resulting dictionary of key-value pairs
154  def parseOptions(self, log, options, keyvals, target):
155  # parse the incoming keyvals dictionary against the source
156  # options. If a source option isn't provided, then
157  # copy it across to the target.
158  opts = list(options.keys())
159  kvkeys = list(keyvals.keys())
160  for opt in opts:
161  found = False
162  for kvkey in kvkeys:
163  if kvkey == opt:
164  # they provided us with an update, so
165  # pass this value into the target - expand
166  # any provided lists
167  if keyvals[kvkey] is None:
168  continue
169  st, outval = self.__convert_value(options[opt][0], keyvals[kvkey])
170  if 0 == st:
171  target[opt] = outval
172  else:
173  if len(keyvals[kvkey]) == 0:
174  # this indicates they do not want this option
175  found = True
176  break
177  if keyvals[kvkey][0][0] == "[":
178  # they provided a list - remove the brackets
179  val = keyvals[kvkey].replace('[','')
180  val = val.replace(']','')
181  # split the input to pickup sets of options
182  newvals = list(val)
183  # convert the values to specified type
184  i=0
185  for val in newvals:
186  st, newvals[i] = self.__convert_value(opt[0], val)
187  i = i + 1
188  target[opt] = newvals
189  else:
190  st, target[opt] = self.__convert_value(opt[0], keyvals[kvkey])
191  found = True
192  break
193  if not found:
194  # they didn't provide this one, so
195  # transfer only the value across
196  target[opt] = options[opt][0]
197  # add in any default settings that have not
198  # been overridden - anything set by this input
199  # stage will override the default
200  if self.defaults is not None:
201  keys = self.defaults.options.keys()
202  for key in keys:
203  if key not in target:
204  target[key] = self.defaults.options[key][0]
205 
206  # now go thru in the reverse direction to see
207  # if any keyvals they provided aren't supported
208  # as this would be an error
209  unsupported_options = []
210  for kvkey in kvkeys:
211  # ignore some standard keys
212  if kvkey in ['section', 'plugin']:
213  continue
214  try:
215  if target[kvkey] is not None:
216  pass
217  except KeyError:
218  # some always need to be passed
219  if kvkey in ['parent', 'asis']:
220  target[kvkey] = keyvals[kvkey]
221  else:
222  unsupported_options.append(kvkey)
223  if unsupported_options:
224  sys.exit("ERROR: Unsupported options for section [%s]: %s" % (log['section'], ",".join(unsupported_options)))
225 
226  log['status'] = 0
227  log['options'] = target
228  return
229 
230  def loadPlugins(self, basedir, topdir):
231  if self.loaded:
232  print("Cannot load plugins multiple times")
233  exit(1)
234  self.loaded = True
235 
236  # find the loader utility so we can bootstrap ourselves
237  try:
238  m = imp.load_source("LoadClasses", os.path.join(basedir, "LoadClasses.py"));
239  except ImportError:
240  print("ERROR: unable to load LoadClasses that must contain the class loader object")
241  exit(1)
242  cls = getattr(m, "LoadClasses")
243  a = cls()
244  # setup the loader object
245  self.loader = a.__class__();
246 
247  # Setup the array of directories we will search for plugins
248  # Note that we always look at the topdir location by default
249  plugindirs = []
250  plugindirs.append(topdir)
251  if self.options['plugindir']:
252  # could be a comma-delimited list, so split on commas
253  x = self.options['plugindir'].split(',')
254  for y in x:
255  # prepend so we always look at the given
256  # location first in case the user wants
257  # to "overload/replace" a default MTT
258  # class definition
259  plugindirs.insert(0, y)
260 
261  # Load plugins from each of the specified plugin dirs
262  for dirPath in plugindirs:
263  if not Path(dirPath).exists():
264  print("Attempted to load plugins from non-existent path:", dirPath)
265  continue
266  try:
267  self.loader.load(dirPath)
268  except Exception as e:
269  print("Exception caught while loading plugins:")
270  print(e)
271  sys.exit(1)
272 
273  # Build the stages plugin manager
274  self.stages = PluginManager()
275  # set the location
276  self.stages.setPluginPlaces(plugindirs)
277  # Get a list of all the categories - this corresponds to
278  # the MTT stages that have been defined. Note that we
279  # don't need to formally define the stages here - anyone
280  # can add a new stage, or delete an old one, by simply
281  # adding or removing a plugin directory.
282  self.stages.setCategoriesFilter(self.loader.stages)
283  # Load all plugins we find there
284  self.stages.collectPlugins()
285 
286  # Build the tools plugin manager - tools differ from sections
287  # in that they are plugins we will use to execute the various
288  # sections. For example, the TestRun section clearly needs the
289  # ability to launch jobs. There are many ways to launch jobs
290  # depending on the environment, and sometimes several ways to
291  # start jobs even within one environment (e.g., mpirun vs
292  # direct launch).
293  self.tools = PluginManager()
294  # location is the same
295  self.tools.setPluginPlaces(plugindirs)
296  # Get the list of tools - not every tool will be capable
297  # of executing. For example, a tool that supports direct launch
298  # against a specific resource manager cannot be used on a
299  # system being managed by a different RM.
300  self.tools.setCategoriesFilter(self.loader.tools)
301  # Load all the tool plugins
302  self.tools.collectPlugins()
303  # Tool plugins are required to provide a function we can
304  # probe to determine if they are capable of operating - check
305  # those now and prune those tools that cannot support this
306  # environment
307 
308  # Build the utilities plugins
309  self.utilities = PluginManager()
310  # set the location
311  self.utilities.setPluginPlaces(plugindirs)
312  # Get the list of available utilities.
313  self.utilities.setCategoriesFilter(self.loader.utilities)
314  # Load all the utility plugins
315  self.utilities.collectPlugins()
316 
317  # since we use these all over the place, find the
318  # ExecuteCmd and ModuleCmd plugins and record them
319  availUtil = list(self.loader.utilities.keys())
320  for util in availUtil:
321  for pluginInfo in self.utilities.getPluginsOfCategory(util):
322  if "ExecuteCmd" == pluginInfo.plugin_object.print_name():
323  self.execmd = pluginInfo.plugin_object
324  elif "ModuleCmd" == pluginInfo.plugin_object.print_name():
325  self.modcmd = pluginInfo.plugin_object
326  # initialize this module
327  self.modcmd.setCommand(self.options)
328  elif "Watchdog" == pluginInfo.plugin_object.print_name():
329  self.watchdog = pluginInfo.plugin_object
330  if self.execmd is not None and self.modcmd is not None and self.watchdog is not None:
331  break
332  if self.execmd is None:
333  print("ExecuteCmd plugin was not found")
334  print("This is a basic capability required")
335  print("for MTT operations - cannot continue")
336  sys.exit(1)
337  # Configure harasser plugin
338  print(self.tools.getPluginsOfCategory("Harasser"))
339  for pluginInfo in self.tools.getPluginsOfCategory("Harasser"):
340  if "Harasser" == pluginInfo.plugin_object.print_name():
341  self.harasser = pluginInfo.plugin_object
342  break
343  if self.harasser is None:
344  print("Harasser plugin was not found")
345  print("This is required for all TestRun plugins")
346  print("cannot continue")
347  sys.exit(1)
348  # similarly, capture the highest priority defaults stage here
349  pri = -1
350  for pluginInfo in self.stages.getPluginsOfCategory("MTTDefaults"):
351  if pri < pluginInfo.plugin_object.priority():
352  self.defaults = pluginInfo.plugin_object
353  pri = pluginInfo.plugin_object.priority()
354 
355  return
356 
357  def printInfo(self):
358  # Print the available MTT sections out, if requested
359  if self.options['listsections']:
360  print("Supported MTT stages:")
361  # print them in the default order of execution
362  for stage in self.loader.stageOrder:
363  print(" " + stage)
364  exit(0)
365 
366  # Print the detected plugins for a given stage
367  if self.options['listplugins']:
368  # if the list is '*', print the plugins for every stage
369  if self.options['listplugins'] == "*":
370  sections = self.loader.stageOrder
371  else:
372  sections = self.options['listplugins'].split(',')
373  print()
374  for section in sections:
375  print(section + ":")
376  try:
377  for pluginInfo in self.stages.getPluginsOfCategory(section):
378  print(" " + pluginInfo.plugin_object.print_name())
379  except KeyError:
380  print(" Invalid stage name " + section)
381  print()
382  exit(1)
383 
384  # Print the options for a given plugin
385  if self.options['liststageoptions']:
386  # if the list is '*', print the options for every stage/plugin
387  if self.options['liststageoptions'] == "*":
388  sections = self.loader.stageOrder
389  else:
390  sections = self.options['liststageoptions'].split(',')
391  print()
392  for section in sections:
393  print(section + ":")
394  try:
395  for pluginInfo in self.stages.getPluginsOfCategory(section):
396  print(" " + pluginInfo.plugin_object.print_name() + ":")
397  pluginInfo.plugin_object.print_options(self, " ")
398  except KeyError:
399  print(" Invalid stage name " + section)
400  print()
401  exit(1)
402 
403  # Print the available MTT tools out, if requested
404  if self.options['listtools']:
405  print("Available MTT tools:")
406  availTools = list(self.loader.tools.keys())
407  for tool in availTools:
408  print(" " + tool)
409  exit(0)
410 
411  # Print the detected tool plugins for a given tool type
412  if self.options['listtoolmodules']:
413  # if the list is '*', print the plugins for every type
414  if self.options['listtoolmodules'] == "*":
415  print()
416  availTools = list(self.loader.tools.keys())
417  else:
418  availTools = self.options['listtoolmodules'].split(',')
419  print()
420  for tool in availTools:
421  print(tool + ":")
422  try:
423  for pluginInfo in self.tools.getPluginsOfCategory(tool):
424  print(" " + pluginInfo.plugin_object.print_name())
425  except KeyError:
426  print(" Invalid tool type name",tool)
427  print()
428  exit(1)
429 
430  # Print the options for a given plugin
431  if self.options['listtooloptions']:
432  # if the list is '*', print the options for every stage/plugin
433  if self.options['listtooloptions'] == "*":
434  availTools = list(self.loader.tools.keys())
435  else:
436  availTools = self.options['listtooloptions'].split(',')
437  print()
438  for tool in availTools:
439  print(tool + ":")
440  try:
441  for pluginInfo in self.tools.getPluginsOfCategory(tool):
442  print(" " + pluginInfo.plugin_object.print_name() + ":")
443  pluginInfo.plugin_object.print_options(self, " ")
444  except KeyError:
445  print(" Invalid tool type name " + tool)
446  print()
447  exit(1)
448 
449  # Print the available MTT utilities out, if requested
450  if self.options['listutils']:
451  print("Available MTT utilities:")
452  availUtils = list(self.loader.utilities.keys())
453  for util in availUtils:
454  print(" " + util)
455  exit(0)
456 
457  # Print the detected utility plugins for a given tool type
458  if self.options['listutilmodules']:
459  # if the list is '*', print the plugins for every type
460  if self.options['listutilmodules'] == "*":
461  print()
462  availUtils = list(self.loader.utilities.keys())
463  else:
464  availUtils = self.options['listutilitymodules'].split(',')
465  print()
466  for util in availUtils:
467  print(util + ":")
468  try:
469  for pluginInfo in self.utilities.getPluginsOfCategory(util):
470  print(" " + pluginInfo.plugin_object.print_name())
471  except KeyError:
472  print(" Invalid utility type name")
473  print()
474  exit(1)
475 
476  # Print the options for a given plugin
477  if self.options['listutiloptions']:
478  # if the list is '*', print the options for every stage/plugin
479  if self.options['listutiloptions'] == "*":
480  availUtils = list(self.loader.utilities.keys())
481  else:
482  availUtils = self.options['listutiloptions'].split(',')
483  print()
484  for util in availUtils:
485  print(util + ":")
486  try:
487  for pluginInfo in self.utilities.getPluginsOfCategory(util):
488  print(" " + pluginInfo.plugin_object.print_name() + ":")
489  pluginInfo.plugin_object.print_options(self, " ")
490  except KeyError:
491  print(" Invalid utility type name " + util)
492  print()
493  exit(1)
494 
495 
496  # if they asked for the version info, print it and exit
497  if self.options['version']:
498  for pluginInfo in self.tools.getPluginsOfCategory("Version"):
499  print("MTT Base: " + pluginInfo.plugin_object.getVersion())
500  print("MTT Client: " + pluginInfo.plugin_object.getClientVersion())
501  sys.exit(0)
502 
503  def openLogger(self):
504  # there must be a logger utility or we can't do
505  # anything useful
506  if not self.utilities.activatePluginByName("Logger", "Base"):
507  print("Required Logger plugin not found or could not be activated")
508  sys.exit(1)
509  # execute the provided test description
510  self.logger = self.utilities.getPluginByName("Logger", "Base").plugin_object
511  self.logger.open(self)
512  return
513 
514  def fill_log_interpolation(self, basestr, sublog):
515  if isinstance(sublog, str):
516  self.config.set("LOG", basestr, sublog.replace("$","$$"))
517  elif isinstance(sublog, dict):
518  for k,v in sublog.items():
519  self.fill_log_interpolation("%s.%s" % (basestr, k), v)
520  elif isinstance(sublog, list):
521  if sum([((isinstance(t, list) or isinstance(t, tuple)) and len(t) == 2) for t in sublog]) == len(sublog):
522  self.fill_log_interpolation(basestr, {k:v for k,v in sublog})
523  else:
524  for i,v in enumerate(sublog):
525  self.fill_log_interpolation("%s.%d" % (basestr, i), v)
526  else:
527  self.fill_log_interpolation(basestr, str(sublog))
528 
529  def configTest(self):
530  # setup the configuration parser
531  self.config = configparser.SafeConfigParser(interpolation=configparser.ExtendedInterpolation())
532 
533  # Set the config parser to make option names case sensitive.
534  self.config.optionxform = str
535 
536  # fill ENV section with environemt variables
537  self.config.add_section('ENV')
538  for k,v in os.environ.items():
539  self.config.set('ENV', k, v.replace("$","$$"))
540 
541  # Add LOG section filled with log results of stages
542  self.config.add_section('LOG')
543  thefulllog = self.logger.getLog(None)
544  for e in thefulllog:
545  self.fill_log_interpolation(e['section'].replace(":","_"), e)
546 
547  # log the list of files - note that the argument parser
548  # puts the input files in a list, with the first member
549  # being the list of input files
550  self.log['inifiles'] = self.args.ini_files[0]
551  # initialize the list of active sections
552  self.actives = []
553  # if they specified a list to execute, then use it
554  sections = []
555  if self.args.section:
556  sections = self.args.section.split(",")
557  skip = False
558  elif self.args.skipsections:
559  sections = self.args.skipsections.split(",")
560  skip = True
561  else:
562  sections = None
563  # cycle thru the input files
564  for testFile in self.log['inifiles']:
565  if not os.path.isfile(testFile):
566  print("Test description file",testFile,"not found!")
567  sys.exit(1)
568  self.config.read(self.log['inifiles'])
569 
570  # Check for ENV input
571  required_env = []
572  all_file_contents = []
573  for testFile in self.log['inifiles']:
574  file_contents = open(testFile, "r").read()
575  file_contents = "\n".join(["%s %d: %s" % (testFile.split("/")[-1],i,l) for i,l in enumerate(file_contents.split("\n")) if not l.lstrip().startswith("#")])
576  all_file_contents.append(file_contents)
577  if "${ENV:" in file_contents:
578  required_env.extend([s.split("}")[0] for s in file_contents.split("${ENV:")[1:]])
579  env_not_found = set([e for e in required_env if e not in os.environ.keys()])
580  lines_with_env_not_found = []
581  for file_contents in all_file_contents:
582  lines_with_env_not_found.extend(["%s: %s"%(",".join([e for e in env_not_found if "${ENV:%s}"%e in l]),l) \
583  for l in file_contents.split("\n") \
584  if sum(["${ENV:%s}"%e in l for e in env_not_found])])
585  if lines_with_env_not_found:
586  print("ERROR: Not all required environment variables are defined.")
587  print("ERROR: Still need:")
588  for l in lines_with_env_not_found:
589  print("ERROR: %s"%l)
590  sys.exit(1)
591 
592  for section in self.config.sections():
593  if section.startswith("SKIP") or section.startswith("skip"):
594  # users often want to temporarily ignore a section
595  # of their test definition file, but don't want to
596  # remove it lest they forget what it did. So let
597  # them just mark the section as "skip" to be ignored
598  continue
599  # if we are to filter the sections, then do so
600  takeus = True
601  if sections is not None:
602  found = False
603  for sec in sections:
604  if sec == section:
605  found = True
606  sections.remove(sec)
607  if skip:
608  takeus = False
609  break
610  if not found and not skip:
611  takeus = False
612  if takeus:
613  self.actives.append(section)
614  if sections is not None and 0 != len(sections) and not skip:
615  print("ERROR: sections were specified for execution and not found:",sections)
616  sys.exit(1)
617  return
618 
619  # Used with combinatorial executor, loads next .ini file to be run with the
620  # sequential executor
621  def configNewTest(self, file):
622  # clear the configuration parser
623  for section in self.config.sections():
624  self.config.remove_section(section)
625  # read in the file
626  self.config.read(file)
627  for section in self.config.sections():
628  if section.startswith("SKIP") or section.startswith("skip"):
629  # users often want to temporarily ignore a section
630  # of their test definition file, but don't want to
631  # remove it lest they forget what it did. So let
632  # them just mark the section as "skip" to be ignored
633  continue
634  if self.logger is not None:
635  self.logger.verbose_print("SECTION: " + section)
636  self.logger.verbose_print(self.config.items(section))
637  return
638 
639  def executeTest(self, enable_loop=True, executor="sequential"):
640 
641  if not self.loaded:
642  print("Plugins have not been loaded - cannot execute test")
643  exit(1)
644  if self.config is None:
645  print("No test definition file was parsed - cannot execute test")
646  exit(1)
647  if not self.tools.getPluginByName(executor, "Executor"):
648  print("Specified executor %s not found" % executor)
649  exit(1)
650  # activate the specified plugin
651  self.tools.activatePluginByName(executor, "Executor")
652  # execute the provided test description
653  executor = self.tools.getPluginByName(executor, "Executor")
654  status = executor.plugin_object.execute(self)
655  if status == 0 and self.options['clean_after'] and os.path.isdir(self.options['scratchdir']):
656  self.logger.verbose_print("Cleaning up scratchdir after successful run")
657  shutil.rmtree(self.options['scratchdir'])
658  # Loop forever if specified
659  if enable_loop and self.options['loopforever']:
660  while True:
661  self.logger.reset()
662  self.executeTest(enable_loop=False, executor=executor)
663  return status
664 
665  def executeCombinatorial(self, enable_loop=True):
666  return self.executeTest(enable_loop=enable_loop, executor="combinatorial")
667 
668  def printOptions(self, options):
669  # if the options are empty, report that
670  if not options:
671  lines = ["None"]
672  return lines
673  # create the list of options
674  opts = []
675  vals = list(options.keys())
676  for val in vals:
677  opts.append(val)
678  if options[val][0] is None:
679  opts.append("None")
680  elif isinstance(options[val][0], bool):
681  if options[val][0]:
682  opts.append("True")
683  else:
684  opts.append("False")
685  elif isinstance(options[val][0], list):
686  opts.append(" ".join(options[val][0]))
687  elif isinstance(options[val][0], int):
688  opts.append(str(options[val][0]))
689  else:
690  opts.append(options[val][0])
691  opts.append(options[val][1])
692  # print the options, their default value, and
693  # the help description in 3 column format
694  max1 = 0
695  max2 = 0
696  for i in range(0,len(opts),3):
697  # we want all the columns to line up
698  # and left-justify, so first find out
699  # the max len of each of the first two
700  # column entries
701  if len(opts[i]) > max1:
702  max1 = len(opts[i])
703  if type(opts[i+1]) is not str:
704  optout = str(opts[i+1])
705  else:
706  optout = opts[i+1]
707  if len(optout) > max2:
708  max2 = len(optout)
709  # provide some spacing
710  max1 = max1 + 4
711  max2 = max2 + 4
712  # cycle thru again, padding each entry to
713  # align the columns
714  lines = []
715  sp = " "
716  for i in range(0,len(opts),3):
717  line = opts[i] + (max1-len(opts[i]))*sp
718  if type(opts[i+1]) is not str:
719  optout = str(opts[i+1])
720  else:
721  optout = opts[i+1]
722  line = line + optout + (max2-len(optout))*sp
723  # to make this more readable, we will wrap the line at
724  # 130 characters. First, see if the line is going to be
725  # too long
726  if 130 < (len(line) + len(opts[i+2])):
727  # split the remaining column into individual words
728  words = opts[i+2].split()
729  first = True
730  for word in words:
731  if (len(line) + len(word)) < 130:
732  if first:
733  line = line + word
734  first = False
735  else:
736  line = line + " " + word
737  else:
738  lines.append(line)
739  line = (max1 + max2)*sp + word
740  if 0 < len(line):
741  lines.append(line)
742  else:
743  # the line is fine - so just add the last piece
744  line = line + opts[i+2]
745  # append the result
746  lines.append(line)
747  # add one blank line
748  lines.append("")
749  return lines
750 
751 
752  def selectPlugin(self, name, category):
753  if category == "stage":
754  try:
755  availStages = list(self.loader.stages.keys())
756  for stage in availStages:
757  for pluginInfo in self.stages.getPluginsOfCategory(stage):
758  if name == pluginInfo.plugin_object.print_name():
759  return pluginInfo.plugin_object
760  # didn't find it
761  return None
762  except:
763  return None
764  elif category == "tool":
765  try:
766  availTools = list(self.loader.tools.keys())
767  for tool in availTools:
768  for pluginInfo in self.tools.getPluginsOfCategory(tool):
769  if name == pluginInfo.plugin_object.print_name():
770  return pluginInfo.plugin_object
771  # didn't find it
772  return None
773  except:
774  return None
775  elif category == "utility":
776  try:
777  availUtils = list(self.loader.utilities.keys())
778  for util in availUtils:
779  for pluginInfo in self.utilities.getPluginsOfCategory(util):
780  if name == pluginInfo.plugin_object.print_name():
781  return pluginInfo.plugin_object
782  # didn't find it
783  return None
784  except:
785  return None
786  else:
787  print("Unrecognized category:",category)
788  return None
def executeCombinatorial
Definition: TestDef.py:665
tuple cls
Definition: pymtt.py:208
def __convert_value
Definition: TestDef.py:86
def printOptions
Definition: TestDef.py:668
def selectPlugin
Definition: TestDef.py:752
def setOptions
Definition: TestDef.py:76
def fill_log_interpolation
Definition: TestDef.py:514
def configNewTest
Definition: TestDef.py:621
def parseOptions
Definition: TestDef.py:154
def _mkdir_recursive
Definition: TestDef.py:35