diff --git a/code/+openminds/+introspection/+internal/@MetaTypeRegistry/MetaTypeRegistry.m b/code/+openminds/+introspection/+internal/@MetaTypeRegistry/MetaTypeRegistry.m index 429cd1f55..17192dbd1 100644 --- a/code/+openminds/+introspection/+internal/@MetaTypeRegistry/MetaTypeRegistry.m +++ b/code/+openminds/+introspection/+internal/@MetaTypeRegistry/MetaTypeRegistry.m @@ -47,6 +47,29 @@ % getSingleton - Method for retrieving singleton object % Defined in separate file in class folder singletonObject = getSingleton(options) + + function notifyModelVersionChanged() + % notifyModelVersionChanged - Release the registry for a change of model version + % + % Syntax: + % openminds.introspection.internal.MetaTypeRegistry.notifyModelVersionChanged() + % + % The registry holds enumeration values (openminds.enum.Types) + % of the model version it was built for, and a registry left in + % place across a version switch keeps the previous version's + % types in memory. This method deletes the registry; it is + % rebuilt on its next use. It is called from + % openminds.selectModelVersion. + % + % See also openminds.selectModelVersion + + singletonObject = getappdata(0, ... + openminds.introspection.internal.MetaTypeRegistry.SINGLETON_NAME); + + if ~isempty(singletonObject) && isvalid(singletonObject) + delete(singletonObject) + end + end end methods (Access = private) diff --git a/code/+openminds/selectModelVersion.m b/code/+openminds/selectModelVersion.m index 12abad5ca..e7ce8332d 100644 --- a/code/+openminds/selectModelVersion.m +++ b/code/+openminds/selectModelVersion.m @@ -73,6 +73,13 @@ function selectModelVersion(versionNumber) % enumerations and the controlled term base class generated for it. addpath(genpath( fullfile(generatedFolder, versionAsString) )) + % MATLAB reads the classes of the selected version only once nothing + % holds those of the previous one. The type registry holds enumeration + % values of the version it was built for, so it is released here: before + % the pause that lets the reload happen, and before the instance library + % resolves types against the reloaded classes. Its next use rebuilds it. + openminds.introspection.internal.MetaTypeRegistry.notifyModelVersionChanged() + % Add a second pause for changes to take effect. pause(1) % Ad hoc value. Usually at least 0.3 - 0.4 seconds is necessary diff --git a/tools/tests/unitTests/InstanceLibraryIntegrationTest.m b/tools/tests/unitTests/InstanceLibraryIntegrationTest.m index 87f3c5a89..a8f3b8de3 100644 --- a/tools/tests/unitTests/InstanceLibraryIntegrationTest.m +++ b/tools/tests/unitTests/InstanceLibraryIntegrationTest.m @@ -90,15 +90,19 @@ function testLibraryVersionFollowsTheModelVersion(testCase) 'The instance library holds no instances for this version.') % Check that the instances were read from the v3.0 folder, not - % merely labelled v3.0. Whether every instance then resolves to - % a type depends on the model classes having been reloaded, - % which MATLAB cannot do while objects of those classes exist - % in the session, so that is not checked here. + % merely labelled v3.0. readFromVersion = contains(library.InstanceTable.Filepath, ... fullfile(filesep, "v3.0", filesep)); testCase.verifyTrue(all(readFromVersion), ... 'The library must read the instances of the selected version.') + + % The instances must also be typed against that version's + % classes. Selecting a version releases the type registry, so + % the classes reload and every instance resolves to a type. + testCase.verifyEmpty( ... + library.InstanceTable(library.InstanceTable.Type == "", :), ... + 'Every instance of the selected version must resolve to a type.') end function testModelVersionWithoutInstancesIsReported(testCase) diff --git a/tools/tests/unitTests/SelectModelVersionTest.m b/tools/tests/unitTests/SelectModelVersionTest.m new file mode 100644 index 000000000..ec42f4fd5 --- /dev/null +++ b/tools/tests/unitTests/SelectModelVersionTest.m @@ -0,0 +1,49 @@ +classdef SelectModelVersionTest < matlab.unittest.TestCase +% SelectModelVersionTest - Tests for selecting a model version within a session +% +% A model version is selected by putting its generated classes on the +% search path. MATLAB reads a class definition from the path again only +% once nothing holds an instance of it, so anything the toolbox itself +% keeps across a switch pins the classes of the previous version. + + methods (Test) + function testTheTypeRegistryDoesNotPinThePreviousVersion(testCase) + % The type registry holds enumeration values of the version it was + % built for. Left in place across a switch, it kept the previous + % version's Types enumeration in memory, and the instance library + % then resolved the selected version's instances against it: every + % type the two versions do not share came out untyped. + % + % The two versions are numbered ones, which are pinned to a schema + % commit and do not change, unlike latest. They are chosen for + % what tells them apart: v5.0 declares AnatomicalAtlas, which v3.0 + % calls BrainAtlas. The first is selected here rather than assumed, + % so that the test does not pass for nothing in a session that is + % on v3.0 or v4.0 already. + + import ommtest.helper.ModelVersionFixture + + testCase.applyFixture(ModelVersionFixture("v5.0")) + testCase.assertTrue(ismember("AnatomicalAtlas", typesInMemory()), ... + 'v5.0 is pinned and declares AnatomicalAtlas; something changed it.') + + % Held, as a session that has looked any type up holds it. + openminds.introspection.internal.MetaTypeRegistry.getSingleton(); + + testCase.applyFixture(ModelVersionFixture("v3.0")) + + membersInMemory = typesInMemory(); + + testCase.verifyTrue(ismember("BrainAtlas", membersInMemory), ... + 'The Types enumeration in memory must be the selected version''s.') + testCase.verifyFalse(ismember("AnatomicalAtlas", membersInMemory), ... + 'The previous version''s Types enumeration must not stay in memory.') + end + end +end + +function names = typesInMemory() +% typesInMemory - The Types enumeration as loaded, not as on the path + names = string({ ... + meta.class.fromName("openminds.enum.Types").EnumerationMemberList.Name}); +end