From b900b048ac5cc70687e9c3aa3635c92ca408e6b1 Mon Sep 17 00:00:00 2001 From: Alan Garny Date: Mon, 19 Oct 2026 11:54:56 +1300 Subject: [PATCH 1/2] New version. --- VERSION.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/VERSION.txt b/VERSION.txt index cac73a68b..1c1da8189 100644 --- a/VERSION.txt +++ b/VERSION.txt @@ -1 +1 @@ -1.20261016.1 +1.20261019.0 From 34fa053a3820c01888abdd7e6660c1e72bda2083 Mon Sep 17 00:00:00 2001 From: Alan Garny Date: Mon, 19 Oct 2026 11:54:47 +1300 Subject: [PATCH 2/2] SedModel: fixed an issue with a model of unknown type. Such a model can be either empty or have issues with imports. As a result of fixing this, we also indirectly fixed an issue with instantiating a SED-ML document (with an empty model) from JavaScript. --- src/sed/sedmodel.cpp | 19 +++++++++--- tests/api/sed/instancetests.cpp | 26 +++++++++++++++++ .../bindings/javascript/sed.instance.test.js | 26 +++++++++++++++++ tests/bindings/python/test_sed_instance.py | 29 +++++++++++++++++++ tests/res/api/sed/empty.cellml | 2 ++ 5 files changed, 98 insertions(+), 4 deletions(-) create mode 100644 tests/res/api/sed/empty.cellml diff --git a/src/sed/sedmodel.cpp b/src/sed/sedmodel.cpp index 48571bd7c..195b84566 100644 --- a/src/sed/sedmodel.cpp +++ b/src/sed/sedmodel.cpp @@ -47,7 +47,19 @@ bool SedModel::Impl::isValid() // Make sure that the CellML file is valid. - switch (mFile->pimpl()->mCellmlFile->type()) { + const auto &cellmlFile {mFile->pimpl()->mCellmlFile}; + + switch (cellmlFile->type()) { + case libcellml::AnalyserModel::Type::UNKNOWN: + // The model is unknown which either means that it doesn't contain any variables (i.e. it is empty as far as we + // are concerned) or its imports couldn't be resolved (in which case the CellML file's issues already tell us + // what is wrong). + + if (!cellmlFile->hasErrors()) { + addError("The CellML file is empty."); + } + + break; case libcellml::AnalyserModel::Type::INVALID: addError("The CellML file is invalid."); @@ -64,15 +76,14 @@ bool SedModel::Impl::isValid() addError("The CellML file is unsuitably constrained."); break; - default: // libcellml::AnalyserModel::Type::UNKNOWN (we should never get this type), - // libcellml::AnalyserModel::Type::ALGEBRAIC, + default: // libcellml::AnalyserModel::Type::ALGEBRAIC, // libcellml::AnalyserModel::Type::DAE, // libcellml::AnalyserModel::Type::NLA, or // libcellml::AnalyserModel::Type::ODE. break; } - addIssues(mFile->pimpl()->mCellmlFile, "CellML"); + addIssues(cellmlFile, "CellML"); return !hasErrors(); } diff --git a/tests/api/sed/instancetests.cpp b/tests/api/sed/instancetests.cpp index 302031829..4e1c84479 100644 --- a/tests/api/sed/instancetests.cpp +++ b/tests/api/sed/instancetests.cpp @@ -44,6 +44,32 @@ TEST(InstanceSedTest, noFile) EXPECT_EQ_ISSUES(instance, EXPECTED_ISSUES); } +TEST(InstanceSedTest, emptyModel) +{ + static const libOpenCOR::ExpectedIssues EXPECTED_ISSUES {{ + {libOpenCOR::Issue::Type::ERROR, "Task | Model: the CellML file is empty."}, + }}; + + auto file {libOpenCOR::File::create(libOpenCOR::resourcePath("api/sed/empty.cellml"))}; + auto document {libOpenCOR::SedDocument::create(file)}; + auto instance {document->instantiate()}; + + EXPECT_EQ_ISSUES(instance, EXPECTED_ISSUES); +} + +TEST(InstanceSedTest, cellmlFileWithInvalidImports) +{ + static const std::string IMPORTER_ERROR_START {"Task | Model | CellML | Importer: "}; + + auto file {libOpenCOR::File::create(libOpenCOR::resourcePath("support/cellml/model_with_invalid_imports.cellml"))}; + auto document {libOpenCOR::SedDocument::create(file)}; + auto instance {document->instantiate()}; + + GTEST_ASSERT_EQ(instance->issueCount(), 1U); + EXPECT_EQ(instance->issue(0)->type(), libOpenCOR::Issue::Type::ERROR); + EXPECT_EQ(instance->issue(0)->description().substr(0, IMPORTER_ERROR_START.size()), IMPORTER_ERROR_START); +} + TEST(InstanceSedTest, invalidCellmlFile) { static const libOpenCOR::ExpectedIssues EXPECTED_ISSUES {{ diff --git a/tests/bindings/javascript/sed.instance.test.js b/tests/bindings/javascript/sed.instance.test.js index b47a5c854..1111840f9 100644 --- a/tests/bindings/javascript/sed.instance.test.js +++ b/tests/bindings/javascript/sed.instance.test.js @@ -50,6 +50,32 @@ test.describe('Sed instance tests', () => { ]); }); + test('Empty model', () => { + const file = new loc.File(utils.resourcePath('api/sed/empty.cellml')); + + file.setContents(utils.fileContents(file.path)); + + const document = new loc.SedDocument(file); + const instance = document.instantiate(); + + assertIssues(loc, instance, [[loc.Issue.Type.ERROR, 'Task | Model: the CellML file is empty.']]); + }); + + test('CellML file with invalid imports', () => { + const IMPORTER_ERROR_START = 'Task | Model | CellML | Importer: '; + + const file = new loc.File(utils.resourcePath('support/cellml/model_with_invalid_imports.cellml')); + + file.setContents(utils.fileContents(file.path)); + + const document = new loc.SedDocument(file); + const instance = document.instantiate(); + + assert.strictEqual(instance.issueCount, 1); + assert.strictEqual(instance.issue(0).type.value, loc.Issue.Type.ERROR.value); + assert.strictEqual(instance.issue(0).description.startsWith(IMPORTER_ERROR_START), true); + }); + test('Invalid CellML file', () => { const file = new loc.File(utils.resourcePath('error.cellml')); diff --git a/tests/bindings/python/test_sed_instance.py b/tests/bindings/python/test_sed_instance.py index f189871cf..746a5e12d 100644 --- a/tests/bindings/python/test_sed_instance.py +++ b/tests/bindings/python/test_sed_instance.py @@ -40,6 +40,35 @@ def test_no_file(): assert_issues(instance, expected_issues) +def test_empty_model(): + expected_issues = [ + [ + loc.Issue.Type.Error, + "Task | Model: the CellML file is empty.", + ], + ] + + file = loc.File(utils.resource_path("api/sed/empty.cellml")) + document = loc.SedDocument(file) + instance = document.instantiate() + + assert_issues(instance, expected_issues) + + +def test_cellml_file_with_invalid_imports(): + importer_error_start = "Task | Model | CellML | Importer: " + + file = loc.File( + utils.resource_path("support/cellml/model_with_invalid_imports.cellml") + ) + document = loc.SedDocument(file) + instance = document.instantiate() + + assert instance.issue_count == 1 + assert instance.issues[0].type == loc.Issue.Type.Error + assert instance.issues[0].description.startswith(importer_error_start) + + def test_invalid_cellml_file(): expected_issues = [ [ diff --git a/tests/res/api/sed/empty.cellml b/tests/res/api/sed/empty.cellml new file mode 100644 index 000000000..f589f84be --- /dev/null +++ b/tests/res/api/sed/empty.cellml @@ -0,0 +1,2 @@ + +