From 46fb12f14b37e4fec5438e3c327320e882040418 Mon Sep 17 00:00:00 2001 From: ehennestad Date: Fri, 19 Jun 2026 11:31:29 +0200 Subject: [PATCH 01/12] Improve type correction for datetime - A scalar datetime was wrapped in a cell. This is an unnecessary mutation which is removed here - A cell array of datetimes where all values either have TimeZone info or do not, is now output as a datetime array. --- +types/+util/correctType.m | 40 ++++++++++++++++++++++++-------------- 1 file changed, 25 insertions(+), 15 deletions(-) diff --git a/+types/+util/correctType.m b/+types/+util/correctType.m index 632b3e0fc..4f1fa3676 100644 --- a/+types/+util/correctType.m +++ b/+types/+util/correctType.m @@ -18,27 +18,37 @@ errorMessage ... ); case 'datetime' - isCellString = iscellstr(val) || (iscell(val) && all(cellfun('isclass', val, 'string'))); - isCellDatetime = iscell(val) && all(cellfun('isclass', val, 'datetime')); - isHeterogeneousCell = isCellString || isCellDatetime; - assert(ischar(val) || isdatetime(val) || isstring(val) || isHeterogeneousCell, ... + isCellString = iscellstr(val) || (iscell(val) && all(cellfun('isclass', val, 'string'))); %#ok + isCellDatetime = iscell(val) && all(cellfun('isclass', val, 'datetime')); + + isValid = ischar(val) || isdatetime(val) || isstring(val) || isCellString || isCellDatetime; + + assert(isValid, ... errorId, sprintf(errorTemplate, 'value is not a timestamp or datetime object')); % convert strings to datetimes if ischar(val) || isstring(val) || isCellString val = formatDatetime(io.timestamp2datetime(val)); - return; - end - if isdatetime(val) - val = num2cell(val); - end - - % set format depending on default values. - for iDatetime = 1:length(val) - % note, must be a for loop since datetimes with/without timezones cannot be - % concatenated. - val{iDatetime} = formatDatetime(val{iDatetime}); + elseif isdatetime(val) && isscalar(val) + val = formatDatetime(val); + else % datetime array or cell array of datetimes values + if isdatetime(val) + val = arrayfun(@formatDatetime, val); + else + val = cellfun(@formatDatetime, val, 'UniformOutput', false); + try + val = [val{:}]; % Try creating a datetime array + catch exception + if strcmp(exception.identifier, 'MATLAB:datetime:cat:IncompatibleTZ') + % Cells are mixed, some datetime value have + % timezone info, some do not. Keep cell output. + else + rethrow(exception) + end + end + end end + case {'single', 'double', 'int64', 'int32', 'int16', 'int8', 'uint64', ... 'uint32', 'uint16', 'uint8'} errorMessage = sprintf(errorTemplate ... From 6513c2b0b8b71eb3dc990354da93d957e62ac3ee Mon Sep 17 00:00:00 2001 From: ehennestad Date: Fri, 19 Jun 2026 11:43:55 +0200 Subject: [PATCH 02/12] Undo parts of last commit: cell of datetime preserved Undo non-scalar cell array to datetime array. Mutating outputs should be minimised --- +types/+util/correctType.m | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/+types/+util/correctType.m b/+types/+util/correctType.m index 4f1fa3676..061d7b428 100644 --- a/+types/+util/correctType.m +++ b/+types/+util/correctType.m @@ -36,16 +36,6 @@ val = arrayfun(@formatDatetime, val); else val = cellfun(@formatDatetime, val, 'UniformOutput', false); - try - val = [val{:}]; % Try creating a datetime array - catch exception - if strcmp(exception.identifier, 'MATLAB:datetime:cat:IncompatibleTZ') - % Cells are mixed, some datetime value have - % timezone info, some do not. Keep cell output. - else - rethrow(exception) - end - end end end From f953063e187b72fa1601e45b9498c2c5f1655a2f Mon Sep 17 00:00:00 2001 From: ehennestad Date: Fri, 19 Jun 2026 17:02:50 +0200 Subject: [PATCH 03/12] Fix broken test Add test ensuring scalar dataset is NOT read as datastub Add test checking that scalar datetime is exported to a scalar dataspace --- +tests/+unit/+io/+backend/HDF5ReaderTest.m | 4 ++-- +tests/+unit/nwbExportTest.m | 10 ++++++++++ 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/+tests/+unit/+io/+backend/HDF5ReaderTest.m b/+tests/+unit/+io/+backend/HDF5ReaderTest.m index f1b0f31e3..65d020002 100644 --- a/+tests/+unit/+io/+backend/HDF5ReaderTest.m +++ b/+tests/+unit/+io/+backend/HDF5ReaderTest.m @@ -19,7 +19,7 @@ function readRootAndSchemaVersion(testCase) testCase.verifyEqual(reader.getSchemaVersion(), util.getSchemaVersion(filename)); end - function readDatasetValueReturnsDataStubForScalarDataset(testCase) + function readDatasetValueReadsScalarDatasetEagerly(testCase) nwb = tests.factory.NWBFile(); filename = "reader-dataset-test.nwb"; nwbExport(nwb, filename); @@ -29,7 +29,7 @@ function readDatasetValueReturnsDataStubForScalarDataset(testCase) datasetInfo = rootInfo.Datasets(strcmp({rootInfo.Datasets.Name}, "session_start_time")); datasetValue = reader.readDatasetValue(datasetInfo, "/session_start_time"); - testCase.verifyClass(datasetValue, "types.untyped.DataStub"); + testCase.verifyFalse(isa(datasetValue, "types.untyped.DataStub")) end end end diff --git a/+tests/+unit/nwbExportTest.m b/+tests/+unit/nwbExportTest.m index a5153939b..773afb218 100644 --- a/+tests/+unit/nwbExportTest.m +++ b/+tests/+unit/nwbExportTest.m @@ -315,6 +315,16 @@ function testExportFileWithStringDataType(testCase) ts.data_unit) end + function testExportWritesScalarDatetimeAsScalarDataset(testCase) + nwb = tests.factory.NWBFile(); + filename = "write_scalar_datetime_test.nwb"; + nwbExport(nwb, filename); + + datasetInfo = h5info(filename, "/session_start_time"); + + testCase.verifyEqual(datasetInfo.Dataspace.Type, 'scalar') + end + function testWasGeneratedByProperty(testCase) nwb = tests.factory.NWBFile(); nwbFilename = testCase.getRandomFilename(); From fa6829fbe1736cbe1d0ac68344256a6dc8175319 Mon Sep 17 00:00:00 2001 From: ehennestad Date: Thu, 18 Jun 2026 14:44:45 +0200 Subject: [PATCH 04/12] Add method to validate properties on export plus tests --- .../+doubles/TypeWithFailingValidator.m | 35 +++++++++++++ .../+types/MetaClassValidatePropertiesTest.m | 52 +++++++++++++++++++ +types/+untyped/MetaClass.m | 38 ++++++++++++++ 3 files changed, 125 insertions(+) create mode 100644 +tests/+unit/+types/+doubles/TypeWithFailingValidator.m create mode 100644 +tests/+unit/+types/MetaClassValidatePropertiesTest.m diff --git a/+tests/+unit/+types/+doubles/TypeWithFailingValidator.m b/+tests/+unit/+types/+doubles/TypeWithFailingValidator.m new file mode 100644 index 000000000..ce6965263 --- /dev/null +++ b/+tests/+unit/+types/+doubles/TypeWithFailingValidator.m @@ -0,0 +1,35 @@ +classdef TypeWithFailingValidator < types.untyped.MetaClass +% TypeWithFailingValidator - Test double for exercising MetaClass.validateProperties. +% Provides a property whose validator always fails and one whose validator +% always passes, plus a thin wrapper that invokes the protected +% validateProperties method from outside the class hierarchy. + + properties + validProperty + invalidProperty + end + + methods + function value = validate_validProperty(~, value) + % Always valid; returns the value unchanged. + end + + function validate_invalidProperty(~, ~) + error('NWB:Test:InvalidPropertyValue', ... + 'This property value is never valid.') + end + + function runValidateProperties(obj, fullpath) + obj.validateProperties(fullpath) + end + end + + methods (Access = protected) + function str = getFooter(~) + % Override the inherited footer, which inspects required + % properties assuming a `types.` namespace this test double does + % not have. + str = ''; + end + end +end diff --git a/+tests/+unit/+types/MetaClassValidatePropertiesTest.m b/+tests/+unit/+types/MetaClassValidatePropertiesTest.m new file mode 100644 index 000000000..1c897be2d --- /dev/null +++ b/+tests/+unit/+types/MetaClassValidatePropertiesTest.m @@ -0,0 +1,52 @@ +classdef MetaClassValidatePropertiesTest < matlab.unittest.TestCase +% MetaClassValidatePropertiesTest - Unit tests for MetaClass.validateProperties, +% the export-time guard that re-runs property validators so that values which +% bypassed strict validation cannot be written back out to a file. + + methods (Test) + function testInvalidPropertyValueRaisesError(testCase) + testType = tests.unit.types.doubles.TypeWithFailingValidator(); + testType.invalidProperty = 1; + + testCase.verifyError( ... + @() testType.runValidateProperties('/some/path'), ... + 'NWB:Export:InvalidPropertyValue') + end + + function testErrorIncludesPropertyLocationAndCause(testCase) + testType = tests.unit.types.doubles.TypeWithFailingValidator(); + testType.invalidProperty = 1; + + try + testType.runValidateProperties('/some/path') + testCase.verifyFail('Expected an error for the invalid property value.') + catch exception + testCase.verifyEqual( ... + exception.identifier, 'NWB:Export:InvalidPropertyValue') + testCase.verifyTrue(contains(exception.message, 'invalidProperty')) + testCase.verifyTrue(contains(exception.message, '/some/path')) + % The original validator error is preserved as a cause. + testCase.verifyNotEmpty(exception.cause) + testCase.verifyEqual( ... + exception.cause{1}.identifier, 'NWB:Test:InvalidPropertyValue') + end + end + + function testEmptyPropertyIsNotValidated(testCase) + % An unset (empty) property is skipped even though its validator + % would fail, because empty optional properties are not exported. + testType = tests.unit.types.doubles.TypeWithFailingValidator(); + + testCase.verifyWarningFree( ... + @() testType.runValidateProperties('/some/path')) + end + + function testValidPropertyValuePasses(testCase) + testType = tests.unit.types.doubles.TypeWithFailingValidator(); + testType.validProperty = 42; + + testCase.verifyWarningFree( ... + @() testType.runValidateProperties('/some/path')) + end + end +end diff --git a/+types/+untyped/MetaClass.m b/+types/+untyped/MetaClass.m index a8e8959e4..5b7392b25 100644 --- a/+types/+untyped/MetaClass.m +++ b/+types/+untyped/MetaClass.m @@ -57,6 +57,7 @@ writer = io.backend.base.Writer.ensure(writer); obj.throwErrorIfCustomConstraintUnfulfilled(fullpath) obj.throwErrorIfMissingRequiredProps(fullpath) + obj.validateProperties(fullpath) obj.metaClass_fullPath = fullpath; %find reference properties propnames = properties(obj); @@ -269,6 +270,43 @@ function throwErrorIfCustomConstraintUnfulfilled(obj, fullpath) class(obj), fullpath, ME.message) end end + + function validateProperties(obj, fullpath) + % validateProperties - Re-run property validators before writing to file. + % Ensures property values that bypassed strict validation (for + % example, values read permissively from a file that does not + % conform to the schema) are not written back out as a new, invalid + % file. Validators run in the default (strict) context here, so a + % schema violation raises an error rather than a warning. + if isempty(fullpath) + fullpath = 'root'; + end + + propertyNames = properties(obj); + for iProperty = 1:numel(propertyNames) + propertyName = propertyNames{iProperty}; + propertyValue = obj.(propertyName); + validatorName = ['validate_' propertyName]; + + % Validate only set properties that have a generated + % validator. An empty value represents an unset optional + % property, which is not written on export. + if ~isempty(propertyValue) && ismethod(obj, validatorName) + try + feval(validatorName, obj, propertyValue); + catch ME + newException = MException( ... + 'NWB:Export:InvalidPropertyValue', ... + ['The value of property "%s" for type "%s" at ', ... + 'file location "%s" is not valid according to ', ... + 'the schema and cannot be exported:\n%s'], ... + propertyName, class(obj), fullpath, ME.message); + newException = newException.addCause(ME); + throw(newException) + end + end + end + end end methods From a01e216c20c116cf9d447122d45905262d83c263 Mon Sep 17 00:00:00 2001 From: Eivind Hennestad Date: Thu, 18 Jun 2026 20:46:50 +0200 Subject: [PATCH 05/12] Ensure strict validation context on export Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --- +types/+untyped/MetaClass.m | 3 +++ 1 file changed, 3 insertions(+) diff --git a/+types/+untyped/MetaClass.m b/+types/+untyped/MetaClass.m index 5b7392b25..c81ba5045 100644 --- a/+types/+untyped/MetaClass.m +++ b/+types/+untyped/MetaClass.m @@ -278,6 +278,9 @@ function validateProperties(obj, fullpath) % conform to the schema) are not written back out as a new, invalid % file. Validators run in the default (strict) context here, so a % schema violation raises an error rather than a warning. + previousValidationContext = types.util.validationContext('strict'); + cleanupValidationContext = onCleanup(@() types.util.validationContext(previousValidationContext)); + if isempty(fullpath) fullpath = 'root'; end From 74b29479d91c62b2db12e3e6deaab1eec8fec9a2 Mon Sep 17 00:00:00 2001 From: Eivind Hennestad Date: Thu, 18 Jun 2026 20:47:07 +0200 Subject: [PATCH 06/12] Potential fix for pull request finding Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --- +types/+untyped/MetaClass.m | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/+types/+untyped/MetaClass.m b/+types/+untyped/MetaClass.m index c81ba5045..913071160 100644 --- a/+types/+untyped/MetaClass.m +++ b/+types/+untyped/MetaClass.m @@ -295,8 +295,24 @@ function validateProperties(obj, fullpath) % validator. An empty value represents an unset optional % property, which is not written on export. if ~isempty(propertyValue) && ismethod(obj, validatorName) + warnState = warning('error', 'NWB:CheckDataType:NeedsManualConversion'); + warnCleanupObj = onCleanup(@() warning(warnState)); try - feval(validatorName, obj, propertyValue); + try + validatedValue = feval(validatorName, obj, propertyValue); + if ~strcmp(class(validatedValue), class(propertyValue)) || ~isequaln(validatedValue, propertyValue) + error('NWB:Export:PropertyValueRequiresNormalization', ... + ['Property "%s" would be modified by its validator. ' ... + 'Assign it via its setter (strict validation) before export.'], ... + propertyName); + end + catch MEValidator + if strcmp(MEValidator.identifier, 'MATLAB:maxlhs') + feval(validatorName, obj, propertyValue); + else + rethrow(MEValidator) + end + end catch ME newException = MException( ... 'NWB:Export:InvalidPropertyValue', ... From 5251ae531f2df4d737ae3171d8266e76e0469cc1 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 18 Jun 2026 19:15:47 +0000 Subject: [PATCH 07/12] Add coercingProperty test double and testCoercingValidatorRaisesError test Co-authored-by: ehennestad <17237719+ehennestad@users.noreply.github.com> --- .../+unit/+types/+doubles/TypeWithFailingValidator.m | 7 +++++++ .../+unit/+types/MetaClassValidatePropertiesTest.m | 12 ++++++++++++ 2 files changed, 19 insertions(+) diff --git a/+tests/+unit/+types/+doubles/TypeWithFailingValidator.m b/+tests/+unit/+types/+doubles/TypeWithFailingValidator.m index ce6965263..bde291c10 100644 --- a/+tests/+unit/+types/+doubles/TypeWithFailingValidator.m +++ b/+tests/+unit/+types/+doubles/TypeWithFailingValidator.m @@ -7,6 +7,7 @@ properties validProperty invalidProperty + coercingProperty end methods @@ -19,6 +20,12 @@ function validate_invalidProperty(~, ~) 'This property value is never valid.') end + function value = validate_coercingProperty(~, value) + % Simulates a validator that coerces the input (e.g., dtype + % conversion). Returns the value as double regardless of input type. + value = double(value); + end + function runValidateProperties(obj, fullpath) obj.validateProperties(fullpath) end diff --git a/+tests/+unit/+types/MetaClassValidatePropertiesTest.m b/+tests/+unit/+types/MetaClassValidatePropertiesTest.m index 1c897be2d..6bd0eeeed 100644 --- a/+tests/+unit/+types/MetaClassValidatePropertiesTest.m +++ b/+tests/+unit/+types/MetaClassValidatePropertiesTest.m @@ -48,5 +48,17 @@ function testValidPropertyValuePasses(testCase) testCase.verifyWarningFree( ... @() testType.runValidateProperties('/some/path')) end + + function testCoercingValidatorRaisesError(testCase) + % A validator that changes (coerces) the value must produce an + % error, because the exported value would differ from what the + % validator accepted. + testType = tests.unit.types.doubles.TypeWithFailingValidator(); + testType.coercingProperty = int32(5); % int32 -> double on validate + + testCase.verifyError( ... + @() testType.runValidateProperties('/some/path'), ... + 'NWB:Export:InvalidPropertyValue') + end end end From 4540a5888b59d7df9f18a43e8a81fa56e20eb32e Mon Sep 17 00:00:00 2001 From: ehennestad Date: Thu, 18 Jun 2026 21:50:03 +0200 Subject: [PATCH 08/12] Update MetaClass.m --- +types/+untyped/MetaClass.m | 2 ++ 1 file changed, 2 insertions(+) diff --git a/+types/+untyped/MetaClass.m b/+types/+untyped/MetaClass.m index 913071160..1afa8df7d 100644 --- a/+types/+untyped/MetaClass.m +++ b/+types/+untyped/MetaClass.m @@ -308,6 +308,8 @@ function validateProperties(obj, fullpath) end catch MEValidator if strcmp(MEValidator.identifier, 'MATLAB:maxlhs') + % Validator does not provide an output. Call + % again without requesting a normalized value. feval(validatorName, obj, propertyValue); else rethrow(MEValidator) From 7582b115a8dba8b153f64cf51629c9f6d90a2116 Mon Sep 17 00:00:00 2001 From: ehennestad Date: Thu, 18 Jun 2026 22:35:01 +0200 Subject: [PATCH 09/12] fix: allow datetime validator normalization on export --- .../+types/+doubles/TypeWithFailingValidator.m | 5 +++++ .../+types/MetaClassValidatePropertiesTest.m | 15 ++++++++++++--- +types/+untyped/MetaClass.m | 4 ++-- 3 files changed, 19 insertions(+), 5 deletions(-) diff --git a/+tests/+unit/+types/+doubles/TypeWithFailingValidator.m b/+tests/+unit/+types/+doubles/TypeWithFailingValidator.m index bde291c10..e6f2c9796 100644 --- a/+tests/+unit/+types/+doubles/TypeWithFailingValidator.m +++ b/+tests/+unit/+types/+doubles/TypeWithFailingValidator.m @@ -8,6 +8,7 @@ validProperty invalidProperty coercingProperty + datetimeProperty end methods @@ -26,6 +27,10 @@ function validate_invalidProperty(~, ~) value = double(value); end + function value = validate_datetimeProperty(~, value) + value = types.util.checkDtype('datetimeProperty', 'datetime', value); + end + function runValidateProperties(obj, fullpath) obj.validateProperties(fullpath) end diff --git a/+tests/+unit/+types/MetaClassValidatePropertiesTest.m b/+tests/+unit/+types/MetaClassValidatePropertiesTest.m index 6bd0eeeed..74aa382ea 100644 --- a/+tests/+unit/+types/MetaClassValidatePropertiesTest.m +++ b/+tests/+unit/+types/MetaClassValidatePropertiesTest.m @@ -50,9 +50,9 @@ function testValidPropertyValuePasses(testCase) end function testCoercingValidatorRaisesError(testCase) - % A validator that changes (coerces) the value must produce an - % error, because the exported value would differ from what the - % validator accepted. + % A validator that changes the MATLAB class must produce an + % error, because the writer would receive a value whose type does + % not match what strict validation accepts. testType = tests.unit.types.doubles.TypeWithFailingValidator(); testType.coercingProperty = int32(5); % int32 -> double on validate @@ -60,5 +60,14 @@ function testCoercingValidatorRaisesError(testCase) @() testType.runValidateProperties('/some/path'), ... 'NWB:Export:InvalidPropertyValue') end + + function testDatetimeFormatterNormalizationPasses(testCase) + testType = tests.unit.types.doubles.TypeWithFailingValidator(); + testType.datetimeProperty = {datetime(2020, 1, 1, ... + 'Format', 'dd-MMM-uuuu HH:mm:ss')}; + + testCase.verifyWarningFree( ... + @() testType.runValidateProperties('/some/path')) + end end end diff --git a/+types/+untyped/MetaClass.m b/+types/+untyped/MetaClass.m index 1afa8df7d..3dacac5d2 100644 --- a/+types/+untyped/MetaClass.m +++ b/+types/+untyped/MetaClass.m @@ -300,9 +300,9 @@ function validateProperties(obj, fullpath) try try validatedValue = feval(validatorName, obj, propertyValue); - if ~strcmp(class(validatedValue), class(propertyValue)) || ~isequaln(validatedValue, propertyValue) + if ~strcmp(class(validatedValue), class(propertyValue)) error('NWB:Export:PropertyValueRequiresNormalization', ... - ['Property "%s" would be modified by its validator. ' ... + ['Property "%s" would be converted by its validator. ' ... 'Assign it via its setter (strict validation) before export.'], ... propertyName); end From a35e1c40a5977f1b1cd92bad82dee971107db589 Mon Sep 17 00:00:00 2001 From: ehennestad Date: Fri, 19 Jun 2026 15:35:02 +0200 Subject: [PATCH 10/12] fix: handle outputless validators during export validation --- +types/+untyped/MetaClass.m | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/+types/+untyped/MetaClass.m b/+types/+untyped/MetaClass.m index 3dacac5d2..d8a47a702 100644 --- a/+types/+untyped/MetaClass.m +++ b/+types/+untyped/MetaClass.m @@ -307,7 +307,8 @@ function validateProperties(obj, fullpath) propertyName); end catch MEValidator - if strcmp(MEValidator.identifier, 'MATLAB:maxlhs') + if any(strcmp(MEValidator.identifier, ... + {'MATLAB:maxlhs', 'MATLAB:TooManyOutputs'})) % Validator does not provide an output. Call % again without requesting a normalized value. feval(validatorName, obj, propertyValue); From 8f162183ea1ae71beec8b3130684de0e9c033f40 Mon Sep 17 00:00:00 2001 From: ehennestad Date: Tue, 30 Jun 2026 17:50:53 +0200 Subject: [PATCH 11/12] Update nwbExportTest.m --- +tests/+unit/nwbExportTest.m | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/+tests/+unit/nwbExportTest.m b/+tests/+unit/nwbExportTest.m index 773afb218..5a64fd465 100644 --- a/+tests/+unit/nwbExportTest.m +++ b/+tests/+unit/nwbExportTest.m @@ -315,14 +315,27 @@ function testExportFileWithStringDataType(testCase) ts.data_unit) end - function testExportWritesScalarDatetimeAsScalarDataset(testCase) + function testExportScalarDatetimeWritesCorrectShape(testCase) + % Verify that in-memory scalar datetime values are exported with + % a shape determined by schema, e.g.: + % session_start_time -> HDF5 scalar dataset + % file_create_date -> HDF5 non-scalar (simple) dataset + nwb = tests.factory.NWBFile(); filename = "write_scalar_datetime_test.nwb"; nwbExport(nwb, filename); - datasetInfo = h5info(filename, "/session_start_time"); + testCase.verifyLength(nwb.session_start_time, 1) + testCase.verifyLength(nwb.file_create_date, 1) + % session_start_time should be scalar + datasetInfo = h5info(filename, "/session_start_time"); testCase.verifyEqual(datasetInfo.Dataspace.Type, 'scalar') + + % file_create_date should be non-scalar + datasetInfo = h5info(filename, "/file_create_date"); + testCase.verifyEqual(datasetInfo.Dataspace.Type, 'simple') + testCase.verifyEqual(datasetInfo.Dataspace.Size, 1) end function testWasGeneratedByProperty(testCase) From 4fe7721a68ff8c1565b141546e2fbf385e8cc13d Mon Sep 17 00:00:00 2001 From: ehennestad Date: Thu, 2 Jul 2026 16:53:36 +0200 Subject: [PATCH 12/12] Update MetaClass.m Update reference to the validation context function (was renamed) --- +types/+untyped/MetaClass.m | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/+types/+untyped/MetaClass.m b/+types/+untyped/MetaClass.m index d8a47a702..1cab590ea 100644 --- a/+types/+untyped/MetaClass.m +++ b/+types/+untyped/MetaClass.m @@ -278,8 +278,8 @@ function validateProperties(obj, fullpath) % conform to the schema) are not written back out as a new, invalid % file. Validators run in the default (strict) context here, so a % schema violation raises an error rather than a warning. - previousValidationContext = types.util.validationContext('strict'); - cleanupValidationContext = onCleanup(@() types.util.validationContext(previousValidationContext)); + previousValidationContext = matnwb.common.validation.internal.context('write'); + cleanupValidationContext = onCleanup(@() matnwb.common.validation.internal.context(previousValidationContext)); if isempty(fullpath) fullpath = 'root';