Skip to content

Commit ac4e260

Browse files
authored
Fixture valid optimizations (#506)
Some changes in fixture-valid test, extracted from #425: * Add checkCapability function * Refactor category suggestion / validation * Add missing focus types "Head" in fixtures * Remove outdated reference to module.exports * Add JSDoc
1 parent 8ddbaec commit ac4e260

File tree

4 files changed

+122
-40
lines changed

4 files changed

+122
-40
lines changed

fixtures/adb/warp-m.json

+1
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
"colorTemperature": 3200
2121
},
2222
"focus": {
23+
"type": "Head",
2324
"panMax": 400,
2425
"tiltMax": 275
2526
}

fixtures/jb-lighting/varyscan-p7.json

+1
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
"degreesMinMax": [8, 48]
2323
},
2424
"focus": {
25+
"type": "Head",
2526
"panMax": 433.6,
2627
"tiltMax": 280
2728
}

tests/fixture-valid.js

+114-39
Original file line numberDiff line numberDiff line change
@@ -362,20 +362,7 @@ function checkFixture(manKey, fixKey, fixtureJson, uniqueValues = null) {
362362
rangesInvalid = !checkRange(i);
363363
}
364364

365-
const switchingChannelAliases = Object.keys(cap.switchChannels);
366-
if (!arraysEqual(switchingChannelAliases, channel.switchingChannelAliases)) {
367-
result.errors.push(`Capability '${cap.name}' (#${i + 1}) must define the same switching channel aliases as all other capabilities in channel '${channel.key}'.`);
368-
}
369-
else {
370-
for (const alias of switchingChannelAliases) {
371-
const chKey = cap.switchChannels[alias];
372-
usedChannelKeys.add(chKey.toLowerCase());
373-
374-
if (channel.fixture.getChannelByKey(chKey) === null) {
375-
result.errors.push(`Channel '${chKey}' is referenced from capability '${cap.name}' (#${i + 1}) in channel '${channel.key}' but is not defined.`);
376-
}
377-
}
378-
}
365+
checkCapability(cap, `Capability '${cap.name}' (${cap.rawDmxRange}) in channel '${channel.key}'`);
379366
}
380367

381368
/**
@@ -434,6 +421,28 @@ function checkFixture(manKey, fixKey, fixtureJson, uniqueValues = null) {
434421

435422
return values;
436423
}
424+
425+
/**
426+
* Check that a capability is valid (except its DMX range).
427+
* @param {!Capability} cap The capability to check.
428+
* @param {!string} errorPrefix An identifier for the capability to use in errors and warnings.
429+
*/
430+
function checkCapability(cap, errorPrefix) {
431+
const switchingChannelAliases = Object.keys(cap.switchChannels);
432+
if (!arraysEqual(switchingChannelAliases, channel.switchingChannelAliases)) {
433+
result.errors.push(`${errorPrefix} must define the same switching channel aliases as all other capabilities.`);
434+
}
435+
else {
436+
switchingChannelAliases.forEach(alias => {
437+
const chKey = cap.switchChannels[alias];
438+
usedChannelKeys.add(chKey.toLowerCase());
439+
440+
if (channel.fixture.getChannelByKey(chKey) === null) {
441+
result.errors.push(`${errorPrefix} references unknown channel '${chKey}'.`);
442+
}
443+
});
444+
}
445+
}
437446
}
438447
}
439448

