diff --git a/src/printer.cpp b/src/printer.cpp index 310354e3b7..c96cffc72f 100644 --- a/src/printer.cpp +++ b/src/printer.cpp @@ -234,40 +234,41 @@ std::string Printer::PrinterImpl::printUnits(const UnitsPtr &units, IdList &idLi std::string Printer::PrinterImpl::printComponent(const ComponentPtr &component, IdList &idList, bool autoIds) { std::string repr; - if (component->isImport()) { - return repr; - } - repr += "name(); - if (!componentName.empty()) { - repr += " name=\"" + componentName + "\""; - } - if (!component->id().empty()) { - repr += " id=\"" + component->id() + "\""; - } else if (autoIds) { - repr += " id=\"" + makeUniqueId(idList) + "\""; - } - size_t variableCount = component->variableCount(); - size_t resetCount = component->resetCount(); - bool hasChildren = false; - if (variableCount > 0 || resetCount > 0 || !component->math().empty()) { - hasChildren = true; - } - if (hasChildren) { - repr += ">"; - for (size_t i = 0; i < variableCount; ++i) { - repr += printVariable(component->variable(i), idList, autoIds); + if (!component->isImport()) { + repr += "name(); + if (!componentName.empty()) { + repr += " name=\"" + componentName + "\""; + } + if (!component->id().empty()) { + repr += " id=\"" + component->id() + "\""; + } else if (autoIds) { + repr += " id=\"" + makeUniqueId(idList) + "\""; } - for (size_t i = 0; i < resetCount; ++i) { - repr += printReset(component->reset(i), idList, autoIds); + size_t variableCount = component->variableCount(); + size_t resetCount = component->resetCount(); + bool hasChildren = false; + if (variableCount > 0 || resetCount > 0 || !component->math().empty()) { + hasChildren = true; } - if (!component->math().empty()) { - repr += printMath(component->math()); + if (hasChildren) { + repr += ">"; + for (size_t i = 0; i < variableCount; ++i) { + repr += printVariable(component->variable(i), idList, autoIds); + } + for (size_t i = 0; i < resetCount; ++i) { + repr += printReset(component->reset(i), idList, autoIds); + } + if (!component->math().empty()) { + repr += printMath(component->math()); + } + + repr += ""; + } else { + repr += "/>"; } - repr += ""; - } else { - repr += "/>"; } + // Traverse through children of this component and add them to the representation. for (size_t i = 0; i < component->componentCount(); ++i) { repr += printComponent(component->component(i), idList, autoIds); @@ -495,7 +496,8 @@ std::string Printer::printModel(const ModelPtr &model, bool autoIds) const } std::string componentEncapsulation; - // Serialise components of the model, imported components have already been dealt with at this point. + // Serialise components of the model, imported components have already been dealt with at this point, + // ... but their locally-defined children have not. for (size_t i = 0; i < model->componentCount(); ++i) { ComponentPtr component = model->component(i); repr += mPimpl->printComponent(component, idList, autoIds); diff --git a/tests/parser/file_parser.cpp b/tests/parser/file_parser.cpp index 377ce6b212..8d501e44ce 100644 --- a/tests/parser/file_parser.cpp +++ b/tests/parser/file_parser.cpp @@ -155,3 +155,15 @@ TEST(Parser, simpleGeneratorModel) std::string a = model->component("my_component")->math(); EXPECT_EQ(e, a); } + +TEST(Parser, parseModelWithImportedEquivVariables) +{ + auto parser = libcellml::Parser::create(); + auto modelContents = fileContents("importingModel.cellml"); + auto model = parser->parseModel(modelContents); + + auto printer = libcellml::Printer::create(); + auto serialisedModel = printer->printModel(model); + + EXPECT_EQ(modelContents, serialisedModel); +} diff --git a/tests/printer/printer.cpp b/tests/printer/printer.cpp index 00d49fca37..1ce4fcc315 100644 --- a/tests/printer/printer.cpp +++ b/tests/printer/printer.cpp @@ -294,6 +294,40 @@ TEST(Printer, printModelWithStandardUnitsAdded) EXPECT_EQ(e, printer->printModel(model)); } +TEST(Printer, printModelImportingModelParentComponent) +{ + auto parser = libcellml::Parser::create(); + auto modelContents = fileContents("importingModelParentComponent.cellml"); + auto model = parser->parseModel(modelContents); + EXPECT_EQ(size_t(0), parser->errorCount()); + + auto validator = libcellml::Validator::create(); + validator->validateModel(model); + EXPECT_EQ(size_t(0), validator->issueCount()); + + auto printer = libcellml::Printer::create(); + auto serialisedModel = printer->printModel(model); + + EXPECT_EQ(modelContents, serialisedModel); +} + +TEST(Printer, printModelImportingModelChildComponent) +{ + auto parser = libcellml::Parser::create(); + auto modelContents = fileContents("importingModelChildComponent.cellml"); + auto model = parser->parseModel(modelContents); + + auto validator = libcellml::Validator::create(); + validator->validateModel(model); + + EXPECT_EQ(size_t(0), validator->issueCount()); + + auto printer = libcellml::Printer::create(); + auto serialisedModel = printer->printModel(model); + + EXPECT_EQ(modelContents, serialisedModel); +} + TEST(Printer, printModelWithAutomaticIdsNoMaths) { const std::string in = "\n" diff --git a/tests/resources/importedModelWithMaps.cellml b/tests/resources/importedModelWithMaps.cellml new file mode 100644 index 0000000000..6dbce72f8e --- /dev/null +++ b/tests/resources/importedModelWithMaps.cellml @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/tests/resources/importingModel.cellml b/tests/resources/importingModel.cellml new file mode 100644 index 0000000000..e617a8e0df --- /dev/null +++ b/tests/resources/importingModel.cellml @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/tests/resources/importingModelChildComponent.cellml b/tests/resources/importingModelChildComponent.cellml new file mode 100644 index 0000000000..cf0163b3f9 --- /dev/null +++ b/tests/resources/importingModelChildComponent.cellml @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + diff --git a/tests/resources/importingModelParentComponent.cellml b/tests/resources/importingModelParentComponent.cellml new file mode 100644 index 0000000000..bf641e7428 --- /dev/null +++ b/tests/resources/importingModelParentComponent.cellml @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + diff --git a/tests/test_utils.cpp b/tests/test_utils.cpp index ab8d427747..25968709f2 100644 --- a/tests/test_utils.cpp +++ b/tests/test_utils.cpp @@ -76,87 +76,104 @@ void printIssues(const libcellml::LoggerPtr &l, bool headings, bool causes, bool } } -void printModel(const libcellml::ModelPtr &model) -{ - std::cout << "The model name is: '" << model->name() << "'" << std::endl; - if (model->id() != "") { - std::cout << "The model id is: '" << model->id() << "'" << std::endl; - } - - // 2.a Print any custom units of the model - std::cout << "The model defines " << model->unitsCount() - << " custom units:" << std::endl; - for (size_t u = 0; u < model->unitsCount(); ++u) { - std::cout << " Units[" << u << "] is '" << model->units(u)->name() << "'" - << std::endl; - } +static const std::string FIXED_INDENT = " "; - // 2.b Print the components of the model - std::cout << "The model has " << model->componentCount() - << " components:" << std::endl; - for (size_t c = 0; c < model->componentCount(); ++c) { - // 2.c Printing the attributes of the component - auto component = model->component(c); - std::string spacer = " "; - printComponent(component, c, spacer); +void printComponent(const libcellml::ComponentPtr &component, size_t c, const std::string &indent, bool includeMaths) +{ + if (c == -1) { + std::cout << "COMPONENT: '" << component->name() << "'"; + } else { + std::cout << indent << "[" << c + 1 << "]: " << component->name(); } -} -void printComponent(const libcellml::ComponentPtr &component, size_t const c, std::string const spacer) -{ - std::cout << spacer << "Component[" << c << "] has name: '" - << component->name() << "'" << std::endl; if (component->id() != "") { - std::cout << spacer << "Component[" << c << "] has id: '" - << component->id() << "'" << std::endl; + std::cout << ", id: " << component->id(); } - std::cout << spacer << "Component[" << c << "] has " - << component->variableCount() - << " variables:" << std::endl; - - // Printing the variables within the component - for (size_t vIndex = 0; vIndex < component->variableCount(); vIndex++) { - auto v = component->variable(vIndex); - std::cout << spacer << " Variable[" << vIndex << "] has name: '" - << v->name() << "'" << std::endl; - if (v->initialValue() != "") { - std::cout << spacer << " Variable[" << vIndex << "] has initial_value: '" - << v->initialValue() << "'" - << std::endl; - } - if (v->units() != nullptr) { - std::cout << spacer << " Variable[" << vIndex << "] has units: '" - << v->units()->name() << "'" << std::endl; + std::cout << std::endl; + std::cout << indent << FIXED_INDENT << "VARIABLES: " << component->variableCount() << " variables" << std::endl; + + // Printing the variables within the component. + for (size_t v = 0; v < component->variableCount(); ++v) { + std::cout << indent << FIXED_INDENT << FIXED_INDENT; + std::cout << "[" << v + 1 << "]: " << component->variable(v)->name(); + if (component->variable(v)->units() != nullptr) { + std::cout << " [" << component->variable(v)->units()->name() << "]"; } - std::cout << spacer << " Variable[" << vIndex << "] has " << v->equivalentVariableCount() << " equivalent variable(s): "; - for (size_t eIndex = 0; eIndex < v->equivalentVariableCount(); ++eIndex) { - auto equivVariable = v->equivalentVariable(eIndex); - std::cout << equivVariable->name() << ", "; + if (component->variable(v)->initialValue() != "") { + std::cout << ", initial = " << component->variable(v)->initialValue(); } std::cout << std::endl; + if (component->variable(v)->equivalentVariableCount() > 0) { + std::cout << indent << FIXED_INDENT << FIXED_INDENT << FIXED_INDENT; + std::string con = " └──> "; + for (size_t e = 0; e < component->variable(v)->equivalentVariableCount(); ++e) { + auto ev = component->variable(v)->equivalentVariable(e); + if (ev == nullptr) { + std::cout << "WHOOPS! Null equivalent variable!"; + continue; + } + libcellml::ComponentPtr ev_parent = std::dynamic_pointer_cast(ev->parent()); + if (ev_parent == nullptr) { + std::cout << "WHOOPS! Null parent component for equivalent variable!"; + continue; + } + std::cout << con << ev_parent->name() << ":" << ev->name(); + if (ev->units() != nullptr) { + std::cout << " [" << ev->units()->name() << "]"; + } + con = ", "; + } + std::cout << std::endl; + } } - // Print the maths within the component - if (component->math() != "") { - std::cout << spacer << " Maths in the component is:" << std::endl; - std::cout << component->math() << std::endl; + // Print the maths within the component. + if (includeMaths) { + if (component->math() != "") { + std::cout << indent << " Maths in the component is:" << std::endl; + std::cout << component->math() << std::endl; + } } // Print the encapsulated components if (component->componentCount() > 0) { - std::cout << spacer << "Component[" << c << "] has " - << component->componentCount() - << " child components:" << std::endl; + std::cout << indent << FIXED_INDENT << "CHILD COMPONENTS: " << component->componentCount() + << " child components" << std::endl; + std::string newIndent = indent + FIXED_INDENT + FIXED_INDENT; - for (size_t c2 = 0; c2 < component->componentCount(); c2++) { + for (size_t c2 = 0; c2 < component->componentCount(); ++c2) { auto child = component->component(c2); - std::string oneMoreSpacer = spacer + " "; - printComponent(child, c2, oneMoreSpacer); + printComponent(child, c2, newIndent, includeMaths); } } } +void printComponent(const libcellml::ComponentPtr &component, bool includeMaths) +{ + printComponent(component, -1, {}, includeMaths); +} + +void printModel(const libcellml::ModelPtr &model, bool includeMaths) +{ + std::cout << "MODEL: '" << model->name() << "'"; + if (model->id() != "") { + std::cout << ", id: '" << model->id() << "'"; + } + std::cout << std::endl; + + std::cout << FIXED_INDENT << "UNITS: " << model->unitsCount() << " custom units" << std::endl; + for (size_t u = 0; u < model->unitsCount(); ++u) { + std::cout << FIXED_INDENT << FIXED_INDENT << "[" << u + 1 << "]: " << model->units(u)->name() << std::endl; + } + + std::cout << FIXED_INDENT << "COMPONENTS: " << model->componentCount() << " components" << std::endl; + for (size_t c = 0; c < model->componentCount(); ++c) { + auto component = model->component(c); + printComponent(component, c, FIXED_INDENT + FIXED_INDENT, includeMaths); + } +} + void expectEqualIssues(const std::vector &issues, const libcellml::LoggerPtr &logger) { diff --git a/tests/test_utils.h b/tests/test_utils.h index d3bd6a86df..fce86ea367 100644 --- a/tests/test_utils.h +++ b/tests/test_utils.h @@ -76,8 +76,10 @@ struct Debug std::string TEST_EXPORT resourcePath(const std::string &resourceRelativePath = ""); std::string TEST_EXPORT fileContents(const std::string &fileName); void TEST_EXPORT printIssues(const libcellml::LoggerPtr &l, bool headings = false, bool causes = false, bool rule = false); -void TEST_EXPORT printComponent(const libcellml::ComponentPtr &component, size_t const c, std::string const spacer); -void TEST_EXPORT printModel(const libcellml::ModelPtr &model); + +void TEST_EXPORT printModel(const libcellml::ModelPtr &model, bool includeMaths = true); +void TEST_EXPORT printComponent(const libcellml::ComponentPtr &component, bool includeMaths = true); + void TEST_EXPORT expectEqualIssues(const std::vector &issues, const libcellml::LoggerPtr &logger); void TEST_EXPORT expectEqualIssuesSpecificationHeadings(const std::vector &issues, const std::vector &specificationHeadings, diff --git a/tests/variable/variable.cpp b/tests/variable/variable.cpp index b9b5380d28..447d10605a 100644 --- a/tests/variable/variable.cpp +++ b/tests/variable/variable.cpp @@ -1700,6 +1700,39 @@ TEST(Variable, variableInterfaceDontDowngrade) EXPECT_EQ("public", v4->interfaceType()); } +TEST(Variable, connectionsPersistAfterImporting) +{ + auto model = libcellml::Model::create("model"); + auto importer = libcellml::Importer::create(); + + auto importedComponent = libcellml::Component::create("importedComponent"); + model->addComponent(importedComponent); + + auto importSource = libcellml::ImportSource::create(); + importSource->setUrl("importedModelWithMaps.cellml"); + importedComponent->setImportSource(importSource); + importedComponent->setImportReference("importMe"); + + EXPECT_TRUE(model->hasUnresolvedImports()); + importer->resolveImports(model, resourcePath()); + EXPECT_FALSE(model->hasUnresolvedImports()); + + model = importer->flattenModel(model); + + EXPECT_NE(nullptr, model->component("importedComponent")); + EXPECT_NE(nullptr, model->component("importedComponent")->component("child1")); + EXPECT_NE(nullptr, model->component("importedComponent")->component("child2")); + EXPECT_NE(nullptr, model->component("importedComponent")->component("child1")->variable("x")); + EXPECT_NE(nullptr, model->component("importedComponent")->component("child2")->variable("x")); + + auto x1 = model->component("importedComponent")->component("child1")->variable("x"); + auto x2 = model->component("importedComponent")->component("child2")->variable("x"); + EXPECT_EQ(size_t(1), x1->equivalentVariableCount()); + EXPECT_EQ(size_t(1), x2->equivalentVariableCount()); + EXPECT_EQ(x1, x2->equivalentVariable(0)); + EXPECT_EQ(x2, x1->equivalentVariable(0)); +} + TEST(Variable, addVariableDuplicates) { auto model = libcellml::Model::create("model");