test.py
Go to the documentation of this file.
00001 #!/usr/bin/env python
00002 #
00003 # Software License Agreement (BSD License)
00004 #
00005 # Copyright (c) 2008, Willow Garage, Inc.
00006 # All rights reserved.
00007 #
00008 # Redistribution and use in source and binary forms, with or without
00009 # modification, are permitted provided that the following conditions
00010 # are met:
00011 #
00012 #  * Redistributions of source code must retain the above copyright
00013 #    notice, this list of conditions and the following disclaimer.
00014 #  * Redistributions in binary form must reproduce the above
00015 #    copyright notice, this list of conditions and the following
00016 #    disclaimer in the documentation and/or other materials provided
00017 #    with the distribution.
00018 #  * Neither the name of the Willow Garage nor the names of its
00019 #    contributors may be used to endorse or promote products derived
00020 #    from this software without specific prior written permission.
00021 #
00022 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
00023 # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
00024 # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
00025 # FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
00026 # COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
00027 # INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
00028 # BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
00029 # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
00030 # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
00031 # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
00032 # ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
00033 # POSSIBILITY OF SUCH DAMAGE.
00034 
00035 ##\author Kevin Watts, Josh Faust
00036 
00037 PKG = 'qualification'
00038 
00039 import os, sys
00040 import roslib
00041 roslib.load_manifest(PKG)
00042 
00043 from xml.dom import minidom
00044 
00045 class NotADirectoryError(Exception): pass
00046 class TestDoesNotExistError(Exception): pass
00047 class FailedLoadError(Exception): pass
00048 
00049 ## Qualification Subtest. Each subtest has a main launch file and may have 
00050 ## pre- and post- startup scripts. All launch files are stored with their
00051 ## complete paths
00052 ##@param subtest str: Launch file of subtest
00053 ##@param key int : Allows the subtests to load in order, lowest key first
00054 ##@param pre_test str (optional): Launch file for pre-subtest script
00055 ##@param post_test str (optional): Launch file for post-subtest script
00056 ##@param name str (optional): Human readable name of subtest
00057 class SubTest(object):
00058   def __init__(self, subtest, key, name, timeout = -1, pre_test=None, post_test=None):
00059     self._test_script = subtest
00060     self._pre_script = pre_test
00061     self._post_script = post_test
00062     self._name = name
00063     self._key = key
00064     self._timeout = timeout
00065 
00066   ##\brief Check that key exists, launch files exist and are ".launch" files
00067   def validate(self):
00068     if self._name is None:
00069       print >> sys.stderr, 'Subtests must be named.'
00070       return False
00071 
00072     if self._key is None:
00073       print >> sys.stderr, 'Subtest key is none.'
00074       return False
00075 
00076     if self._test_script is None:
00077       print >> sys.stderr, 'Subtest has not launch script'
00078       return False
00079 
00080     if not os.path.exists(self._test_script):
00081       print >> sys.stderr, 'Subtest launch file does not exist: %s' % self._test_script
00082       return False
00083 
00084     if not self._test_script.endswith(".launch"):
00085       print >> sys.stderr, 'Given test script is not a launch file: %s' % self._test_script
00086       return False
00087     
00088     if self._pre_script is not None:
00089       if not os.path.exists(self._pre_script):
00090         print >> sys.stderr, 'Subtest pre-launch file is listed, but does not exist: %s' % self._pre_script
00091         return False
00092       if not self._pre_script.endswith(".launch"):
00093         print >> sys.stderr, 'Given pre launch script is not a launch file: %s' % self._pre_script
00094         return False
00095       
00096     if self._post_script is not None:
00097       if not os.path.exists(self._post_script):
00098         print >> sys.stderr, 'Subtest post-launch file is listed, but does not exist: %s' % self._post_script
00099         return False
00100       if not self._post_script.endswith(".launch"):
00101         print >> sys.stderr, 'Given post launch script is not a launch file: %s' % self._post_script
00102         return False
00103 
00104     return True
00105   
00106 
00107   ## Returns subtest key to allow subtests to load in order
00108   def get_key(self):
00109     return self._key
00110 
00111   def get_timeout(self):
00112     return self._timeout
00113 
00114   def get_name(self):
00115     return self._name
00116 
00117 
00118 ##\brief Holds pre-startup/shutdown scripts for qual tests
00119 ##\todo Change private vars to _names
00120 class TestScript(object):
00121   ##@param launch_file: Complete file name of launch file
00122   ##@param name str (optional): Human readable name of pre-startup script
00123   ##@param timeout int (optional) : Timeout in seconds of test
00124   def __init__(self, launch_file, name=None, timeout = -1):
00125     self.launch_file = launch_file
00126     self.name = name
00127     self.timeout = timeout
00128 
00129   
00130   ##\brief Check that launch file exists and is ".launch" file
00131   def validate(self):
00132     if self.name is None:
00133       print >> sys.stderr, 'TestScripts must be named'
00134       return False
00135 
00136     if self.launch_file is None:
00137       print >> sys.stderr, 'No launch file found, invalid TestScript'
00138       return False
00139 
00140     if not os.path.exists(self.launch_file):
00141       print >> sys.stderr, 'TestScript launch file %s does not exist' % self.launch_file
00142       return False
00143 
00144     if not self.launch_file.endswith(".launch"):
00145       print >> sys.stderr, 'TestScript launch file %s is not a .launch file' % self.launch_file
00146       return False
00147 
00148     return True
00149 
00150   def get_timeout(self):
00151     return self.timeout
00152     
00153   ## Returns name, or launch filename if no name
00154   def get_name(self):
00155     if self.name is not None:
00156       return self.name
00157 
00158     return os.path.basename(self.launch_file)
00159       
00160 ##\brief Qualification test to run. 
00161 ##
00162 ##Holds instructions, subtests, pre_startup scripts, etc.
00163 class Test(object):
00164   def __init__(self, name, part = ''):
00165     self._name = name
00166     self._part = part
00167     self._startup_script = None
00168     self._shutdown_script = None
00169     self._instructions_file = None
00170     self.pre_startup_scripts = []
00171     self.subtests = []
00172     
00173     self._check_assembly = False
00174     self.debug_ok = False # If True, it can run outside of debug mode
00175 
00176     self._id = None
00177 
00178   ##\brief Check that all prestartups, subtests, instructions, etc are real files
00179   ##
00180   ## This does not do any kind of parsing of the files themselves. It only
00181   ## checks that they exist, and that all files are ".launch" files. Instructions
00182   ## files must be ".html" files. Checks that test is named.
00183   ##\return True if Test is valid.
00184   def validate(self):
00185     if not self._id:
00186       print >> sys.stderr, 'Qualification tests must have ID'
00187       return False
00188 
00189     if self._name is None:
00190       print >> sys.stderr, 'Qualification tests must be named.'
00191       return False
00192 
00193     if not self._part:
00194       print >> sys.stderr, 'Qualification test %s does not have a part number' % self._name
00195       return False
00196 
00197     if not self.debug_ok and (not unicode(self._part).isnumeric() or len(self._part) != 4):
00198       print >> sys.stderr, 'Qualification test %s does not have a numeric part number. Part: %s' % (self._name, self._part)
00199       return False
00200 
00201     if len(self.subtests) == 0 and len(self.pre_startup_scripts) == 0:
00202       print >> sys.stderr, 'No subtests or prestartup scripts. Not loaded'
00203       return False
00204 
00205     if self._startup_script is not None and not self._startup_script.validate():
00206       print >> sys.stderr, 'Startup script does not exist: %s' % self._startup_script.launch_file
00207       return False
00208 
00209     if self._shutdown_script is not None and not self._shutdown_script.validate():
00210       print >> sys.stderr, 'Shutdown script does not exist: %s' % self._shutdown_script.launch_file
00211       return False
00212 
00213     if self._instructions_file is not None:
00214       if not os.path.exists(self._instructions_file):
00215         print >> sys.stderr, 'Instructions file does not exist: %s' % self._instructions_file
00216         return False
00217       if not self._instructions_file.endswith('.html'):
00218         print >> sys.stderr, 'Instructions file %s is not a ".html" file' % self._instructions_file
00219         return False
00220 
00221     for prestart in self.pre_startup_scripts:
00222       if not prestart.validate():
00223         return False
00224 
00225     for subtest in self.subtests:
00226       if not subtest.validate():
00227         return False
00228 
00229     return True
00230     
00231      
00232   ##\brief Loads qual test from and XML string
00233   ##@param test_str: XML file to load, as string
00234   ##@param test_dir: Base directory of test, appended to qual tests
00235   ##@return True if load succeeded
00236   def load(self, test_str, test_dir):
00237     try:
00238       doc = minidom.parseString(test_str)
00239     except IOError:
00240       print >> sys.stderr, 'Unable to parse test string:\n%s' % test_str
00241       import traceback
00242       traceback.print_exc()
00243       return False
00244     
00245     test_list = doc.getElementsByTagName('test')
00246     if len(test_list) != 1:
00247       print >> sys.stderr, "More than one test found in XML: %s" % test_str
00248       return False
00249 
00250     test_main = test_list[0]
00251     if test_main.attributes.has_key('check-assembly') and \
00252           test_main.attributes['check-assembly'].value.lower() == "true":
00253       self._check_assembly = True
00254 
00255     if test_main.attributes.has_key('id'):
00256       self._id = test_main.attributes['id'].value
00257     else:
00258       print >> sys.stderr, 'No \"id\" found. Unable to load test.'
00259       return False
00260 
00261     pre_startups = doc.getElementsByTagName('pre_startup')
00262     if (pre_startups != None and len(pre_startups) > 0):
00263       for pre_startup in pre_startups:
00264         launch = os.path.join(test_dir, pre_startup.childNodes[0].nodeValue)
00265         name = None
00266         timeout = -1
00267 
00268         if not pre_startup.attributes.has_key('name'):
00269           print >> sys.stderr, 'Prestartup script missing name.'
00270           return False
00271         name = pre_startup.attributes['name'].value              
00272 
00273         if (pre_startup.attributes.has_key('timeout')):
00274           timeout = int(pre_startup.attributes['timeout'].value)           
00275           
00276         self.pre_startup_scripts.append(TestScript(launch, name, timeout))
00277         
00278     elems = doc.getElementsByTagName('startup')
00279     if (elems != None and len(elems) > 0):
00280       launch = os.path.join(test_dir, elems[0].childNodes[0].nodeValue)
00281       name = None
00282       if (elems[0].attributes.has_key('name')):
00283         name = elems[0].attributes['name'].value
00284       else:
00285         name = elems[0].childNodes[0].nodeValue
00286       self._startup_script = TestScript(launch, name)
00287     
00288     elems = doc.getElementsByTagName('shutdown')
00289     if (elems != None and len(elems) > 0):
00290       launch = os.path.join(test_dir, elems[0].childNodes[0].nodeValue)
00291       name = None
00292       timeout = -1
00293 
00294       if (elems[0].attributes.has_key('name')):
00295         name = elems[0].attributes['name'].value
00296 
00297       if (elems[0].attributes.has_key('timeout')):
00298         timeout = int(elems[0].attributes['timeout'].value)           
00299 
00300       self._shutdown_script = TestScript(launch, name, timeout)
00301                                         
00302     elems = doc.getElementsByTagName('instructions')
00303     if (elems != None and len(elems) > 0):
00304       self._instructions_file = os.path.join(test_dir, elems[0].childNodes[0].nodeValue)
00305     
00306     key_count = 1
00307     subtests = doc.getElementsByTagName('subtest')
00308     if (subtests != None and len(subtests) > 0):
00309       for st in subtests:
00310         script = os.path.join(test_dir, st.childNodes[0].nodeValue)
00311         pre = None
00312         post = None
00313         timeout = -1
00314         
00315         if not st.attributes.has_key('name'):
00316           print >> sys.stderr, "Subtest does not have name. XML: %s" % str(st)
00317           return False
00318         name = st.attributes['name'].value
00319 
00320         if (st.attributes.has_key('post')):
00321           post = os.path.join(test_dir, st.attributes['post'].value)
00322         if (st.attributes.has_key('pre')):
00323           pre = os.path.join(test_dir, st.attributes['pre'].value)
00324         if st.attributes.has_key('timeout'):
00325           timeout = int(st.attributes['timeout'].value)
00326         
00327         key = key_count
00328         key_count += 1
00329         
00330         my_sub = SubTest(script, key, name, timeout, pre, post)
00331         self.subtests.append(my_sub)
00332 
00333     return True
00334                                         
00335   ##\todo Change functions names to Python style or properties
00336   ##\brief Name or basename or startup script
00337   def getName(self):
00338     print >> sys.stderr, "Using deprecated Test.getName()"
00339     return self._name
00340  
00341   def get_name(self):
00342     return self._name
00343 
00344   @property
00345   def name(self): return self._name
00346     
00347   ##\brief Full path to startup script
00348   def getStartupScript(self):
00349     return self._startup_script
00350 
00351   @property
00352   def startup_script(self): return self._startup_script
00353                                 
00354   ##\brief Full path to shutdown script
00355   def getShutdownScript(self):
00356     return self._shutdown_script
00357   
00358   @property
00359   def shutdown_script(self): return self._shutdown_script
00360   
00361   ##\brief Full path to instructions file
00362   def getInstructionsFile(self):
00363     return self._instructions_file
00364   
00365   @property
00366   def instructions_file(self): return self._instructions_file
00367 
00368   @property
00369   def check_assembly(self): return self._check_assembly
00370 
00371   @property
00372   def testid(self): return self._id
00373 
00374   @property
00375   def part_number(self): return self._part


qualification
Author(s): Kevin Watts (watts@willowgarage.com), Josh Faust (jfaust@willowgarage.com)
autogenerated on Sat Dec 28 2013 17:57:34