From a27308c853bfa400013514234a513f699cd7d876 Mon Sep 17 00:00:00 2001 From: Kay Robbins <1189050+VisLab@users.noreply.github.com> Date: Sun, 29 Dec 2024 06:17:26 -0600 Subject: [PATCH] Updated the tests and minor cleanup of docs --- bids/types/basic.js | 14 ++- bids/types/dataset.js | 2 +- bids/types/json.js | 22 ++-- bids/types/tsv.js | 13 ++- bids/utils.js | 6 +- bids/validator/sidecarValidator.js | 101 ++++++++-------- bids/validator/tsvValidator.js | 171 ++++++++++++++++++--------- bids/validator/validator.js | 43 +++++++ parser/eventManager.js | 2 +- spec_tests/javascriptTests.json | 146 +++++++++++++++++++++++ spec_tests/jsonTests.spec.js | 6 +- tests/bidsTests.spec.js | 14 +-- tests/testData/bidsTests.data.js | 182 +++++++++++++++++++++-------- 13 files changed, 534 insertions(+), 188 deletions(-) create mode 100644 bids/validator/validator.js diff --git a/bids/types/basic.js b/bids/types/basic.js index 662ae219..20e1793e 100644 --- a/bids/types/basic.js +++ b/bids/types/basic.js @@ -22,6 +22,12 @@ export class BidsFile { */ _validatorClass + /** + * + * @param {string} name - The name of the file -- used for messages. + * @param {object} file - The representation of the file for error messages. + * @param validatorClass + */ constructor(name, file, validatorClass) { this.name = name this.file = file @@ -31,17 +37,17 @@ export class BidsFile { /** * Determine whether this file has any HED data. * - * @returns {boolean} + * @returns {boolean} - True if this file has HED data. */ hasHedData() { return false } /** - * Validate this validator's tsv file + * Validate this validator's tsv file. * - * @param {Schemas} schemas - * @returns {BidsIssue[]} Any issues found during validation of this TSV file. + * @param {Schemas} schemas - The HED schemas used to validate this file. + * @returns {BidsIssue[]} - Any issues found during validation of this TSV file. */ validate(schemas) { if (!this.hasHedData()) { diff --git a/bids/types/dataset.js b/bids/types/dataset.js index ede11909..dc956d49 100644 --- a/bids/types/dataset.js +++ b/bids/types/dataset.js @@ -5,7 +5,7 @@ export class BidsDataset { */ eventData /** - * The dataset's sidecar data. + * The dataset's bidsFile data. * @type {BidsSidecar[]} */ sidecarData diff --git a/bids/types/json.js b/bids/types/json.js index de3f3f57..15fbfebe 100644 --- a/bids/types/json.js +++ b/bids/types/json.js @@ -28,17 +28,17 @@ export class BidsJsonFile extends BidsFile { export class BidsSidecar extends BidsJsonFile { /** - * The extracted keys for this sidecar. + * The extracted keys for this bidsFile. * @type {Map} */ sidecarKeys /** - * The extracted HED data for this sidecar. + * The extracted HED data for this bidsFile. * @type {Map>} */ hedData /** - * The parsed HED data for this sidecar. + * The parsed HED data for this bidsFile. * @type {Map>} */ parsedHedData @@ -72,7 +72,7 @@ export class BidsSidecar extends BidsJsonFile { /** * Constructor. * - * @param {string} name The name of the sidecar file. + * @param {string} name The name of the bidsFile file. * @param {Object} sidecarData The raw JSON data. * @param {Object} file The file object representing this file. * @param {DefinitionManager } defManager - The external definitions to use @@ -99,7 +99,7 @@ export class BidsSidecar extends BidsJsonFile { } /** - * Create the sidecar key map from the JSON. + * Create the bidsFile key map from the JSON. * @private */ _filterHedStrings() { @@ -173,7 +173,7 @@ export class BidsSidecar extends BidsJsonFile { } /** - * Parse this sidecar's HED strings within the sidecar structure. + * Parse this bidsFile's HED strings within the bidsFile structure. * * The parsed strings are placed into {@link parsedHedData}. * @@ -197,7 +197,7 @@ export class BidsSidecar extends BidsJsonFile { } /** - * Generate a mapping of an individual BIDS sidecar's curly brace references. + * Generate a mapping of an individual BIDS bidsFile's curly brace references. * * @private */ @@ -212,7 +212,7 @@ export class BidsSidecar extends BidsJsonFile { this._parseCategorySplice(sidecarKey, hedData) } else if (hedData) { IssueError.generateAndThrow('internalConsistencyError', { - message: 'Unexpected type found in sidecar parsedHedData map.', + message: 'Unexpected type found in bidsFile parsedHedData map.', }) } } @@ -289,7 +289,7 @@ export class BidsSidecarKey { */ parsedValueString /** - * Weak reference to the sidecar. + * Weak reference to the bidsFile. * @type {BidsSidecar} */ sidecar @@ -301,7 +301,7 @@ export class BidsSidecarKey { * * @param {string} key The name of this key. * @param {string|Object} data The data for this key. - * @param {BidsSidecar} sidecar The parent sidecar. + * @param {BidsSidecar} sidecar The parent bidsFile. */ constructor(key, data, sidecar) { this.name = key @@ -331,7 +331,7 @@ export class BidsSidecarKey { } /** - * Parse the value string in a sidecar + * Parse the value string in a bidsFile * @param {Schemas} hedSchemas - The HED schemas to use. * @param {boolean} fullCheck - If true, then assume in final form and not up for potential splice. * @returns {Issue[]} diff --git a/bids/types/tsv.js b/bids/types/tsv.js index c2b116d6..13c7906f 100644 --- a/bids/types/tsv.js +++ b/bids/types/tsv.js @@ -26,7 +26,7 @@ export class BidsTsvFile extends BidsFile { */ potentialSidecars /** - * The pseudo-sidecar object representing the merged sidecar data. + * The pseudo-bidsFile object representing the merged bidsFile data. * @type {BidsSidecar} */ mergedSidecar @@ -40,7 +40,7 @@ export class BidsTsvFile extends BidsFile { * @param {{headers: string[], rows: string[][]}|Map|string} tsvData This file's TSV data. * @param {object} file The file object representing this file. * @param {string[]} potentialSidecars The list of potential JSON sidecars. - * @param {object} mergedDictionary The merged sidecar data. + * @param {object} mergedDictionary The merged bidsFile data. * @param {DefinitionManager} defManager */ constructor(name, tsvData, file, potentialSidecars = [], mergedDictionary = {}, defManager) { @@ -147,6 +147,15 @@ export class BidsTsvElement { const onsetString = this.onset ? ` with onset=${this.onset.toString()}` : '' return this.hedString + ` in TSV file "${this.fileName}" at line(s) ${this.tsvLine}` + onsetString } + + /** + * Create a string list of the + * @param BidsTsvElements[] elements - A list of elements to construct line numbers from. + * @returns {string} - A string with the list of line numbers for error messages. + */ + static getTsvLines(elements) { + return elements.map((element) => element.tsvLine).join(',') + } } /** diff --git a/bids/utils.js b/bids/utils.js index cf5341b8..a6a61e39 100644 --- a/bids/utils.js +++ b/bids/utils.js @@ -1,8 +1,8 @@ /** - * Determine whether a sidecar value has HED data. + * Determine whether a bidsFile value has HED data. * - * @param {object} sidecarValue A BIDS sidecar value. - * @returns {boolean} Whether the sidecar value has HED data. + * @param {object} sidecarValue A BIDS bidsFile value. + * @returns {boolean} Whether the bidsFile value has HED data. */ export const sidecarValueHasHed = function (sidecarValue) { return sidecarValue !== null && typeof sidecarValue === 'object' && sidecarValue.HED !== undefined diff --git a/bids/validator/sidecarValidator.js b/bids/validator/sidecarValidator.js index d01e7730..0fbfc3b6 100644 --- a/bids/validator/sidecarValidator.js +++ b/bids/validator/sidecarValidator.js @@ -1,117 +1,116 @@ import { BidsHedIssue } from '../types/issues' import ParsedHedString from '../../parser/parsedHedString' -// IMPORTANT: This import cannot be shortened to '../../validator', as this creates a circular dependency until v4.0.0. -//import { validateHedString } from '../../validator/event/init' import { generateIssue, IssueError } from '../../common/issues/issues' import { getCharacterCount } from '../../utils/string.js' +import { BidsValidator } from './validator' + /** * Validator for HED data in BIDS JSON sidecars. */ -export class BidsHedSidecarValidator { - /** - * The BIDS sidecar being validated. - * @type {BidsSidecar} - */ - sidecar - /** - * The HED schema collection being validated against. - * @type {Schemas} - */ - hedSchemas - /** - * The issues found during validation. - * @type {BidsIssue[]} - */ - issues - +export class BidsHedSidecarValidator extends BidsValidator { /** - * Constructor. + * Constructor for the BidsHedSidecarValidator. * - * @param {BidsSidecar} sidecar The BIDS sidecar being validated. - * @param {Schemas} hedSchemas + * @param {BidsSidecar} sidecar - The BIDS bidsFile being validated. + * @param {Schemas} hedSchemas - The schemas used for the sidecar validation. */ constructor(sidecar, hedSchemas) { - this.sidecar = sidecar - this.hedSchemas = hedSchemas - this.issues = [] + super(sidecar, hedSchemas) } /** - * Validate a BIDS JSON sidecar file. This method returns the complete issue list for convenience. + * Validate a BIDS JSON bidsFile file. This method returns the complete issue list for convenience. * - * @returns {BidsIssue[]} Any issues found during validation of this sidecar file. + * @returns {BidsIssue[]} - Any issues found during validation of this bidsFile file. */ validate() { // Allow schema to be set a validation time -- this is checked by the superclass of BIDS file const sidecarParsingIssues = BidsHedIssue.fromHedIssues( - this.sidecar.parseHedStrings(this.hedSchemas), - this.sidecar.file, + this.bidsFile.parseHedStrings(this.hedSchemas), + this.bidsFile.file, ) this.issues.push(...sidecarParsingIssues) if (sidecarParsingIssues.length > 0) { return this.issues } - this.issues.push(...this._validateStrings(), ...this.validateCurlyBraces()) + this.issues.push(...this._validateStrings(), ...this._validateCurlyBraces()) return this.issues } /** - * Validate this sidecar's HED strings. + * Validate this bidsFile's HED strings. * * @returns {BidsIssue[]} All issues found. */ _validateStrings() { const issues = [] - for (const [sidecarKeyName, hedData] of this.sidecar.parsedHedData) { + for (const [sidecarKeyName, hedData] of this.bidsFile.parsedHedData) { if (hedData instanceof ParsedHedString) { - // Value options have HED as string + // Value options have HED as string. issues.push(...this._checkDetails(sidecarKeyName, hedData, true)) } else if (hedData instanceof Map) { - // Categorical options have HED as a Map + // Categorical options have HED as a Map. for (const valueString of hedData.values()) { - const placeholdersAllowed = this.sidecar.sidecarKeys.get(sidecarKeyName).hasDefinitions + const placeholdersAllowed = this.bidsFile.sidecarKeys.get(sidecarKeyName).hasDefinitions issues.push(...this._checkDetails(sidecarKeyName, valueString, placeholdersAllowed)) } } else { IssueError.generateAndThrow('internalConsistencyError', { - message: 'Unexpected type found in sidecar parsedHedData map.', + message: 'Unexpected type found in bidsFile parsedHedData map.', }) } } return issues } + /** + * Check definitions and placeholders for a string associated with a sidecar key. + * + * @param {string} sidecarKeyName - The name of the sidecar key associated with string to be checked. + * @param {ParsedHedString} hedString - The parsed string to be checked. + * @returns {BidsHedIssue[]} - Issues associated with the check. + * @private + */ _checkDetails(sidecarKeyName, hedString) { const issues = this._checkDefs(sidecarKeyName, hedString, true) issues.push(...this._checkPlaceholders(sidecarKeyName, hedString)) return issues } - _checkDefs(sidecarKeyName, sidecarString, placeholdersAllowed) { - let issues = this.sidecar.definitions.validateDefs(sidecarString, this.hedSchemas, placeholdersAllowed) + /** + * Validate the Def and Def-expand usage against the sidecar definitions. + * + * @param {string} sidecarKeyName - Name of the sidecar key for this HED string + * @param {ParsedHedString} hedString - The parsed HED string object associated with this key. + * @param {boolean} placeholdersAllowed - If true, placeholders are allowed here. + * @returns {BidsHedIssue[]} - Issues encountered such as missing definitions or improper Def-expand values. + * @private + */ + _checkDefs(sidecarKeyName, hedString, placeholdersAllowed) { + let issues = this.bidsFile.definitions.validateDefs(hedString, this.hedSchemas, placeholdersAllowed) if (issues.length > 0) { - return BidsHedIssue.fromHedIssues(issues, this.sidecar.file, { sidecarKeyName: sidecarKeyName }) + return BidsHedIssue.fromHedIssues(issues, this.bidsFile.file, { sidecarKeyName: sidecarKeyName }) } - issues = this.sidecar.definitions.validateDefExpands(sidecarString, this.hedSchemas, placeholdersAllowed) - return BidsHedIssue.fromHedIssues(issues, this.sidecar.file, { sidecarKeyName: sidecarKeyName }) + issues = this.bidsFile.definitions.validateDefExpands(hedString, this.hedSchemas, placeholdersAllowed) + return BidsHedIssue.fromHedIssues(issues, this.bidsFile.file, { sidecarKeyName: sidecarKeyName }) } _checkPlaceholders(sidecarKeyName, hedString) { const numberPlaceholders = getCharacterCount(hedString.hedString, '#') - const sidecarKey = this.sidecar.sidecarKeys.get(sidecarKeyName) + const sidecarKey = this.bidsFile.sidecarKeys.get(sidecarKeyName) if (!sidecarKey.valueString && !sidecarKey.hasDefinitions && numberPlaceholders > 0) { return [ BidsHedIssue.fromHedIssue( generateIssue('invalidSidecarPlaceholder', { column: sidecarKeyName, string: hedString.hedString }), - this.sidecar.file, + this.bidsFile.file, ), ] } else if (sidecarKey.valueString && numberPlaceholders === 0) { return [ BidsHedIssue.fromHedIssue( generateIssue('missingPlaceholder', { column: sidecarKeyName, string: hedString.hedString }), - this.sidecar.file, + this.bidsFile.file, ), ] } @@ -119,7 +118,7 @@ export class BidsHedSidecarValidator { return [ BidsHedIssue.fromHedIssue( generateIssue('invalidSidecarPlaceholder', { column: sidecarKeyName, string: hedString.hedString }), - this.sidecar.file, + this.bidsFile.file, ), ] } @@ -127,13 +126,13 @@ export class BidsHedSidecarValidator { } /** - * Validate this sidecar's curly braces. + * Validate this bidsFile's curly braces -- checking recursion and missing columns. * * @returns {BidsIssue[]} All issues found. */ - validateCurlyBraces() { + _validateCurlyBraces() { const issues = [] - const references = this.sidecar.columnSpliceMapping + const references = this.bidsFile.columnSpliceMapping for (const [key, referredKeys] of references) { for (const referredKey of referredKeys) { @@ -141,15 +140,15 @@ export class BidsHedSidecarValidator { issues.push( BidsHedIssue.fromHedIssue( generateIssue('recursiveCurlyBracesWithKey', { column: referredKey, referrer: key }), - this.sidecar.file, + this.bidsFile.file, ), ) } - if (!this.sidecar.parsedHedData.has(referredKey) && referredKey !== 'HED') { + if (!this.bidsFile.parsedHedData.has(referredKey) && referredKey !== 'HED') { issues.push( BidsHedIssue.fromHedIssue( generateIssue('undefinedCurlyBraces', { column: referredKey }), - this.sidecar.file, + this.bidsFile.file, ), ) } diff --git a/bids/validator/tsvValidator.js b/bids/validator/tsvValidator.js index d6c40c19..0c57b426 100644 --- a/bids/validator/tsvValidator.js +++ b/bids/validator/tsvValidator.js @@ -1,5 +1,6 @@ import { BidsHedIssue, BidsIssue } from '../types/issues' -import { BidsTsvRow } from '../types/tsv' +import { BidsTsvElement, BidsTsvRow } from '../types/tsv' +import { BidsValidator } from './validator' import { parseHedString } from '../../parser/parser' import ParsedHedString from '../../parser/parsedHedString' import { generateIssue } from '../../common/issues/issues' @@ -10,45 +11,33 @@ import { EventManager } from '../../parser/eventManager' /** * Validator for HED data in BIDS TSV files. */ -export class BidsHedTsvValidator { +export class BidsHedTsvValidator extends BidsValidator { /** * The BIDS TSV file being validated. - * @type {BidsTsvFile} - */ - tsvFile - /** - * The HED schema collection being validated against. - * @type {Schemas} - */ - hedSchemas - /** - * The issues found during validation. - * @type {BidsIssue[]} + * @type {ReservedChecker} */ - issues + special /** * Constructor. * - * @param {BidsTsvFile} tsvFile The BIDS TSV file being validated. - * @param {Schemas} hedSchemas + * @param {BidsTsvFile} tsvFile - The BIDS TSV file being validated. + * @param {Schemas} hedSchemas - The HED schemas used to validate the tsv file. */ constructor(tsvFile, hedSchemas) { - this.tsvFile = tsvFile - this.hedSchemas = hedSchemas // Will be set when the file is validated + super(tsvFile, hedSchemas) this.special = ReservedChecker.getInstance() - this.issues = [] } /** * Validate a BIDS TSV file. This method returns the complete issue list for convenience. * - * @returns {BidsIssue[]} Any issues found during validation of this TSV file. + * @returns {BidsIssue[]} - Any issues found during validation of this TSV file. */ validate() { - // Validate the BIDS sidecar if it exists. - if (this.tsvFile.mergedSidecar) { - const sidecarIssues = this.tsvFile.mergedSidecar.validate(this.hedSchemas) + // Validate the BIDS bidsFile if it exists. + if (this.bidsFile.mergedSidecar) { + const sidecarIssues = this.bidsFile.mergedSidecar.validate(this.hedSchemas) this.issues.push(...sidecarIssues) if (BidsIssue.anyAreErrors(sidecarIssues)) { return this.issues @@ -62,7 +51,7 @@ export class BidsHedTsvValidator { return this.issues } // Now do a full validation - const bidsHedTsvParser = new BidsHedTsvParser(this.tsvFile, this.hedSchemas) + const bidsHedTsvParser = new BidsHedTsvParser(this.bidsFile, this.hedSchemas) const [bidsEvents, parsingIssues] = bidsHedTsvParser.parse() this.issues.push(...parsingIssues) if (!BidsIssue.anyAreErrors(this.issues)) { @@ -74,15 +63,14 @@ export class BidsHedTsvValidator { /** * Validate this TSV file's HED column. * - * @returns {BidsIssue[]} All issues found. + * @returns {BidsIssue[]} - Issues found in validating the HED column without sidecar information. * @private */ _validateHedColumn() { - if (this.tsvFile.hedColumnHedStrings.length === 0) { - // no HED column strings to validate + if (this.bidsFile.hedColumnHedStrings.length === 0) { return [] } - return this.tsvFile.hedColumnHedStrings.flatMap((hedString, rowIndexMinusTwo) => + return this.bidsFile.hedColumnHedStrings.flatMap((hedString, rowIndexMinusTwo) => this._validateHedColumnString(hedString, rowIndexMinusTwo + 2), ) } @@ -90,9 +78,9 @@ export class BidsHedTsvValidator { /** * Validate a string in this TSV file's HED column. * - * @param {string} hedString The string to be validated. - * @param {number} rowIndex The index of this row in the TSV file. - * @returns {BidsIssue[]} All issues found. + * @param {string} hedString - The string to be validated. + * @param {number} rowIndex - The index of this row in the TSV file. + * @returns {BidsIssue[]} - Specific issues found in validating the HED column * @private */ _validateHedColumnString(hedString, rowIndex) { @@ -100,14 +88,15 @@ export class BidsHedTsvValidator { return [] } + // Find basic parsing issues and return if unable to parse the string. (Warnings are okay.) const issues = [] const [parsedString, parsingIssues] = parseHedString(hedString, this.hedSchemas, false, false, false) - issues.push(...BidsHedIssue.fromHedIssues(parsingIssues, this.tsvFile.file, { tsvLine: rowIndex })) - + issues.push(...BidsHedIssue.fromHedIssues(parsingIssues, this.bidsFile.file, { tsvLine: rowIndex })) if (parsedString === null) { return issues } + // The HED column is not allowed to have column splices. if (parsedString.columnSplices.length > 0) { issues.push( BidsHedIssue.fromHedIssue( @@ -115,57 +104,119 @@ export class BidsHedTsvValidator { string: parsedString.hedString, tsvLine: rowIndex.toString(), }), - this.tsvFile.file, + this.bidsFile.file, ), ) return issues } + // Check whether definitions used exist and are used correctly. const defIssues = [ - ...this.tsvFile.mergedSidecar.definitions.validateDefs(parsedString, this.hedSchemas, false), - ...this.tsvFile.mergedSidecar.definitions.validateDefExpands(parsedString, this.hedSchemas, false), + ...this.bidsFile.mergedSidecar.definitions.validateDefs(parsedString, this.hedSchemas, false), + ...this.bidsFile.mergedSidecar.definitions.validateDefExpands(parsedString, this.hedSchemas, false), ] - const convertedIssues = BidsHedIssue.fromHedIssues(defIssues, this.tsvFile.file, { tsvLine: rowIndex }) + const convertedIssues = BidsHedIssue.fromHedIssues(defIssues, this.bidsFile.file, { tsvLine: rowIndex }) issues.push(...convertedIssues) return issues } /** - * Validate the HED data in a combined event TSV file/sidecar BIDS data collection. + * Validate the HED data in a combined event TSV file/bidsFile BIDS data collection. * - * @param {BidsTsvElement[]} elements - The HED strings in the data collection. - * @returns {BidsHedIssue[]} - errors for dataset + * @param {BidsTsvElement[]} elements - The element objects, which include the strings and row information. + * @returns {BidsHedIssue[]} - The errors resulting from final validation, including dataset-level checks. */ validateDataset(elements) { + // Final top-tag detection cannot be done until the strings are fully assembled and finalized. const issues = this._checkNoTopTags(elements) if (issues.length > 0) { return issues } - if (this.tsvFile.isTimelineFile) { + // Temporal files have to check Onset, Inset, Offset consistency. + if (this.bidsFile.isTimelineFile) { return this._validateTemporal(elements) } + // Non-temporal files cannot have temporal tags. return this._checkNoTime(elements) } /** * Check the temporal relationships among events. + * * @param {BidsTsvElement[]} elements - The elements representing the tsv file. - * @returns {BidsHedIssue[]} - Errors in temporal relationships among events + * @returns {BidsHedIssue[]} - Errors in temporal relationships among events. * @private */ _validateTemporal(elements) { + // Check basic temporal conflicts such as Offset before Onset, or temporal tags with same def at same time. const eventManager = new EventManager() const [eventList, temporalIssues] = eventManager.parseEvents(elements) if (temporalIssues.length > 0) { return temporalIssues } + // There still may be non-temporal duplicates when multiple rows with the same onset. + const duplicateErrors = this._checkDuplicatesAcrossRows(elements) + if (duplicateErrors.length > 0) { + return duplicateErrors + } return eventManager.validate(eventList) } + /** + * Check for duplicate tags when multiple rows with the same onset. + * + * @param {BidsTsvElement[]} elements - The elements representing the tsv file. + * @returns {BidsHedIssue[]} - Errors in temporal relationships among events. + * @private + * + * Note: duplicate onsets are relatively rare and duplicates for single rows are checked when a ParsedHedString is + * constructed. + */ + _checkDuplicatesAcrossRows(elements) { + const duplicateMap = this._getOnsetMap(elements) + const issues = [] + for (const elementList of duplicateMap.values()) { + if (elementList.length === 1) { + continue + } + // Assemble the HED strings associated with same onset into single string. Use the parse duplicate detection. + const rowString = elementList.map((element) => element.hedString).join(',') + const [parsedString, parsingIssues] = parseHedString(rowString, this.hedSchemas, true, false, false) + if (parsingIssues.length > 0) { + const tsvLines = BidsTsvElement.getTsvLines(elementList) + issues.push(...BidsHedIssue.fromHedIssues(parsingIssues, this.bidsFile.file, { tsvLine: tsvLines })) + } + } + return issues + } + + /** + * Get map of onsets to BidsTsvElements. + * + * @param {BidsTsvElement[]} elements - The elements representing the tsv file. + * @returns {Map} - Map of onset value to a list of elements with that onset. + * @private + */ + _getOnsetMap(elements) { + const onsetMap = new Map() + for (const element of elements) { + if (!element.hedString) { + continue + } + if (onsetMap.has(element.onset)) { + onsetMap.get(element.onset).push(element) + } else { + onsetMap.set(element.onset, [element]) + } + } + return onsetMap + } + /** * Top group tag requirements may not be satisfied until all splices have been done. - * @param {BidsTsvElement[]} elements - The elements to be checked - * @returns {BidsHedIssue[]} - Issues from final check of top groups + * + * @param {BidsTsvElement[]} elements - The elements to be checked. + * @returns {BidsHedIssue[]} - Issues from final check of top groups. * @private */ _checkNoTopTags(elements) { @@ -189,7 +240,8 @@ export class BidsHedTsvValidator { /** * Verify that this non-temporal file does not contain any temporal tags. * - * @param {BidsTsvElement[]} elements + * @param {BidsTsvElement[]} elements - The elements representing a tsv file (with HED string parsed). + * @returns {BidsHedIssue[]} - Issues from checking non-temporal files for temporal tags. */ _checkNoTime(elements) { const timeIssues = [] @@ -198,7 +250,7 @@ export class BidsHedTsvValidator { timeIssues.push( BidsHedIssue.fromHedIssue( generateIssue('temporalTagInNonTemporalContext', { string: element.hedString, tsvLine: element.tsvLine }), - this.tsvFile.file, + this.bidsFile.file, ), ) } @@ -207,6 +259,9 @@ export class BidsHedTsvValidator { } } +/** + * Class that performs basic parsing and splicing. + */ export class BidsHedTsvParser { static nullSet = new Set([null, undefined, '', 'n/a']) static braceRegEx = /\{([^{}]*?)\}/g @@ -237,9 +292,9 @@ export class BidsHedTsvParser { } /** - * Combine the BIDS sidecar HED data into a BIDS TSV file's HED data. + * Combine the BIDS bidsFile HED data into a BIDS TSV file's HED data. * - * @returns {[BidsTsvElement[], BidsHedIssue[]]} The combined HED string collection for this BIDS TSV file. + * @returns {[BidsTsvElement[], BidsHedIssue[]]} The combined HED string collection for the BIDS TSV file. */ parse() { const tsvHedRows = this._generateHedRows() @@ -249,18 +304,19 @@ export class BidsHedTsvParser { } /** - * Parse element HED strings - * @param { BidsTsvElement []} tsvElements - - * @returns {BidsHedIssue[]} + * Parse element HED strings. + * + * @param { BidsTsvElement []} elements - The objects representing tsv rows with their parsed HEd strings. + * @returns {BidsHedIssue[]} - The issues resulting in creating the parsed HED strings. */ - _parseElementStrings(tsvElements) { - if (tsvElements.length === 0) { + _parseElementStrings(elements) { + if (elements.length === 0) { return [] } // Add the parsed HED strings to the elements and quite if there are serious errors const cummulativeIssues = [] - for (const element of tsvElements) { + for (const element of elements) { const [parsedHedString, parsingIssues] = parseHedString(element.hedString, this.hedSchemas, true, false, false) element.parsedHedString = parsedHedString if (parsingIssues.length > 0) { @@ -346,8 +402,8 @@ export class BidsHedTsvParser { /** * Generate a mapping from tsv columns to strings (may have splices in the strings) * - * @param {Map} rowCells The column-to-value mapping for a single row. - * @returns {Map} A mapping of column names to their corresponding parsed sidecar strings. + * @param {Map} rowCells - The column-to-value mapping for a single row. + * @returns {Map} - A mapping of column names to their corresponding parsed bidsFile strings. * @private */ _getColumnMapping(rowCells) { @@ -361,7 +417,7 @@ export class BidsHedTsvParser { return columnMap } - // Check for the columns with HED data in the sidecar + // Check for the columns with HED data in the bidsFile for (const [columnName, columnValues] of this.tsvFile.mergedSidecar.parsedHedData.entries()) { if (!rowCells.has(columnName)) { continue @@ -385,6 +441,7 @@ export class BidsHedTsvParser { /** * Update the map to splice-in the values for columns that have splices. + * * @param { Map } columnMap - Map of column name to HED string for a row. * * Note: Updates the map in place. diff --git a/bids/validator/validator.js b/bids/validator/validator.js new file mode 100644 index 00000000..559c843f --- /dev/null +++ b/bids/validator/validator.js @@ -0,0 +1,43 @@ +import { BidsIssue } from '../types/issues' + +/** + * Validator base class for HED data in BIDS TSV files. + */ +export class BidsValidator { + /** + * The BIDS file being validated. + * @type {BidsFile} + */ + bidsFile + /** + * The HED schema collection being validated against. + * @type {Schemas} + */ + hedSchemas + /** + * The issues found during validation. + * @type {BidsIssue[]} + */ + issues + + /** + * Bids validator base class. + * + * @param {BidsFile} bidsFile - The BIDS TSV file being validated. + * @param {Schemas} hedSchemas - The HED schemas used for validation. + */ + constructor(bidsFile, hedSchemas) { + this.bidsFile = bidsFile + this.hedSchemas = hedSchemas // Will be set when the file is validated + this.issues = [] + } + + /** + * Validate a BIDS TSV file. This method returns the complete issue list for convenience. + * + * @returns {BidsIssue[]} - Any issues found during validation of this TSV file. + */ + validate() { + return this.issues + } +} diff --git a/parser/eventManager.js b/parser/eventManager.js index 0c4c33ac..c39554d6 100644 --- a/parser/eventManager.js +++ b/parser/eventManager.js @@ -177,7 +177,7 @@ export class EventManager { // onset2: currentEvent.onset.toString(), // tsvLine2: currentEvent.element.tsvLine, // }), - // event.tsvFile, + // event.bidsFile, // ), // ] } diff --git a/spec_tests/javascriptTests.json b/spec_tests/javascriptTests.json index c27dc03b..e63f341b 100644 --- a/spec_tests/javascriptTests.json +++ b/spec_tests/javascriptTests.json @@ -3752,6 +3752,152 @@ } } }, + { + "error_code": "TAG_EXPRESSION_REPEATED", + "alt_codes": [], + "name": "tags-duplicated-across-multiple-rows", + "description": "Tags are repeated because two rows have the same onset value.", + "warning": false, + "schema": "8.3.0", + "definitions": ["(Definition/Acc/#, (Acceleration/# m-per-s^2, Red))", "(Definition/MyColor, (Label/Pie))"], + "tests": { + "string_tests": { + "fails": [], + "passes": [] + }, + "sidecar_tests": { + "fails": [], + "passes": [] + }, + "event_tests": { + "fails": [ + [ + ["onset", "duration", "HED"], + [4.5, 0, "(Red, Blue, Green)"], + [5.5, 0, "(Red, Blue, Green)"], + [4.5, 0, "(Blue, Red, Green)"] + ] + ], + "passes": [ + [ + ["onset", "duration", "HED"], + [4.5, 0, "(Red, Blue, Green)"], + [5.5, 0, "(Red, Blue, Green)"], + [4.5, 0, "(Blue, (Red, Green))"] + ] + ] + }, + "combo_tests": { + "fails": [ + { + "sidecar": { + "event_code": { + "HED": { + "ball": "(Green, ((Blue, Orange, (Black, Purple))), White), Blue, Orange" + } + } + }, + "events": [ + ["onset", "duration", "event_code", "HED"], + [4.5, 0, "ball", "(Red, Blue, Green)"], + [5.5, 0, "ball", "(Red, Blue, Green)"], + [4.5, 0, "n/a", "(Blue, Red, Green)"] + ] + } + ], + "passes": [ + { + "sidecar": { + "event_code": { + "HED": { + "ball": "(Green, ((Blue, Orange, (Black, Purple))), White), Blue, Orange" + } + } + }, + "events": [ + ["onset", "duration", "event_code", "HED"], + [4.5, 0, "ball", "(Red, Blue, Green)"], + [5.5, 0, "ball", "(Red, Blue, Green)"], + [4.5, 0, "n/a", "(Blue, (Red, Green))"] + ] + } + ] + } + } + }, + { + "error_code": "TAG_EXPRESSION_REPEATED", + "alt_codes": [], + "name": "tags-with-duplicated-onsets-across-multiple-rows", + "description": "Tags are repeated because two rows have the same onset value.", + "warning": false, + "schema": "8.3.0", + "definitions": ["(Definition/Acc/#, (Acceleration/# m-per-s^2, Red))", "(Definition/MyColor, (Label/Pie))"], + "tests": { + "string_tests": { + "fails": [], + "passes": [] + }, + "sidecar_tests": { + "fails": [], + "passes": [] + }, + "event_tests": { + "fails": [ + [ + ["onset", "duration", "HED"], + [4.5, 0, "(Onset, Def/MyColor)"], + [5.5, 0, "(Onset, Def/MyColor)"], + [4.5, 0, "(Def/MyColor, Onset)"] + ] + ], + "passes": [ + [ + ["onset", "duration", "HED"], + [4.5, 0, "(Onset, Def/MyColor)"], + [5.5, 0, "(Onset, Def/MyColor)"], + [4.5, 0, "(Blue, (Red, Green))"] + ] + ] + }, + "combo_tests": { + "fails": [ + { + "sidecar": { + "event_code": { + "HED": { + "ball": "(Green, ((Blue, Orange, (Black, Purple))), White), Blue, Orange" + } + } + }, + "events": [ + ["onset", "duration", "event_code", "HED"], + [4.5, 0, "ball", "(Onset, Def/MyColor)"], + [5.5, 0, "ball", "(Red, Blue, Green)"], + [4.5, 0, "n/a", "(Def/MyColor, Onset)"] + ] + } + ], + "passes": [ + { + "sidecar": { + "event_code": { + "HED": { + "ball": "(Green, ((Blue, Orange, (Black, Purple))), White), Blue, Orange" + } + } + }, + "events": [ + ["onset", "duration", "event_code", "HED"], + [4.5, 0, "ball", "(Onset, Def/MyColor)"], + [5.5, 0, "ball", "(Def/MyColor, Onset)"], + [4.5, 0, "n/a", "Red"] + ] + } + ] + } + } + }, { "error_code": "TAG_EXTENDED", "alt_codes": [], diff --git a/spec_tests/jsonTests.spec.js b/spec_tests/jsonTests.spec.js index 81b4fd7c..d631374a 100644 --- a/spec_tests/jsonTests.spec.js +++ b/spec_tests/jsonTests.spec.js @@ -17,9 +17,9 @@ const skipMap = new Map() const runAll = true //const runMap = new Map([['DEF_EXPAND_INVALID', ['def-expand-invalid-missing-placeholder']]]) //const runMap = new Map([['TAG_GROUP_ERROR', ['tag-group-error-missing']]]) -const runMap = new Map([['TAG_GROUP_ERROR', ['tag-group-error-missing']]]) +const runMap = new Map([['TAG_EXPRESSION_REPEATED', ['tags-duplicated-across-multiple-rows']]]) +//const runOnly = new Set(["eventsPass"]) const runOnly = new Set() - const skippedErrors = { VERSION_DEPRECATED: 'not handling in the spec tests.', ELEMENT_DEPRECATED: 'not handling tag deprecated in the spec tests.', @@ -185,7 +185,7 @@ describe('HED validation using JSON tests', () => { try { const defManager = new DefinitionManager() defManager.addDefinitions(defList) - const bidsSide = new BidsSidecar(`sidecar`, JSON.parse(side), { relativePath: 'sidecar test' }, defManager) + const bidsSide = new BidsSidecar(`sidecar`, JSON.parse(side), { relativePath: 'bidsFile test' }, defManager) issues = bidsSide.validate(hedSchema) } catch (e) { issues = [convertIssue(e)] diff --git a/tests/bidsTests.spec.js b/tests/bidsTests.spec.js index fb5a5316..d792dfa2 100644 --- a/tests/bidsTests.spec.js +++ b/tests/bidsTests.spec.js @@ -13,8 +13,8 @@ import { DefinitionManager } from '../parser/definitionManager' // Ability to select individual tests to run //const skipMap = new Map([['definition-tests', ['invalid-missing-definition-for-def', 'invalid-nested-definition']]]) const skipMap = new Map() -const runAll = false -const runMap = new Map([['temporal-tests', ['delayed-onset-with-offset-before-with-sidecar']]]) +const runAll = true +const runMap = new Map([['duplicate-tag-tests', ['invalid-duplicate-multiple-onset']]]) describe('BIDS validation', () => { const schemaMap = new Map([['8.3.0', undefined]]) @@ -52,7 +52,7 @@ describe('BIDS validation', () => { `${header}: input definitions "${test.definitions}" have conflicts "${defAddIssues}"`, ) - // Validate the sidecar by itself + // Validate the bidsFile by itself const sidecarName = test.testname + '.json' const bidsSidecar = new BidsSidecar( 'thisOne', @@ -64,7 +64,7 @@ describe('BIDS validation', () => { const sidecarIssues = bidsSidecar.validate(thisSchema) assertErrors(test, 'Sidecar only', test.sidecarErrors, sidecarIssues) - // Validate the events file with no sidecar + // Validate the events file with no bidsFile const eventName = test.testname + '.tsv' const parsedTsv = parseTSV(test.eventsString) assert.instanceOf(parsedTsv, Map, `${eventName} cannot be parsed`) @@ -81,7 +81,7 @@ describe('BIDS validation', () => { const noSideIssues = bidsTsv.validate(thisSchema) assertErrors(test, 'Events', test.tsvErrors, noSideIssues) - // Validate the events file with the sidecar (use the definitions from the sidecar) + // Validate the events file with the bidsFile (use the definitions from the bidsFile) const defManager3 = new DefinitionManager() defManager3.addDefinitions(defList) const bidsTsvSide = new BidsTsvFile( @@ -101,8 +101,8 @@ describe('BIDS validation', () => { if (shouldRun(name, test.testname, runAll, runMap, skipMap)) { validate(test) } else { - // eslint-disable-next-line no-console - //console.log(`----Skipping bidsTest ${name}: ${test.testname}`) + //eslint-disable-next-line no-console + console.log(`----Skipping bidsTest ${name}: ${test.testname}`) } }) } diff --git a/tests/testData/bidsTests.data.js b/tests/testData/bidsTests.data.js index 56bb7f4e..ed9add6e 100644 --- a/tests/testData/bidsTests.data.js +++ b/tests/testData/bidsTests.data.js @@ -8,7 +8,7 @@ export const bidsTestData = [ tests: [ { testname: 'no-hed-at-all-but-both-tsv-json-non-empty', - explanation: 'Neither the sidecar or tsv has HED but neither non-empty', + explanation: 'Neither the bidsFile or tsv has HED but neither non-empty', schemaVersion: '8.3.0', definitions: ['(Definition/Acc/#, (Acceleration/# m-per-s^2, Red))', '(Definition/MyColor, (Label/Pie))'], sidecar: { @@ -38,7 +38,7 @@ export const bidsTestData = [ }, { testname: 'empty-json-empty-tsv', - explanation: 'Both sidecar and tsv are empty except for white space', + explanation: 'Both bidsFile and tsv are empty except for white space', schemaVersion: '8.3.0', definitions: ['(Definition/Acc/#, (Acceleration/# m-per-s^2, Red))', '(Definition/MyColor, (Label/Pie))'], sidecar: {}, @@ -92,7 +92,7 @@ export const bidsTestData = [ tests: [ { testname: 'invalid-bad-tag-in-tsv', - explanation: 'Unrelated sidecar is valid but HED column tag is invalid', + explanation: 'Unrelated bidsFile is valid but HED column tag is invalid', schemaVersion: '8.3.0', definitions: ['(Definition/Acc/#, (Acceleration/# m-per-s^2, Red))', '(Definition/MyColor, (Label/Pie))'], sidecar: { @@ -173,8 +173,8 @@ export const bidsTestData = [ ], }, { - testname: 'valid-sidecar-tsv-curly-brace', - explanation: 'The sidecar is valid, but tsv HED column has braces}', + testname: 'valid-bidsFile-tsv-curly-brace', + explanation: 'The bidsFile is valid, but tsv HED column has braces}', schemaVersion: '8.3.0', definitions: ['(Definition/Acc/#, (Acceleration/# m-per-s^2, Red))', '(Definition/MyColor, (Label/Pie))'], sidecar: { @@ -188,14 +188,14 @@ export const bidsTestData = [ sidecarErrors: [], tsvErrors: [ BidsHedIssue.fromHedIssue(generateIssue('curlyBracesInHedColumn', { string: 'Red,{blue}', tsvLine: '2' }), { - path: 'valid-sidecar-tsv-curly-brace.tsv', - relativePath: 'valid-sidecar-tsv-curly-brace.tsv', + path: 'valid-bidsFile-tsv-curly-brace.tsv', + relativePath: 'valid-bidsFile-tsv-curly-brace.tsv', }), ], comboErrors: [ BidsHedIssue.fromHedIssue(generateIssue('curlyBracesInHedColumn', { string: 'Red,{blue}', tsvLine: '2' }), { - path: 'valid-sidecar-tsv-curly-brace.tsv', - relativePath: 'valid-sidecar-tsv-curly-brace.tsv', + path: 'valid-bidsFile-tsv-curly-brace.tsv', + relativePath: 'valid-bidsFile-tsv-curly-brace.tsv', }), ], }, @@ -482,6 +482,92 @@ export const bidsTestData = [ ), ], }, + { + testname: 'invalid-duplicate-multiple-rows', + explanation: 'Duplicates across rows.', + schemaVersion: '8.3.0', + definitions: ['(Definition/Acc/#, (Acceleration/# m-per-s^2, Red))', '(Definition/MyColor, (Label/Pie))'], + sidecar: { + event_code: { + HED: { + ball: '(Green, ((Blue, Orange, (Black, Purple))), White), Blue, Orange', + }, + }, + }, + eventsString: + 'onset\tduration\tevent_code\tHED\n' + + '19\t0\tball\t(Red, Blue, Green)\n' + + '20\t0\tball\t(Blue, Red, Green)\n19\t0\tn/a\t(Blue, Red, Green)\n', + sidecarErrors: [], + tsvErrors: [ + BidsHedIssue.fromHedIssue( + generateIssue('duplicateTag', { + tags: '[(Blue,Green,Red)]', + string: '(Red, Blue, Green),(Blue, Red, Green)', + }), + { + path: 'invalid-duplicate-multiple-rows.tsv', + relativePath: 'invalid-duplicate-multiple-rows.tsv', + }, + ), + ], + comboErrors: [ + BidsHedIssue.fromHedIssue( + generateIssue('duplicateTag', { + tags: '[(Blue,Green,Red)]', + string: + '(Green, ((Blue, Orange, (Black, Purple))), White), Blue, Orange,(Red, Blue, Green),(Blue, Red, Green)', + }), + { + path: 'invalid-duplicate-multiple-rows.tsv', + relativePath: 'invalid-duplicate-multiple-rows.tsv', + }, + ), + ], + }, + { + testname: 'invalid-duplicate-multiple-onset', + explanation: 'Duplicates across rows because onsets are the same.', + schemaVersion: '8.3.0', + definitions: ['(Definition/Acc/#, (Acceleration/# m-per-s^2, Red))', '(Definition/MyColor, (Label/Pie))'], + sidecar: { + event_code: { + HED: { + ball: '(Green, ((Blue, Orange, (Black, Purple))), White), Blue, Orange', + }, + }, + }, + eventsString: + 'onset\tduration\tevent_code\tHED\n' + + '19\t0\tball\t(Onset, Def/MyColor)\n' + + '20\t0\tball\t(Blue, Red, Green)\n19\t0\tn/a\t(Def/MyColor, Onset)\n', + sidecarErrors: [], + tsvErrors: [ + BidsHedIssue.fromHedIssue( + generateIssue('duplicateTag', { + tags: '[(Def/MyColor,Onset)]', + string: '(Onset, Def/MyColor),(Def/MyColor, Onset)', + }), + { + path: 'invalid-duplicate-multiple-onset.tsv', + relativePath: 'invalid-duplicate-multiple-onset.tsv', + }, + ), + ], + comboErrors: [ + BidsHedIssue.fromHedIssue( + generateIssue('duplicateTag', { + tags: '[(Def/MyColor,Onset)]', + string: + '(Green, ((Blue, Orange, (Black, Purple))), White), Blue, Orange,(Onset, Def/MyColor),(Def/MyColor, Onset)', + }), + { + path: 'invalid-duplicate-multiple-onset.tsv', + relativePath: 'invalid-duplicate-multiple-onset.tsv', + }, + ), + ], + }, ], }, { @@ -489,8 +575,8 @@ export const bidsTestData = [ description: 'Curly braces tested in various places', tests: [ { - testname: 'valid-curly-brace-in-sidecar-with-value-splice', - explanation: 'Valid curly brace in sidecar and valid value is spliced in', + testname: 'valid-curly-brace-in-bidsFile-with-value-splice', + explanation: 'Valid curly brace in bidsFile and valid value is spliced in', schemaVersion: '8.3.0', definitions: ['(Definition/Acc/#, (Acceleration/# m-per-s^2, Red))', '(Definition/MyColor, (Label/Pie))'], sidecar: { @@ -511,8 +597,8 @@ export const bidsTestData = [ comboErrors: [], }, { - testname: 'valid-curly-brace-in-sidecar-with-tsv-n/a', - explanation: 'Valid curly brace in sidecar and valid tsv with n/a', + testname: 'valid-curly-brace-in-bidsFile-with-tsv-n/a', + explanation: 'Valid curly brace in bidsFile and valid tsv with n/a', schemaVersion: '8.3.0', definitions: ['(Definition/Acc/#, (Acceleration/# m-per-s^2, Red))', '(Definition/MyColor, (Label/Pie))'], sidecar: { @@ -534,8 +620,8 @@ export const bidsTestData = [ comboErrors: [], }, { - testname: 'valid-curly-brace-in-sidecar-with-category-splice', - explanation: 'Valid curly brace in sidecar and valid value is spliced in', + testname: 'valid-curly-brace-in-bidsFile-with-category-splice', + explanation: 'Valid curly brace in bidsFile and valid value is spliced in', schemaVersion: '8.3.0', definitions: ['(Definition/Acc/#, (Acceleration/# m-per-s^2, Red))', '(Definition/MyColor, (Label/Pie))'], sidecar: { @@ -558,8 +644,8 @@ export const bidsTestData = [ comboErrors: [], }, { - testname: 'valid-curly-brace-in-sidecar-with-n/a-splice', - explanation: 'Valid curly brace in sidecar and but tsv splice entry is n/a', + testname: 'valid-curly-brace-in-bidsFile-with-n/a-splice', + explanation: 'Valid curly brace in bidsFile and but tsv splice entry is n/a', schemaVersion: '8.3.0', definitions: ['(Definition/Acc/#, (Acceleration/# m-per-s^2, Red))', '(Definition/MyColor, (Label/Pie))'], sidecar: { @@ -581,7 +667,7 @@ export const bidsTestData = [ }, { testname: 'valid-HED-column-splice', - explanation: 'Valid curly brace in sidecar with valid HED column splice', + explanation: 'Valid curly brace in bidsFile with valid HED column splice', schemaVersion: '8.3.0', definitions: ['(Definition/Acc/#, (Acceleration/# m-per-s^2, Red))', '(Definition/MyColor, (Label/Pie))'], sidecar: { @@ -603,7 +689,7 @@ export const bidsTestData = [ }, { testname: 'valid-HED-column-splice-with-n/a', - explanation: 'Valid curly brace in sidecar with HED column entry n/a', + explanation: 'Valid curly brace in bidsFile with HED column entry n/a', schemaVersion: '8.3.0', definitions: ['(Definition/Acc/#, (Acceleration/# m-per-s^2, Red))', '(Definition/MyColor, (Label/Pie))'], sidecar: { @@ -620,7 +706,7 @@ export const bidsTestData = [ }, { testname: 'valid-HED-curly-brace-but-tsv-has-no-HED-column', - explanation: 'A {HED} column splice is used in a sidecar but the tsv has no HED column', + explanation: 'A {HED} column splice is used in a bidsFile but the tsv has no HED column', schemaVersion: '8.3.0', definitions: ['(Definition/Acc/#, (Acceleration/# m-per-s^2, Red))', '(Definition/MyColor, (Label/Pie))'], sidecar: { @@ -698,7 +784,7 @@ export const bidsTestData = [ }, { testname: 'invalid-recursive-curly-braces', - explanation: 'Mutually recursive curly braces in sidecar.', + explanation: 'Mutually recursive curly braces in bidsFile.', schemaVersion: '8.3.0', definitions: ['(Definition/Acc/#, (Acceleration/# m-per-s^2, Red))', '(Definition/MyColor, (Label/Pie))'], sidecar: { @@ -751,7 +837,7 @@ export const bidsTestData = [ }, { testname: 'invalid-self-recursive-curly-braces', - explanation: 'Mutually recursive curly braces in sidecar.', + explanation: 'Mutually recursive curly braces in bidsFile.', schemaVersion: '8.3.0', definitions: ['(Definition/Acc/#, (Acceleration/# m-per-s^2, Red))', '(Definition/MyColor, (Label/Pie))'], sidecar: { @@ -832,7 +918,7 @@ export const bidsTestData = [ tests: [ { testname: 'valid-placeholder-used-in-tsv', - explanation: 'The sidecar has a placeholder that is used in the tsv', + explanation: 'The bidsFile has a placeholder that is used in the tsv', schemaVersion: '8.3.0', definitions: ['(Definition/Acc/#, (Acceleration/# m-per-s^2, Red))', '(Definition/MyColor, (Label/Pie))'], sidecar: { @@ -853,7 +939,7 @@ export const bidsTestData = [ }, { testname: 'valid-placeholder-not-used', - explanation: 'The sidecar has a placeholder that is not used in the tsv', + explanation: 'The bidsFile has a placeholder that is not used in the tsv', schemaVersion: '8.3.0', definitions: ['(Definition/Acc/#, (Acceleration/# m-per-s^2, Red))', '(Definition/MyColor, (Label/Pie))'], sidecar: { @@ -874,7 +960,7 @@ export const bidsTestData = [ }, { testname: 'invalid-no-placeholder-value-column', - explanation: 'The sidecar has a value column with no placeholder tag', + explanation: 'The bidsFile has a value column with no placeholder tag', schemaVersion: '8.3.0', definitions: ['(Definition/Acc/#, (Acceleration/# m-per-s^2, Red))', '(Definition/MyColor, (Label/Pie))'], sidecar: { @@ -905,7 +991,7 @@ export const bidsTestData = [ }, { testname: 'invalid-multiple-placeholders-in-value-column', - explanation: 'The sidecar has a value column with no placeholder tag', + explanation: 'The bidsFile has a value column with no placeholder tag', schemaVersion: '8.3.0', definitions: ['(Definition/Acc/#, (Acceleration/# m-per-s^2, Red))', '(Definition/MyColor, (Label/Pie))'], sidecar: { @@ -942,7 +1028,7 @@ export const bidsTestData = [ tests: [ { testname: 'valid-units-on-a-placeholder', - explanation: 'The sidecar has invalid units on a placeholder', + explanation: 'The bidsFile has invalid units on a placeholder', schemaVersion: '8.3.0', definitions: ['(Definition/Acc/#, (Acceleration/# m-per-s^2, Red))', '(Definition/MyColor, (Label/Pie))'], sidecar: { @@ -957,7 +1043,7 @@ export const bidsTestData = [ }, { testname: 'wrong-units-on-a-placeholder', - explanation: 'The sidecar has wrong units on a placeholder', + explanation: 'The bidsFile has wrong units on a placeholder', schemaVersion: '8.3.0', definitions: ['(Definition/Acc/#, (Acceleration/# m-per-s^2, Red))', '(Definition/MyColor, (Label/Pie))'], sidecar: { @@ -993,7 +1079,7 @@ export const bidsTestData = [ tests: [ { testname: 'valid-definition-no-placeholder', - explanation: 'Simple definition in sidecar', + explanation: 'Simple definition in bidsFile', schemaVersion: '8.3.0', definitions: ['(Definition/Acc/#, (Acceleration/# m-per-s^2, Red))', '(Definition/MyColor, (Label/Pie))'], sidecar: { @@ -1013,7 +1099,7 @@ export const bidsTestData = [ }, { testname: 'valid-definition-with-placeholder', - explanation: 'Definition in sidecar has a placeholder', + explanation: 'Definition in bidsFile has a placeholder', schemaVersion: '8.3.0', definitions: ['(Definition/Acc/#, (Acceleration/# m-per-s^2, Red))', '(Definition/MyColor, (Label/Pie))'], sidecar: { @@ -1033,7 +1119,7 @@ export const bidsTestData = [ }, { testname: 'valid-def-with-placeholder', - explanation: 'Def in sidecar has a placeholder', + explanation: 'Def in bidsFile has a placeholder', schemaVersion: '8.3.0', definitions: ['(Definition/Acc/#, (Acceleration/# m-per-s^2, Red))', '(Definition/MyColor, (Label/Pie))'], sidecar: { @@ -1048,7 +1134,7 @@ export const bidsTestData = [ }, { testname: 'valid-definition-with-nested-placeholder', - explanation: 'Definition in sidecar has nested placeholder', + explanation: 'Definition in bidsFile has nested placeholder', schemaVersion: '8.3.0', definitions: ['(Definition/Acc/#, (Acceleration/# m-per-s^2, Red))', '(Definition/MyColor, (Label/Pie))'], sidecar: { @@ -1068,7 +1154,7 @@ export const bidsTestData = [ }, { testname: 'valid-definition-no-group', - explanation: 'The sidecar with definition that has no internal group.', + explanation: 'The bidsFile with definition that has no internal group.', schemaVersion: '8.3.0', definitions: ['(Definition/Acc/#, (Acceleration/# m-per-s^2, Red))', '(Definition/MyColor, (Label/Pie))'], sidecar: { @@ -1088,7 +1174,7 @@ export const bidsTestData = [ }, { testname: 'invalid-def-expand-no-group', - explanation: 'The sidecar with definition that has no internal group.', + explanation: 'The bidsFile with definition that has no internal group.', schemaVersion: '8.3.0', definitions: ['(Definition/Acc/#, (Acceleration/# m-per-s^2, Red))', '(Definition/MyColor, (Label/Pie))'], sidecar: { @@ -1127,7 +1213,7 @@ export const bidsTestData = [ }, { testname: 'invalid-missing-definition-for-def', - explanation: 'The sidecar uses a def with no definition', + explanation: 'The bidsFile uses a def with no definition', schemaVersion: '8.3.0', definitions: ['(Definition/Acc/#, (Acceleration/# m-per-s^2, Red))', '(Definition/MyColor, (Label/Pie))'], sidecar: { @@ -1158,7 +1244,7 @@ export const bidsTestData = [ }, { testname: 'invalid-missing-definition-for-def-expand', - explanation: 'The sidecar uses a def-expand with no definition', + explanation: 'The bidsFile uses a def-expand with no definition', schemaVersion: '8.3.0', definitions: ['(Definition/Acc/#, (Acceleration/# m-per-s^2, Red))', '(Definition/MyColor, (Label/Pie))'], sidecar: { @@ -1189,7 +1275,7 @@ export const bidsTestData = [ }, { testname: 'invalid-nested-definition', - explanation: 'The sidecar has a definition inside a definition', + explanation: 'The bidsFile has a definition inside a definition', schemaVersion: '8.3.0', definitions: ['(Definition/Acc/#, (Acceleration/# m-per-s^2, Red))', '(Definition/MyColor, (Label/Pie))'], sidecar: { @@ -1231,7 +1317,7 @@ export const bidsTestData = [ }, { testname: 'invalid-multiple-definition-tags', - explanation: 'The sidecar has multiple definition tags in same definition', + explanation: 'The bidsFile has multiple definition tags in same definition', schemaVersion: '8.3.0', definitions: ['(Definition/Acc/#, (Acceleration/# m-per-s^2, Red))', '(Definition/MyColor, (Label/Pie))'], sidecar: { @@ -1273,7 +1359,7 @@ export const bidsTestData = [ }, { testname: 'invalid-definition-with-extra-groups', - explanation: 'The sidecar has a definition with extra internal group', + explanation: 'The bidsFile has a definition with extra internal group', schemaVersion: '8.3.0', definitions: ['(Definition/Acc/#, (Acceleration/# m-per-s^2, Red))', '(Definition/MyColor, (Label/Pie))'], sidecar: { @@ -1315,7 +1401,7 @@ export const bidsTestData = [ }, { testname: 'invalid-definition-with-extra-sibling', - explanation: 'The sidecar has a definition with an extra internal sibling', + explanation: 'The bidsFile has a definition with an extra internal sibling', schemaVersion: '8.3.0', definitions: ['(Definition/Acc/#, (Acceleration/# m-per-s^2, Red))', '(Definition/MyColor, (Label/Pie))'], sidecar: { @@ -1390,7 +1476,7 @@ export const bidsTestData = [ }, { testname: 'invalid-definition-with-missing-placeholder', - explanation: 'Definition in sidecar has missing placeholder', + explanation: 'Definition in bidsFile has missing placeholder', schemaVersion: '8.3.0', definitions: ['(Definition/Acc/#, (Acceleration/# m-per-s^2, Red))', '(Definition/MyColor, (Label/Pie))'], sidecar: { @@ -1426,7 +1512,7 @@ export const bidsTestData = [ }, { testname: 'invalid-definition-with-fixed-placeholder', - explanation: 'Definition in sidecar has a fixed value instead of placeholder', + explanation: 'Definition in bidsFile has a fixed value instead of placeholder', schemaVersion: '8.3.0', definitions: ['(Definition/Acc/#, (Acceleration/# m-per-s^2, Red))', '(Definition/MyColor, (Label/Pie))'], sidecar: { @@ -1466,7 +1552,7 @@ export const bidsTestData = [ }, { testname: 'invalid-definition-has-multiple-placeholders', - explanation: 'Definition in sidecar has multiple placeholders', + explanation: 'Definition in bidsFile has multiple placeholders', schemaVersion: '8.3.0', definitions: ['(Definition/Acc/#, (Acceleration/# m-per-s^2, Red))', '(Definition/MyColor, (Label/Pie))'], sidecar: { @@ -1506,7 +1592,7 @@ export const bidsTestData = [ }, { testname: 'invalid-definition-not-isolated', - explanation: 'Definition in sidecar appears with other tags', + explanation: 'Definition in bidsFile appears with other tags', schemaVersion: '8.3.0', definitions: ['(Definition/Acc/#, (Acceleration/# m-per-s^2, Red))', '(Definition/MyColor, (Label/Pie))'], sidecar: { @@ -1782,8 +1868,8 @@ export const bidsTestData = [ ], }, { - testname: 'delayed-onset-with-offset-before-with-sidecar', - explanation: 'offset appears before an onset with a sidecar in complex delayed scenario', + testname: 'delayed-onset-with-offset-before-with-bidsFile', + explanation: 'offset appears before an onset with a bidsFile in complex delayed scenario', schemaVersion: '8.3.0', definitions: ['(Definition/Acc/#, (Acceleration/# m-per-s^2, Red))', '(Definition/MyColor, (Label/Pie))'], sidecar: { @@ -1803,8 +1889,8 @@ export const bidsTestData = [ BidsHedIssue.fromHedIssue( generateIssue('inactiveOnset', { tag: 'Offset', definition: 'mycolor' }), { - path: 'delayed-onset-with-offset-before-with-sidecar.tsv', - relativePath: 'delayed-onset-with-offset-before-with-sidecar.tsv', + path: 'delayed-onset-with-offset-before-with-bidsFile.tsv', + relativePath: 'delayed-onset-with-offset-before-with-bidsFile.tsv', }, { tsvLine: '5' }, ),