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