From ffbbcb80f7499ee530fddcc84548ca4a5dd53b49 Mon Sep 17 00:00:00 2001 From: mpv Date: Fri, 12 Aug 2016 17:57:57 +0300 Subject: [PATCH] Implementation of checking the python dump functionality in any unit test (in the frames of the python dump, issue #1648 ) --- src/ConstructionPlugin/Test/TestPoint.py | 2 + src/GeomDataAPI/GeomDataAPI_Dir.h | 3 + src/Model/Model_SelectionNaming.cpp | 19 +- src/ModelHighAPI/CMakeLists.txt | 4 + .../ModelHighAPI_FeatureStore.cpp | 256 ++++++++++++++++++ src/ModelHighAPI/ModelHighAPI_FeatureStore.h | 55 ++++ src/ModelHighAPI/ModelHighAPI_Tools.cpp | 121 +++++++++ src/ModelHighAPI/ModelHighAPI_Tools.h | 7 +- src/PythonAPI/model/services/__init__.py | 1 + 9 files changed, 452 insertions(+), 16 deletions(-) create mode 100644 src/ModelHighAPI/ModelHighAPI_FeatureStore.cpp create mode 100644 src/ModelHighAPI/ModelHighAPI_FeatureStore.h diff --git a/src/ConstructionPlugin/Test/TestPoint.py b/src/ConstructionPlugin/Test/TestPoint.py index 5ea7e780b..4afd7f2b9 100644 --- a/src/ConstructionPlugin/Test/TestPoint.py +++ b/src/ConstructionPlugin/Test/TestPoint.py @@ -60,3 +60,5 @@ assert (len(aPoint.result()) > 0) # aPoint = model.addPoint(aDocument, aSketchLine1.result()[0], aPlane.result()[0]) # aSession.finishOperation() # assert (len(aPoint.result()) > 0) + +assert(model.checkPythonDump()) \ No newline at end of file diff --git a/src/GeomDataAPI/GeomDataAPI_Dir.h b/src/GeomDataAPI/GeomDataAPI_Dir.h index 4e61cd8f0..bd91b7d42 100644 --- a/src/GeomDataAPI/GeomDataAPI_Dir.h +++ b/src/GeomDataAPI/GeomDataAPI_Dir.h @@ -52,4 +52,7 @@ class GeomDataAPI_Dir : public ModelAPI_Attribute GEOMDATAAPI_EXPORT virtual ~GeomDataAPI_Dir(); }; +//! Pointer on attribute object +typedef std::shared_ptr AttributeDirPtr; + #endif diff --git a/src/Model/Model_SelectionNaming.cpp b/src/Model/Model_SelectionNaming.cpp index 7156e75ac..a9275e4d1 100644 --- a/src/Model/Model_SelectionNaming.cpp +++ b/src/Model/Model_SelectionNaming.cpp @@ -8,6 +8,10 @@ #include "Model_Document.h" #include #include +#include +#include +#include +#include #include #include @@ -26,8 +30,6 @@ #include #include #include -#include -#include #include #include @@ -40,7 +42,6 @@ Model_SelectionNaming::Model_SelectionNaming(TDF_Label theSelectionLab) myLab = theSelectionLab; } - std::string Model_SelectionNaming::getShapeName( std::shared_ptr theDoc, const TopoDS_Shape& theShape) { @@ -53,13 +54,6 @@ std::string Model_SelectionNaming::getShapeName( aName = TCollection_AsciiString(anAttr->Get()).ToCString(); if(!aName.empty()) { const TDF_Label& aLabel = theDoc->findNamingName(aName); - /* MPV: the same shape with the same name may be duplicated on different labels (selection of the same construction object) - if(!aLabel.IsEqual(aNS->Label())) { - //aName.erase(); //something is wrong, to be checked!!! - aName += "_SomethingWrong"; - return aName; - }*/ - static const std::string aPostFix("_"); TNaming_Iterator anItL(aNS); for(int i = 1; anItL.More(); anItL.Next(), i++) { @@ -75,8 +69,6 @@ std::string Model_SelectionNaming::getShapeName( return aName; } - - bool isTrivial (const TopTools_ListOfShape& theAncestors, TopTools_IndexedMapOfShape& theSMap) { // a trivial case: F1 & F2, aNumber = 1, i.e. intersection gives 1 edge. @@ -599,9 +591,6 @@ std::string Model_SelectionNaming::shortName( return aName; } -#include -#include - // type ::= COMP | COMS | SOLD | SHEL | FACE | WIRE | EDGE | VERT bool Model_SelectionNaming::selectSubShape(const std::string& theType, const std::string& theSubShapeName, std::shared_ptr theDoc, diff --git a/src/ModelHighAPI/CMakeLists.txt b/src/ModelHighAPI/CMakeLists.txt index 7b650a573..a5280ec4e 100644 --- a/src/ModelHighAPI/CMakeLists.txt +++ b/src/ModelHighAPI/CMakeLists.txt @@ -14,6 +14,7 @@ SET(PROJECT_HEADERS ModelHighAPI_Selection.h ModelHighAPI_Services.h ModelHighAPI_Tools.h + ModelHighAPI_FeatureStore.h ) SET(PROJECT_SOURCES @@ -26,6 +27,7 @@ SET(PROJECT_SOURCES ModelHighAPI_Selection.cpp ModelHighAPI_Services.cpp ModelHighAPI_Tools.cpp + ModelHighAPI_FeatureStore.cpp ) SET(PROJECT_LIBRARIES @@ -33,6 +35,7 @@ SET(PROJECT_LIBRARIES Events GeomAPI GeomDataAPI + GeomAlgoAPI ModelAPI ) @@ -58,6 +61,7 @@ INCLUDE_DIRECTORIES( ${PROJECT_SOURCE_DIR}/src/Config ${PROJECT_SOURCE_DIR}/src/Events ${PROJECT_SOURCE_DIR}/src/GeomAPI + ${PROJECT_SOURCE_DIR}/src/GeomAlgoAPI ${PROJECT_SOURCE_DIR}/src/GeomDataAPI ${PROJECT_SOURCE_DIR}/src/ModelAPI ${PROJECT_SOURCE_DIR}/src/PartSetPlugin diff --git a/src/ModelHighAPI/ModelHighAPI_FeatureStore.cpp b/src/ModelHighAPI/ModelHighAPI_FeatureStore.cpp new file mode 100644 index 000000000..ed67336a7 --- /dev/null +++ b/src/ModelHighAPI/ModelHighAPI_FeatureStore.cpp @@ -0,0 +1,256 @@ +// Copyright (C) 2016-20xx CEA/DEN, EDF R&D --> + +// File: ModelHighAPI_FeatureStore.cpp +// Created: 12 August 2016 +// Author: Mikhail PONIKAROV + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include + +ModelHighAPI_FeatureStore::ModelHighAPI_FeatureStore(FeaturePtr theFeature) { + storeData(theFeature->data(), myAttrs); + // iterate results to store + std::list allResults; + ModelAPI_Tools::allResults(theFeature, allResults); + std::list::iterator aRes = allResults.begin(); + for(; aRes != allResults.end(); aRes++) { + std::map aResDump; + storeData((*aRes)->data(), aResDump); + myRes.push_back(aResDump); + } +} + +std::string ModelHighAPI_FeatureStore::compare(FeaturePtr theFeature) { + std::string anError = compareData(theFeature->data(), myAttrs); + if (!anError.empty()) { + return "Features '" + theFeature->name() + "' differ:" + anError; + } + std::list allResults; + ModelAPI_Tools::allResults(theFeature, allResults); + std::list::iterator aRes = allResults.begin(); + std::list >::iterator aResIter = myRes.begin(); + for(; aRes != allResults.end() && aResIter != myRes.end(); aRes++, aResIter++) { + anError = compareData((*aRes)->data(), *aResIter); + if (!anError.empty()) + return "Results of feature '" + theFeature->name() + "' '" + (*aRes)->data()->name() + + "' differ:" + anError; + } + if (aRes != allResults.end()) { + return "Current model has more results '" + (*aRes)->data()->name() + "'"; + } + if (aResIter != myRes.end()) { + return "Original model had more results '" + (*aResIter)["__name__"] + "'"; + } + return ""; // ok +} + +void ModelHighAPI_FeatureStore::storeData(std::shared_ptr theData, + std::map& theAttrs) +{ + theAttrs["__name__"] = theData->name(); // store name to keep also this information and output if needed + std::list > allAttrs = theData->attributes(""); + std::list >::iterator anAttr = allAttrs.begin(); + for(; anAttr != allAttrs.end(); anAttr++) { + theAttrs[(*anAttr)->id()] = dumpAttr(*anAttr); + } + ResultPtr aShapeOwner = std::dynamic_pointer_cast(theData->owner()); + if (aShapeOwner.get() && aShapeOwner->shape().get()) { + theAttrs["__shape__"] = dumpShape(aShapeOwner->shape()); + } +} + +std::string ModelHighAPI_FeatureStore::compareData(std::shared_ptr theData, + std::map& theAttrs) +{ + std::map aThis; + storeData(theData, aThis); + std::map::iterator aThisIter = aThis.begin(); + for(; aThisIter != aThis.end(); aThisIter++) { + if (theAttrs.find(aThisIter->first) == theAttrs.end()) { + return "original model had no attribute '" + aThisIter->first + "'"; + } + if (theAttrs[aThisIter->first] != aThisIter->second) { + return "attribute '" + aThisIter->first + "' is different '" + + theAttrs[aThisIter->first] + "' != '" + aThisIter->second; + } + } + // iterate back to find lack attribute in the current model + std::map::iterator anOrigIter = theAttrs.begin(); + for(; anOrigIter != theAttrs.end(); anOrigIter++) { + if (aThis.find(anOrigIter->first) == aThis.end()) { + return "current model had no attribute '" + anOrigIter->first + "'"; + } + } + return ""; +} + +std::string ModelHighAPI_FeatureStore::dumpAttr(const AttributePtr& theAttr) { + if (!theAttr->isInitialized()) { + return "__notinitialized__"; + } + std::string aType = theAttr->attributeType(); + std::ostringstream aResult; + if (aType == ModelAPI_AttributeDocRef::typeId()) { + AttributeDocRefPtr anAttr = std::dynamic_pointer_cast(theAttr); + DocumentPtr aDoc = ModelAPI_Session::get()->moduleDocument(); + if (anAttr->value() != aDoc) { + ResultPtr aRes = ModelAPI_Tools::findPartResult(aDoc, anAttr->value()); + if (aRes.get()) { + aResult<data()->name(); // Part result name (the same as saved file name) + } + } else { + aResult<kind(); // PartSet + } + } else if (aType == ModelAPI_AttributeInteger::typeId()) { + AttributeIntegerPtr anAttr = std::dynamic_pointer_cast(theAttr); + if (anAttr->text().empty()) + aResult<value(); + else + aResult<text(); + } else if (aType == ModelAPI_AttributeDouble::typeId()) { + AttributeDoublePtr anAttr = std::dynamic_pointer_cast(theAttr); + if (anAttr->text().empty()) + aResult<value(); + else + aResult<text(); + } else if (aType == ModelAPI_AttributeBoolean::typeId()) { + AttributeBooleanPtr anAttr = std::dynamic_pointer_cast(theAttr); + aResult<value(); + } else if (aType == ModelAPI_AttributeString::typeId()) { + AttributeStringPtr anAttr = std::dynamic_pointer_cast(theAttr); + aResult<value(); + } else if (aType == ModelAPI_AttributeReference::typeId()) { + AttributeReferencePtr anAttr = + std::dynamic_pointer_cast(theAttr); + if (anAttr->value().get()) { + aResult<value()->data()->name(); + } else { + aResult<<"__empty__"; + } + } else if (aType == ModelAPI_AttributeSelection::typeId()) { + AttributeSelectionPtr anAttr = + std::dynamic_pointer_cast(theAttr); + aResult<namingName(); + } else if (aType == ModelAPI_AttributeSelectionList::typeId()) { + AttributeSelectionListPtr anAttr = + std::dynamic_pointer_cast(theAttr); + for(int a = 0; a < anAttr->size(); a++) { + if (a != 0) + aResult<<" "; + aResult<value(a)->namingName(); + } + } else if (aType == ModelAPI_AttributeRefAttr::typeId()) { + AttributeRefAttrPtr anAttr = + std::dynamic_pointer_cast(theAttr); + ObjectPtr anObj = anAttr->isObject() ? anAttr->object() : anAttr->attr()->owner(); + if (anObj.get()) { + aResult<data()->name(); + if (!anAttr->isObject()) { + aResult<<" "<attr()->id(); + } + } else { + aResult<<"__empty__"; + } + } else if (aType == ModelAPI_AttributeRefList::typeId()) { + AttributeRefListPtr anAttr = + std::dynamic_pointer_cast(theAttr); + std::list aList = anAttr->list(); + for(std::list::iterator aL = aList.begin(); aL != aList.end(); aL++) { + if (aL != aList.begin()) + aResult<<" "; + if (aL->get()) { + aResult<<(*aL)->data()->name(); + } else { + aResult<<"__empty__"; + } + } + } else if (aType == ModelAPI_AttributeRefAttrList::typeId()) { + AttributeRefAttrListPtr anAttr = + std::dynamic_pointer_cast(theAttr); + std::list > aList = anAttr->list(); + std::list >::iterator aL = aList.begin(); + for(; aL != aList.end(); aL++) { + if (aL != aList.begin()) + aResult<<" "; + ObjectPtr anObj = aL->second.get() ? aL->second->owner() : aL->first; + if (anObj.get()) { + aResult<data()->name(); + if (aL->second.get()) { + aResult<<" "<second->id(); + } + } else { + aResult<<"__empty__"; + } + } + } else if (aType == ModelAPI_AttributeIntArray::typeId()) { + AttributeIntArrayPtr anAttr = + std::dynamic_pointer_cast(theAttr); + for(int a = 0; a < anAttr->size(); a++) + aResult<value(a)<<" "; + } else if (aType == ModelAPI_AttributeDoubleArray::typeId()) { + AttributeDoubleArrayPtr anAttr = + std::dynamic_pointer_cast(theAttr); + for(int a = 0; a < anAttr->size(); a++) + aResult<value(a)<<" "; + } else if (aType == GeomDataAPI_Point::typeId()) { + AttributePointPtr anAttr = std::dynamic_pointer_cast(theAttr); + aResult<x()<<" "<y()<<" "<z(); + } else if (aType == GeomDataAPI_Dir::typeId()) { + AttributeDirPtr anAttr = std::dynamic_pointer_cast(theAttr); + aResult<x()<<" "<y()<<" "<z(); + } else if (aType == GeomDataAPI_Point2D::typeId()) { + AttributePoint2DPtr anAttr = std::dynamic_pointer_cast(theAttr); + aResult<x()<<" "<y()<<" "; + } else { + aResult<<"__unknownattribute__"; + } + return aResult.str(); +} + +std::string ModelHighAPI_FeatureStore::dumpShape(std::shared_ptr& theShape) { + TopoDS_Shape aShape = theShape->impl(); + if (aShape.IsNull()) { + return "null"; + } + std::ostringstream aResult; + // output the number of shapes of different types + TopAbs_ShapeEnum aType = TopAbs_COMPOUND; + for(; aType <= TopAbs_VERTEX; aType = TopAbs_ShapeEnum((int)aType + 1)) { + TopExp_Explorer anExp(aShape, aType); + int aCount = 0; + for(; anExp.More(); anExp.Next()) aCount++; + TopAbs::Print(aType, aResult); + aResult<<": "< aCenter = GeomAlgoAPI_ShapeTools::centreOfMass(theShape); + aResult<<"Center of mass: " + <x()<<" "<y()<<" "<z()< + +// File: ModelHighAPI_FeatureStore.h +// Created: 12 August 2016 +// Author: Mikhail PONIKAROV + +#ifndef ModelHighAPI_FeatureStore_H_ +#define ModelHighAPI_FeatureStore_H_ + +#include "ModelHighAPI.h" + +#include +#include +#include + +class ModelAPI_Feature; +class ModelAPI_Data; +class GeomAPI_Shape; +class ModelAPI_Attribute; + +typedef std::shared_ptr FeaturePtr; +typedef std::shared_ptr AttributePtr; + +/**\class ModelHighAPI_FeatureStore + * \ingroup CPPHighAPI + * \brief Allows to compare the original and the dumped/executed feature + */ +class ModelHighAPI_FeatureStore { + /// dumps of attributes of the stored feature (id -> dump) + std::map myAttrs; + /// dumps of attributes of results (list of results id -> dumps) + std::list > myRes; +public: + // unused constructor for the map container needs + ModelHighAPI_FeatureStore() {} + // constructor that initializes this object by feature to store + ModelHighAPI_FeatureStore(FeaturePtr theFeature); + // compares the stored feature information with the given feature + std::string compare(FeaturePtr theFeature); + +private: + /// stores the information about all attributes of data in map + void storeData(std::shared_ptr theData, + std::map& theAttrs); + /// compares the information about all attributes of data with map + /// returns not empty string with error if something is different + std::string compareData(std::shared_ptr theData, + std::map& theAttrs); + /// dumps the attribute content to the string + std::string dumpAttr(const AttributePtr& theAttr); + /// dumps the shape main charatceristics to string + std::string dumpShape(std::shared_ptr& theShape); +}; + +#endif \ No newline at end of file diff --git a/src/ModelHighAPI/ModelHighAPI_Tools.cpp b/src/ModelHighAPI/ModelHighAPI_Tools.cpp index f61e4169d..c19f47409 100644 --- a/src/ModelHighAPI/ModelHighAPI_Tools.cpp +++ b/src/ModelHighAPI/ModelHighAPI_Tools.cpp @@ -6,6 +6,7 @@ //-------------------------------------------------------------------------------------- #include "ModelHighAPI_Tools.h" +#include //-------------------------------------------------------------------------------------- #include #include @@ -27,6 +28,12 @@ #include #include #include +#include +#include +#include +#include +//-------------------------------------------------------------------------------------- +#include //-------------------------------------------------------------------------------------- #include "ModelHighAPI_Double.h" #include "ModelHighAPI_Integer.h" @@ -34,7 +41,10 @@ #include "ModelHighAPI_Reference.h" #include "ModelHighAPI_Selection.h" +#include + #include +#include //-------------------------------------------------------------------------------------- void fillAttribute(const std::shared_ptr & theValue, @@ -238,4 +248,115 @@ GeomAPI_Shape::ShapeType getShapeType(const ModelHighAPI_Selection& theSelection return aShapeType; } +/// stores the features information, recoursively stores sub-documetns features +std::string storeFeatures(const std::string& theDocName, DocumentPtr theDoc, + std::map >& theStore, + const bool theCompare) // if false => store +{ + std::map >::iterator aDocFind; + if (theCompare) { + aDocFind = theStore.find(theDocName); + if (aDocFind == theStore.end()) { + return "Document '" + theDocName + "' not found"; + } + } + // store the model features information: iterate all features + int aFeaturesCount = 0; // stores the number of compared features for this document to compate + std::set aProcessed; // processed features names (that are in the current document) + std::list allFeatures = theDoc->allFeatures(); + std::list::iterator allIter = allFeatures.begin(); + for(; allIter != allFeatures.end(); allIter++) { + FeaturePtr aFeat = *allIter; + if (theCompare) { + std::map::iterator + aFeatFind = aDocFind->second.find(aFeat->name()); + if (aFeatFind == aDocFind->second.end()) { + return "Document '" + theDocName + "' feature '" + aFeat->name() + "' not found"; + } + std::string anError = aFeatFind->second.compare(aFeat); + if (!anError.empty()) { + return anError; + } + aFeaturesCount++; + aProcessed.insert(aFeat->name()); + } else { + theStore[theDocName][aFeat->name()] = ModelHighAPI_FeatureStore(aFeat); + } + // iterate all results of this feature + std::list allResults; + ModelAPI_Tools::allResults(aFeat, allResults); + std::list::iterator aRes = allResults.begin(); + for(; aRes != allResults.end(); aRes++) { + if ((*aRes)->groupName() == ModelAPI_ResultPart::group()) { // recoursively store features of sub-documents + DocumentPtr aDoc = std::dynamic_pointer_cast(*aRes)->partDoc(); + if (aDoc.get()) { + std::string anError = storeFeatures((*aRes)->data()->name(), aDoc, theStore, theCompare); + if (!anError.empty()) + return anError; + } + } + } + } + // checks the number of compared features + if (theCompare) { + if (aDocFind->second.size() != aFeaturesCount) { + // search for disappeared feature + std::string aLostName; + std::map::iterator aLostIter; + for(aLostIter = aDocFind->second.begin(); aLostIter != aDocFind->second.end(); aLostIter++) { + if (aProcessed.find(aLostIter->first) == aProcessed.end()) { + aLostName = aLostIter->first; + } + } + return "For document '" + theDocName + + "' the number of features becomes smaller, there is no feature '" + aLostName + "'"; + } + } + return ""; // ok +} + +//================================================================================================== +bool checkPythonDump() +{ + SessionPtr aSession = ModelAPI_Session::get(); + // dump all to the python file + aSession->startOperation("Check python dump"); + FeaturePtr aDump = aSession->moduleDocument()->addFeature("Dump"); + if (aDump.get()) { + aDump->string("file_path")->setValue("check_dump.py"); // to the current folder + aDump->string("file_format")->setValue("py"); // to the current folder + aDump->execute(); + } + bool isProblem = !aDump.get() || !aDump->error().empty(); // after "finish" dump will be removed + aSession->finishOperation(); + if (isProblem) + return false; // something is wrong during dump + + // map from document name to feature name to feature data + std::map > aStore; + std::string anError = storeFeatures( + aSession->moduleDocument()->kind(), aSession->moduleDocument(), aStore, false); + if (!anError.empty()) { + Events_InfoMessage anError("checkPythonDump", anError); + anError.send(); + return false; + } + // close all before importation of the script + aSession->closeAll(); + // execute the dumped + Config_ModuleReader::loadScript("check_dump"); + + // compare with the stored data + anError = storeFeatures( + aSession->moduleDocument()->kind(), aSession->moduleDocument(), aStore, true); + if (!anError.empty()) { + std::cout<