From 25301ae9f8ea13ec81a4bc81cff748b3c0dc7204 Mon Sep 17 00:00:00 2001 From: boulant Date: Fri, 21 Oct 2011 16:04:50 +0000 Subject: [PATCH] Add python modules in KERNE_PY. Test with kernelpy_autotest.sh Doc in kernel.rst --- configure.ac | 1 + doc/docutils/kernel.rst | 44 +++ doc/salome/kernel_services_cpphelpers.dox | 55 +++ src/KERNEL_PY/kernel/Makefile.am | 13 +- src/KERNEL_PY/kernel/__init__.py | 70 +++- src/KERNEL_PY/kernel/datamodeler.py | 338 +++++++++++++++++++ src/KERNEL_PY/kernel/diclookup.py | 119 +++++++ src/KERNEL_PY/kernel/enumerate.py | 214 ++++++++++++ src/KERNEL_PY/kernel/kernelpy_autotest.sh.in | 77 +++++ src/KERNEL_PY/kernel/pyunittester.py | 170 ++++++++++ src/KERNEL_PY/kernel/services.py | 103 ++++++ src/KERNEL_PY/kernel/testdata.py | 159 +++++++++ src/KERNEL_PY/kernel/uiexception.py | 108 ++++++ src/KERNEL_PY/kernel/unittester.py | 71 ++++ 14 files changed, 1540 insertions(+), 2 deletions(-) create mode 100644 doc/salome/kernel_services_cpphelpers.dox create mode 100644 src/KERNEL_PY/kernel/datamodeler.py create mode 100644 src/KERNEL_PY/kernel/diclookup.py create mode 100644 src/KERNEL_PY/kernel/enumerate.py create mode 100644 src/KERNEL_PY/kernel/kernelpy_autotest.sh.in create mode 100644 src/KERNEL_PY/kernel/pyunittester.py create mode 100644 src/KERNEL_PY/kernel/services.py create mode 100644 src/KERNEL_PY/kernel/testdata.py create mode 100644 src/KERNEL_PY/kernel/uiexception.py create mode 100644 src/KERNEL_PY/kernel/unittester.py diff --git a/configure.ac b/configure.ac index 692c572c6..6943c530d 100644 --- a/configure.ac +++ b/configure.ac @@ -578,6 +578,7 @@ AC_OUTPUT([ \ src/HDFPersist/Makefile \ src/KERNEL_PY/Makefile \ src/KERNEL_PY/kernel/Makefile \ + src/KERNEL_PY/kernel/kernelpy_autotest.sh \ src/KERNEL_PY/kernel/logconfig.py \ src/KERNEL_PY/kernel/parametric/Makefile \ src/Launcher/Makefile \ diff --git a/doc/docutils/kernel.rst b/doc/docutils/kernel.rst index f73f234d7..4b7327f92 100644 --- a/doc/docutils/kernel.rst +++ b/doc/docutils/kernel.rst @@ -29,9 +29,53 @@ :members: :show-inheritance: +:mod:`enumerate` -- Emulates a C-like enum for python +----------------------------------------------------- + +.. automodule:: salome.kernel.enumerate + :members: + +:mod:`uiexception` -- Exception for user error management +--------------------------------------------------------- + +.. automodule:: salome.kernel.uiexception + :members: + +:mod:`datamodeler` -- Helper for modeling user data +--------------------------------------------------- + +.. automodule:: salome.kernel.datamodeler + :members: + +.. automodule:: salome.kernel.testdata + :members: + +:mod:`diclookup` -- Smart dictionnary with key/value lookup +----------------------------------------------------------- + +.. automodule:: salome.kernel.diclookup + :members: + +:mod:`service` -- Helper for using SALOME kernel services +--------------------------------------------------------- + +.. automodule:: salome.kernel.services + :members: :mod:`studyedit` -- Study editor -------------------------------- .. automodule:: salome.kernel.studyedit :members: + +:mod:`unittester` -- Run very basic unit tests +---------------------------------------------- + +.. automodule:: salome.kernel.unittester + :members: + +:mod:`pyunittester` -- Simple wrapper of the pyunit framework +------------------------------------------------------------- + +.. automodule:: salome.kernel.pyunittester + :members: diff --git a/doc/salome/kernel_services_cpphelpers.dox b/doc/salome/kernel_services_cpphelpers.dox new file mode 100644 index 000000000..8d4db367c --- /dev/null +++ b/doc/salome/kernel_services_cpphelpers.dox @@ -0,0 +1,55 @@ +/*! + \page KERNEL_Services_CppHelpers KERNEL Services helper functions for C++ programmers + +You'l find here a documentation for using the KernelHelper package of +the KERNEL module. This package provides the programmer with helper +functions to deal with the KERNEL services and the study. + +-# \ref S1_KSH +-# \ref S2_KSH +-# \ref S3_KSH + +\section S1_KSH Quick Overview + +When developing a SALOME application, programmers often have to deal +with fundamentals objects as the %NamingService, the %LifeCycleCorba, +the %ResourcesManager and the %SalomeLauncher and have to manage the +data in a %Study object. + +This documentation illustrates how to use these objects in standard +C++ use cases and with the helper functions of the KernelHelper +package of the KERNEL module. The KernelHelper package should be used +by end user programmers, i.e. programmers that don't need the +complexity of the complete KERNEL API, typically when developing a +SALOME module for a domain specific application. + +For illustration of this short introduction, the code below creates an +executable binary program that calls the SalomeTestComponent using the +LifeCycleCorba and requests the Coucou service of this component (the +test component is included in the KERNEL module): + +\code +#include "SALOME_KernelServices.hxx" +#include "Basics_Utils.hxx" + +#include +#include CORBA_CLIENT_HEADER(SALOME_TestComponent) + +void main (int argc, char * argv[]) { + Engines::EngineComponent_var component = + KERNEL::getLifeCycleCORBA()->FindOrLoad_Component( "FactoryServer","SalomeTestComponent" ); + + Engines::TestComponent_var engine = Engines::TestComponent::_narrow(component); + STDLOG(engine->Coucou(123.)); +} +\endcode + +\section S2_KSH Usage of the SALOME_KernelServices + +TO WRITE + +\section S3_KSH Usage of the SALOME_StudyEditor + +TO WRITE + +*/ diff --git a/src/KERNEL_PY/kernel/Makefile.am b/src/KERNEL_PY/kernel/Makefile.am index c43657488..73a02e2b4 100644 --- a/src/KERNEL_PY/kernel/Makefile.am +++ b/src/KERNEL_PY/kernel/Makefile.am @@ -27,6 +27,17 @@ mypkgpython_PYTHON = \ logger.py \ studyedit.py \ termcolor.py \ - logconfig.py + logconfig.py \ + unittester.py \ + pyunittester.py \ + enumerate.py \ + uiexception.py \ + datamodeler.py \ + diclookup.py \ + services.py \ + testdata.py + +bin_SCRIPTS=\ + kernelpy_autotest.sh SUBDIRS = parametric diff --git a/src/KERNEL_PY/kernel/__init__.py b/src/KERNEL_PY/kernel/__init__.py index 09e1eb4fa..09833de65 100644 --- a/src/KERNEL_PY/kernel/__init__.py +++ b/src/KERNEL_PY/kernel/__init__.py @@ -18,4 +18,72 @@ # See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com # -__all__ = [ "deprecation", "logger", "termcolor", "studyedit", "logconfig" ] +__all__ = [ "deprecation", "logger", "termcolor", "logconfig" ] + +# WARN: This file SHOULD NOT import salome stuff so that modules of the +# package could be used outside of a SALOME session context when +# possible. For example logger.py, datamodeler.py, enumerate.py, +# diclookup.py, ... does not need a SALOME running application. + +# +# ============================================================================== +# Generic serialization functions for the module datamodeler +# ============================================================================== +# +import pickle + +def serialize(dataToSerialize): + """ + Creates a string description of the specified data. + .. attribute:: dataToSerialize + a data object to serialize + """ + serialstring = pickle.dumps(dataToSerialize) + return serialstring + +def unserialize(stringToUnserialize): + """ + Creates a data object from its string description. The string description + is supposed to be obtained by a call to serialize. + .. attribute:: stringToUnserialize + a string description of the object + """ + data = pickle.loads(stringToUnserialize) + return data + +class Callable: + """ + This class is used to create class-method (see MetaData) + """ + def __init__(self, anycallable): + self.__call__ = anycallable +# +# ============================================================================== +# Basic use cases and unit tests +# ============================================================================== +# + +def TEST_serialization(): + from testdata import TestData + ref_name = "my study case" + studyData = TestData() + studyData.setName(ref_name) + + print "serialize data ..." + serialString = serialize(studyData) + print "unserialize data ..." + unserialData = unserialize(serialString) + + res_name = unserialData.getName() + print res_name + if ( ref_name != res_name ): + return False + + unserialData.log() + unserialData.setName("an other name") + + return True + +if __name__ == "__main__": + import unittester + unittester.run("salome/kernel/__init__","TEST_serialization") diff --git a/src/KERNEL_PY/kernel/datamodeler.py b/src/KERNEL_PY/kernel/datamodeler.py new file mode 100644 index 000000000..9625a51da --- /dev/null +++ b/src/KERNEL_PY/kernel/datamodeler.py @@ -0,0 +1,338 @@ +# -*- coding: iso-8859-1 -*- +# Copyright (C) 2010 EDF R&D +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# +# See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com +# +# -* Makefile *- +# + +__author__="gboulant" +__date__ ="$15 avr. 2010 19:44:17$" + +from uiexception import DevelException + +# Most usable class types +TypeString= "".__class__ +__ref_integer = 0 +TypeInteger = __ref_integer.__class__ +__ref_double = 0.0 +TypeDouble = __ref_double.__class__ +__ref_list = [] +TypeList = __ref_list.__class__ +__ref_dict = {} +TypeDictionnary = __ref_dict.__class__ + +# There is no control to do for these attributes. They are attributes for the +# class management and not data of the model. +UNCHECKED_ATTRIBUTES = [ + "_typemap", + "_rangemap", + "_defaultmap", + "_voidmap" +] + +class DataModeler: + """ + This class is a placeholder for modeling data. An object based on this class + (particular instance or specialized derived class) can defined attributes with + the following properties: + + - a type : the class or the type of the attribute. Setting an attribute to + a value whose type is not the specified type raises an exception. + - a range : a list of the possible values for the attribute. Setting an + attribute to a value not in the range raises an exception + - a default: the default value of an attribute when an instance is created + - a void flag: the attribute can be authorized to be None or not using this + flag. Setting an attribute to a None value while the flag is not set to + True raises an exception. By default, a None value is not allowed. + + These properties are dictionnaries mapping the attribute name to its + associated value for the property. + + A typical usage is to derived this class in a specialized form where the + attributes names and there properties are defined in the constructor. See + use cases at the end of this file. + + """ + def __init__(self, typemap=None, rangemap=None, defaultmap=None, voidmap=None): + self._typemap = {} + self._rangemap = {} # possible values + self._defaultmap = {} # defaults values + self._voidmap = {} # None values are allowed + + if typemap is not None: + self._typemap.update(typemap) + if rangemap is not None: + self._rangemap.update(rangemap) + if voidmap is not None: + self._voidmap.update(voidmap) + + # Default initialization (if any) + if defaultmap is not None: + self._defaultmap.update(defaultmap) + for name in self._defaultmap.keys(): + self.__setattr__(name,self._defaultmap[name]) + + def addAttribute(self, name, type=None, range=None, default=None, void=None): + """ + A None argument means that no entry is created in the associated maps. + """ + self._typemap[name] = type + + if range is not None: + self._rangemap[name] = range + + if void is not None: + self._voidmap[name] = void + + if (not void) and (default is None): + return + + self.__setattr__(name,default) + + def __setattr__(self, name, val): + if name in UNCHECKED_ATTRIBUTES: + self.__dict__[name] = val + return + + #__GBO_DEBUG_ + if name == "_typemap": + print "WARNING WARNING WARNING : changing value of _typemap by ",val + + if name not in self._typemap.keys(): + raise DevelException("The class "+str(self.__class__)+" has no attribute "+str(name)) + + if val is None: + if not self.__isVoidAllowed(name): + raise DevelException("The attribute "+str(name)+" can't be None") + else: + # We can stop here and set the value to None + self.__dict__[name] = None + return + + if self.__isNotValidType(name,val): + raise DevelException("The attribute "+str(name)+" must be an instance of "+str(self._typemap[name])) + + if self.__isNotValidRange(name,val): + raise DevelException("The attribute "+str(name)+" must be a value in :"+str(self._rangemap[name])) + + self.__dict__[name] = val + + def __getattribute__(self, name): + if name in UNCHECKED_ATTRIBUTES: + return self.__dict__[name] + + if name not in self._typemap.keys(): + raise DevelException("The class "+str(self.__class__)+" has no attribute "+str(name)) + # The attribute coulb be requested while it has not been created yet (for + # example if we did't call the setter before). + if not self.__dict__.has_key(name): + return None + + return self.__dict__[name] + + def __isNotValidType(self, name, val): + isNotValid = ( + ( self._typemap[name] is not None) and + ( not isinstance(val,self._typemap[name]) ) ) + + return isNotValid + + def __isNotValidRange(self, name, val): + isNotValid = ( + ( self._rangemap is not None) and + ( self._rangemap.has_key(name) ) and + ( self._rangemap[name] is not None ) and + ( val not in self._rangemap[name] ) ) + + return isNotValid + + def __isVoidAllowed(self,name): + isVoidAllowed = ( + ( self._voidmap is not None) and + ( self._voidmap.has_key(name) ) and + ( self._voidmap[name] is True ) ) + + return isVoidAllowed + + def log(self): + print "DATAMODELER ["+str(self.__class__)+"]: self._typemap.keys() = "+str(self._typemap.keys()) + + + + +# +# ============================================================================== +# Basic use cases and unit tests +# ============================================================================== +# +def TEST_usecase(): + typemap={} + typemap["stringdata"] = TypeString + typemap["integerdata"] = TypeInteger + typemap["anydata"] = None # can be anything + + data = DataModeler(typemap) + + sdata = "toto" + idata = 3 + data.stringdata = sdata + data.integerdata = idata + + data.anydata = 5.3 + data.anydata = "any value" + data.anydata = True + + print data.integerdata + return True + +def TEST_addAttribute(): + typemap={} + typemap["stringdata"] = TypeString + typemap["integerdata"] = TypeInteger + data = DataModeler(typemap) + data.stringdata = "a string value" + + ref_value = 1.3 + data.addAttribute( + name = "myAttr", + type = TypeDouble, + range = None, + default = ref_value, + void = False) + + try: + if data.myAttr != ref_value: + return False + data.myAttr = 5.3 + #data.myAttr = 5 + except Exception, e: + print e + return False + + try: + data.myAttr = "bad type value" + return False + except Exception, e: + print e + return True + +def TEST_badAttributeName(): + map={} + map["stringdata"] = TypeString + map["integerdata"] = TypeInteger + + data = DataModeler(map) + + # this should raise an exception + try: + data.myatt = 3 + return False + except Exception, e: + print "OK : "+str(e) + return True + +def TEST_badAttributeType(): + map={} + map["stringdata"] = TypeString + map["integerdata"] = TypeInteger + + data = DataModeler(map) + # this should raise an exception + try: + data.stringdata = 2 + return False + except Exception, e: + print "OK : "+str(e) + return True + +def TEST_badAttributeRange(): + map={} + map["stringdata"] = TypeString + map["integerdata"] = TypeInteger + + range={} + ref_integervalue = 3 + range["integerdata"] = [1,ref_integervalue,7] + + data = DataModeler(map,range) + # this should not raise an exception + try: + data.integerdata = ref_integervalue + data.stringdata = "anything (no restriction has been defined)" + except Exception, e: + print e + return False + + # this should raise an exception + try: + data.integerdata = 9999 # a value not in the range + return False + except Exception, e: + print e + return True + +def TEST_voidAttributeAllowed(): + map={} + map["stringdata"] = TypeString + map["integerdata"] = TypeInteger + + voidmap={} + voidmap["stringdata"] = True + + data = DataModeler(typemap=map,voidmap=voidmap) + try: + # this should not raise an exception + data.stringdata = None + print data.stringdata + except Exception, e: + print e + return False + + try: + # this should raise an exception + data.integerdata = None + return False + except Exception, e: + print e + return True + +def TEST_defaultValues(): + typemap={} + typemap["stringdata"] = TypeString + typemap["integerdata"] = TypeInteger + + ref_value = "my initial value" + defaultmap={} + defaultmap["stringdata"] = ref_value + + data = DataModeler(typemap=typemap,defaultmap=defaultmap) + print data.stringdata + if data.stringdata != ref_value: + return False + else: + return True + +if __name__ == "__main__": + from unittester import run + run("salome/kernel/datamodeler","TEST_usecase") + run("salome/kernel/datamodeler","TEST_addAttribute") + run("salome/kernel/datamodeler","TEST_badAttributeName") + run("salome/kernel/datamodeler","TEST_badAttributeType") + run("salome/kernel/datamodeler","TEST_badAttributeRange") + run("salome/kernel/datamodeler","TEST_voidAttributeAllowed") + run("salome/kernel/datamodeler","TEST_defaultValues") diff --git a/src/KERNEL_PY/kernel/diclookup.py b/src/KERNEL_PY/kernel/diclookup.py new file mode 100644 index 000000000..5a1c0f737 --- /dev/null +++ b/src/KERNEL_PY/kernel/diclookup.py @@ -0,0 +1,119 @@ +# -*- coding: iso-8859-1 -*- +# Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +# +# Copyright (C) 2003-2007 OPEN CASCADE, EADS/CCR, LIP6, CEA/DEN, +# CEDRAT, EDF R&D, LEG, PRINCIPIA R&D, BUREAU VERITAS +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# +# See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com +# + +__author__="gboulant" +__date__ ="$21 mai 2010 18:00:23$" + + +# search a dictionary for key or value +# using named functions or a class +# tested with Python25 by Ene Uran 01/19/2008 + +def find_key(dic, val): + """return the key of dictionary dic given the value""" + return [k for k, v in dic.iteritems() if v == val][0] + +def find_value(dic, key): + """return the value of dictionary dic given the key""" + return dic[key] + +class Lookup(dict): + """ + a dictionary which can lookup value by key, or keys by value + """ + def __init__(self, items=[]): + """items can be a list of pair_lists or a dictionary""" + dict.__init__(self, items) + + def get_keys(self, value): + """find the key(s) as a list given a value""" + return [item[0] for item in self.items() if item[1] == value] + + def get_key(self, value): + """ + find the key associated to the given a value. If several keys exist, + only the first is given. To get the whole list, use get_keys instead. + """ + list = self.get_keys(value) + if len(list) == 0: + return None + return list[0] + + def get_value(self, key): + """find the value given a key""" + return self[key] + +# +# ============================================================================== +# Use cases and unit tests +# ============================================================================== +# +def TEST_getTestDictionnary(): + # dictionary of chemical symbols + symbol_dic = { + 'C': 'carbon', + 'H': 'hydrogen', + 'N': 'nitrogen', + 'Li': 'lithium', + 'Be': 'beryllium', + 'B': 'boron' + } + return symbol_dic + +def TEST_find_value(): + symbol_dic = TEST_getTestDictionnary() + print find_key(symbol_dic, 'boron') # B + print find_value(symbol_dic, 'B') # boron + print find_value(symbol_dic, 'H') # hydrogen + if find_key(symbol_dic, 'nitrogen') != 'N': + return False + return True + +def TEST_lookup(): + symbol_dic = TEST_getTestDictionnary() + + name = 'lithium' + symbol = 'Li' + # use a dictionary as initialization argument + look = Lookup(symbol_dic) + print look.get_key(name) # [Li'] + if look.get_key(name) != symbol: + print "get "+str(look.get_key(name))+" while "+str(symbol)+" was expected" + return False + print look.get_value(symbol) # lithium + + # use a list of pairs instead of a dictionary as initialization argument + # (will be converted to a dictionary by the class internally) + age_list = [['Fred', 23], ['Larry', 28], ['Ene', 23]] + look2 = Lookup(age_list) + print look2.get_keys(23) # ['Ene', 'Fred'] + if look2.get_keys(23)[0] != 'Ene' or look2.get_keys(23)[1] != 'Fred': + print "get "+str(look2.get_keys(23))+" while ['Ene', 'Fred'] was expected" + return False + print look2.get_value('Fred') # 23 + return True + +if __name__ == '__main__': + import unittester + unittester.run("diclookup", "TEST_find_value") + unittester.run("diclookup", "TEST_lookup") diff --git a/src/KERNEL_PY/kernel/enumerate.py b/src/KERNEL_PY/kernel/enumerate.py new file mode 100644 index 000000000..3e4ab3c68 --- /dev/null +++ b/src/KERNEL_PY/kernel/enumerate.py @@ -0,0 +1,214 @@ +# -*- coding: iso-8859-1 -*- +# Copyright (C) 2010 EDF R&D +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# +# See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com +# +# -* Makefile *- +# + +__author__ = "gboulant" +__date__ = "$1 avr. 2010 09:08:02$" + +class Enumerate(object): + """ + This class emulates a C-like enum for python. It is initialized with a list + of strings to be used as the enum symbolic keys. The enum values are automatically + generated as sequencing integer starting at the specified offset value. + """ + def __init__(self, keys, offset=0): + """ + Canonical constructor + @keys a list of string to be used as the enum symbolic keys. The enum values + are automatically generated as a sequence of integers starting at the specified + offset value. + """ + self._dict_keynumbers = {} + for number, key in enumerate(keys): + value = offset + number + setattr(self, key, value) + self._dict_keynumbers[key] = value + + def contains(self, key): + """ + Return true if this enumerate contains the specified key string + @key a key string to test + """ + return (key in self._dict_keynumbers.keys()) + + def isValid(self, value): + """ + Returns true if the specified integer value is defined as an identifier + in this enumarate. + @value a value to test + """ + return (value in self._dict_keynumbers.values()) + + def listkeys(self): + """ + Returns the list of keys in this enumerate. + """ + list = self._dict_keynumbers.keys() + list.sort() + return list + + def listvalues(self): + """ + Returns the list of values specified to initiate this enumerate. + """ + list = self._dict_keynumbers.values() + list.sort() + return list + + def keyOf(self, value): + """ + Returns the symbolic key string associated to the specified identifier + value. + @param value : an integer value whose associated key string is requested. + """ + if not self.isValid(value): + return None + # _MEM_ We assume here that the keys and associated values are in the + # same order in their list. + return self._dict_keynumbers.keys()[self._dict_keynumbers.values().index(value)] + + # If not, weshould use a longer implementation such that: + #for key in self._dict_keynumbers.keys(): + # if self._dict_keynumbers[key] == value: + # return key + +# +# ============================================================================== +# Basic use cases and unit test functions +# ============================================================================== +# + +def TEST_simple(): + TYPES_LIST = Enumerate([ + 'SEP', + 'OTHER' + ]) + print TYPES_LIST.listvalues() + return True + +def TEST_createFromList(): + codes = Enumerate([ + 'KERNEL', # This should take the value 0 + 'GUI', # This should take the value 1 + 'GEOM', # ... + 'MED', + 'SMESH']) + + print codes.KERNEL + print codes.GEOM + if (codes.KERNEL == 0 and codes.GEOM == 2): + return True + else: + return False + +def TEST_createFromString(): + aList = "KERNEL GUI GEOM MED" + + codes = Enumerate(aList.split()) + + print codes.KERNEL + print codes.GEOM + if (codes.KERNEL == 0 and codes.GEOM == 2): + return True + else: + return False + +def TEST_contains(): + codes = Enumerate([ + 'KERNEL', # This should take the value 0 + 'GUI', # This should take the value 1 + 'GEOM', # ... + 'MED', + 'SMESH']) + + print "VISU in enumerate?", codes.contains("VISU") + if (not codes.contains("VISU")): + return True + else: + return False + +def TEST_isValid(): + codes = Enumerate([ + 'KERNEL', # This should take the value 0 + 'GUI', # This should take the value 1 + 'GEOM', # ... + 'MED', + 'SMESH']) + + if (not codes.isValid(23)): + return True + else: + return False + +def TEST_offset(): + codes = Enumerate([ + 'KERNEL', # This should take the value 0 + 'GUI', # This should take the value 1 + 'GEOM', # ... + 'MED', + 'SMESH'], offset=20) + + print codes.KERNEL + print codes.GEOM + if (codes.KERNEL == 20 and codes.GEOM == 22): + return True + else: + return False + +def TEST_listvalues(): + codes = Enumerate([ + 'KERNEL', # This should take the value 0 + 'GUI', # This should take the value 1 + 'GEOM', # ... + 'MED', + 'SMESH'], offset=20) + + print codes.listvalues() + if codes.listvalues() != [20, 21, 22, 23, 24]: + return False + return True + +def TEST_keyOf(): + codes = Enumerate([ + 'KERNEL', # This should take the value 0 + 'GUI', # This should take the value 1 + 'GEOM', # ... + 'MED', + 'SMESH']) + + if ( codes.keyOf(codes.KERNEL) != 'KERNEL' or + codes.keyOf(codes.GUI) != 'GUI' or + codes.keyOf(codes.GEOM) != 'GEOM' or + codes.keyOf(codes.MED) != 'MED' or + codes.keyOf(codes.SMESH) != 'SMESH'): + return False + return True + +if __name__ == "__main__": + import unittester + unittester.run("enumerate", "TEST_simple") + unittester.run("enumerate", "TEST_createFromList") + unittester.run("enumerate", "TEST_createFromString") + unittester.run("enumerate", "TEST_contains") + unittester.run("enumerate", "TEST_isValid") + unittester.run("enumerate", "TEST_offset") + unittester.run("enumerate", "TEST_listvalues") + unittester.run("enumerate", "TEST_keyOf") diff --git a/src/KERNEL_PY/kernel/kernelpy_autotest.sh.in b/src/KERNEL_PY/kernel/kernelpy_autotest.sh.in new file mode 100644 index 000000000..f2dd3337e --- /dev/null +++ b/src/KERNEL_PY/kernel/kernelpy_autotest.sh.in @@ -0,0 +1,77 @@ +#!/bin/sh +# Copyright (C) 2010 CEA/DEN, EDF R&D, OPEN CASCADE +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# +# See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com +# +# -* Makefile *- +# +# Author : Guillaume Boulant (EDF) +# + +# +# This script runs the unit tests embedded in each of the python +# modules of the package salome.kernel. +# +# This script should be executed in a SALOME shell session, +# for example the shell obtained from the command runSession +# provided by the SALOME application. +# + +# This first list does not need a SALOME application. Only the +# environnement is required (for example, run these script in a +# session obtained with runSession +listfiles="\ + unittester.py \ + enumerate.py \ + uiexception.py \ + datamodeler.py \ + testdata.py \ + diclookup.py \ + pyunittester.py \ + __init__.py" + +# This files should be add to the list +#listfiles=$listfiles"\ +# deprecation.py \ +# logger.py \ +# termcolor.py \ +# logconfig.py" + +# This list REQUIRES a running SALOME application +#listfiles=$listfiles"\ +# services.py \ +# studyedit.py" + +# This files are not used yet (but we test them) +#listfiles=$listfiles"\ +# salome/kernel/threadhelper.py" + +MODULE_ROOT_DIR=@prefix@ +MODULE_PYTHON_DIR=$MODULE_ROOT_DIR/lib/python@PYTHON_VERSION@/site-packages/salome +PYTHONPATH=$MODULE_PYTHON_DIR:$PYTHONPATH +export PYTHONPATH + +here=$(pwd) +package_path="salome/kernel" +cd $MODULE_PYTHON_DIR +for file in $listfiles; do + # Uncomment this line (and comment the next one) to display + # the start line of a test and not only the result: + #python $file | grep '^\[TEST' + python $package_path/$file | grep '^\[TEST' | grep -v 'test in progress' +done +cd $here diff --git a/src/KERNEL_PY/kernel/pyunittester.py b/src/KERNEL_PY/kernel/pyunittester.py new file mode 100644 index 000000000..0a90d3e95 --- /dev/null +++ b/src/KERNEL_PY/kernel/pyunittester.py @@ -0,0 +1,170 @@ +# -*- coding: iso-8859-1 -*- +# Copyright (C) 2011 EDF R&D, CEA +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# +# See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.co + +import sys +from salome.kernel import termcolor +def printfile(filename): + stream = open(filename,'r') + lines = stream.readlines() + stream.close() + for line in lines: + if not termcolor.canDisplayColor(sys.stdout): + msg = line.split('\n')[0] + else: + msg = termcolor.makeColoredMessage(line.split('\n')[0], termcolor.BLUE) + print msg + +import os +import unittest +from unittest import TestCase +from uiexception import DevelException + +class PyUnitTester(): + ''' + This class is a simple wrapper of the pyunit framework. + ''' + def __init__(self): + self.__listTestSuite=[] + self.setLogFilename("testlog.txt") + + def addTestCase(self,testCaseClass): + # We test some attributes to validate that the argument + # corresponds to a unittest.TestCase class (WARN: it's not an + # instance but a class keyword) + if "assertTrue" not in dir(testCaseClass): + raise DevelException("Not a pyunit test case") + + suite = unittest.TestLoader().loadTestsFromTestCase(testCaseClass) + self.__listTestSuite.append(suite) + + def setLogFilename(self, filename): + self.__logfilename = filename + + def run(self): + # We first open the output stream + if os.path.exists(self.__logfilename): + os.remove(self.__logfilename) + logfile = open(self.__logfilename,'w') + + # Then, define and execute the runner to play the test suites + runner = unittest.TextTestRunner(stream=logfile,verbosity=2) + for suite in self.__listTestSuite: + runner.run(suite) + + # Finally close the ouput stream and print the report + logfile.close() + self.printreport() + + def printreport(self): + printfile(self.__logfilename) + +def execAndConvertExceptionToBoolean(function): + """ + This can be used to just execute a test function that is + considered as OK simply if no exception is raised (can be test by + an assertTrue). + """ + try: + result = function() + if result is None: + return True + return result + except Exception, e: + print e + return False + +# Simple helper function for most cases where there is only one +# TestCase classe. +def run(testCaseClass): + """ + Run the test suite provided by the specified TestCase class. + """ + tester = PyUnitTester() + tester.addTestCase(testCaseClass) + tester.run() + +# +# ============================================================================== +# Simple use cases +# ============================================================================== +# +class MyTestCase(unittest.TestCase): + def test_myTestOk_01(self): + r=True + print "myTestOk_01: should be OK" + self.assertTrue(r) + + def test_myTestOk_02(self): + r=True + print "myTestOk_02: should be OK" + self.assertTrue(r) + + def test_myTestNotOk(self): + r=False + print "myTestNotOk: should be NOT OK" + self.assertTrue(r) + +def functionRaisingAnException(): + raise Exception("An error occurs") + +def functionReturningFalse(): + return False + +def functionReturningTrue(): + return True + +def functionReturningNothing(): + print "functionReturningNothing" + +class MyTestCase2(unittest.TestCase): + def test_myTest_01(self): + r=execAndConvertExceptionToBoolean(functionRaisingAnException) + print "test 01: this test should be NOT OK" + self.assertTrue(r) + + def test_myTest_02(self): + r=execAndConvertExceptionToBoolean(functionReturningFalse) + print "test 02: this test should be NOT OK" + self.assertTrue(r) + + def test_myTest_03(self): + r=execAndConvertExceptionToBoolean(functionReturningTrue) + print "test 03: this test should be OK" + self.assertTrue(r) + + def test_myTest_04(self): + r=execAndConvertExceptionToBoolean(functionReturningNothing) + print "test 04: this test should be OK" + self.assertTrue(r) + + def test_myTest_05(self): + r=True + print "test 05: this test should be OK" + self.assertTrue(r) + + +def TEST_basic_usecase(): + tester = PyUnitTester() + # Note that the class keywords must be passed + tester.addTestCase(MyTestCase) + tester.addTestCase(MyTestCase2) + tester.run() + +if __name__ == "__main__": + TEST_basic_usecase() diff --git a/src/KERNEL_PY/kernel/services.py b/src/KERNEL_PY/kernel/services.py new file mode 100644 index 000000000..304cd2f0b --- /dev/null +++ b/src/KERNEL_PY/kernel/services.py @@ -0,0 +1,103 @@ +# -*- coding: iso-8859-1 -*- +# Copyright (C) 2007-2011 CEA/DEN, EDF R&D, OPEN CASCADE +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# +# See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com +# + +# Author: Guillaume Boulant (EDF/R&D) + + +import salome +if salome.lcc is None: + try: + salome.salome_init() + except RuntimeError, e: + print e + +# +# ============================================================================== +# Helper functions for SALOME components +# ============================================================================== +# + +# +# componantName is the name of the component as declared in the XML +# SALOME catalog. A loadable library with name libEngine.so +# is supposed to be reachable. This library is supposed to provide a +# factory function with the prototype: +# +# PortableServer::ObjectId * Engine_factory( +# CORBA::ORB_ptr orb, +# PortableServer::POA_ptr poa, +# PortableServer::ObjectId * contId, +# const char *instanceName, +# const char *interfaceName); +# +# corbaModule is the name of the IDL module that contains the +# definition of the interface of the component. This name corresponds +# to the namespace of the servant classes. +# +# containerType specified the container in which the servants are +# executed. +# +def getComponent(componentName = "SalomeTestComponent", + corbaModule = "Engines", + containerType = "FactoryServer"): + print "INF: getting component %s from CORBA module %s ..."%(componentName,corbaModule) + __import__(corbaModule) + component=salome.lcc.FindOrLoadComponent(containerType,componentName) + if component is None: + print "ERR: the SALOME component "+componentName+" can't be reached" + print "INF: component %s obtained from CORBA module %s"%(componentName,corbaModule) + return component + +# Not that an alternative (and maybe better) method to get a component +# is to use the module catalog. Here, we just use the catalog to get +# the list of components defined in the current session. +import SALOME_ModuleCatalog +def getComponentList(): + obj = salome.naming_service.Resolve('Kernel/ModulCatalog') + catalog = obj._narrow(SALOME_ModuleCatalog.ModuleCatalog) + if not catalog: + raise RuntimeError, "Can't accesss module catalog" + return catalog.GetComponentList() + +# +# ============================================================================== +# Basic use cases and unit tests +# ============================================================================== +# + +def TEST_getComponent(): + component=getComponent(componentName = "SalomeTestComponent") + + ref_string = 'TestComponent_i : L = 3' + res_string = component.Coucou(3) + if ref_string != ref_string: + return False + return True + +def TEST_getComponentList(): + componentList=getComponentList() + if 'SalomeTestComponent' not in componentList: + return False + return True + +if __name__ == "__main__": + import unittester + unittester.run("salome/kernel/__init__","TEST_getComponent") + unittester.run("salome/kernel/__init__","TEST_getComponentList") diff --git a/src/KERNEL_PY/kernel/testdata.py b/src/KERNEL_PY/kernel/testdata.py new file mode 100644 index 000000000..9390af31a --- /dev/null +++ b/src/KERNEL_PY/kernel/testdata.py @@ -0,0 +1,159 @@ +# -*- coding: iso-8859-1 -*- +# Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +# +# Copyright (C) 2003-2007 OPEN CASCADE, EADS/CCR, LIP6, CEA/DEN, +# CEDRAT, EDF R&D, LEG, PRINCIPIA R&D, BUREAU VERITAS +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# +# See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com +# + +__author__="gboulant" +__date__ ="$17 avr. 2010 19:44:36$" + +from enumerate import Enumerate +from datamodeler import DataModeler, TypeString, TypeInteger +from salome.kernel import Callable + +class TestData(DataModeler): + """ + This class is the placeholder for meta data associated to a study case. + The meta-data are used for organisation and indexing purposes + """ + TYPES_LIST=Enumerate([ + 'SEP', + 'OTHER' + ]) + + def __init__(self): + DataModeler.__init__(self) + self.addAttribute( + name = "NAME", + type = TypeString, + range = None + ) + self.addAttribute( + name = "TYPE", + type = TypeInteger, + range = self.TYPES_LIST.listvalues() + ) + + def setName(self, value): + self.NAME = value + + def getName(self): + return self.NAME + + def setType(self, value): + self.TYPE = value + + def getType(self): + return self.TYPE + + def getDefault(): + """ + Create a default instance of TestData + @class-method + """ + testdata = TestData() + testdata.NAME = "my case" + testdata.TYPE = TestData.TYPES_LIST.SEP + return testdata + + getDefault = Callable(getDefault) +# +# ============================================================================== +# Basic use cases and unit tests +# ============================================================================== +# +from uiexception import UiException + +def TEST_getName(): + testdata = TestData() + testdata.setName("Sous-epaisseur") + testdata.setType(TestData.TYPES_LIST.SEP) + if ( testdata.NAME != "Sous-epaisseur" ): + return False + return True + + +def TEST_useBadKey(): + testdata = TestData() + try: + testdata.unknown = "unknown" + # This should not arrive here + return False + except UiException, err: + print err + return True + +def TEST_useBadType(): + testdata = TestData() + try: + testdata.TYPE = "unknown" + # This should not arrive here + return False + except UiException, err: + print err + return True + +def TEST_useBadRange(): + testdata = TestData() + + try: + testdata.TYPE = TestData.TYPES_LIST.SEP + testdata.setType(TestData.TYPES_LIST.SEP) + # This should arrive here + except UiException, err: + # And not here + print err + return False + + try: + testdata.TYPE = 9999 # a type that does not exist in the range + # This should not arrive here + return False + except UiException, err: + print err + return True + +def TEST_serialize(): + import salome.kernel + ref_testdata = TestData() + ref_testdata.setName("The firts name") + res_testdata = salome.kernel.unserialize(salome.kernel.serialize(ref_testdata)) + + print res_testdata.getName() + + if res_testdata.getName() != ref_testdata.getName(): + return False + + # Is the unserialized data still functional? + try: + res_testdata.setName("An other name") + print res_testdata.getName() + except: + print e + return False + return True + +if __name__ == "__main__": + from unittester import run + run("salome/kernel/testdata","TEST_getName") + run("salome/kernel/testdata","TEST_useBadKey") + run("salome/kernel/testdata","TEST_useBadType") + run("salome/kernel/testdata","TEST_useBadRange") + run("salome/kernel/testdata","TEST_serialize") diff --git a/src/KERNEL_PY/kernel/uiexception.py b/src/KERNEL_PY/kernel/uiexception.py new file mode 100644 index 000000000..4c91b1c3b --- /dev/null +++ b/src/KERNEL_PY/kernel/uiexception.py @@ -0,0 +1,108 @@ +# -*- coding: iso-8859-1 -*- +# Copyright (C) 2010 EDF R&D +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# +# See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com +# +# -* Makefile *- +# +__author__="gboulant" +__date__ ="$31 mars 2010 11:59:33$" + +from enumerate import Enumerate + +class UiException(Exception): + + TYPES = Enumerate([ + 'USER', # This type should be displayed to end user using a dialog box + 'ADMIN', # This type should be displayed to admin user in console log + 'DEVEL' # This type should be displayed to developer only + ]) + + _UImessage = "An error occurs" + _type = TYPES.USER + + """ + This exception should be used for functionnal error management, at least in the GUI + part of the application, for example to set user oriented messages at point + of exception raise. + WARN: The exception should NOT be used to hide defaults in the algorithm, but + only predicted error in the specified use case. + """ + def __init__(self, message, type=TYPES.USER): + """Canonical constructor""" + Exception.__init__(self,message) + self.setUIMessage(message) + self.setType(type) + + def setUIMessage(self, UImessage): + self._UImessage = UImessage + + def getUIMessage(self): + return self._UImessage + + def setType(self, type): + """Specify the type of this exception. To be choosen in the TYPES list.""" + if not self.TYPES.isValid(type): + raise UiException("The exception type "+str(type)+" can't be used",self.TYPES.DEVEL) + self._type = type + + def getType(self): + return self._type + + def __str__(self): + return self.getUIMessage() + + +def UserException(msg): + return UiException(msg,UiException.TYPES.USER) + +def AdminException(msg): + return UiException(msg,UiException.TYPES.ADMIN) + +def DevelException(msg): + return UiException(msg,UiException.TYPES.DEVEL) + +# +# ============================================================================== +# Basic use cases and unit test functions +# ============================================================================== +# +def somethingGoesWrong(): + raise UiException("Something goes wrong") + +def TEST_uimessage(): + try: + somethingGoesWrong() + return False + except UiException, err: + print 'ERROR: %s' % str(err) + if ( str(err) == "Something goes wrong" ): + return True + else: + return False + +def TEST_specificException(): + print DevelException("err") + print AdminException("err") + print UserException("err") + return True + +if __name__ == "__main__": + import unittester + unittester.run("uiexception","TEST_uimessage") + unittester.run("uiexception","TEST_specificException") + diff --git a/src/KERNEL_PY/kernel/unittester.py b/src/KERNEL_PY/kernel/unittester.py new file mode 100644 index 000000000..60a7474f8 --- /dev/null +++ b/src/KERNEL_PY/kernel/unittester.py @@ -0,0 +1,71 @@ +# -*- coding: iso-8859-1 -*- +# Copyright (C) 2010 EDF R&D +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# +# See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com +__author__="gboulant" +__date__ ="$1 avr. 2010 09:45:21$" + +import sys + +def run(modulePath, functionName): + """ + This function should be used for very basic unit tests and only for a + rapid development. A better way should be the pyunit framework. + The function functionName is supposed here to return a boolean value + indicating if the test is OK (True) or NOT OK (False) + """ + moduleName = modulePath.replace('/','.') + __import__ (moduleName) + module=sys.modules[moduleName] + func = getattr(module,functionName) + tabsize = 70-len(moduleName)-len(functionName) + print "[TEST] %s.%s %s test in progress" % (moduleName, functionName,"."*tabsize) + ok = func() + if ( ok ): + print "[TEST] %s.%s %s OK" % (moduleName, functionName,"."*tabsize) + else: + print "[TEST] %s.%s %s NOT OK" % (moduleName, functionName,"."*tabsize) + +def tryfunction(function,*argv): + """ + This function is for debug only. It executes the specified function with the + specified arguments in a try/except bloc so that to display the exception in + the case where an exception is raised (usefull to debug server side of a CORBA + process). + """ + print "[TEST] trying the function %s" % function + try: + return function(*argv) + except Exception, e: + print e + raise e + + +# +# ============================================================================== +# Simple use cases +# ============================================================================== +# +def TEST_myTestIsOk(): + return True + +def TEST_myTestIsNotOk(): + return False + +if __name__ == "__main__": + run("salome/kernel/unittester","TEST_myTestIsOk") + run("salome/kernel/unittester","TEST_myTestIsNotOk") -- 2.39.2