From 3b3b0f238be831cfdf7a194d112e5a7ca0376edf Mon Sep 17 00:00:00 2001
From: LunarCapsule127 <287847627+LunarCapsule127@users.noreply.github.com>
Date: Thu, 29 Oct 2026 10:15:24 +0000
Subject: [PATCH] Add tests for real number handling holes.
Addresses issue #1505.
Add parser and validator tests describing the expected behavior for
real numbers entered through unit exponent and multiplier attributes,
variable initial values, MathML cn elements, and cn elements inside
degree and logbase elements:
- a leading '+' sign is valid (CellML 2.0 section 1.3.3.2);
- the degenerate strings '-.', '-' and '.' are invalid;
- empty cn elements are invalid.
These tests are expected to fail until the implementation is updated.
---
tests/parser/parser.cpp | 60 +++++
tests/validator/validator.cpp | 403 ++++++++++++++++++++++++++++++++++
2 files changed, 463 insertions(+)
diff --git a/tests/parser/parser.cpp b/tests/parser/parser.cpp
index 531cb3c67c..319182a84a 100644
--- a/tests/parser/parser.cpp
+++ b/tests/parser/parser.cpp
@@ -682,6 +682,66 @@ TEST(Parser, modelWithInvalidUnits)
EXPECT_EQ(e, a);
}
+TEST(Parser, unitAttributesWithLeadingPlusSign)
+{
+ // CellML 2.0 section 1.3.3.2: a real number string may begin with a '+' sign.
+ const std::string in =
+ "\n"
+ "\n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ "\n";
+
+ libcellml::ParserPtr parser = libcellml::Parser::create();
+ libcellml::ModelPtr model = parser->parseModel(in);
+
+ EXPECT_EQ(size_t(0), parser->issueCount());
+
+ EXPECT_EQ(2.0, model->units("millivolt_squared")->unitAttributeExponent(0));
+ EXPECT_EQ(1.5, model->units("scaled_volt")->unitAttributeMultiplier(0));
+ EXPECT_EQ(10.0, model->units("scaled_e_notation_volt")->unitAttributeExponent(0));
+ EXPECT_EQ(2.5e-3, model->units("scaled_e_notation_volt")->unitAttributeMultiplier(0));
+}
+
+TEST(Parser, unitAttributesWithDegenerateRealNumbers)
+{
+ const std::string in =
+ "\n"
+ "\n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ "\n";
+
+ const std::vector expectedIssues = {
+ "Unit referencing 'volt' in units 'degenerate_exponents' has an exponent with the value '-.' that is not a representation of a CellML real valued number.",
+ "Unit referencing 'ampere' in units 'degenerate_exponents' has an exponent with the value '-' that is not a representation of a CellML real valued number.",
+ "Unit referencing 'second' in units 'degenerate_exponents' has an exponent with the value '.' that is not a representation of a CellML real valued number.",
+ "Unit referencing 'volt' in units 'degenerate_multipliers' has a multiplier with the value '-.' that is not a representation of a CellML real valued number.",
+ "Unit referencing 'ampere' in units 'degenerate_multipliers' has a multiplier with the value '-' that is not a representation of a CellML real valued number.",
+ "Unit referencing 'second' in units 'degenerate_multipliers' has a multiplier with the value '.' that is not a representation of a CellML real valued number.",
+ };
+
+ libcellml::ParserPtr parser = libcellml::Parser::create();
+ parser->parseModel(in);
+
+ EXPECT_EQ_ISSUES(expectedIssues, parser);
+}
+
TEST(Parser, emptyEncapsulation)
{
const std::string in =
diff --git a/tests/validator/validator.cpp b/tests/validator/validator.cpp
index b3527f98db..149d37efcc 100644
--- a/tests/validator/validator.cpp
+++ b/tests/validator/validator.cpp
@@ -294,6 +294,73 @@ TEST(Validator, invalidVariableInitialValuesAndInterfaces)
EXPECT_EQ_ISSUES(expectedIssues, validator);
}
+TEST(Validator, variableInitialValuesWithLeadingPlusSign)
+{
+ // CellML 2.0 section 1.3.3.2: a real number string may begin with a '+' sign.
+ libcellml::ValidatorPtr validator = libcellml::Validator::create();
+ libcellml::ModelPtr model = libcellml::Model::create();
+ libcellml::ComponentPtr c1 = libcellml::Component::create();
+ libcellml::VariablePtr v1 = libcellml::Variable::create();
+ libcellml::VariablePtr v2 = libcellml::Variable::create();
+ libcellml::VariablePtr v3 = libcellml::Variable::create();
+ model->addComponent(c1);
+ c1->addVariable(v1);
+ c1->addVariable(v2);
+ c1->addVariable(v3);
+
+ model->setName("model");
+ c1->setName("component");
+ v1->setName("v1");
+ v1->setUnits("dimensionless");
+ v1->setInitialValue("+2");
+ v2->setName("v2");
+ v2->setUnits("dimensionless");
+ v2->setInitialValue("+1.5");
+ v3->setName("v3");
+ v3->setUnits("dimensionless");
+ v3->setInitialValue("+1.5e+3");
+
+ validator->validateModel(model);
+
+ EXPECT_EQ(size_t(0), validator->issueCount());
+}
+
+TEST(Validator, variableInitialValuesWithDegenerateRealNumbers)
+{
+ const std::vector expectedIssues = {
+ "Variable 'v1' in component 'component' has an invalid initial value '-.'. Initial values must be a real number string or a variable reference.",
+ "Variable 'v2' in component 'component' has an invalid initial value '-'. Initial values must be a real number string or a variable reference.",
+ "Variable 'v3' in component 'component' has an invalid initial value '.'. Initial values must be a real number string or a variable reference.",
+ };
+
+ libcellml::ValidatorPtr validator = libcellml::Validator::create();
+ libcellml::ModelPtr model = libcellml::Model::create();
+ libcellml::ComponentPtr c1 = libcellml::Component::create();
+ libcellml::VariablePtr v1 = libcellml::Variable::create();
+ libcellml::VariablePtr v2 = libcellml::Variable::create();
+ libcellml::VariablePtr v3 = libcellml::Variable::create();
+ model->addComponent(c1);
+ c1->addVariable(v1);
+ c1->addVariable(v2);
+ c1->addVariable(v3);
+
+ model->setName("model");
+ c1->setName("component");
+ v1->setName("v1");
+ v1->setUnits("dimensionless");
+ v1->setInitialValue("-.");
+ v2->setName("v2");
+ v2->setUnits("dimensionless");
+ v2->setInitialValue("-");
+ v3->setName("v3");
+ v3->setUnits("dimensionless");
+ v3->setInitialValue(".");
+
+ validator->validateModel(model);
+
+ EXPECT_EQ_ISSUES(expectedIssues, validator);
+}
+
libcellml::ModelPtr createImportUnitsModel()
{
libcellml::ModelPtr m = libcellml::Model::create();
@@ -963,6 +1030,342 @@ TEST(Validator, invalidMathMLCiAndCnElementsWithCellMLUnits)
EXPECT_EQ_ISSUES(expectedIssues, v);
}
+libcellml::ModelPtr createModelWithMath(const std::string &math)
+{
+ libcellml::ModelPtr m = libcellml::Model::create();
+ libcellml::ComponentPtr c = libcellml::Component::create();
+ libcellml::VariablePtr v1 = libcellml::Variable::create();
+ libcellml::VariablePtr v2 = libcellml::Variable::create();
+ libcellml::VariablePtr v3 = libcellml::Variable::create();
+
+ m->setName("modelName");
+ c->setName("componentName");
+ v1->setName("A");
+ v2->setName("B");
+ v3->setName("t");
+ v1->setUnits("dimensionless");
+ v2->setUnits("dimensionless");
+ v3->setUnits("dimensionless");
+
+ c->addVariable(v1);
+ c->addVariable(v2);
+ c->addVariable(v3);
+ c->setMath(math);
+ m->addComponent(c);
+
+ return m;
+}
+
+TEST(Validator, mathCnElementsWithLeadingPlusSign)
+{
+ // CellML 2.0 section 1.3.3.2: a real number string may begin with a '+' sign.
+ const std::string math =
+ "\n";
+
+ libcellml::ValidatorPtr v = libcellml::Validator::create();
+ libcellml::ModelPtr m = createModelWithMath(math);
+
+ v->validateModel(m);
+
+ EXPECT_EQ(size_t(0), v->issueCount());
+}
+
+TEST(Validator, mathCnElementsWithDegenerateRealNumbers)
+{
+ const std::string math =
+ "\n";
+ const std::vector expectedIssues = {
+ "Math has a 'cn' element of 'real' type with no valid text node (representing a basic number) as a child.",
+ "Math has a 'cn' element of 'real' type with no valid text node (representing a basic number) as a child.",
+ "Math has a 'cn' element of 'real' type with no valid text node (representing a basic number) as a child.",
+ "Math has a 'cn' element of 'e-notation' type with no valid text node (representing a basic number), no 'sep' element, and/or no valid text node (representing an integer) as children.",
+ "Math has a 'cn' element of 'e-notation' type with no valid text node (representing a basic number), no 'sep' element, and/or no valid text node (representing an integer) as children.",
+ "Math has a 'cn' element of 'e-notation' type with no valid text node (representing a basic number), no 'sep' element, and/or no valid text node (representing an integer) as children.",
+ };
+
+ libcellml::ValidatorPtr v = libcellml::Validator::create();
+ libcellml::ModelPtr m = createModelWithMath(math);
+
+ v->validateModel(m);
+
+ EXPECT_EQ_ISSUES(expectedIssues, v);
+}
+
+TEST(Validator, mathEmptyCnElements)
+{
+ const std::string math =
+ "\n";
+ const std::vector expectedIssues = {
+ "Math has a 'cn' element of 'real' type with no valid text node (representing a basic number) as a child.",
+ "Math has a 'cn' element of 'real' type with no valid text node (representing a basic number) as a child.",
+ };
+
+ libcellml::ValidatorPtr v = libcellml::Validator::create();
+ libcellml::ModelPtr m = createModelWithMath(math);
+
+ v->validateModel(m);
+
+ EXPECT_EQ_ISSUES(expectedIssues, v);
+}
+
+TEST(Validator, mathDegreeElementsWithLeadingPlusSign)
+{
+ // CellML 2.0 section 1.3.3.2: a real number string may begin with a '+' sign.
+ const std::string math =
+ "\n";
+
+ libcellml::ValidatorPtr v = libcellml::Validator::create();
+ libcellml::ModelPtr m = createModelWithMath(math);
+
+ v->validateModel(m);
+
+ EXPECT_EQ(size_t(0), v->issueCount());
+}
+
+TEST(Validator, mathDegreeElementsWithDegenerateRealNumbers)
+{
+ const std::string math =
+ "\n";
+ const std::vector expectedIssues = {
+ "Math has a 'cn' element of 'real' type with no valid text node (representing a basic number) as a child.",
+ "Math has a 'cn' element of 'real' type with no valid text node (representing a basic number) as a child.",
+ "Math has a 'cn' element of 'real' type with no valid text node (representing a basic number) as a child.",
+ };
+
+ libcellml::ValidatorPtr v = libcellml::Validator::create();
+ libcellml::ModelPtr m = createModelWithMath(math);
+
+ v->validateModel(m);
+
+ EXPECT_EQ_ISSUES(expectedIssues, v);
+}
+
+TEST(Validator, mathDegreeElementsWithEmptyCnElements)
+{
+ const std::string math =
+ "\n";
+ const std::vector expectedIssues = {
+ "Math has a 'cn' element of 'real' type with no valid text node (representing a basic number) as a child.",
+ "Math has a 'cn' element of 'real' type with no valid text node (representing a basic number) as a child.",
+ };
+
+ libcellml::ValidatorPtr v = libcellml::Validator::create();
+ libcellml::ModelPtr m = createModelWithMath(math);
+
+ v->validateModel(m);
+
+ EXPECT_EQ_ISSUES(expectedIssues, v);
+}
+
+TEST(Validator, mathLogbaseElementsWithDegenerateRealNumbers)
+{
+ const std::string math =
+ "\n";
+ const std::vector expectedIssues = {
+ "Math has a 'cn' element of 'real' type with no valid text node (representing a basic number) as a child.",
+ "Math has a 'cn' element of 'real' type with no valid text node (representing a basic number) as a child.",
+ "Math has a 'cn' element of 'real' type with no valid text node (representing a basic number) as a child.",
+ };
+
+ libcellml::ValidatorPtr v = libcellml::Validator::create();
+ libcellml::ModelPtr m = createModelWithMath(math);
+
+ v->validateModel(m);
+
+ EXPECT_EQ_ISSUES(expectedIssues, v);
+}
+
+TEST(Validator, mathLogbaseElementsWithEmptyCnElements)
+{
+ const std::string math =
+ "\n";
+ const std::vector expectedIssues = {
+ "Math has a 'cn' element of 'real' type with no valid text node (representing a basic number) as a child.",
+ "Math has a 'cn' element of 'real' type with no valid text node (representing a basic number) as a child.",
+ };
+
+ libcellml::ValidatorPtr v = libcellml::Validator::create();
+ libcellml::ModelPtr m = createModelWithMath(math);
+
+ v->validateModel(m);
+
+ EXPECT_EQ_ISSUES(expectedIssues, v);
+}
+
TEST(Validator, validMathMLCiAndCnElementsWithCellMLUnits)
{
const std::string math =