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");