From 934f350db8649f87389c84b22948a467083ac697 Mon Sep 17 00:00:00 2001 From: Alexander Jones Date: Fri, 17 May 2024 14:49:29 -0500 Subject: [PATCH 1/4] Remove experimental schema loading code Also clean up some tests and add other minor improvements. --- bids/schema.js | 7 +---- common/issues/data.js | 20 -------------- common/schema/config.js | 6 ---- common/schema/loader.js | 60 ++-------------------------------------- common/schema/types.js | 19 ++++--------- converter/index.js | 4 +-- converter/schema.js | 12 -------- tests/event.spec.js | 2 +- tests/schema.spec.js | 18 ++++-------- validator/index.js | 4 +-- validator/schema/init.js | 46 ++++-------------------------- 11 files changed, 23 insertions(+), 175 deletions(-) diff --git a/bids/schema.js b/bids/schema.js index 74482323..489f18ac 100644 --- a/bids/schema.js +++ b/bids/schema.js @@ -31,14 +31,9 @@ export function buildBidsSchemas(dataset, schemaDefinition) { } } -export function validateSchemasSpec(schemasSpec) { - // TODO: implement +function validateSchemasSpec(schemasSpec) { if (schemasSpec instanceof SchemasSpec) { return [schemasSpec, []] - } else if (schemasSpec instanceof Map) { - const newSchemasSpec = new SchemasSpec() - newSchemasSpec.data = schemasSpec - return [newSchemasSpec, []] } else { return [null, [generateIssue('invalidSchemaSpecification', { spec: JSON.stringify(schemasSpec) })]] } diff --git a/common/issues/data.js b/common/issues/data.js index 719b4db1..94d690a0 100644 --- a/common/issues/data.js +++ b/common/issues/data.js @@ -252,26 +252,6 @@ export default { level: 'error', message: stringTemplate`The supplied schema specification is invalid. Specification: ${'spec'}.`, }, - requestedSchemaLoadFailedFallbackUsed: { - hedCode: 'SCHEMA_LOAD_FAILED', - level: 'warning', - message: stringTemplate`The requested schema failed to load. The fallback schema bundled with this validator will be used instead. Specification: ${'spec'}.`, - }, - requestedSchemaLoadFailedNoFallbackUsed: { - hedCode: 'SCHEMA_LOAD_FAILED', - level: 'error', - message: stringTemplate`The requested schema failed to load. The validator did not attempt to load a fallback schema. Specification: ${'spec'}.`, - }, - fallbackSchemaLoadFailed: { - hedCode: 'SCHEMA_LOAD_FAILED', - level: 'error', - message: stringTemplate`The fallback schema bundled with this validator failed to load. No HED validation was performed.`, - }, - noFallbackSchemaForLibrary: { - hedCode: 'SCHEMA_LOAD_FAILED', - level: 'error', - message: stringTemplate`No fallback schema was found for library "${'library'}". No HED validation was performed.`, - }, bundledSchemaLoadFailed: { hedCode: 'SCHEMA_LOAD_FAILED', level: 'error', diff --git a/common/schema/config.js b/common/schema/config.js index 81ef34b3..8a4dc62b 100644 --- a/common/schema/config.js +++ b/common/schema/config.js @@ -9,9 +9,3 @@ export const localSchemaList = new Map([ ['HED_testlib_1.0.2', require('../../data/HED_testlib_1.0.2.xml')], ['HED_testlib_2.0.0', require('../../data/HED_testlib_2.0.0.xml')], ]) - -export const fallbackFilePath = new Map([ - ['', 'data/HED8.2.0.xml'], - ['score', 'data/HED_score_1.1.0.xml'], - ['testlib', 'data/HED_testlib_2.0.0.xml'], -]) diff --git a/common/schema/loader.js b/common/schema/loader.js index 252b68f5..5d9e0cae 100644 --- a/common/schema/loader.js +++ b/common/schema/loader.js @@ -6,71 +6,15 @@ import xml2js from 'xml2js' import * as files from '../../utils/files' import { generateIssue } from '../issues/issues' -import { fallbackFilePath, localSchemaList } from './config' +import { localSchemaList } from './config' /** * Load schema XML data from a schema version or path description. * * @param {SchemaSpec} schemaDef The description of which schema to use. - * @param {boolean} useFallback Whether to use a bundled fallback schema if the requested schema cannot be loaded. - * @param {boolean} reportNoFallbackError Whether to report an error on a failed schema load when no fallback was used. * @returns {Promise|Promise<[object, Issue[]]>} The schema XML data or an error. */ -export async function loadSchema(schemaDef = null, useFallback = true, reportNoFallbackError = true) { - try { - const xmlData = await loadPromise(schemaDef) - if (xmlData === null) { - return Promise.reject([generateIssue('invalidSchemaSpecification', { spec: JSON.stringify(schemaDef) })]) - } - return [xmlData, []] - } catch (issues) { - return loadFallbackSchema(schemaDef, useFallback, reportNoFallbackError, issues) - } -} - -/** - * Load fallback schema XML data from a schema version or path description. - * - * @param {SchemaSpec} schemaDef The description of which schema to use. - * @param {boolean} useFallback Whether to use a bundled fallback schema if the requested schema cannot be loaded. - * @param {boolean} reportNoFallbackError Whether to report an error on a failed schema load when no fallback was used. - * @param {Issue[]} issues Any issues already found. - * @returns {Promise|Promise<[object, Issue[]]>} The fallback schema XML data or an error. - */ -async function loadFallbackSchema(schemaDef, useFallback, reportNoFallbackError, issues) { - if (!useFallback) { - if (reportNoFallbackError) { - issues.push(generateIssue('requestedSchemaLoadFailedNoFallbackUsed', { spec: JSON.stringify(schemaDef) })) - } - throw issues - } - - issues.push(generateIssue('requestedSchemaLoadFailedFallbackUsed', { spec: JSON.stringify(schemaDef) })) - - const fallbackSchemaPath = fallbackFilePath.get(schemaDef.library) - if (fallbackSchemaPath === undefined) { - issues.push(generateIssue('noFallbackSchemaForLibrary', { library: schemaDef.library })) - throw issues - } - - try { - const fallbackXmlData = await loadLocalSchema(fallbackSchemaPath) - return [fallbackXmlData, issues] - } catch (fallbackIssues) { - fallbackIssues.push(generateIssue('fallbackSchemaLoadFailed', {})) - throw issues.concat(fallbackIssues) - } -} - -/** - * Load schema XML data from a schema version or path description. - * - * @todo Rename to {@link loadSchema} in 4.0.0. - * - * @param {SchemaSpec} schemaDef The description of which schema to use. - * @returns {Promise|Promise<[object, Issue[]]>} The schema XML data or an error. - */ -export async function loadSchemaFromSpec(schemaDef = null) { +export default async function loadSchema(schemaDef = null) { const xmlData = await loadPromise(schemaDef) if (xmlData === null) { throw [generateIssue('invalidSchemaSpecification', { spec: JSON.stringify(schemaDef) })] diff --git a/common/schema/types.js b/common/schema/types.js index d2b6d05d..59fd1188 100644 --- a/common/schema/types.js +++ b/common/schema/types.js @@ -1,7 +1,5 @@ /** HED schema classes */ -import castArray from 'lodash/castArray' - import { getGenerationForSchemaVersion } from '../../utils/hedData' /** @@ -238,6 +236,9 @@ export class Schemas { * @returns {Schema} */ get standardSchema() { + if (this.schemas === null) { + return undefined + } for (const schema of this.schemas.values()) { if (schema.library === '') { return schema @@ -367,7 +368,7 @@ export class SchemaSpec { export class SchemasSpec { /** * The specification mapping data. - * @type {Map} + * @type {Map} */ data @@ -385,7 +386,7 @@ export class SchemasSpec { */ *[Symbol.iterator]() { for (const [key, value] of this.data.entries()) { - yield [key, castArray(value)] + yield [key, value] } } @@ -403,14 +404,4 @@ export class SchemasSpec { } return this } - - /** - * Determine whether this specification already has a schema with the given nickname. - * - * @param {SchemaSpec} schemaSpec A schema specification with a nickname. - * @returns {boolean} Whether the nickname exists in this specification. - */ - isDuplicate(schemaSpec) { - return this.data.has(schemaSpec.nickname) - } } diff --git a/converter/index.js b/converter/index.js index f378888f..f6278a35 100644 --- a/converter/index.js +++ b/converter/index.js @@ -1,10 +1,8 @@ import { convertHedStringToLong, convertHedStringToShort } from './converter' -import { buildSchema } from './schema' -export { convertHedStringToLong, convertHedStringToShort, buildSchema } +export { convertHedStringToLong, convertHedStringToShort } export default { convertHedStringToLong, convertHedStringToShort, - buildSchema, } diff --git a/converter/schema.js b/converter/schema.js index b15854ef..e80bccb0 100644 --- a/converter/schema.js +++ b/converter/schema.js @@ -1,6 +1,3 @@ -import { Schemas } from '../common/schema/types' -import { buildSchema as validatorBuildSchema } from '../validator/schema/init' - import { Mapping, TagEntry } from './types' import { getTagName } from '../utils/hedStrings' import { generateIssue, IssueError } from '../common/issues/issues' @@ -48,12 +45,3 @@ export const buildMappingObject = function (entries) { } return new Mapping(shortTagData, longTagData) } - -/** - * Build a schema container object containing a short-long mapping from a base schema version or path description. - * - * @param {{path: string?, version: string?}} schemaDef The description of which schema to use. - * @returns {Promise|Promise} The schema container object or an error. - * @deprecated - */ -export const buildSchema = (schemaDef) => validatorBuildSchema(schemaDef) diff --git a/tests/event.spec.js b/tests/event.spec.js index cfca3e01..f99d2960 100644 --- a/tests/event.spec.js +++ b/tests/event.spec.js @@ -1009,7 +1009,7 @@ describe('HED string and event validation', () => { onsetAndOffsetWithDifferentValues: '(Def/Acc/5.4, Offset), (Def/Acc/4.3, Onset)', sameOffsetAndOnset: '(Def/MyColor, Offset), (Def/MyColor, Onset)', sameOnsetAndOffset: '(Def/MyColor, Onset), (Def/MyColor, Offset)', - duplicateOnset: '(Def/MyColor, Red, Onset), (Def/MyColor, Onset)', + duplicateOnset: '(Def/MyColor, (Red), Onset), (Def/MyColor, Onset)', } const expectedIssues = { onsetAndOffsetWithDifferentValues: [], diff --git a/tests/schema.spec.js b/tests/schema.spec.js index d192fccf..f6a02bfb 100644 --- a/tests/schema.spec.js +++ b/tests/schema.spec.js @@ -64,9 +64,9 @@ describe('HED schemas', () => { }) }) - describe.skip('Remote HED schemas', () => { + describe('Remote HED schemas', () => { it('a HED 3 schema can be loaded remotely', () => { - const spec1 = new SchemaSpec('', '8.2.0', '', '') + const spec1 = new SchemaSpec('', '2.1.0', 'testlib', '') const specs = new SchemasSpec().addSchemaSpec(spec1) return buildSchemas(specs).then(([hedSchemas, issues]) => { assert.isEmpty(issues, 'Schema loading issues occurred') @@ -479,7 +479,7 @@ describe('HED schemas', () => { } describe('HED 3 SchemaSpec tests', () => { - it('should be return a SchemaSpec and no issues when valid', () => { + it('should return a SchemaSpec and no issues when valid', () => { const tests = { just_version: '8.1.0', just_library: 'score_1.0.0', @@ -533,7 +533,7 @@ describe('HED schemas', () => { }) describe('HED 3 SchemasSpec tests', () => { - it('should be return a SchemasSpec and no issues when valid', () => { + it('should return a SchemasSpec and no issues when valid', () => { const schemas1 = new SchemasSpec() schemas1.addSchemaSpec(new SchemaSpec('', '8.1.0', '', '')) @@ -559,21 +559,15 @@ describe('HED schemas', () => { ) }) - it.skip('should return issues when invalid', () => { - const schemas1 = new SchemasSpec() - schemas1.addSchemaSpec(new SchemaSpec('', '8.1.0', '', '')) - + it('should return issues when invalid', () => { const tests = { - // bad_version: '3.1.a', - duplicate_key: ['8.1.0', '8.0.0'], + bad_version: '3.1.a', } const expectedResults = { bad_version: new SchemasSpec(), - duplicate_key: schemas1, } const expectedIssues = { bad_version: [generateIssue('invalidSchemaSpecification', { spec: '3.1.a' })], - duplicate_key: [generateIssue('invalidSchemaNickname', { spec: '8.0.0', nickname: '' })], } return checkWithIssues( diff --git a/validator/index.js b/validator/index.js index c349086a..b62be896 100644 --- a/validator/index.js +++ b/validator/index.js @@ -1,7 +1,7 @@ import { BidsDataset, BidsEventFile, BidsJsonFile, BidsSidecar, validateBidsDataset } from '../bids' import { validateHedDataset } from './dataset' import { validateHedEvent, validateHedString } from './event' -import { buildSchema, buildSchemas } from './schema/init' +import { buildSchemas } from './schema/init' export { BidsDataset, @@ -12,7 +12,6 @@ export { validateHedDataset, validateHedEvent, validateHedString, - buildSchema, buildSchemas, } @@ -25,6 +24,5 @@ export default { validateHedDataset, validateHedEvent, validateHedString, - buildSchema, buildSchemas, } diff --git a/validator/schema/init.js b/validator/schema/init.js index c76f422d..afd4f074 100644 --- a/validator/schema/init.js +++ b/validator/schema/init.js @@ -1,9 +1,8 @@ -import castArray from 'lodash/castArray' import zip from 'lodash/zip' - import semver from 'semver' + import { Schema, Schemas, Hed2Schema, Hed3Schema, SchemasSpec, PartneredSchema } from '../../common/schema/types' -import { loadSchema } from '../../common/schema/loader' +import loadSchema from '../../common/schema/loader' import { buildMappingObject } from '../../converter/schema' import { setParent } from '../../utils/xml2js' @@ -73,50 +72,17 @@ const buildSchemaObjects = function (xmlData) { /** * Build a schema collection object from a schema specification. * - * @param {{path: string?, version: string?, libraries: Object?}} schemaDef The description of which schemas to use. - * @param {boolean} useFallback Whether to use a bundled fallback schema if the requested schema cannot be loaded. - * @returns {Promise|Promise} The schema container object or an error. - * @deprecated - */ -/* DEPRECATED!!!! DO NOT EDIT!!!! */ -export const buildSchema = function (schemaDef = {}, useFallback = true) { - return loadSchema(schemaDef, useFallback).then(([xmlData, baseSchemaIssues]) => { - const baseSchema = buildSchemaObject(xmlData) - if (schemaDef.libraries === undefined) { - return new Schemas(baseSchema) - } - const [libraryKeys, libraryDefs] = zip(...Object.entries(schemaDef.libraries)) - return Promise.all( - libraryDefs.map((libraryDef) => { - return loadSchema(libraryDef, false) - }), - ).then((libraryXmlDataAndIssues) => { - const [libraryXmlData, libraryXmlIssues] = zip(...libraryXmlDataAndIssues) - const librarySchemaObjects = libraryXmlData.map(buildSchemaObject) - const schemas = new Map(zip(libraryKeys, librarySchemaObjects)) - schemas.set('', baseSchema) - return new Schemas(schemas) - }) - }) -} - -/** - * Build a schema collection object from a schema specification. - * - * @param {Map|SchemasSpec} schemaSpecs The description of which schemas to use. + * @param {SchemasSpec} schemaSpecs The description of which schemas to use. * @returns {Promise|Promise<[Schemas, Issue[]]>} The schema container object and any issues found. */ export const buildSchemas = function (schemaSpecs) { - if (schemaSpecs instanceof SchemasSpec) { - schemaSpecs = schemaSpecs.data - } - const schemaKeys = Array.from(schemaSpecs.keys()) + const schemaKeys = Array.from(schemaSpecs.data.keys()) /* Data format example: * [[[xmlData, issues], ...], [[xmlData, issues], [xmlData, issues], ...]] */ return Promise.all( schemaKeys.map((k) => { - const specs = castArray(schemaSpecs.get(k)) - return Promise.all(specs.map((spec) => loadSchema(spec, false, false))) + const specs = schemaSpecs.data.get(k) + return Promise.all(specs.map((spec) => loadSchema(spec))) }), ).then((schemaXmlDataAndIssues) => { const [schemaXmlData, schemaXmlIssues] = zip( From 17ccbb2d5345b03efd9f50bdda1e4cf4ebc9eff5 Mon Sep 17 00:00:00 2001 From: Alexander Jones Date: Fri, 24 May 2024 10:02:50 -0500 Subject: [PATCH 2/4] Revamp schema loading issue handling --- bids/schema.js | 70 ++++++------- bids/validate.js | 24 ++--- common/schema/loader.js | 27 +++--- common/schema/types.js | 11 --- converter/__tests__/converter.spec.js | 6 +- tests/bids.spec.js | 13 ++- tests/dataset.spec.js | 9 +- tests/event.spec.js | 18 ++-- tests/schema.spec.js | 135 +++++++++----------------- tests/stringParser.spec.js | 12 +-- utils/__tests__/hed.spec.js | 3 +- validator/schema/init.js | 11 +-- 12 files changed, 128 insertions(+), 211 deletions(-) diff --git a/bids/schema.js b/bids/schema.js index 489f18ac..5a729fde 100644 --- a/bids/schema.js +++ b/bids/schema.js @@ -2,69 +2,53 @@ import castArray from 'lodash/castArray' import semver from 'semver' import { buildSchemas } from '../validator/schema/init' -import { generateIssue } from '../common/issues/issues' +import { generateIssue, IssueError } from '../common/issues/issues' import { SchemaSpec, SchemasSpec } from '../common/schema/types' -const alphanumericRegExp = new RegExp('^[a-zA-Z0-9]+$') +const alphabeticRegExp = new RegExp('^[a-zA-Z]+$') /** * Build a HED schema collection based on the defined BIDS schemas. * * @param {BidsDataset} dataset The BIDS dataset being validated. - * @param {object} schemaDefinition The version spec for the schema to be loaded. - * @returns {Promise<[Schemas,Issue[]]>|Promise} A Promise with the schema collection and any issues found, or an issue list upon failure. + * @param {SchemasSpec} schemaDefinition The version spec for the schema to be loaded. + * @returns {Promise} A Promise with the schema collection and any issues found, or an issue list upon failure. + * @throws {IssueError} If the schema specification is invalid or missing. */ -export function buildBidsSchemas(dataset, schemaDefinition) { +export async function buildBidsSchemas(dataset, schemaDefinition) { let schemasSpec - let issues if (schemaDefinition) { - ;[schemasSpec, issues] = validateSchemasSpec(schemaDefinition) + schemasSpec = validateSchemasSpec(schemaDefinition) } else if (dataset.datasetDescription.jsonData?.HEDVersion) { - ;[schemasSpec, issues] = parseSchemasSpec(dataset.datasetDescription.jsonData.HEDVersion) + schemasSpec = parseSchemasSpec(dataset.datasetDescription.jsonData.HEDVersion) } else { - ;[schemasSpec, issues] = [null, [generateIssue('invalidSchemaSpecification', { spec: 'no schema available' })]] - } - if (issues.length > 0) { - return Promise.reject(issues) - } else { - return buildSchemas(schemasSpec).then(([schemas]) => [schemas, issues]) + throw new IssueError(generateIssue('invalidSchemaSpecification', { spec: 'no schema available' })) } + return buildSchemas(schemasSpec) } function validateSchemasSpec(schemasSpec) { if (schemasSpec instanceof SchemasSpec) { - return [schemasSpec, []] + return schemasSpec } else { - return [null, [generateIssue('invalidSchemaSpecification', { spec: JSON.stringify(schemasSpec) })]] + throw new IssueError(generateIssue('invalidSchemaSpecification', { spec: JSON.stringify(schemasSpec) })) } } export function parseSchemasSpec(hedVersion) { const schemasSpec = new SchemasSpec() const processVersion = castArray(hedVersion) - const issues = [] for (const schemaVersion of processVersion) { - const [schemaSpec, verIssues] = parseSchemaSpec(schemaVersion) - if (verIssues.length > 0) { - issues.push(...verIssues) - } else { - schemasSpec.addSchemaSpec(schemaSpec) - } + const schemaSpec = parseSchemaSpec(schemaVersion) + schemasSpec.addSchemaSpec(schemaSpec) } - return [schemasSpec, issues] + return schemasSpec } export function parseSchemaSpec(schemaVersion) { - const [[nickname, schema], nicknameIssues] = splitNicknameAndSchema(schemaVersion) - if (nicknameIssues.length > 0) { - return [null, nicknameIssues] - } - const [[library, version], libraryIssues] = splitLibraryAndVersion(schema, schemaVersion) - if (libraryIssues.length > 0) { - return [null, libraryIssues] - } - const schemaSpec = new SchemaSpec(nickname, version, library) - return [schemaSpec, []] + const [nickname, schema] = splitNicknameAndSchema(schemaVersion) + const [library, version] = splitLibraryAndVersion(schema, schemaVersion) + return new SchemaSpec(nickname, version, library) } function splitNicknameAndSchema(schemaVersion) { @@ -72,17 +56,17 @@ function splitNicknameAndSchema(schemaVersion) { let nickname = '' let schema if (nicknameSplit.length > 2) { - return [['', ''], [generateIssue('invalidSchemaSpecification', { spec: schemaVersion })]] + throw new IssueError(generateIssue('invalidSchemaSpecification', { spec: schemaVersion })) } if (nicknameSplit.length > 1) { ;[nickname, schema] = nicknameSplit - if (nickname === '' || !alphanumericRegExp.test(nickname)) { - return [['', ''], [generateIssue('invalidSchemaNickname', { nickname: nickname, spec: schemaVersion })]] + if (!alphabeticRegExp.test(nickname)) { + throw new IssueError(generateIssue('invalidSchemaNickname', { nickname: nickname, spec: schemaVersion })) } } else { schema = nicknameSplit[0] } - return [[nickname, schema], []] + return [nickname, schema] } function splitLibraryAndVersion(schemaVersion, originalVersion) { @@ -90,18 +74,18 @@ function splitLibraryAndVersion(schemaVersion, originalVersion) { let library = '' let version if (versionSplit.length > 2) { - return [['', ''], [generateIssue('invalidSchemaSpecification', { spec: originalVersion })]] + throw new IssueError(generateIssue('invalidSchemaSpecification', { spec: originalVersion })) } if (versionSplit.length > 1) { ;[library, version] = versionSplit - if (library === '' || !alphanumericRegExp.test(library)) { - return [['', ''], [generateIssue('invalidSchemaSpecification', { spec: originalVersion })]] + if (!alphabeticRegExp.test(library)) { + throw new IssueError(generateIssue('invalidSchemaSpecification', { spec: originalVersion })) } } else { version = versionSplit[0] } if (!semver.valid(version)) { - return [['', ''], [generateIssue('invalidSchemaSpecification', { spec: originalVersion })]] + throw new IssueError(generateIssue('invalidSchemaSpecification', { spec: originalVersion })) } - return [[library, version], []] + return [library, version] } diff --git a/bids/validate.js b/bids/validate.js index dbaba2e1..545453f7 100644 --- a/bids/validate.js +++ b/bids/validate.js @@ -8,19 +8,21 @@ import { BidsHedColumnValidator } from './validator/bidsHedColumnValidator' * Validate a BIDS dataset. * * @param {BidsDataset} dataset The BIDS dataset. - * @param {object} schemaDefinition The version spec for the schema to be loaded. + * @param {SchemasSpec} schemaDefinition The version spec for the schema to be loaded. * @returns {Promise} Any issues found. */ -export function validateBidsDataset(dataset, schemaDefinition) { - return buildBidsSchemas(dataset, schemaDefinition).then( - ([hedSchemas, schemaLoadIssues]) => { - return new BidsHedValidator(dataset, hedSchemas) - .validateFullDataset() - .catch(BidsIssue.generateInternalErrorPromise) - .then((issues) => issues.concat(BidsHedIssue.fromHedIssues(schemaLoadIssues, dataset.datasetDescription.file))) - }, - (issues) => BidsHedIssue.fromHedIssues(issues, dataset.datasetDescription.file), - ) +export async function validateBidsDataset(dataset, schemaDefinition) { + try { + const hedSchemas = await buildBidsSchemas(dataset, schemaDefinition) + const validator = new BidsHedValidator(dataset, hedSchemas) + try { + return validator.validateFullDataset() + } catch (internalError) { + return BidsIssue.generateInternalErrorPromise(internalError) + } + } catch (schemaIssues) { + return BidsHedIssue.fromHedIssues(schemaIssues, dataset.datasetDescription.file) + } } /** diff --git a/common/schema/loader.js b/common/schema/loader.js index 5d9e0cae..63c628b8 100644 --- a/common/schema/loader.js +++ b/common/schema/loader.js @@ -4,7 +4,7 @@ import xml2js from 'xml2js' import * as files from '../../utils/files' -import { generateIssue } from '../issues/issues' +import { generateIssue, IssueError } from '../issues/issues' import { localSchemaList } from './config' @@ -12,28 +12,29 @@ import { localSchemaList } from './config' * Load schema XML data from a schema version or path description. * * @param {SchemaSpec} schemaDef The description of which schema to use. - * @returns {Promise|Promise<[object, Issue[]]>} The schema XML data or an error. + * @returns {Promise} The schema XML data. + * @throws {IssueError} If the schema could not be loaded. */ export default async function loadSchema(schemaDef = null) { const xmlData = await loadPromise(schemaDef) if (xmlData === null) { - throw [generateIssue('invalidSchemaSpecification', { spec: JSON.stringify(schemaDef) })] + throw new IssueError(generateIssue('invalidSchemaSpecification', { spec: JSON.stringify(schemaDef) })) } - return [xmlData, []] + return xmlData } /** * Choose the schema Promise from a schema version or path description. * * @param {SchemaSpec} schemaDef The description of which schema to use. - * @returns {Promise} The schema XML data or an error. + * @returns {Promise} The schema XML data. + * @throws {IssueError} If the schema could not be loaded. */ async function loadPromise(schemaDef) { if (schemaDef === null) { return null - } else if (schemaDef.path) { - // TODO: Replace with localPath in 4.0.0. - return loadLocalSchema(schemaDef.path) + } else if (schemaDef.localPath) { + return loadLocalSchema(schemaDef.localPath) } else if (localSchemaList.has(schemaDef.localName)) { return loadBundledSchema(schemaDef) } else { @@ -46,6 +47,7 @@ async function loadPromise(schemaDef) { * * @param {SchemaSpec} schemaDef The standard schema version to load. * @returns {Promise} The schema XML data. + * @throws {IssueError} If the schema could not be loaded. */ function loadRemoteSchema(schemaDef) { let url @@ -62,6 +64,7 @@ function loadRemoteSchema(schemaDef) { * * @param {string} path The path to the schema XML data. * @returns {Promise} The schema XML data. + * @throws {IssueError} If the schema could not be loaded. */ function loadLocalSchema(path) { return loadSchemaFile(files.readFile(path), 'localSchemaLoadFailed', { path: path }) @@ -72,13 +75,14 @@ function loadLocalSchema(path) { * * @param {SchemaSpec} schemaDef The description of which schema to use. * @returns {Promise} The schema XML data. + * @throws {IssueError} If the schema could not be loaded. */ async function loadBundledSchema(schemaDef) { try { return parseSchemaXML(localSchemaList.get(schemaDef.localName)) } catch (error) { - const issueArgs = { spec: schemaDef, error: error.message } - throw [generateIssue('bundledSchemaLoadFailed', issueArgs)] + const issueArgs = { spec: JSON.stringify(schemaDef), error: error.message } + throw new IssueError(generateIssue('bundledSchemaLoadFailed', issueArgs)) } } @@ -89,6 +93,7 @@ async function loadBundledSchema(schemaDef) { * @param {string} issueCode The issue code. * @param {Object} issueArgs The issue arguments passed from the calling function. * @returns {Promise} The parsed schema XML data. + * @throws {IssueError} If the schema could not be loaded. */ async function loadSchemaFile(xmlDataPromise, issueCode, issueArgs) { try { @@ -96,7 +101,7 @@ async function loadSchemaFile(xmlDataPromise, issueCode, issueArgs) { return parseSchemaXML(data) } catch (error) { issueArgs.error = error.message - throw [generateIssue(issueCode, issueArgs)] + throw new IssueError(generateIssue(issueCode, issueArgs)) } } diff --git a/common/schema/types.js b/common/schema/types.js index 59fd1188..cf443537 100644 --- a/common/schema/types.js +++ b/common/schema/types.js @@ -349,17 +349,6 @@ export class SchemaSpec { return 'HED_' + this.library + '_' + this.version } } - - /** - * Alias to old name of localPath. - * - * @todo Replace with localPath in 4.0.0. - * - * @returns {string} The local path for this schema. - */ - get path() { - return this.localPath - } } /** diff --git a/converter/__tests__/converter.spec.js b/converter/__tests__/converter.spec.js index fda27d6c..a52e2ad7 100644 --- a/converter/__tests__/converter.spec.js +++ b/converter/__tests__/converter.spec.js @@ -26,8 +26,7 @@ describe('HED string conversion', () => { * @returns {Promise} */ const validatorBase = function (testStrings, expectedResults, expectedIssues, testFunction) { - return hedSchemaPromise.then(([hedSchemas, issues]) => { - assert.isEmpty(issues, 'Schema loading issues occurred') + return hedSchemaPromise.then((hedSchemas) => { for (const [testStringKey, testString] of Object.entries(testStrings)) { const [testResult, issues] = testFunction(hedSchemas.baseSchema, testString, testString, 0) assert.strictEqual(testResult, expectedResults[testStringKey], testString) @@ -591,8 +590,7 @@ describe('HED string conversion', () => { * @returns {Promise} */ const validatorBase = function (testStrings, expectedResults, expectedIssues, testFunction) { - return hedSchemaPromise.then(([hedSchemas, issues]) => { - assert.isEmpty(issues, 'Schema loading issues occurred') + return hedSchemaPromise.then((hedSchemas) => { for (const [testStringKey, testString] of Object.entries(testStrings)) { const [testResult, issues] = testFunction(hedSchemas, testString) assert.strictEqual(testResult, expectedResults[testStringKey], testString) diff --git a/tests/bids.spec.js b/tests/bids.spec.js index c24532d1..3b3d329b 100644 --- a/tests/bids.spec.js +++ b/tests/bids.spec.js @@ -438,11 +438,11 @@ describe('BIDS datasets', () => { describe('HED 3 library schema with version spec', () => { it('should validate HED 3 in BIDS event files sidecars and libraries using version spec', () => { - const [specs0] = parseSchemasSpec(['8.1.0']) - const [specs1] = parseSchemasSpec(['8.1.0', 'ts:testlib_1.0.2']) - const [specs2] = parseSchemasSpec(['ts:testlib_1.0.2']) - const [specs3] = parseSchemasSpec(['8.1.0', 'ts:testlib_1.0.2', 'bg:testlib_1.0.2']) - const [specs4] = parseSchemasSpec(['ts:testlib_1.0.2', 'bg:testlib_1.0.2']) + const specs0 = parseSchemasSpec(['8.1.0']) + const specs1 = parseSchemasSpec(['8.1.0', 'ts:testlib_1.0.2']) + const specs2 = parseSchemasSpec(['ts:testlib_1.0.2']) + const specs3 = parseSchemasSpec(['8.1.0', 'ts:testlib_1.0.2', 'bg:testlib_1.0.2']) + const specs4 = parseSchemasSpec(['ts:testlib_1.0.2', 'bg:testlib_1.0.2']) const testDatasets1 = { library_and_defs_base_ignored: new BidsDataset([goodEvents[0]], [], goodDatasetDescriptions[1]), library_and_defs_no_base: new BidsDataset([goodEvents[0]], [], goodDatasetDescriptions[3]), @@ -701,8 +701,7 @@ describe('BIDS datasets', () => { '(Red, Blue), (Green, (Yellow))', ] const dataset = new BidsDataset(tsvFiles, []) - return buildBidsSchemas(dataset, specs).then(([hedSchemas, schemaIssues]) => { - assert.isEmpty(schemaIssues, 'Schema failed to load') + return buildBidsSchemas(dataset, specs).then((hedSchemas) => { const parsedExpectedStrings = [] for (const expectedString of expectedStrings) { const [parsedExpectedString, parsingIssues] = parseHedString(expectedString, hedSchemas) diff --git a/tests/dataset.spec.js b/tests/dataset.spec.js index 09f9f134..1412d5da 100644 --- a/tests/dataset.spec.js +++ b/tests/dataset.spec.js @@ -26,8 +26,7 @@ describe('HED dataset validation', () => { * @param {Object} expectedIssues The expected issues. */ const validator = function (testDatasets, expectedIssues) { - return hedSchemaPromise.then(([hedSchemas, issues]) => { - assert.isEmpty(issues, 'Schema loading issues occurred') + return hedSchemaPromise.then((hedSchemas) => { for (const [testDatasetKey, testDataset] of Object.entries(testDatasets)) { assert.property(expectedIssues, testDatasetKey, testDatasetKey + ' is not in expectedIssues') const [, testIssues] = hed.validateHedEvents(testDataset, hedSchemas, null, true) @@ -82,8 +81,7 @@ describe('HED dataset validation', () => { * @param {Object} expectedIssues The expected issues. */ const validator = function (testDatasets, expectedIssues) { - return hedSchemaPromise.then(([hedSchemas, issues]) => { - assert.isEmpty(issues, 'Schema loading issues occurred') + return hedSchemaPromise.then((hedSchemas) => { for (const [testDatasetKey, testDataset] of Object.entries(testDatasets)) { assert.property(expectedIssues, testDatasetKey, testDatasetKey + ' is not in expectedIssues') const [, testIssues] = hed.validateHedDataset(testDataset, hedSchemas, true) @@ -220,8 +218,7 @@ describe('HED dataset validation', () => { * @param {Object} expectedIssues The expected issues. */ const validator = function (testDatasets, testContext, expectedIssues) { - return hedSchemaPromise.then(([hedSchemas, issues]) => { - assert.isEmpty(issues, 'Schema loading issues occurred') + return hedSchemaPromise.then((hedSchemas) => { for (const [testDatasetKey, testDataset] of Object.entries(testDatasets)) { assert.property(expectedIssues, testDatasetKey, testDatasetKey + ' is not in expectedIssues') const [, testIssues] = hed.validateHedDatasetWithContext(testDataset, testContext, hedSchemas, true) diff --git a/tests/event.spec.js b/tests/event.spec.js index f99d2960..ca1fdc55 100644 --- a/tests/event.spec.js +++ b/tests/event.spec.js @@ -356,8 +356,7 @@ describe('HED string and event validation', () => { * @param {Object?} testOptions Any needed custom options for the validator. */ const validatorSemanticBase = function (testStrings, expectedIssues, testFunction, testOptions = {}) { - return hedSchemaPromise.then(([hedSchemas, issues]) => { - assert.isEmpty(issues, 'Schema loading issues occurred') + return hedSchemaPromise.then((hedSchemas) => { validatorBase(hedSchemas, Hed2Validator, testStrings, expectedIssues, testFunction, testOptions) }) } @@ -698,8 +697,7 @@ describe('HED string and event validation', () => { describe('HED Strings', () => { const validator = function (testStrings, expectedIssues, expectValuePlaceholderString = false) { - return hedSchemaPromise.then(([hedSchemas, issues]) => { - assert.isEmpty(issues, 'Schema loading issues occurred') + return hedSchemaPromise.then((hedSchemas) => { for (const [testStringKey, testString] of Object.entries(testStrings)) { assert.property(expectedIssues, testStringKey, testStringKey + ' is not in expectedIssues') const [, testIssues] = hed.validateHedString(testString, hedSchemas, true, expectValuePlaceholderString) @@ -743,8 +741,7 @@ describe('HED string and event validation', () => { * @param {Object?} testOptions Any needed custom options for the validator. */ const validatorSemanticBase = function (testStrings, expectedIssues, testFunction, testOptions = {}) { - return hedSchemaPromise.then(([hedSchemas, issues]) => { - assert.isEmpty(issues, 'Schema loading issues occurred') + return hedSchemaPromise.then((hedSchemas) => { validatorBase(hedSchemas, Hed2Validator, testStrings, expectedIssues, testFunction, testOptions) }) } @@ -908,8 +905,7 @@ describe('HED string and event validation', () => { * @param {Object?} testOptions Any needed custom options for the validator. */ const validatorSemanticBase = function (testStrings, expectedIssues, testFunction, testOptions = {}) { - return hedSchemaPromise.then(([hedSchemas, issues]) => { - assert.isEmpty(issues, 'Schema loading issues occurred') + return hedSchemaPromise.then((hedSchemas) => { validatorBase(hedSchemas, testStrings, expectedIssues, testFunction, testOptions) }) } @@ -1818,8 +1814,7 @@ describe('HED string and event validation', () => { * @param {Object?} testOptions Any needed custom options for the validator. */ const validatorSemanticBase = function (testStrings, expectedIssues, testFunction, testOptions = {}) { - return hedSchemaPromise.then(([hedSchemas, issues]) => { - assert.isEmpty(issues, 'Schema loading issues occurred') + return hedSchemaPromise.then((hedSchemas) => { validatorBase(hedSchemas, testStrings, expectedIssues, testFunction, testOptions) }) } @@ -1838,8 +1833,7 @@ describe('HED string and event validation', () => { * @param {Object?} testOptions Any needed custom options for the validator. */ const validatorSemantic2 = function (testStrings, expectedIssues, testFunction, testOptions = {}) { - return hedSchemaPromise2.then(([hedSchemas, issues]) => { - assert.isEmpty(issues, 'Schema loading issues occurred') + return hedSchemaPromise2.then((hedSchemas) => { validatorBase(hedSchemas, testStrings, expectedIssues, testFunction, testOptions) }) } diff --git a/tests/schema.spec.js b/tests/schema.spec.js index f6a02bfb..f0ffeba0 100644 --- a/tests/schema.spec.js +++ b/tests/schema.spec.js @@ -11,8 +11,7 @@ describe('HED schemas', () => { it('a standard schema can be loaded from locally stored schema', () => { const spec1 = new SchemaSpec('', '8.0.0', '', '') const specs = new SchemasSpec().addSchemaSpec(spec1) - return buildSchemas(specs).then(([hedSchemas, issues]) => { - assert.isEmpty(issues, 'Schema loading issues occurred') + return buildSchemas(specs).then((hedSchemas) => { assert.strictEqual(hedSchemas.baseSchema.version, spec1.version, 'Schema has wrong version number') assert.strictEqual(hedSchemas.generation, 3, 'Schema collection has wrong generation') }) @@ -21,8 +20,7 @@ describe('HED schemas', () => { it('a library schema can be loaded from locally stored schema', () => { const spec1 = new SchemaSpec('', '2.0.0', 'testlib', '') const specs = new SchemasSpec().addSchemaSpec(spec1) - return buildSchemas(specs).then(([hedSchemas, issues]) => { - assert.isEmpty(issues, 'Schema loading issues occurred') + return buildSchemas(specs).then((hedSchemas) => { assert.strictEqual(hedSchemas.baseSchema.version, spec1.version, 'Schema has wrong version number') assert.strictEqual(hedSchemas.baseSchema.library, spec1.library, 'Schema has wrong library name') assert.strictEqual(hedSchemas.generation, 3, 'Schema collection has wrong generation') @@ -32,8 +30,7 @@ describe('HED schemas', () => { it('a base schema with a nickname can be loaded from locally stored schema', () => { const spec1 = new SchemaSpec('nk', '8.0.0', '', '') const specs = new SchemasSpec().addSchemaSpec(spec1) - return buildSchemas(specs).then(([hedSchemas, issues]) => { - assert.isEmpty(issues, 'Schema loading issues occurred') + return buildSchemas(specs).then((hedSchemas) => { const schema1 = hedSchemas.getSchema(spec1.nickname) assert.strictEqual(schema1.version, spec1.version, 'Schema has wrong version number') assert.strictEqual(schema1.library, spec1.library, 'Schema has wrong library name') @@ -46,8 +43,7 @@ describe('HED schemas', () => { const spec2 = new SchemaSpec('ts', '2.0.0', 'testlib', '') const spec3 = new SchemaSpec('', '2.0.0', 'testlib', '') const specs = new SchemasSpec().addSchemaSpec(spec1).addSchemaSpec(spec2).addSchemaSpec(spec3) - return buildSchemas(specs).then(([hedSchemas, issues]) => { - assert.isEmpty(issues, 'Schema loading issues occurred') + return buildSchemas(specs).then((hedSchemas) => { const schema1 = hedSchemas.getSchema(spec1.nickname) const schema2 = hedSchemas.getSchema(spec2.nickname) const schema3 = hedSchemas.getSchema(spec3.nickname) @@ -68,8 +64,7 @@ describe('HED schemas', () => { it('a HED 3 schema can be loaded remotely', () => { const spec1 = new SchemaSpec('', '2.1.0', 'testlib', '') const specs = new SchemasSpec().addSchemaSpec(spec1) - return buildSchemas(specs).then(([hedSchemas, issues]) => { - assert.isEmpty(issues, 'Schema loading issues occurred') + return buildSchemas(specs).then((hedSchemas) => { const schema1 = hedSchemas.getSchema(spec1.nickname) assert.strictEqual(schema1.version, spec1.version, 'Schema has wrong version number') assert.strictEqual(schema1.library, spec1.library, 'Schema has wrong library name') @@ -84,8 +79,7 @@ describe('HED schemas', () => { const localHedSchemaVersion = '7.1.1' const schemaSpec = new SchemaSpec('', '', '', localHedSchemaFile) const schemasSpec = new SchemasSpec().addSchemaSpec(schemaSpec) - return buildSchemas(schemasSpec).then(([hedSchemas, issues]) => { - assert.isEmpty(issues, 'Schema loading issues occurred') + return buildSchemas(schemasSpec).then((hedSchemas) => { assert.strictEqual(hedSchemas.generation, 2, 'Schema collection has wrong generation') const hedSchemaVersion = hedSchemas.baseSchema.version assert.strictEqual(hedSchemaVersion, localHedSchemaVersion, 'Schema has wrong version number') @@ -98,8 +92,7 @@ describe('HED schemas', () => { const localHedLibrarySchemaFile = 'tests/data/HED_testlib_2.0.0.xml' const schemaSpec = new SchemaSpec(localHedLibrarySchemaName, '', '', localHedLibrarySchemaFile) const schemasSpec = new SchemasSpec().addSchemaSpec(schemaSpec) - return buildSchemas(schemasSpec).then(([hedSchemas, issues]) => { - assert.isEmpty(issues, 'Schema loading issues occurred') + return buildSchemas(schemasSpec).then((hedSchemas) => { assert.strictEqual(hedSchemas.generation, 3, 'Schema collection has wrong generation') const hedSchema = hedSchemas.getSchema(localHedLibrarySchemaName) assert.strictEqual(hedSchema.generation, 3, 'Schema has wrong generation') @@ -133,8 +126,7 @@ describe('HED schemas', () => { 'takesValue', 'unique', ] - return hedSchemaPromise.then(([hedSchemas, issues]) => { - assert.isEmpty(issues, 'Schema loading issues occurred') + return hedSchemaPromise.then((hedSchemas) => { const dictionaries = hedSchemas.baseSchema.attributes.tagAttributes assert.hasAllKeys(dictionaries, tagDictionaryKeys) }) @@ -142,16 +134,14 @@ describe('HED schemas', () => { it('should have unit dictionaries for all required unit attributes', () => { const unitDictionaryKeys = ['SIUnit', 'unitSymbol'] - return hedSchemaPromise.then(([hedSchemas, issues]) => { - assert.isEmpty(issues, 'Schema loading issues occurred') + return hedSchemaPromise.then((hedSchemas) => { const dictionaries = hedSchemas.baseSchema.attributes.unitAttributes assert.hasAllKeys(dictionaries, unitDictionaryKeys) }) }) it('should contain all of the required tags', () => { - return hedSchemaPromise.then(([hedSchemas, issues]) => { - assert.isEmpty(issues, 'Schema loading issues occurred') + return hedSchemaPromise.then((hedSchemas) => { const requiredTags = ['event/category', 'event/description', 'event/label'] const dictionariesRequiredTags = hedSchemas.baseSchema.attributes.tagAttributes['required'] assert.hasAllKeys(dictionariesRequiredTags, requiredTags) @@ -159,8 +149,7 @@ describe('HED schemas', () => { }) it('should contain all of the positioned tags', () => { - return hedSchemaPromise.then(([hedSchemas, issues]) => { - assert.isEmpty(issues, 'Schema loading issues occurred') + return hedSchemaPromise.then((hedSchemas) => { const positionedTags = ['event/category', 'event/description', 'event/label', 'event/long name'] const dictionariesPositionedTags = hedSchemas.baseSchema.attributes.tagAttributes['position'] assert.hasAllKeys(dictionariesPositionedTags, positionedTags) @@ -168,8 +157,7 @@ describe('HED schemas', () => { }) it('should contain all of the unique tags', () => { - return hedSchemaPromise.then(([hedSchemas, issues]) => { - assert.isEmpty(issues, 'Schema loading issues occurred') + return hedSchemaPromise.then((hedSchemas) => { const uniqueTags = ['event/description', 'event/label', 'event/long name'] const dictionariesUniqueTags = hedSchemas.baseSchema.attributes.tagAttributes['unique'] assert.hasAllKeys(dictionariesUniqueTags, uniqueTags) @@ -177,8 +165,7 @@ describe('HED schemas', () => { }) it('should contain all of the tags with default units', () => { - return hedSchemaPromise.then(([hedSchemas, issues]) => { - assert.isEmpty(issues, 'Schema loading issues occurred') + return hedSchemaPromise.then((hedSchemas) => { const defaultUnitTags = { 'attribute/blink/time shut/#': 's', 'attribute/blink/duration/#': 's', @@ -191,8 +178,7 @@ describe('HED schemas', () => { }) it('should contain all of the unit classes with their units and default units', () => { - return hedSchemaPromise.then(([hedSchemas, issues]) => { - assert.isEmpty(issues, 'Schema loading issues occurred') + return hedSchemaPromise.then((hedSchemas) => { const defaultUnits = { acceleration: 'm-per-s^2', currency: '$', @@ -241,8 +227,7 @@ describe('HED schemas', () => { }) it('should contain the correct (large) numbers of tags with certain attributes', () => { - return hedSchemaPromise.then(([hedSchemas, issues]) => { - assert.isEmpty(issues, 'Schema loading issues occurred') + return hedSchemaPromise.then((hedSchemas) => { const expectedAttributeTagCount = { isNumeric: 80, predicateType: 20, @@ -272,8 +257,7 @@ describe('HED schemas', () => { }) it('should identify if a tag has a certain attribute', () => { - return hedSchemaPromise.then(([hedSchemas, issues]) => { - assert.isEmpty(issues, 'Schema loading issues occurred') + return hedSchemaPromise.then((hedSchemas) => { const testStrings = { value: 'Attribute/Location/Reference frame/Relative to participant/Azimuth/#', valueParent: 'Attribute/Location/Reference frame/Relative to participant/Azimuth', @@ -364,8 +348,7 @@ describe('HED schemas', () => { }) it('should contain all of the tag group tags', () => { - return hedSchemaPromise.then(([hedSchemas, issues]) => { - assert.isEmpty(issues, 'Schema loading issues occurred') + return hedSchemaPromise.then((hedSchemas) => { const tagGroupTags = ['property/organizational-property/def-expand'] const schemaTagGroupTags = hedSchemas.baseSchema.entries.definitions .get('tags') @@ -375,8 +358,7 @@ describe('HED schemas', () => { }) it('should contain all of the top-level tag group tags', () => { - return hedSchemaPromise.then(([hedSchemas, issues]) => { - assert.isEmpty(issues, 'Schema loading issues occurred') + return hedSchemaPromise.then((hedSchemas) => { const tagGroupTags = [ 'property/organizational-property/definition', 'property/organizational-property/event-context', @@ -391,8 +373,7 @@ describe('HED schemas', () => { }) it('should contain all of the unit classes with their units and default units', () => { - return hedSchemaPromise.then(([hedSchemas, issues]) => { - assert.isEmpty(issues, 'Schema loading issues occurred') + return hedSchemaPromise.then((hedSchemas) => { const defaultUnits = { accelerationUnits: 'm-per-s^2', angleUnits: 'radian', @@ -442,8 +423,7 @@ describe('HED schemas', () => { }) it('should contain the correct (large) numbers of tags with certain attributes', () => { - return hedSchemaPromise.then(([hedSchemas, issues]) => { - assert.isEmpty(issues, 'Schema loading issues occurred') + return hedSchemaPromise.then((hedSchemas) => { const expectedAttributeTagCount = { requireChild: 7, takesValue: 88, @@ -468,13 +448,27 @@ describe('HED schemas', () => { }) }) + /** + * Check tests with issues. + * + * This base function uses the generic {@link HedValidator} validator class. + * + * @param {Object} testStrings A mapping of test strings. + * @param {Object} expectedResults The expected results for each test string. + * @param {Object} expectedIssues The expected issues for each test string. + * @param {function(string): void} testFunction A test-specific function that executes the required validation check. + */ const checkWithIssues = function (testStrings, expectedResults, expectedIssues, testFunction) { for (const [testStringKey, testString] of Object.entries(testStrings)) { assert.property(expectedResults, testStringKey, testStringKey + ' is not in expectedResults') assert.property(expectedIssues, testStringKey, testStringKey + ' is not in expectedIssues') - const [testResult, testIssues] = testFunction(testString) - assert.deepStrictEqual(testResult, expectedResults[testStringKey], testString) - assert.sameDeepMembers(testIssues, expectedIssues[testStringKey], testString) + try { + const testResult = testFunction(testString) + assert.deepStrictEqual(testResult, expectedResults[testStringKey], testString) + } catch (issueError) { + const testIssues = [issueError.issue] + assert.sameDeepMembers(testIssues, expectedIssues[testStringKey], testString) + } } } @@ -496,17 +490,8 @@ describe('HED schemas', () => { base_with_nick: [], } - return checkWithIssues( - tests, - expectedResults, - expectedIssues, - (string) => { - const [sp, issues] = parseSchemaSpec(string) - return [sp, issues] - }, - 10000, - ) - }) + return checkWithIssues(tests, expectedResults, expectedIssues, parseSchemaSpec) + }, 10000) it('should return issues when invalid', () => { const tests = { @@ -519,17 +504,8 @@ describe('HED schemas', () => { bad_version: [generateIssue('invalidSchemaSpecification', { spec: '3.1.a' })], } - return checkWithIssues( - tests, - expectedResults, - expectedIssues, - (string) => { - const [sp, issues] = parseSchemaSpec(string) - return [sp, issues] - }, - 10000, - ) - }) + return checkWithIssues(tests, expectedResults, expectedIssues, parseSchemaSpec) + }, 10000) }) describe('HED 3 SchemasSpec tests', () => { @@ -547,17 +523,8 @@ describe('HED schemas', () => { just_version: [], } - return checkWithIssues( - tests, - expectedResults, - expectedIssues, - (string) => { - const [sp, issues] = parseSchemasSpec(string) - return [sp, issues] - }, - 10000, - ) - }) + return checkWithIssues(tests, expectedResults, expectedIssues, parseSchemasSpec) + }, 10000) it('should return issues when invalid', () => { const tests = { @@ -570,17 +537,8 @@ describe('HED schemas', () => { bad_version: [generateIssue('invalidSchemaSpecification', { spec: '3.1.a' })], } - return checkWithIssues( - tests, - expectedResults, - expectedIssues, - (string) => { - const [sp, issues] = parseSchemasSpec(string) - return [sp, issues] - }, - 10000, - ) - }) + return checkWithIssues(tests, expectedResults, expectedIssues, parseSchemasSpec) + }, 10000) }) describe('HED 3 partnered schemas', () => { @@ -618,8 +576,7 @@ describe('HED schemas', () => { assert.deepStrictEqual(issue, generateIssue('lazyPartneredSchemasShareTag', { tag: 'Piano-sound' })) }, ), - buildSchemas(specs2).then(([schemas, issues]) => { - assert.isEmpty(issues, 'Issues occurred when parsing the combination of testlib_2.0.0 and testlib_3.0.0') + buildSchemas(specs2).then((schemas) => { assert.instanceOf( schemas.getSchema('testlib'), PartneredSchema, diff --git a/tests/stringParser.spec.js b/tests/stringParser.spec.js index bdf30b2a..441fa62d 100644 --- a/tests/stringParser.spec.js +++ b/tests/stringParser.spec.js @@ -331,8 +331,7 @@ describe('HED string parsing', () => { ], } - return hedSchemaPromise.then(([hedSchemas, issues]) => { - assert.isEmpty(issues, 'Schema loading issues occurred') + return hedSchemaPromise.then((hedSchemas) => { for (const [testStringKey, testString] of Object.entries(testStrings)) { const [parsedString, issues] = parseHedString(testString, hedSchemas) assert.isEmpty(Object.values(issues).flat(), 'Parsing issues occurred') @@ -386,8 +385,7 @@ describe('HED string parsing', () => { }, } - return hedSchemaPromise.then(([hedSchemas, issues]) => { - assert.isEmpty(issues, 'Schema loading issues occurred') + return hedSchemaPromise.then((hedSchemas) => { return validatorWithIssues(testStrings, expectedResults, expectedIssues, (string) => { const [parsedString, issues] = parseHedString(string, hedSchemas) const canonicalTags = parsedString.tags.map((parsedTag) => { @@ -408,8 +406,7 @@ describe('HED string parsing', () => { ] const issues = [] const parsedStrings = [] - return hedSchemaPromise.then(([hedSchemas, schemaIssues]) => { - assert.isEmpty(schemaIssues, 'Schema loading issues occurred') + return hedSchemaPromise.then((hedSchemas) => { for (const hedString of hedStrings) { const [parsedString, parsingIssues] = parseHedString(hedString, hedSchemas) parsedStrings.push(parsedString) @@ -435,8 +432,7 @@ describe('HED string parsing', () => { const hedStrings = ['Sensory-event, Visual-presentation, {stim_file}', '(Image, {body_part}, Pathname/#)', 'Face'] const issues = [] const parsedStrings = [] - return hedSchemaPromise.then(([hedSchemas, schemaIssues]) => { - assert.isEmpty(schemaIssues, 'Schema loading issues occurred') + return hedSchemaPromise.then((hedSchemas) => { for (const hedString of hedStrings) { const [parsedString, parsingIssues] = parseHedString(hedString, hedSchemas) parsedStrings.push(parsedString) diff --git a/utils/__tests__/hed.spec.js b/utils/__tests__/hed.spec.js index aa1d5ba2..bfa85141 100644 --- a/utils/__tests__/hed.spec.js +++ b/utils/__tests__/hed.spec.js @@ -198,8 +198,7 @@ describe('HED tag string utility functions', () => { const invalidVolumeString = '200 cm' const currencyUnits = ['dollars', '$', 'points', 'fraction'] const volumeUnits = ['m^3'] - return hedSchemaPromise.then(([hedSchemas, issues]) => { - assert.isEmpty(issues, 'Schema loading issues occurred') + return hedSchemaPromise.then((hedSchemas) => { const strippedDollarsString = hed.validateUnits(dollarsString, currencyUnits, hedSchemas.baseSchema.attributes) const strippedVolumeString = hed.validateUnits(volumeString, volumeUnits, hedSchemas.baseSchema.attributes) const strippedPrefixedVolumeString = hed.validateUnits( diff --git a/validator/schema/init.js b/validator/schema/init.js index afd4f074..e3a6cc51 100644 --- a/validator/schema/init.js +++ b/validator/schema/init.js @@ -73,23 +73,20 @@ const buildSchemaObjects = function (xmlData) { * Build a schema collection object from a schema specification. * * @param {SchemasSpec} schemaSpecs The description of which schemas to use. - * @returns {Promise|Promise<[Schemas, Issue[]]>} The schema container object and any issues found. + * @returns {Promise} The schema container object and any issues found. */ export const buildSchemas = function (schemaSpecs) { const schemaKeys = Array.from(schemaSpecs.data.keys()) /* Data format example: - * [[[xmlData, issues], ...], [[xmlData, issues], [xmlData, issues], ...]] */ + * [[xmlData, ...], [xmlData, xmlData, ...], ...] */ return Promise.all( schemaKeys.map((k) => { const specs = schemaSpecs.data.get(k) return Promise.all(specs.map((spec) => loadSchema(spec))) }), - ).then((schemaXmlDataAndIssues) => { - const [schemaXmlData, schemaXmlIssues] = zip( - ...schemaXmlDataAndIssues.map((schemaKeyXmlDataAndIssues) => zip(...schemaKeyXmlDataAndIssues)), - ) + ).then((schemaXmlData) => { const schemaObjects = schemaXmlData.map(buildSchemaObjects) const schemas = new Map(zip(schemaKeys, schemaObjects)) - return [new Schemas(schemas), schemaXmlIssues.flat(2)] + return new Schemas(schemas) }) } From e0d085df8ad78fe6b74076ff556746f973422661 Mon Sep 17 00:00:00 2001 From: Alexander Jones Date: Fri, 24 May 2024 10:33:26 -0500 Subject: [PATCH 3/4] Convert BidsHedValidator to async --- bids/validate.js | 30 +++++++++++++----------------- 1 file changed, 13 insertions(+), 17 deletions(-) diff --git a/bids/validate.js b/bids/validate.js index 545453f7..d745b198 100644 --- a/bids/validate.js +++ b/bids/validate.js @@ -60,25 +60,21 @@ class BidsHedValidator { /** * Validate a full BIDS dataset using a HED schema collection. * - * @returns {Promise|Promise} Any issues found. + * @returns {Promise} Any issues found. */ - validateFullDataset() { - try { - const sidecarValidator = new BidsHedSidecarValidator(this.dataset, this.hedSchemas) - const hedColumnValidator = new BidsHedColumnValidator(this.dataset, this.hedSchemas) - const sidecarErrorsFound = this._pushIssues(sidecarValidator.validateSidecars()) - const hedColumnErrorsFound = this._pushIssues(hedColumnValidator.validate()) - if (sidecarErrorsFound || hedColumnErrorsFound) { - return Promise.resolve(this.issues) - } - for (const eventFileData of this.dataset.eventData) { - const tsvValidator = new BidsHedTsvValidator(eventFileData, this.hedSchemas) - this.issues.push(...tsvValidator.validate()) - } - return Promise.resolve(this.issues) - } catch (e) { - return Promise.reject(e) + async validateFullDataset() { + const sidecarValidator = new BidsHedSidecarValidator(this.dataset, this.hedSchemas) + const hedColumnValidator = new BidsHedColumnValidator(this.dataset, this.hedSchemas) + const sidecarErrorsFound = this._pushIssues(sidecarValidator.validateSidecars()) + const hedColumnErrorsFound = this._pushIssues(hedColumnValidator.validate()) + if (sidecarErrorsFound || hedColumnErrorsFound) { + return this.issues + } + for (const eventFileData of this.dataset.eventData) { + const tsvValidator = new BidsHedTsvValidator(eventFileData, this.hedSchemas) + this.issues.push(...tsvValidator.validate()) } + return this.issues } /** From 9b6ad7cad216ab5ff223bdf66066cef5f1b0788b Mon Sep 17 00:00:00 2001 From: Alexander Jones Date: Fri, 24 May 2024 11:04:39 -0500 Subject: [PATCH 4/4] Remove BIDS HED-2G tests (BIDS does not support HED-2G) --- tests/bids.spec.data.js | 4 ++-- tests/bids.spec.js | 13 ------------- 2 files changed, 2 insertions(+), 15 deletions(-) diff --git a/tests/bids.spec.data.js b/tests/bids.spec.data.js index c803b745..dd9183f0 100644 --- a/tests/bids.spec.data.js +++ b/tests/bids.spec.data.js @@ -592,8 +592,8 @@ const tsvFiles = [ [sidecars[0][2], 'onset\tduration\tage\tHED\n' + '7\tferry\t30\tAge/30\n'], [sidecars[0][0], 'onset\tduration\tcolor\n' + '7\troyal\tpurple\n'], ], - // sub05 - Valid combined sidecar/TSV data from HED 2 - [[sidecars[3][0], 'onset\tduration\ttest\tHED\n' + '7\tsomething\tfirst\tEvent/Duration/55 ms']], + // sub05 - Valid combined sidecar/TSV data from HED 2 - Deprecated + [], // sub06 - Valid combined sidecar/TSV data with library [ [sidecars[4][0], 'onset\tduration\tevent_type\tsize\n' + '7\tn/a\tshow_face\t6\n' + '7\tn/a\tleft_press\t7\n'], diff --git a/tests/bids.spec.js b/tests/bids.spec.js index 3b3d329b..cde241cd 100644 --- a/tests/bids.spec.js +++ b/tests/bids.spec.js @@ -249,19 +249,6 @@ describe('BIDS datasets', () => { }, 10000) }) - describe.skip('HED 2 combined datasets', () => { - it('should validate HED 2 data in BIDS event files combined with JSON sidecar data', () => { - const goodDatasets = bidsTsvFiles[4] - const testDatasets = { - all_good: new BidsDataset(goodDatasets, []), - } - const expectedIssues = { - all_good: [], - } - return validator(testDatasets, expectedIssues, specs2) - }, 10000) - }) - describe('HED 3 library schema tests', () => { let goodEvents let goodDatasetDescriptions, badDatasetDescriptions