From 4bb652cb4b315aa8b20ac257dc3121a1d1a9c357 Mon Sep 17 00:00:00 2001 From: Nicolas Oliver Date: Thu, 17 Aug 2017 11:17:24 -0700 Subject: [PATCH] use sensortemplate.json metadata to test json files --- .gitignore | 3 + src/sensortemplate/sensortemplate.json | 294 +++++++++++++++++++------ tests/node/test.js | 156 +++++++++---- 3 files changed, 352 insertions(+), 101 deletions(-) diff --git a/.gitignore b/.gitignore index 93411d51..d93e5509 100644 --- a/.gitignore +++ b/.gitignore @@ -11,3 +11,6 @@ build*/ # Temp files *.swp *~ + +# Node modules +**/node_modules diff --git a/src/sensortemplate/sensortemplate.json b/src/sensortemplate/sensortemplate.json index 9a91b018..a4914c72 100644 --- a/src/sensortemplate/sensortemplate.json +++ b/src/sensortemplate/sensortemplate.json @@ -1,79 +1,249 @@ { - "// Library": {"comment": "Library name. Usually synonymous with the chip ID (e.g. fooinc0378). However, in the case where this library implements multiple sensor classes, choose a name which describes the full functionality (e.g. fooincled).", "required": true}, + "// Library": { + "comment": "Library name. Usually synonymous with the chip ID (e.g. fooinc0378). However, in the case where this library implements multiple sensor classes, choose a name which describes the full functionality (e.g. fooincled).", + "type": "string", + "required": true + }, "Library": "sensortemplate", - "// Description": {"comment": "Library description. This library provides support for the fooincled family of sensors.", "required": true}, + "// Description": { + "comment": "Library description. This library provides support for the fooincled family of sensors.", + "type": "string", + "required": true + }, "Description": "This is a sensor library template used for new sensors", - "// Sensor Class": {"comment": "Sensor/actuator class object which corresponds to the sensor/actuator C++ class (case sensitive).", "required": true}, - "Sensor Class": - { - "SensorTemplate": - { - "// Name": {"comment": "Sensor display name as indicated by the datasheet or manufacturer", "required": true}, + "// Sensor Class": { + "comment": "Sensor/actuator class object which corresponds to the sensor/actuator C++ class (case sensitive).", + "type": "object", + "required": true + }, + "Sensor Class": { + "// TemplateItem": { + "comment": "Template Item", + "type": "object", + "required": true + }, + "TemplateItem": { + "// Name": { + "comment": "Sensor display name as indicated by the datasheet or manufacturer", + "type": "string", + "required": true + }, "Name": "Template Sensor", - "// Description": {"comment": "In-depth description of the C++ sensor/actuator class. Include any necessary information on what is required for this implementation to work.", "required": true}, + "// Description": { + "comment": "In-depth description of the C++ sensor/actuator class. Include any necessary information on what is required for this implementation to work.", + "type": "string", + "required": true + }, "Description": "This sensor template can be used to generate code stubs for new sensor library development", - "// Aliases": {"comment": "Known vendor/manufacturer part numbers or names for the sensor/actuator/s supported by this class. Part numbers/IDs are preferred when available.", "required": false}, - "Aliases": ["template"], - "// Categories": {"comment": "One or more categories that his sensor class fits in.", "required": true}, - "Categories": ["other"], - "// Connections": {"comment": "One or more connection types for this sensor class.", "required": true}, - "Connections": ["i2c"], - "// Project Type": {"comment": "One or more application fields or project types sensor is suited for (e.g. prototyping, industrial)", "required": true}, - "Project Type": ["prototyping"], - "// Manufacturers": {"comment": "One or more manufacturers for the hardware supported by this class.", "required": true}, - "Manufacturers": ["generic"], - "// Kits": {"comment": "One or more retail kits that provide this sensor", "required": false}, + "// Aliases": { + "comment": "Known vendor/manufacturer part numbers or names for the sensor/actuator/s supported by this class. Part numbers/IDs are preferred when available.", + "type": "array", + "required": false + }, + "Aliases": [ + "template" + ], + "// Categories": { + "comment": "One or more categories that his sensor class fits in.", + "type": "array", + "required": true + }, + "Categories": [ + "other" + ], + "// Connections": { + "comment": "One or more connection types for this sensor class.", + "type": "array", + "required": true + }, + "Connections": [ + "i2c" + ], + "// Project Type": { + "comment": "One or more application fields or project types sensor is suited for (e.g. prototyping, industrial)", + "type": "array", + "required": true + }, + "Project Type": [ + "prototyping" + ], + "// Manufacturers": { + "comment": "One or more manufacturers for the hardware supported by this class.", + "type": "array", + "required": true + }, + "Manufacturers": [ + "generic" + ], + "// Kits": { + "comment": "One or more retail kits that provide this sensor", + "type": "array", + "required": false + }, "Kits": [], - "// Images": {"comment": "A single small (0-20k) html-viewable image file (located in docs/images). Please do not use existing, copyrighted images with your sensors.", "required": false}, + "// Image": { + "comment": "A single small (0-20k) html-viewable image file (located in docs/images). Please do not use existing, copyrighted images with your sensors.", + "type": "string", + "required": false + }, "Image": "sensortemplate.jpg", - "// Examples": {"comment": "Functional examples showing the main use cases for this sensor class. At least one is required.", "required": true}, - "Examples": - { - "// Java": {"comment": "One or more Java examples.", "required": false}, - "Java": ["SensorTemplateSample.java"], - "// Python": {"comment": "One or more Python examples.", "required": false}, - "Python": ["sensortemplate.py"], - "// Node.js": {"comment": "One or more Node.js examples.", "required": false}, - "Node.js": ["sensortemplate.js"], - "// C++": {"comment": "One or more C++ examples.", "required": true}, - "C++": ["sensortemplate.cxx"], - "// C": {"comment": "One or more C examples.", "required": false}, + "// Examples": { + "comment": "Functional examples showing the main use cases for this sensor class. At least one is required.", + "type": "object", + "required": true + }, + "Examples": { + "// Java": { + "comment": "One or more Java examples.", + "type": "array", + "required": false + }, + "Java": [ + "SensorTemplateSample.java" + ], + "// Python": { + "comment": "One or more Python examples.", + "type": "array", + "required": false + }, + "Python": [ + "sensortemplate.py" + ], + "// Node.js": { + "comment": "One or more Node.js examples.", + "type": "array", + "required": false + }, + "Node.js": [ + "sensortemplate.js" + ], + "// C++": { + "comment": "One or more C++ examples.", + "type": "array", + "required": true + }, + "C++": [ + "sensortemplate.cxx" + ], + "// C": { + "comment": "One or more C examples.", + "type": "array", + "required": false + }, "C": null }, - "// Specifications": {"comment": "Catch-all for HW specifications.", "required": false}, - "Specifications": - { - "// Vdd": {"comment": "Example specification. Provides unit value and range.", "required": false}, - "Vdd": {"unit": "v", "low" : 1.7, "high": 3.6}, - "// Ioff": {"comment": "Example specification. Provides unit value and range.", "required": false}, - "Ioff" : {"unit": "mA", "typical": 0.0}, - "// Iavg": {"comment": "Example specification. Provides unit value and range.", "required": false}, - "Iavg": {"unit": "mA", "low" : 1, "high": 2}, - "// Measured Range": {"comment": "Example specification. Provides unit value and range.", "required": false}, - "Measured Range": {"unit": "raw", "low" : 0, "high": 1023}, - "// Other Specs": {"comment": "Example specification. Provides unit value and range."}, + "// Specifications": { + "comment": "Catch-all for HW specifications.", + "type": "object", + "required": false + }, + "Specifications": { + "// Vdd": { + "comment": "Example specification. Provides unit value and range.", + "type": "object", + "required": false + }, + "Vdd": { + "// unit": { + "comment": "Example specification. Provides unit value and range.", + "type": "string", + "required": false + }, + "unit": "v", + "low": 1.7, + "high": 3.6 + }, + "// Ioff": { + "comment": "Example specification. Provides unit value and range.", + "type": "object", + "required": false + }, + "Ioff": { + "unit": "mA", + "typical": 0 + }, + "// Iavg": { + "comment": "Example specification. Provides unit value and range.", + "type": "object", + "required": false + }, + "Iavg": { + "unit": "mA", + "low": 1, + "high": 2 + }, + "// Measured Range": { + "comment": "Example specification. Provides unit value and range.", + "type": "object", + "required": false + }, + "Measured Range": { + "unit": "raw", + "low": 0, + "high": 1023 + }, + "// Other Specs": { + "comment": "Example specification. Provides unit value and range.", + "type": "object", + "required": false + }, "Other Specs": {} }, - "// Platforms": {"comment": "Catch-all for listing platforms used to validate this sensor class.", "required": false}, - "Platforms": - { - "// Intel Joule Module": {"comment": "Example platform.", "required": false}, - "Intel Joule Module": - { - "// Notes": {"comment": "JSON Object or Array which documents this platform.", "required": false}, - "Notes": ["Requires pull-up resistors with carrier board"], - "// Images": {"comment": "JSON Object or Array which documents this platform.", "required": false}, - "Images": [""] + "// Platforms": { + "comment": "Catch-all for listing platforms used to validate this sensor class.", + "type": "object", + "required": false + }, + "Platforms": { + "// Intel Joule Module": { + "comment": "Example platform.", + "type": "object", + "required": false + }, + "Intel Joule Module": { + "// Notes": { + "comment": "JSON Object or Array which documents this platform.", + "type": "array", + "required": false + }, + "Notes": [ + "Requires pull-up resistors with carrier board" + ], + "// Images": { + "comment": "JSON Object or Array which documents this platform.", + "type": "array", + "required": false + }, + "Images": [ + "" + ] } }, - "// Urls": {"comment": "Collection of external URLs which provide additional documentation for this sensor class.", "required": true}, - "Urls" : - { - "// Product Pages": {"comment": "Provide at least one URL for a product web-site where users can get additional information on this sensor.", "required": true}, - "Product Pages": ["https://software.intel.com/en-us/iot/hardware/sensors/short-title-case-description-from-datasheet-manditory"], - "// Datasheets": {"comment": "Array of product datasheets", "required": false}, + "// Urls": { + "comment": "Collection of external URLs which provide additional documentation for this sensor class.", + "type": "object", + "required": true + }, + "Urls": { + "// Product Pages": { + "comment": "Provide at least one URL for a product web-site where users can get additional information on this sensor.", + "type": "array", + "required": true + }, + "Product Pages": [ + "https://software.intel.com/en-us/iot/hardware/sensors/short-title-case-description-from-datasheet-manditory" + ], + "// Datasheets": { + "comment": "Array of product datasheets", + "type": "array", + "required": false + }, "Datasheets": [], - "// Schematics": {"comment": "Array of product schematic pages.", "required": false}, + "// Schematics": { + "comment": "Array of product schematic pages.", + "type": "array", + "required": false + }, "Schematics": [] } } diff --git a/tests/node/test.js b/tests/node/test.js index 487ee4c6..fbe29563 100644 --- a/tests/node/test.js +++ b/tests/node/test.js @@ -6,52 +6,130 @@ var rootPath = path.resolve(__dirname, '../../'); var srcPath = path.resolve(rootPath, 'src'); var examplePath = path.resolve(rootPath, 'examples'); +var sensorTemplateJson = require(path.join(srcPath, 'sensortemplate/sensortemplate.json')); + +/** + * Get type name + */ +function getTypeName(value) { + if(Array.isArray(value)) { + return 'array' + } + return typeof value; +} + +/** + * Check property + * @param {object} target Target Object to check + * @param {string} propertyName Target property to check in target object + * @param {object} propertySpecs Property specs + * @param {string} propertySpecs.comment + * @param {string} propertySpecs.type + * @param {boolean} propertySpecs.required + */ +function checkProperty(target, propertyName, propertySpecs) { + // Fail if a required property is missing + if (propertySpecs.required) { + expect(target).to.have.property(propertyName); + } + // Check non required property + if (target[propertyName]) { + var propertyType = getTypeName(target[propertyName]); + var errorMsg = propertyName + ' property should be a ' + propertySpecs.type; + expect(propertyType).to.be.equal(propertySpecs.type, errorMsg); + } +} + +/** + * Check object specs + * @param {object} object Json schema specification + * @param {object} target Json object to check + */ +function checkObjectSpecs(object, target) { + for (var key in object) { + /** + * Properties specs start with a // character + * Properties that are not specs are skipped + */ + if (key.indexOf('//') !== 0) { + continue; + } + + /** + * We need to do a closure here to make + * async calls like it() to hold the + * current values of object and key in + * the iteration + */ + (function (object, key) { + var keySpecs = object[key]; + var realKeyName = key.replace('// ', ''); + + /** + * "TemplateItem" is a special case + * For objects, the TemplateItem define + * the specs for each of the items in the object + */ + if (realKeyName === 'TemplateItem') { + for (var key in target) { + (function(key, target){ + /** + * Create a test case for each property in the + * target object. This test will check the property + * spectations + */ + it('Test for ' + key + ' property', function () { + checkProperty(target, key, keySpecs); + }); + + if (target[key] && keySpecs.type === 'object') { + (function (object, target) { + describe(key, function () { + checkObjectSpecs(object, target); + }); + })(object['TemplateItem'], target[key]); + } + })(key, target); + } + } + else { + /** + * Create a test case for each property in the + * target object. This test will check the property + * spectations + */ + it('Test for ' + realKeyName + ' property', function () { + checkProperty(target, realKeyName, keySpecs); + }); + + if (target[realKeyName] && keySpecs.type === 'object') { + (function (object, target) { + describe(realKeyName, function () { + checkObjectSpecs(object, target); + }); + })(object[realKeyName], target[realKeyName]); + } + } + })(object, key); + } +} + shell.find(srcPath) .filter(function (file) { return file.match(/\.json$/); }) + .filter(function (file) { + // Filter sensortemplate.json file + return !file.match(/sensortemplate\.json$/); + }) .forEach(function (jsonFile) { var relativePath = path.relative(rootPath, jsonFile); + /** + * For each json file found, create a Test Suite + */ describe(relativePath, function () { - // Check required fields - [ - 'Library', - 'Description', - 'Sensor Class' - ].forEach(function (fieldName) { - it('should have a ' + fieldName + ' property', function () { - var parsedJsonFile = require(jsonFile); - expect(parsedJsonFile).to.have.property(fieldName); - }); - }); - - // Check Sensor Class required fields - [ - 'Name', - 'Description', - 'Aliases', - 'Categories', - 'Connections', - 'Project Type', - 'Manufacturers', - 'Image', - 'Examples', - 'Specifications', - 'Platforms', - 'Urls' - ].forEach(function (fieldName) { - var parsedJsonFile = require(jsonFile); - sensorClass = parsedJsonFile['Sensor Class']; - - for(var className in sensorClass) { - it(className + ' should have a ' + fieldName + ' property', function () { - var parsedJsonFile = require(jsonFile); - sensorClass = parsedJsonFile['Sensor Class']; - expect(sensorClass[className]).to.have.property(fieldName); - }); - } - }); + var parsedJson = require(jsonFile); + checkObjectSpecs(sensorTemplateJson, parsedJson); }); }); -