@@ -707,41 +716,107 @@ function checkFixture(manKey, fixKey, fixtureJson, uniqueValues = null) {
707716
* Checks if the used channels fits to the fixture's categories and raise warnings suggesting to add/remove a category.
708717
*/
709718
function checkCategories() {
710-
const fixtureChannels = fixture.availableChannels.concat(fixture.matrixChannels.map(matrixCh => matrixCh.wrappedChannel));
719+
const categories = {
720+
'Color Changer': {
721+
isSuggested: isColorChanger(),
722+
suggestedPhrase: `there are at least one 'Multi-Color' or two 'Single Color' channels`,
723+
invalidPhrase: `there is no 'Multi-Color' and less than two 'Single Color' channels`
724+
},
725+
'Moving Head': {
726+
isSuggested: isMovingHead(),
727+
isInvalid: isNotMovingHead(),
728+
suggestedPhrase: `focus.type is 'Head' or there's a 'Pan' and a 'Tilt' capability`,
729+
invalidPhrase: `focus.type is not 'Head'`
730+
},
731+
'Scanner': {
732+
isSuggested: isScanner(),
733+
isInvalid: isNotScanner(),
734+
suggestedPhrase: `focus.type is 'Mirror' or there's a 'Pan' and a 'Tilt' capability`,
735+
invalidPhrase: `focus.type is not 'Mirror'`
736+
},
737+
'Smoke': {
738+
isInvalid: !hasChannelOfType(`Fog`),
739+
invalidPhrase: `there is not 'Fog' channel.`
740+
},
741+
'Hazer': {
742+
isInvalid: !hasChannelOfType(`Fog`),
743+
invalidPhrase: `there is not 'Fog' channel.`
744+
}
745+
};
746+
747+
for (const categoryName of Object.keys(categories)) {
748+
const categoryUsed = fixture.categories.includes(categoryName);
749+
const category = categories[categoryName];
711750

712-
const hasMultiColorChannel = fixtureChannels.some(channel => channel.type === `Multi-Color`);
713-
const hasMultipleSingleColorChannels = fixtureChannels.filter(channel => channel.type === `Single Color`).length > 1;
714-
const hasColorChangerCategory = fixture.categories.includes(`Color Changer`);
715-
if (!hasColorChangerCategory && hasMultiColorChannel) {
716-
result.warnings.push(`Category 'Color Changer' suggested since there is a 'Multi-Color' channel.`);
751+
if (!(`isInvalid` in category)) {
752+
category.isInvalid = !category.isSuggested;
753+
}
754+
755+
if (!categoryUsed && category.isSuggested) {
756+
result.warnings.push(`Category '${categoryName}' suggested since ${category.suggestedPhrase}.`);
757+
}
758+
else if (categoryUsed && category.isInvalid) {
759+
result.errors.push(`Category '${categoryName}' invalid since ${category.invalidPhrase}.`);
760+
}
717761
}
718-
else if (!hasColorChangerCategory && hasMultipleSingleColorChannels) {
719-
result.warnings.push(`Category 'Color Changer' suggested since there are multiple 'Single Color' channels.`);
762+
763+
/**
764+
* @returns {!boolean} Whether the 'Color Changer' category is suggested.
765+
*/
766+
function isColorChanger() {
767+
return hasChannelOfType(`Multi-Color`) || hasChannelOfType(`Single Color`, 2);
720768
}
721-
else if (hasColorChangerCategory && !hasMultiColorChannel && !hasMultipleSingleColorChannels) {
722-
result.warnings.push(`Category 'Color Changer' invalid since there is no 'Multi-Color' and less than 2 'Single Color' channels.`);
769+
770+
/**
771+
* @returns {!boolean} Whether the 'Moving Head' category is suggested.
772+
*/
773+
function isMovingHead() {
774+
const hasFocusTypeHead = fixture.physical !== null && fixture.physical.focusType === `Head`;
775+
const hasOtherFocusType = fixture.physical !== null && fixture.physical.focusType !== null;
776+
777+
return hasFocusTypeHead || (hasPanTiltChannels() && !hasOtherFocusType);
723778
}
724779

725-
const hasFocusTypeHead = fixture.physical !== null && fixture.physical.focusType === `Head`;
726-
const hasMovingHeadCategory = fixture.categories.includes(`Moving Head`);
727-
if (!hasMovingHeadCategory && hasFocusTypeHead) {
728-
result.warnings.push(`Category 'Moving Head' suggested since focus.type is 'Head'.`);
780+
/**
781+
* @returns {!boolean} Whether the 'Moving Head' category is invalid.
782+
*/
783+
function isNotMovingHead() {
784+
return fixture.physical === null || fixture.physical.focusType !== `Head`;
729785
}
730-
else if (hasMovingHeadCategory && !hasFocusTypeHead) {
731-
result.warnings.push(`Category 'Moving Head' invalid since focus.type is not 'Head'.`);
786+
787+
/**
788+
* @returns {!boolean} Whether the 'Scanner' category is suggested.
789+
*/
790+
function isScanner() {
791+
const hasFocusTypeMirror = fixture.physical !== null && fixture.physical.focusType === `Mirror`;
792+
const hasOtherFocusType = fixture.physical !== null && fixture.physical.focusType !== null;
793+
794+
return hasFocusTypeMirror || (hasPanTiltChannels() && !hasOtherFocusType);
732795
}
733796

734-
const hasFogChannel = fixtureChannels.some(channel => channel.type === `Fog`);
735-
const hasSmokeCategory = fixture.categories.includes(`Smoke`);
736-
const hasHazerCategory = fixture.categories.includes(`Hazer`);
737-
if (!(hasSmokeCategory || hasHazerCategory) && hasFogChannel) {
738-
result.warnings.push(`Categories 'Smoke' and/or 'Hazer' suggested since there is a 'Fog' channel.`);
797+
/**
798+
* @returns {!boolean} Whether the 'Scanner' category is invalid.
799+
*/
800+
function isNotScanner() {
801+
return fixture.physical === null || fixture.physical.focusType !== `Mirror`;
739802
}
740-
else if (hasSmokeCategory && !hasFogChannel) {
741-
result.warnings.push(`Category 'Smoke' invalid since there is no 'Fog' channel.`);
803+
804+
/**
805+
* @returns {!boolean} Whether the fixture has both a Pan and a Tilt channel.
806+
*/
807+
function hasPanTiltChannels() {
808+
return hasChannelOfType(`Pan`) && hasChannelOfType(`Tilt`);
742809
}
743-
else if (hasHazerCategory && !hasFogChannel) {
744-
result.warnings.push(`Category 'Hazer' invalid since there is no 'Fog' channel.`);
810+
811+
/**
812+
* @param {!string} type What channel type to search for.
813+
* @param {!number} [minimum=1] How many occurences are needed to succeed.
814+
* @returns {!boolean} Whether the given channel type occurs at least at the given minimum times in the fixture.
815+
*/
816+
function hasChannelOfType(type, minimum = 1) {
817+
return fixture.normalizedChannels.filter(
818+
ch => ch.type === type
819+
).length >= minimum;
745820
}
746821
}
747822

tests/fixtures-valid.js

+6-1
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,11 @@ for (const manKey of fs.readdirSync(fixturePath)) {
4343
}
4444
}
4545

46+
/**
47+
* Checks (asynchronously) the given fixture by adding a Promise to the promises array that resolves with a result object.
48+
* @param {!string} manKey The manufacturer key.
49+
* @param {!string} fixKey The fixture key.
50+
*/
4651
function handleFixtureFile(manKey, fixKey) {
4752
const filename = `${manKey}/${fixKey}.json`;
4853
const result = {
@@ -109,7 +114,7 @@ promises.push(new Promise((resolve, reject) => {
109114
const validate = (new Ajv()).compile(manufacturerSchema);
110115
const valid = validate(manufacturers);
111116
if (!valid) {
112-
result.errors.push(module.exports.getErrorString(`File does not match schema.`, validate.errors));
117+
result.errors.push(getErrorString(`File does not match schema.`, validate.errors));
113118
return resolve(result);
114119
}
115120

0 commit comments

Comments
 (0)