test_ospl

Module test_ospl |
[hide private]
[frames] | no frames]

# Source Code for Module test_ospl

      1  import os
      2
      3 -class TestError (Exception):
      4      """
      5      A simple exception for test problems
      6      """
      7 -    def __init__(self, value):
      8          self.value = value
      9 -    def __str__(self):
     10          return repr(self.value)
     11
     12 -class Process:
     13      """Represents a process"""
     14
     15 -    def __init__(self, node=None, test_case=None):
     16          """Constructs a Process associating it with a particualar TestScenario and Node"""
     17          self.node_ = node
     18          self.test_case_ = test_case
     19          self.handle_ = -1
     20          self.process_port_ = test_case.get_unique_port()
     21          self.args_ = ""
     22          self.process_number_ = 0
     23          self.log_file_ = ""
     24          self.working_dir_ = ""
     25          self.command_ = ""
     26          self.uses_test_sync_lib_ = 1
     27
     28 -    def set_command(self, command):
     29          """Set the process executable name"""
     30          self.command_ = command
     31
     32 -    def get_command(self):
     33          """Get the process executable name"""
     34          return self.command_
     35
     36 -    def set_log_file(self, file_name):
     37          """Set a log file name that is to be used for standard out and error collection
     38          for this process. Will be created in the working directory.
     39          Take care to make this unique within this scenario if you don't want
     40          it to be overwritten."""
     41          self.log_file_ = file_name
     42
     43 -    def get_log_file(self):
     44          """Return the standard out / error log file name for this process that has been set
     45          explicitly or a suitable default otherwise."""
     46          if self.log_file_ == "":
     47              log_file_name = '{STAF/Config/Sep/File}%s.log' % (self.get_command())
     48          else:
     49              log_file_name = '{STAF/Config/Sep/File}%s' % (self.log_file_)
     50          return log_file_name
     51
     52 -    def set_working_dir(self, working_dir):
     53          """Explicitly set the process working directory path.
     54          Required if you don't want to take the default (which will be
     55          testsuite/tests/<scenario name>
     56          or whatever else has been set explicitly on the scenario
     57          """
     58          self.working_dir_ = working_dir
     59
     60 -    def get_working_dir(self):
     61          """Get the process working directory path. Returns default for this
     62          TestScenario on this Process's Node"""
     63          if self.working_dir_ == "":
     64              if self.test_case_ == None:
     65                  raise TestError ("Can't get a default working directory for Process " +
     66                                   self.command_ + " not associated with a TestScenario")
     67              if self.node_ == None:
     68                  raise TestError ("Can't get a default working directory for Process " +
     69                                   self.command_ + " not associated with a Node")
     70              return self.test_case_.get_working_dir(self.node_)
     71
     72          return self.working_dir_
     73
     74 -    def set_args(self, args):
     75          """Set the process 'user' arguments"""
     76          self.args_ = args
     77
     78 -    def get_args(self):
     79          """Get the process 'user' arguments"""
     80          return self.args_
     81
     82 -    def set_uses_test_sync_lib(self, does_use_it = 1):
     83          """Set whether this process uses the test sync lib or not.
     84          Default is true. Set to 0 to prevent STAX providing extra config args
     85          when this process is started."""
     86          self.uses_test_sync_lib_ = does_use_it
     87
     88 -    def get_test_lib_args(self):
     89          """
     90          Get the additional process args required to initialise the
     91          OSPL test lib interprocess synchronisation stuff (if required)
     92          You can of course do this 'manually' but this does it for you
     93          if you've stuck to the usual case.
     94          """
     95          if self.uses_test_sync_lib_ == 0:
     96              return ""
     97
     98          extra_args = " -ORBListenEndpoints iiop://:" + str(self.process_port_)
     99          for other_process in self.test_case_.processes_:
    100              if other_process == self:
    101                  pass
    102              else:
    103                  extra_args += " -ORBInitRef " + other_process.get_command () + "=corbaloc:iiop:" + \
    104                                other_process.node_.host_name_ + ":" + str(other_process.process_port_) + \
    105                                "/" + other_process.get_command ()
    106          return extra_args
    107
    108 -    def get_process_env(self):
    109          """
    110          Return the env that this process should be spawned or run with. Adds the
    111          """
    112          cloned_env = (self.node_.get_ospl_env())[:]
    113          i = 0
    114          while i < len(cloned_env):
    115              upper_case_env_line = cloned_env[i].upper()
    116              if upper_case_env_line.startswith('PATH=') or upper_case_env_line.startswith('LD_LIBRARY_PATH='):
    117                  cloned_env[i] += '{STAF/Config/Sep/Path}%s{STAF/Config/Sep/Path}%s{STAF/Config/Sep/File}testsuite{STAF/Config/Sep/File}tests{STAF/Config/Sep/File}testlibs' % \
    118                                                                                        (self.get_working_dir(),
    119                                                                                        self.node_.get_test_root())
    120              i += 1
    121          return cloned_env
    122
    123 -    def get_process_id_number(self):
    124          """
    125          Return a unique number identifying this process instance within the test run.
    126          Handy as a key if starting asynchronously.
    127          """
    128          return self.process_number_
    129
    130 -class TestNode:
    131      """
    132      A machine environment that a part of a TestScenario runs on.
    133
    134      Each requires a STAF daemon.
    135      """
    136 -    def __init__(self, host_name, test_root, ospl_home = None, staf_port = 6500, staf_url = ""):
    137          """Constructor for a TestNode. Required are the/a DNS name of the host, the root at
    138          which a source checkout of OSPL exists. Optionally the value of OSPL_HOME that is to be tested
    139          or None if you are taking responsibility for configuring the STAF daemon environment
    140          yourself & the location of the STAF daemon if not at the default port"""
    141          self.host_name_ = host_name
    142          self.test_root_ = test_root
    143          self.staf_port_ = staf_port
    144          self.staf_url_ = staf_url
    145          self.requires_static_load_ = 0
    146          if self.staf_url_ == "":
    147              self.staf_url_ = "tcp://" + self.host_name_ + "@" + str(self.staf_port_)
    148          self.initialised_ = 0
    149          self.ospl_home_ = ospl_home
    150          self.ospl_env_ = []
    151
    152 -    def get_test_root(self):
    153          """
    154          Get the location that the built test checkout resides at on this machine
    155          for this run
    156          """
    157          return self.test_root_
    158
    159 -    def get_ospl_home(self):
    160          """
    161          Get the value of OSPL_HOME on this TestNode within this particular TestRun
    162          """
    163          return self.ospl_home_
    164
    165 -    def get_ospl_env(self):
    166          """
    167          Return the list of environment variable values applicable to the
    168          configured OSPL_HOME (if any) within this particular TestRun on this TestNode
    169          """
    170          return self.ospl_env_
    171
    172 -    def get_ospl_uri(self):
    173          """
    174          Convenience accessor for OSPL_URI env prop on this box. We need this to make sure we can
    175          ospl list only the default domain to find out if it is running or not
    176          """
    177          for env_line in self.ospl_env_:
    178              if env_line.startswith("OSPL_URI="):
    179                  return env_line[9:] # return the value after the =
    180          return ""
    181
    182 -    def get_staf_url(self):
    183          """
    184          Returns the url that the STAF daemon is running at on this Node
    185          """
    186          return self.staf_url_
    187
    188 -    def get_host_name(self):
    189          """
    190          Returns the Node hostname or IP or whatever
    191          """
    192          return self.host_name_
    193
    194 -    def is_windows(self):
    195          """
    196          Returns true if this TestNode is a windows box
    197          """
    198          return self.config_map_["OSName"].startswith('Win')
    199
    200 -    def file_sep(self):
    201          """
    202          Returns the file path separator for this TestNode. i.e. / or \
    203          """
    204          return self.config_map_["FileSep"]
    205
    206 -    def path_sep(self):
    207          """
    208          Returns the path sepator on this TestNode. i.e. : or ;
    209          """
    210          return self.config_map_["PathSep"]
    211
    212 -    def os_name(self):
    213          """
    214          Returns a string describing the OS on this TestNode
    215          """
    216          return (self.config_map_["OSName"] + " " + self.config_map_["OSMajorVersion"]
    217                  + "." + self.config_map_["OSMinorVersion"] + "." + self.config_map_["OSRevision"])
    218
    219 -class TestRun:
    220      """
    221      Represents a collection of TestScenario objects involving a number (1 or more)
    222      of TestNode's
    223      """
    224 -    def __init__(self, nodes = [], run_id = ""):
    225          self.nodes_ = nodes
    226          self.test_run_id_ = run_id
    227          if len (self.nodes_) == 0 :
    228              self.nodes_ = [ TestScenario.LOCALHOST ]
    229          self.test_cases_ = []
    230          self.verbose_ = 1
    231          self.process_count_ = 0
    232
    233      the_test_run_ = None
    234
    235      #@staticmethod
    236      #def get_test_run():
    237      #def get_test_run():
    238      #    """
    239      #    Static accessor for the TestRun
    240      #    """
    241      #    return TestRun.the_test_run_
    242
    243      # get_test_run = staticmethod(get_test_run)
    244
    245 -    def create_test_scenario(self, test_name):
    246          """
    247          Factory method to create a TestScenario within this test run.
    248          """
    249          self.test_cases_.append(TestScenario(test_name, self))
    250          return self.test_cases_[len(self.test_cases_) - 1]
    251
    252 -class TestScenario:
    253      """
    254      Represents a single test scenario within a test. A scenario has
    255      1 or more Nodes, and an arbitrary number of processes
    256      """
    257
    258 -    def __init__(self, name="", test_run=TestRun.the_test_run_, description=""):
    259          self.name_ = name
    260          self.test_run_ = test_run
    261          self.required_nodes_ = 1
    262          self.description_ = description
    263          self.nodes_ = test_run.nodes_
    264          self.node_index_ = 0
    265          self.locked_and_loaded_ = 0
    266          self.processes_ = []
    267          self.next_port_ = 23231
    268          self.work_dir_override_ = ""
    269
    270 -    def get_name(self):
    271          """
    272          Accessor for the TestScenario name
    273          """
    274          return self.name_
    275
    276 -    def requires_multiple_nodes(self, min_number = 2):
    277          """
    278          Specify that this test scenario requires a *minimum* number of nodes to
    279          do its thing. The default is 1 i.e. single node. If your test cannot operate
    280          at all on one node specify an appropriate higher value. Do *not* specify a
    281          higher value just because the test is less meaningful on less.
    282          Keep this value as low as possible.
    283          """
    284          self.required_nodes_ = min_number
    285
    286 -    def get_next_node(self):
    287          """
    288          Gets a new node definition from the scenario. Will be a new node iff the scenario has enough for it to be so.
    289          """
    290          next_node = self.nodes_[self.node_index_]
    291          if (self.node_index_ + 1) >= len (self.nodes_) :
    292              pass
    293          else:
    294              self.node_index_ += 1
    295          return next_node
    296
    297 -    def get_working_dir(self, node):
    298          """
    299          Get the default working directory for this scenario on the given Node.
    300          Assumes this scenario directory is under the usual test root and is named
    301          as per convention.
    302          """
    303          if self.name_ == "":
    304              raise TestError("Attempt to get the default working directory on "  +
    305                              node.get_host_name() + " from unnamed scenario")
    306
    307          return (node.get_test_root()
    308                 + "{STAF/Config/Sep/File}testsuite{STAF/Config/Sep/File}tests{STAF/Config/Sep/File}"
    309                 + self.name_)
    310
    311 -    def get_log_dest_dir(self):
    312          """
    313          Where the logs need to be copied to
    314          """
    315          if self.name_ == "":
    316              raise TestError("TestScenario must have a name")
    317
    318          return ("{STAF/Config/Sep/File}..{STAF/Config/Sep/File}"
    319                 + self.name_)
    320
    321 -    def lock_and_load(self):
    322          """
    323          Checks the requirements of the whole TestScenario are met by the TestRun it
    324          is within. Will actually have build and load the test processes in some embedded
    325          circumstances if there's no other alternative. Scenario should exit if this does not return True.
    326          No changes should be made to the scenario after this.
    327          """
    328          if self.locked_and_loaded_ :
    329              raise TestError("Duplicate call to lock_and_load scenario: " + self.name_)
    330
    331          if self.check_test_reqs_met () :
    332              # Hook for static build & load actions
    333              self.locked_and_loaded_ = 1
    334
    335          return self.locked_and_loaded_
    336
    337 -    def check_test_reqs_met(self):
    338          """
    339          Check the TestScenario requirements can be satisfied by the TestRun it is
    340          within.
    341
    342          If it can't it will throw an error and the scenario should be recorded as skipped.
    343          """
    344          if self.required_nodes_ > len (self.nodes_):
    345              raise TestError("TestRun does not comprise enough Nodes for scenario: " + self.name_)
    346
    347          return 1
    348
    349 -    def define_process(self, node):
    350          """
    351          Declare a process to run on the specified Node within this TestScenario
    352          """
    353          process_result = Process(node, self)
    354          self.processes_.append(process_result)
    355          self.test_run_.process_count_ += 1
    356          process_result.process_number_ = self.test_run_.process_count_
    357          return process_result
    358
    359 -    def get_unique_port(self):
    360          """
    361          Returns a unique port. Used to make soure Process instances'
    362          IPC can be initialised in a non conflicting manner.
    363          """
    364          self.next_port_ += 1
    365          return self.next_port_
    366
    367      LOCALHOST = TestNode("local", os.path.join(os.getcwd(), ".."))
    368