diff --git a/src/analyser.cpp b/src/analyser.cpp index 00c89e83a8..16691bf10a 100644 --- a/src/analyser.cpp +++ b/src/analyser.cpp @@ -402,9 +402,14 @@ AnalyserInternalVariablePtr Analyser::AnalyserImpl::internalVariable(const Varia { // Find and return, if there is one, the internal variable associated with // the given variable. + auto rawPtr = reinterpret_cast(variable.get()); + if (mInternalVariableMap.count(rawPtr) > 0) { + return mInternalVariableMap[rawPtr]; + } for (const auto &internalVariable : mInternalVariables) { if (mAnalyserModel->areEquivalentVariables(variable, internalVariable->mVariable)) { + mInternalVariableMap[rawPtr] = internalVariable; return internalVariable; } } @@ -415,6 +420,7 @@ AnalyserInternalVariablePtr Analyser::AnalyserImpl::internalVariable(const Varia auto res = AnalyserInternalVariable::create(variable); mInternalVariables.push_back(res); + mInternalVariableMap[rawPtr] = res; return res; } @@ -2321,6 +2327,7 @@ void Analyser::AnalyserImpl::analyseModel(const ModelPtr &model) mAnalyserModel = AnalyserModel::AnalyserModelImpl::create(model); mInternalVariables.clear(); + mInternalVariableMap.clear(); mInternalEquations.clear(); mCiCnUnits.clear(); diff --git a/src/analyser_p.h b/src/analyser_p.h index 43ea5d9a1d..237e5c3b4a 100644 --- a/src/analyser_p.h +++ b/src/analyser_p.h @@ -161,6 +161,7 @@ class Analyser::AnalyserImpl: public Logger::LoggerImpl AnalyserExternalVariablePtrs mExternalVariables; AnalyserInternalVariablePtrs mInternalVariables; + std::unordered_map mInternalVariableMap; AnalyserInternalEquationPtrs mInternalEquations; GeneratorProfilePtr mGeneratorProfile = GeneratorProfile::create(); diff --git a/src/analysermodel.cpp b/src/analysermodel.cpp index 3cddd3ea39..958f5e668b 100644 --- a/src/analysermodel.cpp +++ b/src/analysermodel.cpp @@ -48,35 +48,51 @@ AnalyserModel::~AnalyserModel() delete mPimpl; } -void AnalyserModel::AnalyserModelImpl::buildEquivalentVariablesCache(const ComponentPtr &component) +void exploreEquivalentVariables(const VariablePtr &variable, std::set &equivalentGroup, std::set &visited) { - for (size_t i = 0; i < component->variableCount(); ++i) { - auto variable = component->variable(i); + auto rawPtr = reinterpret_cast(variable.get()); - for (size_t j = 0; j < variable->equivalentVariableCount(); ++j) { - auto equivalentVariable = variable->equivalentVariable(j); - auto v1 = reinterpret_cast(variable.get()); - auto v2 = reinterpret_cast(equivalentVariable.get()); + if (visited.count(rawPtr) == 0) { + visited.insert(rawPtr); + equivalentGroup.insert(rawPtr); - if (v2 < v1) { - std::swap(v1, v2); - } - - uniteEquivalentVariableAddresses(v1, v2); + for (size_t i = 0; i < variable->equivalentVariableCount(); ++i) { + exploreEquivalentVariables(variable->equivalentVariable(i), equivalentGroup, visited); } } - - for (size_t i = 0; i < component->componentCount(); ++i) { - buildEquivalentVariablesCache(component->component(i)); - } } void AnalyserModel::AnalyserModelImpl::buildEquivalentVariablesCache() { + std::set visited; + std::vector> equivalentVariableGroups; mEquivalentVariableCache.clear(); for (size_t i = 0; i < mModel->componentCount(); ++i) { - buildEquivalentVariablesCache(mModel->component(i)); + buildEquivalentVariablesCache(mModel->component(i), visited, equivalentVariableGroups); + } +} + +void AnalyserModel::AnalyserModelImpl::buildEquivalentVariablesCache(const ComponentPtr &component, std::set &visited, std::vector> &equivalentVariableGroups) +{ + for (size_t i = 0; i < component->variableCount(); ++i) { + auto variable = component->variable(i); + auto rawPtr = reinterpret_cast(variable.get()); + + if (visited.count(rawPtr) == 0) { + std::set equivalentGroup; + exploreEquivalentVariables(variable, equivalentGroup, visited); + size_t groupIndex = equivalentVariableGroups.size(); + + for (uintptr_t v : equivalentGroup) { + mEquivalentVariableCache[v] = groupIndex; + } + equivalentVariableGroups.push_back(equivalentGroup); + } + } + + for (size_t i = 0; i < component->componentCount(); ++i) { + buildEquivalentVariablesCache(component->component(i), visited, equivalentVariableGroups); } } @@ -546,7 +562,8 @@ bool AnalyserModel::areEquivalentVariables(const VariablePtr &variable1, const auto v1 = reinterpret_cast(variable1.get()); const auto v2 = reinterpret_cast(variable2.get()); - return mPimpl->findVariableAddress(v1) == mPimpl->findVariableAddress(v2); + return (mPimpl->mEquivalentVariableCache.count(v1) > 0) + && (mPimpl->mEquivalentVariableCache[v1] == mPimpl->mEquivalentVariableCache[v2]); } } // namespace libcellml diff --git a/src/analysermodel_p.h b/src/analysermodel_p.h index adee6853e2..61e4d05e70 100644 --- a/src/analysermodel_p.h +++ b/src/analysermodel_p.h @@ -17,6 +17,7 @@ limitations under the License. #pragma once #include +#include #include #include "libcellml/analysermodel.h" @@ -46,34 +47,7 @@ struct AnalyserModel::AnalyserModelImpl std::vector mAnalyserEquations; - std::unordered_map mEquivalentVariableCache; - - uintptr_t findVariableAddress(uintptr_t x) - { - auto it = mEquivalentVariableCache.find(x); - - if (it == mEquivalentVariableCache.end()) { - mEquivalentVariableCache[x] = x; - - return x; - } - - if (it->second != x) { - it->second = findVariableAddress(it->second); - } - - return it->second; - } - - void uniteEquivalentVariableAddresses(uintptr_t x, uintptr_t y) - { - const uintptr_t &rootX = findVariableAddress(x); - const uintptr_t &rootY = findVariableAddress(y); - - if (rootX != rootY) { - mEquivalentVariableCache[rootY] = rootX; - } - } + std::unordered_map mEquivalentVariableCache; bool mNeedEqFunction = false; bool mNeedNeqFunction = false; @@ -104,8 +78,8 @@ struct AnalyserModel::AnalyserModelImpl static AnalyserModelPtr create(const ModelPtr &model = nullptr); - void buildEquivalentVariablesCache(const ComponentPtr &component); void buildEquivalentVariablesCache(); + void buildEquivalentVariablesCache(const ComponentPtr &component, std::set &visited, std::vector> &equivalentVariableGroups); AnalyserModelImpl(const ModelPtr &model); }; diff --git a/src/api/libcellml/analysermodel.h b/src/api/libcellml/analysermodel.h index 688a143e03..b264be7729 100644 --- a/src/api/libcellml/analysermodel.h +++ b/src/api/libcellml/analysermodel.h @@ -604,10 +604,6 @@ class LIBCELLML_EXPORT AnalyserModel * analysis phase (@ref Analyser::analyseModel). The cache may become * out of date if the model is changed after the model has been analysed. * - * @note This function is primarily designed for use during model analysis - * by the @ref Analyser. While external usage is not programmatically - * restricted, it is not the primary intended use case. - * * @param variable1 The @ref Variable to test if it is equivalent to * @p variable2. * @param variable2 The @ref Variable that is potentially equivalent to @@ -616,8 +612,7 @@ class LIBCELLML_EXPORT AnalyserModel * @return @c true if @p variable1 is equivalent to @p variable2 and * @c false otherwise. */ - bool areEquivalentVariables(const VariablePtr &variable1, - const VariablePtr &variable2); + bool areEquivalentVariables(const VariablePtr &variable1, const VariablePtr &variable2); private: AnalyserModel(const ModelPtr &model); /**< Constructor, @private. */ diff --git a/src/api/libcellml/variable.h b/src/api/libcellml/variable.h index 2d9e6292dc..1542aed286 100644 --- a/src/api/libcellml/variable.h +++ b/src/api/libcellml/variable.h @@ -174,14 +174,17 @@ class LIBCELLML_EXPORT Variable: public NamedEntity * * Get the connection identifier set for the equivalence defined with the given variables. * The variables are commutative. If no connection identifier is set the empty string is returned. + * The optional parameter @p search will traverse the equivalence network to find the connection identifier for the + * equivalence defined by the two variables. By default this is true. * * If the two variables are not equivalent the empty string is returned. * * @param variable1 Variable one of the equivalence. * @param variable2 Variable two of the equivalence. + * @param search Optional parameter to search the equivalence network for the connection identifier, true by default. * @return the @c std::string connection identifier. */ - static std::string equivalenceConnectionId(const VariablePtr &variable1, const VariablePtr &variable2); + static std::string equivalenceConnectionId(const VariablePtr &variable1, const VariablePtr &variable2, bool search = true); /** * @brief Clear equivalent connection identifier for this equivalence. diff --git a/src/internaltypes.h b/src/internaltypes.h index e21e35ed67..29673e55c0 100644 --- a/src/internaltypes.h +++ b/src/internaltypes.h @@ -42,13 +42,14 @@ using UniqueNames = std::set; /**< Type definition for a set of uni using NodeAttributeNamespaceInfo = std::vector>; /**< Type definition for attribute namespace information. */ // VariableMap +using VariableStdPair = std::pair; /**< Type definition for Variable pointer pair using standard library. */ using VariableMap = std::vector; /**< Type definition for vector of VariablePair.*/ using VariableMapIterator = VariableMap::const_iterator; /**< Type definition of const iterator for vector of VariablePair.*/ // ComponentMap -using ComponentPair = std::pair; /**< Type definition for Component pointer pair.*/ -using ComponentMap = std::vector; /**< Type definition for vector of ComponentPair.*/ -using ComponentMapIterator = ComponentMap::const_iterator; /**< Type definition of const iterator for vector of ComponentPair.*/ +using ComponentStdPair = std::pair; /**< Type definition for Component pointer pair using standard library.*/ +using ComponentMap = std::vector; /**< Type definition for vector of ComponentStdPair.*/ +using ComponentMapIterator = ComponentMap::const_iterator; /**< Type definition of const iterator for vector of ComponentStdPair.*/ using VariablePtrs = std::vector; /**< Type definition for list of variables. */ @@ -79,6 +80,9 @@ using UnitsConstPtr = std::shared_ptr; /**< Type definition for sha using ConnectionMap = std::map; /**< Type definition for a connection map.*/ using NamePairList = std::vector; /**< Type definition for a list of a pair of names. */ +using ComponentRawPtrPair = std::pair; /**< Type definition for pair of raw component pointers. */ +using ConnectionIdMap = std::map; /**< Type definition for map of pair of raw component pointers to connection ID. */ + /** * @brief Class for defining an epoch in the history of a @ref Component or @ref Units. * diff --git a/src/printer.cpp b/src/printer.cpp index 6a39d3a5f5..d88bed4e1d 100644 --- a/src/printer.cpp +++ b/src/printer.cpp @@ -77,7 +77,7 @@ std::string printConnections(const ComponentMap &componentMap, const VariableMap for (auto iterPair = componentMap.begin(); iterPair < componentMap.end(); ++iterPair) { ComponentPtr currentComponent1 = iterPair->first; ComponentPtr currentComponent2 = iterPair->second; - ComponentPair currentComponentPair = std::make_pair(currentComponent1, currentComponent2); + ComponentStdPair currentComponentPair = std::make_pair(currentComponent1, currentComponent2); // Check whether this set of connections has already been serialised. bool pairFound = false; for (const auto &serialisedIterPair : serialisedComponentMap) { @@ -178,7 +178,7 @@ void buildMapsForComponentsVariables(const ComponentPtr &component, ComponentMap ComponentPtr component1 = owningComponent(variable); ComponentPtr component2 = owningComponent(equivalentVariable); // Also create a component map pair corresponding with the variable map pair. - ComponentPair componentPair = std::make_pair(component1, component2); + ComponentStdPair componentPair = std::make_pair(component1, component2); componentMap.push_back(componentPair); } } diff --git a/src/validator.cpp b/src/validator.cpp index 55a1db1186..7e5720bc0c 100644 --- a/src/validator.cpp +++ b/src/validator.cpp @@ -639,8 +639,9 @@ class Validator::ValidatorImpl: public LoggerImpl * @param component The component to check. * @param idMap The IdMap object to construct. * @param reportedConnections A set of connection identifiers to prevent duplicate reporting. + * @param connectionIds A map of connection identifiers to prevent duplicate reporting of connections. */ - void buildComponentIdMap(const ComponentPtr &component, IdMap &idMap, std::set &reportedConnections); + void buildComponentIdMap(const ComponentPtr &component, IdMap &idMap, std::set &reportedConnections, const ConnectionIdMap &connectionIds); /** @brief Utility function to add an item to the idMap. * @@ -2692,11 +2693,56 @@ void Validator::ValidatorImpl::addIdMapItem(const std::string &id, const std::st } } +void gatherComponents(const ComponentPtr &component, std::vector &allComponents) +{ + allComponents.push_back(component); + for (size_t c = 0; c < component->componentCount(); ++c) { + gatherComponents(component->component(c), allComponents); + } +} + IdMap Validator::ValidatorImpl::buildModelIdMap(const ModelPtr &model) { IdMap idMap; std::string info; std::set reportedConnections; + + std::vector allComponents; + for (size_t c = 0; c < model->componentCount(); ++c) { + gatherComponents(model->component(c), allComponents); + } + + struct PairHash + { + size_t operator()(const ComponentRawPtrPair &p) const + { + return std::hash()(p.first) ^ (std::hash()(p.second) << 1); + } + }; + + ConnectionIdMap connectionIds; + std::unordered_set visitedPairs; + + for (const auto &comp : allComponents) { + auto rawPtr = comp.get(); + const size_t varCount = comp->variableCount(); + for (size_t i = 0; i < varCount; ++i) { + auto currentVariable = comp->variable(i); + for (size_t e = 0; e < currentVariable->equivalentVariableCount(); ++e) { + auto equiv = currentVariable->equivalentVariable(e); + auto equivParent = owningComponent(equiv); + if (equivParent != nullptr) { + auto equivRawPtr = equivParent.get(); + auto key = (rawPtr < equivRawPtr) ? ComponentRawPtrPair {rawPtr, equivRawPtr} : ComponentRawPtrPair {equivRawPtr, rawPtr}; + if (!visitedPairs.insert(key).second) { + continue; // Skip if we've already processed this pair + } + connectionIds[key] = Variable::equivalenceConnectionId(currentVariable, equiv, false); + } + } + } + } + // Model. if (!model->id().empty()) { info = " - model '" + model->name() + "'"; @@ -2748,12 +2794,12 @@ IdMap Validator::ValidatorImpl::buildModelIdMap(const ModelPtr &model) // Start recursion through encapsulation hierarchy. for (size_t c = 0; c < model->componentCount(); ++c) { - buildComponentIdMap(model->component(c), idMap, reportedConnections); + buildComponentIdMap(model->component(c), idMap, reportedConnections, connectionIds); } return idMap; } -void Validator::ValidatorImpl::buildComponentIdMap(const ComponentPtr &component, IdMap &idMap, std::set &reportedConnections) +void Validator::ValidatorImpl::buildComponentIdMap(const ComponentPtr &component, IdMap &idMap, std::set &reportedConnections, const ConnectionIdMap &connectionIds) { std::string info; @@ -2807,7 +2853,10 @@ void Validator::ValidatorImpl::buildComponentIdMap(const ComponentPtr &component addIdMapItem(mappingId, info, idMap); } // Connections. - auto connectionId = Variable::equivalenceConnectionId(item, equiv); + auto key = component.get() < equivParent.get() ? ComponentRawPtrPair {component.get(), equivParent.get()} : ComponentRawPtrPair {equivParent.get(), component.get()}; + + auto connectionId = connectionIds.at(key); + // auto connectionId = Variable::equivalenceConnectionId(item, equiv); std::string connection = component->name() < equivParent->name() ? component->name() + equivParent->name() : equivParent->name() + component->name(); if ((s1 < s2) && !connectionId.empty() && (reportedConnections.count(connection) == 0)) { std::string connectionDescription = @@ -2879,7 +2928,7 @@ void Validator::ValidatorImpl::buildComponentIdMap(const ComponentPtr &component // Child components. for (size_t c = 0; c < component->componentCount(); ++c) { - buildComponentIdMap(component->component(c), idMap, reportedConnections); + buildComponentIdMap(component->component(c), idMap, reportedConnections, connectionIds); } } diff --git a/src/variable.cpp b/src/variable.cpp index 5644b63219..35d06f47fa 100644 --- a/src/variable.cpp +++ b/src/variable.cpp @@ -223,7 +223,8 @@ bool haveEquivalentVariables(const Variable *variable1, testedVariables.push_back(variable2); - for (size_t i = 0; i < variable2->equivalentVariableCount(); ++i) { + const size_t variable2EquivalentVariableCount = variable2->equivalentVariableCount(); + for (size_t i = 0; i < variable2EquivalentVariableCount; ++i) { Variable *equivalentVariable2 = variable2->equivalentVariable(i).get(); if ((std::find(testedVariables.begin(), testedVariables.end(), equivalentVariable2) == testedVariables.end()) @@ -431,18 +432,22 @@ std::string Variable::equivalenceMappingId(const VariablePtr &variable1, const V return id; } -std::string Variable::equivalenceConnectionId(const VariablePtr &variable1, const VariablePtr &variable2) +std::string Variable::equivalenceConnectionId(const VariablePtr &variable1, const VariablePtr &variable2, bool search) { std::string id; if ((variable1 != nullptr) && (variable2 != nullptr)) { - if (variable1->hasEquivalentVariable(variable2, true)) { - auto map = createConnectionMap(variable1, variable2); - for (auto &it : map) { - id = it.first->pFunc()->equivalentConnectionId(it.second); - } - if (id.empty()) { - id = variable1->pFunc()->equivalentConnectionId(variable2); + if (search) { + if (variable1->hasEquivalentVariable(variable2, true)) { + auto map = createConnectionMap(variable1, variable2); + for (auto &it : map) { + id = it.first->pFunc()->equivalentConnectionId(it.second); + } + if (id.empty()) { + id = variable1->pFunc()->equivalentConnectionId(variable2); + } } + } else { + id = variable1->pFunc()->equivalentConnectionId(variable2); } } return id; diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index b2b96add12..cffc108e58 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -47,6 +47,7 @@ include(units/tests.cmake) include(validator/tests.cmake) include(variable/tests.cmake) include(version/tests.cmake) +include(investigations/tests.cmake) set(TEST_EXPORTDEFINITIONS_H "${CMAKE_CURRENT_BINARY_DIR}/test_exportdefinitions.h") diff --git a/tests/investigations/investigations.cpp b/tests/investigations/investigations.cpp new file mode 100644 index 0000000000..0dddba1172 --- /dev/null +++ b/tests/investigations/investigations.cpp @@ -0,0 +1,157 @@ +/* +Copyright libCellML Contributors + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +#include "gtest/gtest.h" +#include + +#include + +#include "test_utils.h" + +const char *BENCHMARKING_MODEL_ROOT = std::getenv("BENCHMARKING_MODEL_ROOT"); + +TEST(Investigations, DISABLED_exponentialTimeConsumption11) +{ + const std::string modelPath = std::string(BENCHMARKING_MODEL_ROOT) + "image_to_model_11_vessels/image_to_model.cellml"; + const std::string modelImportPath = std::string(BENCHMARKING_MODEL_ROOT) + "image_to_model_11_vessels/"; + + auto importer = libcellml::Importer::create(false); + + auto parser = libcellml::Parser::create(false); + auto originalModel = parser->parseModel(fileContents(modelPath, true)); + EXPECT_EQ(size_t(1), parser->issueCount()); + + importer->resolveImports(originalModel, modelImportPath); + + EXPECT_EQ(size_t(3), importer->issueCount()); + + auto flatModel = importer->flattenModel(originalModel); + EXPECT_EQ(size_t(0), importer->issueCount()); + EXPECT_NE(nullptr, flatModel); + + auto analyser = libcellml::Analyser::create(); + analyser->analyseModel(flatModel); + EXPECT_EQ(size_t(0), analyser->issueCount()); + printIssues(analyser); +} + +TEST(Investigations, DISABLED_exponentialTimeConsumption246) +{ + const std::string modelPath = std::string(BENCHMARKING_MODEL_ROOT) + "image_to_model_246_vessels/image_to_model.cellml"; + const std::string modelImportPath = std::string(BENCHMARKING_MODEL_ROOT) + "image_to_model_246_vessels/"; + auto importer = libcellml::Importer::create(false); + + auto parser = libcellml::Parser::create(false); + auto originalModel = parser->parseModel(fileContents(modelPath, true)); + EXPECT_EQ(size_t(1), parser->issueCount()); + + importer->resolveImports(originalModel, modelImportPath); + + EXPECT_EQ(size_t(3), importer->issueCount()); + + auto flatModel = importer->flattenModel(originalModel); + EXPECT_EQ(size_t(0), importer->issueCount()); + EXPECT_NE(nullptr, flatModel); + + auto analyser = libcellml::Analyser::create(); + analyser->analyseModel(flatModel); + EXPECT_EQ(size_t(0), analyser->issueCount()); + printIssues(analyser); +} + +TEST(Investigations, DISABLED_exponentialTimeConsumption380) +{ + const std::string modelPath = std::string(BENCHMARKING_MODEL_ROOT) + "image_to_model_380_vessels/image_to_model.cellml"; + const std::string modelImportPath = std::string(BENCHMARKING_MODEL_ROOT) + "image_to_model_380_vessels/"; + auto importer = libcellml::Importer::create(false); + + auto parser = libcellml::Parser::create(false); + auto originalModel = parser->parseModel(fileContents(modelPath, true)); + EXPECT_EQ(size_t(1), parser->issueCount()); + + importer->resolveImports(originalModel, modelImportPath); + + EXPECT_EQ(size_t(3), importer->issueCount()); + + auto flatModel = importer->flattenModel(originalModel); + EXPECT_EQ(size_t(0), importer->issueCount()); + EXPECT_NE(nullptr, flatModel); + + auto analyser = libcellml::Analyser::create(); + analyser->analyseModel(flatModel); + EXPECT_EQ(size_t(0), analyser->issueCount()); + printIssues(analyser); +} + +TEST(Investigations, DISABLED_exponentialTimeConsumption524) +{ + const std::string modelPath = std::string(BENCHMARKING_MODEL_ROOT) + "image_to_model_524_vessels/image_to_model.cellml"; + const std::string modelImportPath = std::string(BENCHMARKING_MODEL_ROOT) + "image_to_model_524_vessels/"; + auto importer = libcellml::Importer::create(false); + + auto parser = libcellml::Parser::create(false); + auto originalModel = parser->parseModel(fileContents(modelPath, true)); + EXPECT_EQ(size_t(1), parser->issueCount()); + + importer->resolveImports(originalModel, modelImportPath); + + EXPECT_EQ(size_t(3), importer->issueCount()); + + auto flatModel = importer->flattenModel(originalModel); + EXPECT_EQ(size_t(0), importer->issueCount()); + EXPECT_NE(nullptr, flatModel); + + auto analyser = libcellml::Analyser::create(); + analyser->analyseModel(flatModel); + EXPECT_EQ(size_t(4), analyser->issueCount()); + printIssues(analyser); +} + +TEST(Investigations, DISABLED_exponentialTimeConsumptionOthers) +{ + const std::vector vesselCounts = {246, 380, 524}; + + auto importer = libcellml::Importer::create(false); + auto parser = libcellml::Parser::create(false); + auto printer = libcellml::Printer::create(); + auto analyser = libcellml::Analyser::create(); + auto generator = libcellml::Generator::create(); + + for (int vesselCount : vesselCounts) { + SCOPED_TRACE("Vessel count: " + std::to_string(vesselCount)); + std::string modelPath = std::string(BENCHMARKING_MODEL_ROOT) + "image_to_model_" + std::to_string(vesselCount) + "_vessels/image_to_model.cellml"; + std::string modelImportPath = std::string(BENCHMARKING_MODEL_ROOT) + "image_to_model_" + std::to_string(vesselCount) + "_vessels/"; + auto originalModel = parser->parseModel(fileContents(modelPath, true)); + EXPECT_EQ(size_t(1), parser->issueCount()); + + importer->resolveImports(originalModel, modelImportPath); + + EXPECT_EQ(size_t(0), importer->issueCount()); + for (size_t i = 0; i < importer->issueCount(); ++i) { + Debug() << "[" << i << "]: " << importer->issue(i)->description(); + } + + auto flatModel = importer->flattenModel(originalModel); + EXPECT_EQ(size_t(0), importer->issueCount()); + EXPECT_NE(nullptr, flatModel); + + analyser->analyseModel(flatModel); + EXPECT_EQ(size_t(0), analyser->issueCount()); + + const auto implementationCode = generator->implementationCode(analyser->analyserModel()); + EXPECT_NE(size_t(0), implementationCode.length()); + } +} diff --git a/tests/investigations/tests.cmake b/tests/investigations/tests.cmake new file mode 100644 index 0000000000..1b01f81fa1 --- /dev/null +++ b/tests/investigations/tests.cmake @@ -0,0 +1,18 @@ + +# Set the test name, 'test_' will be prepended to the +# name set here +set(CURRENT_TEST investigations) +# Set a category name to enable running commands like: +# ctest -R +# which will run the tests matching this category-label. +# Can be left empty (or just not set) +set(${CURRENT_TEST}_CATEGORY misc) +list(APPEND LIBCELLML_TESTS ${CURRENT_TEST}) +# Using absolute path relative to this file +set(${CURRENT_TEST}_SRCS + ${CMAKE_CURRENT_LIST_DIR}/investigations.cpp +) +set(${CURRENT_TEST}_HDRS +) + + diff --git a/tests/test_utils.cpp b/tests/test_utils.cpp index 21f57ed540..483fe307ad 100644 --- a/tests/test_utils.cpp +++ b/tests/test_utils.cpp @@ -35,12 +35,18 @@ std::string resourcePath(const std::string &resourceRelativePath) return TESTS_RESOURCE_LOCATION + "/" + resourceRelativePath; } -std::string fileContents(const std::string &fileName) +std::string fileContents(const std::string &fileName, bool absolute) { - std::ifstream file(resourcePath(fileName)); + std::ifstream file; + if (absolute) { + file.open(fileName); + } else { + file.open(resourcePath(fileName)); + } std::stringstream buffer; buffer << file.rdbuf(); + file.close(); return buffer.str(); } diff --git a/tests/test_utils.h b/tests/test_utils.h index 1199138682..199a9e5e38 100644 --- a/tests/test_utils.h +++ b/tests/test_utils.h @@ -82,7 +82,7 @@ std::chrono::steady_clock::time_point TEST_EXPORT timeNow(); int TEST_EXPORT elapsedTime(const std::chrono::steady_clock::time_point &startTime); std::string TEST_EXPORT resourcePath(const std::string &resourceRelativePath = ""); -std::string TEST_EXPORT fileContents(const std::string &fileName); +std::string TEST_EXPORT fileContents(const std::string &fileName, bool absolute = false); void TEST_EXPORT printIssues(const libcellml::LoggerPtr &l, bool headings = false, bool cellmlElementTypes = false, bool rule = false); void TEST_EXPORT printModel(const libcellml::ModelPtr &model, bool includeMaths = true);