aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorGabriel Schulhof <gabriel.schulhof@intel.com>2016-06-01 15:20:09 +0300
committerGabriel Schulhof <gabriel.schulhof@intel.com>2016-06-15 11:31:35 +0000
commit6a8fce657ae9a26da891ed86a66225c66aa9c798 (patch)
tree2cd408d4436b0a56335c6ec1a8254e935e0ad739
parent62e3a344c52c4ea2cd728efaade37892c58fe414 (diff)
All: Maintenance release to fix bugs and bring up-to-spec1.1.0-4
Build: * Take first directory under ./depbuild as the SOURCE * Skip macros when generating bindings for constants Examples: * Add more console.log() statements for description JS API: * Handle malformed create(POST) requests * Bring presence up-to-spec * Implement findPlatforms() Tests: * Remove two second timeout between clients * Modify suite to accommodate multiple servers Fixes gh-55 Fixes gh-53 Closes gh-56 Change-Id: I23d699901b07731c56bfd7ab6c37e6dda5b67344 Signed-off-by: Gabriel Schulhof <gabriel.schulhof@intel.com> Reviewed-on: https://gerrit.iotivity.org/gerrit/8665
-rw-r--r--README.md4
-rwxr-xr-xbuild-csdk.sh2
-rwxr-xr-xgenerate-constants.sh2
-rw-r--r--grunt-build/tasks/ocf-suite.js8
-rw-r--r--js/high-level-client-example.js9
-rw-r--r--lib/OicClient.js282
-rw-r--r--lib/OicServer.js12
-rw-r--r--package.json4
-rw-r--r--tests/assert-to-console.js7
-rw-r--r--tests/suite.js52
-rw-r--r--tests/tests/API Create Delete/client.js119
-rw-r--r--tests/tests/API Create Delete/server.js154
-rw-r--r--tests/tests/API Device Information/client.js92
-rw-r--r--tests/tests/API Device Rediscovery.js13
-rw-r--r--tests/tests/API Presence/client.js321
-rw-r--r--tests/tests/API Presence/common-server.js (renamed from tests/tests/API Presence/server.js)53
-rw-r--r--tests/tests/API Presence/server1.js1
-rw-r--r--tests/tests/API Presence/server2.js1
18 files changed, 735 insertions, 401 deletions
diff --git a/README.md b/README.md
index b381240..6e3aedb 100644
--- a/README.md
+++ b/README.md
@@ -110,8 +110,8 @@ Make sure no firewall is running (or one is properly configured to allow iotivit
[1.1.0]: https://gerrit.iotivity.org/gerrit/gitweb?p=iotivity.git;a=tree;hb=1.1.0
[snapshot]: https://gerrit.iotivity.org/gerrit/gitweb?p=iotivity.git;a=snapshot;h=1.1.0;sf=tgz
[scons]: http://www.scons.org/
-[install.sh]: https://raw.githubusercontent.com/otcshare/iotivity-node/1.1.0-3/install.sh
-[octbstack.pc.in]: https://raw.githubusercontent.com/otcshare/iotivity-node/1.1.0-3/octbstack.pc.in
+[install.sh]: https://raw.githubusercontent.com/otcshare/iotivity-node/1.1.0-4/install.sh
+[octbstack.pc.in]: https://raw.githubusercontent.com/otcshare/iotivity-node/1.1.0-4/octbstack.pc.in
[iotivity wiki]: https://wiki.iotivity.org/faq_s
[video]: https://www.youtube.com/watch?v=95VTB_qgYfw
[specification]: https://github.com/solettaproject/soletta/blob/v1_beta19/doc/js-spec/oic.md
diff --git a/build-csdk.sh b/build-csdk.sh
index ec76774..c966631 100755
--- a/build-csdk.sh
+++ b/build-csdk.sh
@@ -81,7 +81,7 @@ if test "x${DO_BUILD}x" = "xtruex"; then
cd ../../ || exit 1
else
- SOURCE=$(ls -d ./depbuild/iotivity*)
+ SOURCE=$(ls -d ./depbuild/iotivity* | while read; do if test -d "${REPLY}"; then echo "${REPLY}"; break; fi; done )
fi
SOURCE="${SOURCE}" PREFIX="$(pwd)/deps/iotivity" INSTALL_PC="${DO_PC}" "$(pwd)/install.sh" || exit 1
diff --git a/generate-constants.sh b/generate-constants.sh
index 5bcd7c1..ae60917 100755
--- a/generate-constants.sh
+++ b/generate-constants.sh
@@ -32,7 +32,7 @@
parseFileForConstants() { # $1: filename
grep '^#define' < "$1" | \
awk '{
- if ( NF > 2 ) {
+ if ( NF > 2 && $2 !~ /[()]/ ) {
print( "SET_CONSTANT_" ( ( substr($3, 1, 1) == "\"" ) ? "STRING": "NUMBER" ) " " $2 );
}
}' | \
diff --git a/grunt-build/tasks/ocf-suite.js b/grunt-build/tasks/ocf-suite.js
index f71093e..a930adf 100644
--- a/grunt-build/tasks/ocf-suite.js
+++ b/grunt-build/tasks/ocf-suite.js
@@ -18,8 +18,14 @@ var _ = require( "lodash" ),
path = require( "path" );
grunt.task.registerTask( "ocf-suite", "Run the OCF test suite", function() {
+ var testSuite;
var done = this.async();
- var testSuite = require( "ocf-test-suite" );
+
+ if ( typeof Promise === "undefined" ) {
+ return done();
+ }
+
+ testSuite = require( "ocf-test-suite" );
_.extend( testSuite, {
defaultCallbacks: _.extend( testSuite.defaultCallbacks, {
diff --git a/js/high-level-client-example.js b/js/high-level-client-example.js
index 7cf8295..9d4ce9f 100644
--- a/js/high-level-client-example.js
+++ b/js/high-level-client-example.js
@@ -12,8 +12,11 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-var observationCount = 0,
- device = require( "iotivity-node" )( "client" );
+var device,
+ observationCount = 0;
+
+console.log( "Acquiring OCF device" );
+device = require( "iotivity-node" )( "client" );
// Add a listener that will receive the results of the discovery
device.addEventListener( "resourcefound", function( event ) {
@@ -38,6 +41,8 @@ device.addEventListener( "resourcefound", function( event ) {
event.resource.addEventListener( "change", resourceUpdate );
}
} );
+
+console.log( "Issuing discovery request" );
device.findResources().catch( function( error ) {
console.error( error.stack ? error.stack : ( error.message ? error.message : error ) );
process.exit( 1 );
diff --git a/lib/OicClient.js b/lib/OicClient.js
index 8005bfd..46c9aba 100644
--- a/lib/OicClient.js
+++ b/lib/OicClient.js
@@ -30,20 +30,46 @@ _.extend( devicePrototype, {
_construct: ( function( _super ) {
return function() {
- utils.setPrivate( this, [ "_handles", "_devices" ] );
+ utils.setPrivate( this, [ "_handles", "_devices", "_presenceListeningEnabled" ] );
this._handles = {};
// Map of deviceId: { info: OicDeviceInfo, address: OCDevAddr }
this._devices = {};
+ this._presenceListeningEnabled = false;
+
// Define legacy event handlers such that setting them will attach event handlers
utils.addLegacyEventHandler( this, "resourcefound" );
utils.addLegacyEventHandler( this, "devicefound" );
+ utils.addLegacyEventHandler( this, "platformfound" );
utils.addLegacyEventHandler( this, "discoveryerror" );
+ utils.addLegacyEventHandler( this, "devicechange" );
return _super.apply( this, arguments );
};
} )( devicePrototype._construct ),
+ _platformInfoFromOCPlatformInfo: function( ocPlatformInfo ) {
+ return _.extend( {},
+ ( ( "platformID" in ocPlatformInfo ) ?
+ { id: ocPlatformInfo.platformID } : {} ),
+ ( ( "operatingSystemVersion" in ocPlatformInfo ) ?
+ { osVersion: ocPlatformInfo.operatingSystemVersion } : {} ),
+ ( ( "modelNumber" in ocPlatformInfo ) ?
+ { model: ocPlatformInfo.modelNumber } : {} ),
+ ( ( "manufacturerName" in ocPlatformInfo ) ?
+ { manufacturerName: ocPlatformInfo.manufacturerName } : {} ),
+ ( ( "manufacturerUrl" in ocPlatformInfo ) ?
+ { manufacturerUrl: ocPlatformInfo.manufacturerUrl } : {} ),
+ ( ( "dateOfManufacture" in ocPlatformInfo ) ?
+ { manufactureDate: new Date( ocPlatformInfo.dateOfManufacture ) } : {} ),
+ ( ( "platformVersion" in ocPlatformInfo ) ?
+ { platformVersion: ocPlatformInfo.platformVersion } : {} ),
+ ( ( "firmwareVersion" in ocPlatformInfo ) ?
+ { firmwareVersion: ocPlatformInfo.firmwareVersion } : {} ),
+ ( ( "supportUrl" in ocPlatformInfo ) ?
+ { supportUrl: ocPlatformInfo.supportUrl } : {} ) );
+ },
+
// Cancel any and all handles upon stopping the stack
_stopStack: ( function( _super ) {
return function() {
@@ -257,6 +283,30 @@ _.extend( devicePrototype, {
}, this ) );
},
+ _cleanHandle: function( prefix, reject ) {
+ var result;
+
+ // If we have a previous open-ended handle of type @prefix that has not yet
+ // been marked as stale by the bindings then cancel and discard it
+ if ( this._handles[ prefix ] ) {
+ if ( !this._handles[ prefix ].stale ) {
+ result = iotivity.OCCancel( this._handles[ prefix ],
+ iotivity.OCQualityOfService.OC_HIGH_QOS,
+ null, 0 );
+ if ( result !== iotivity.OCStackResult.OC_STACK_OK && reject ) {
+ reject(
+ _.extend( new Error(
+ prefix + ": failed to cancel previous handle" ), {
+ result: result
+ } ) );
+ return false;
+ }
+ }
+ }
+ delete this._handles[ prefix ];
+ return true;
+ },
+
// _openEndedRequest(): Perform an open-ended request
// @options recognizes the following keys:
// prefix: String: Where in this._handles to store the handle
@@ -270,24 +320,9 @@ _.extend( devicePrototype, {
var result,
handleReceptacle = {};
- // If we have a previous open-ended handle of type @options.prefix that has not yet
- // been marked as stale by the bindings then cancel and discard it
- if ( this._handles[ options.prefix ] ) {
- if ( !this._handles[ options.prefix ].stale ) {
- result = iotivity.OCCancel( this._handles[ options.prefix ],
- iotivity.OCQualityOfService.OC_HIGH_QOS,
- null, 0 );
- if ( result !== iotivity.OCStackResult.OC_STACK_OK && options.reject ) {
- options.reject(
- _.extend( new Error(
- options.prefix + ": failed to cancel previous handle" ), {
- result: result
- } ) );
- return;
- }
- }
+ if ( !this._cleanHandle( options.prefix, options.reject ) ) {
+ return;
}
- delete this._handles[ options.prefix ];
// Issue a new request and store its handle
result = iotivity.OCDoResource( handleReceptacle, iotivity.OCMethod[ options.method ],
@@ -313,6 +348,40 @@ _.extend( devicePrototype, {
return result;
},
+ findPlatforms: function() {
+ return new Promise( _.bind( function( fulfill, reject ) {
+ if ( this._info.device.role === "server" ) {
+ reject( new Error( "Not supported" ) );
+ return;
+ }
+ this._openEndedRequest( {
+ fulfill: fulfill,
+ reject: reject,
+ prefix: "findPlatforms",
+ query: iotivity.OC_RSRVD_PLATFORM_URI,
+ method: "OC_REST_DISCOVER",
+ destination: null,
+ responseHandler: _.bind( function findPlatformsHandler( handle, response ) {
+ if ( !( response && response.result === ocStackOk && response.payload &&
+ response.payload.info ) ) {
+ this.dispatchEvent( "discoveryerror", {
+ type: "discoveryerror",
+ error: this._errorFromResponse( response,
+ "findPlatforms: OCDoResource response failed" )
+ } );
+ } else {
+ this.dispatchEvent( "platformfound", {
+ type: "platformfound",
+ platform: this._platformInfoFromOCPlatformInfo( response.payload.info )
+ } );
+ }
+
+ return keepTransaction;
+ }, this )
+ } );
+ }, this ) );
+ },
+
findDevices: function() {
return new Promise( _.bind( function( fulfill, reject ) {
if ( this._info.device.role === "server" ) {
@@ -336,10 +405,7 @@ _.extend( devicePrototype, {
error: this._errorFromResponse( response,
"findDevices: OCDoResource response failed" )
}, ( deviceId ? { deviceId: deviceId } : {} ) ) );
- return deleteTransaction;
- }
-
- if ( response.payload ) {
+ } else if ( response.payload ) {
this._addDevice( deviceId, {
uuid: deviceId,
name: response.payload.deviceName,
@@ -347,63 +413,94 @@ _.extend( devicePrototype, {
}, response.devAddr );
}
- fulfill();
return keepTransaction;
}, this )
} );
}, this ) );
},
- _addDevice: function( deviceId, info, address, resources ) {
- var existingDevice = this._devices[ deviceId ];
+ _listenForPresence: function( deviceId, fulfill, reject ) {
+ var address = ( this._devices[ deviceId ] && this._devices[ deviceId ].address );
- // If we are already aware of this device, update its properties. Otherwise, create a
- // new device.
- this._devices[ deviceId ] = _.extend(
- ( existingDevice ? existingDevice : {} ),
- { address: address },
- ( info ? { info: info } : {} ) );
-
- // Add presence notifications to the newly added device
- this._openEndedRequest( {
+ if ( !address ) {
+ if ( reject ) {
+ reject( new Error( deviceId + ": address not found" ) );
+ }
+ return;
+ }
+ return this._openEndedRequest( {
prefix: deviceId,
method: "OC_REST_PRESENCE",
query: iotivity.OC_RSRVD_PRESENCE_URI,
destination: address,
+ fulfill: fulfill,
+ reject: reject,
responseHandler: _.bind( function( handle, response ) {
- var index;
+ var index,
+ returnValue = keepTransaction;
if ( response && response.payload && response.payload.type ===
iotivity.OCPayloadType.PAYLOAD_TYPE_PRESENCE ) {
- switch ( response.payload.trigger ) {
- case iotivity.OCPresenceTrigger.OC_PRESENCE_TRIGGER_CREATE:
- this._addDevice( deviceId, { uuid: deviceId }, response.devAddr );
- this.findResources( { deviceId: deviceId } );
- break;
-
- case iotivity.OCPresenceTrigger.OC_PRESENCE_TRIGGER_DELETE:
- delete this._devices[ deviceId ];
- for ( index in this._resources ) {
- if ( this._resources[ index ].id.deviceId === deviceId ) {
- this._resources[ index ].dispatchEvent( "delete", {
- type: "delete"
- } );
- delete this._resources[ index ];
- }
- }
- break;
+ var deviceChangeEvent = {
+ type: "devicechange",
+ device: ( this._devices[ deviceId ] && this._devices[ deviceId ].info )
+ };
- default:
+ switch ( response.payload.trigger ) {
- // FIXME: What is OC_PRESENCE_TRIGGER_CHANGE?
- break;
+ case iotivity.OCPresenceTrigger.OC_PRESENCE_TRIGGER_CREATE:
+ this._addDevice( deviceId, { uuid: deviceId }, response.devAddr );
+ this.findResources( { deviceId: deviceId } );
+ deviceChangeEvent.changeType = "added";
+ break;
+
+ case iotivity.OCPresenceTrigger.OC_PRESENCE_TRIGGER_DELETE:
+ delete this._devices[ deviceId ];
+ for ( index in this._resources ) {
+ if ( this._resources[ index ].id.deviceId === deviceId ) {
+ this._resources[ index ].dispatchEvent( "delete", {
+ type: "delete"
+ } );
+ delete this._resources[ index ];
+ }
+ }
+ deviceChangeEvent.changeType = "deleted";
+
+ // FIXME(?): Remove the presence handler because the device is gone
+ // anyway, but more importantly because iotivity issues two DELETE
+ // events for some reason, and by the time the second one rolls around
+ // we will have deleted the device info from the this._devices hash.
+ returnValue = deleteTransaction;
+ break;
+
+ default:
+ deviceChangeEvent.changeType = "changed";
+ break;
}
+
+ this.dispatchEvent( "devicechange", deviceChangeEvent );
}
- return iotivity.OCStackApplicationResult.OC_STACK_KEEP_TRANSACTION;
+ return returnValue;
}, this )
} );
+ },
+
+ _addDevice: function( deviceId, info, address, resources ) {
+ var existingDevice = this._devices[ deviceId ];
+
+ // If we are already aware of this device, update its properties. Otherwise, create a
+ // new device.
+ this._devices[ deviceId ] = _.extend(
+ ( existingDevice ? existingDevice : {} ),
+ { address: address },
+ ( info ? { info: info } : {} ) );
+
+ // Add presence notifications to the newly added device
+ if ( this._presenceListeningEnabled && !existingDevice ) {
+ this._listenForPresence( deviceId );
+ }
this.dispatchEvent( "devicefound", _.extend( {
type: "devicefound",
@@ -434,19 +531,9 @@ _.extend( devicePrototype, {
prefix: "getPlatformInfo",
resourceId: { deviceId: id, path: "/oic/p" },
method: "OC_REST_GET",
- createAnswer: function createAnswerForGetPlatformInfo( handle, response ) {
- return {
- id: response.payload.info.platformID,
- osVersion: response.payload.info.operatingSystemVersion,
- model: response.payload.info.modelNumber,
- manufacturerName: response.payload.info.manufacturerName,
- manufacturerUrl: response.payload.info.manufacturerUrl,
- manufactureDate: new Date( response.payload.info.dateOfManufacture ),
- platformVersion: response.payload.info.platformVersion,
- firmwareVersion: response.payload.info.firmwareVersion,
- supportUrl: response.payload.info.supportUrl
- };
- }
+ createAnswer: _.bind( function createAnswerForGetPlatformInfo( handle, response ) {
+ return this._platformInfoFromOCPlatformInfo( response.payload.info );
+ }, this )
} );
},
@@ -505,19 +592,24 @@ _.extend( devicePrototype, {
},
create: function( resource ) {
- return this._oneShotRequest( {
+ return ( resource.id && resource.id.path &&
+ Array.isArray( resource.resourceTypes ) && resource.resourceTypes.length >= 1 &&
+ Array.isArray( resource.interfaces ) && resource.interfaces.length >= 1 &&
+ Object.keys( resource.properties ).length >= 1 ) ?
+ this._oneShotRequest( {
prefix: "create",
resourceId: resource.id,
method: "OC_REST_POST",
payload: _.extend( utils.objectToPayload( resource.properties ), {
- types: resource.resourceTypes || [],
- interfaces: resource.interfaces || [],
+ types: resource.resourceTypes,
+ interfaces: resource.interfaces,
uri: resource.id.path
} ),
createAnswer: function createResourceFromResponse() {
return this._maybeAddResource( resource );
}
- } );
+ } ) :
+ Promise.reject( new Error( "create: Invalid OicResourceInit" ) );
},
retrieve: function( id, queryParameters ) {
@@ -554,6 +646,50 @@ _.extend( devicePrototype, {
delete this._resources[ id.deviceId + ":" + id.path ];
}
} );
+ },
+
+ subscribe: function( deviceId ) {
+ if ( arguments.length < 1 ) {
+
+ // If no arguments are passed, turn on the global presence listening flag and
+ // turn on presence listening for all existing devices.
+ this._presenceListeningEnabled = true;
+ Object.keys( this._devices ).forEach( _.bind( function( item ) {
+ this._listenForPresence( item );
+ }, this ) );
+ return Promise.resolve();
+ }
+
+ if ( !( this._devices[ deviceId ] && this._devices[ deviceId ].address ) ) {
+ return Promise.reject(
+ new Error( "subscribe: address for device " + deviceId + " not found" ) );
+ }
+
+ return new Promise( _.bind( function( fulfill, reject ) {
+ this._listenForPresence( deviceId, fulfill, reject );
+ }, this ) );
+ },
+
+ unsubscribe: function( deviceId ) {
+ if ( arguments.length < 1 ) {
+
+ // If no arguments are passed, turn off the global presence listening flag and
+ // turn off presence listening for all existing devices.
+ this._presenceListeningEnabled = false;
+ Object.keys( this._devices ).forEach( _.bind( this._cleanHandle, this ) );
+ return;
+ }
+
+ if ( !( this._devices[ deviceId ] && this._devices[ deviceId ].address ) ) {
+ return Promise.reject(
+ new Error( "unsubscribe: address for device " + deviceId + " not found" ) );
+ }
+
+ return new Promise( _.bind( function( fulfill, reject ) {
+ if ( this._cleanHandle( deviceId, reject ) ) {
+ fulfill();
+ }
+ }, this ) );
}
} );
diff --git a/lib/OicServer.js b/lib/OicServer.js
index 3d8326c..c255373 100644
--- a/lib/OicServer.js
+++ b/lib/OicServer.js
@@ -162,6 +162,15 @@ _.extend( devicePrototype, {
} else if ( request.method === iotivity.OCMethod.OC_REST_POST ) {
oicReq.type = "create";
+ // Make sure we have everything we need for creating a new resource
+ if ( !( request.payload &&
+ Array.isArray( request.payload.interfaces ) &&
+ request.payload.interfaces.length >= 1 &&
+ Array.isArray( request.payload.types ) &&
+ request.payload.types.length >= 1 ) ) {
+ return iotivity.OCEntityHandlerResult.OC_EH_ERROR;
+ }
+
oicReq.res = {
id: {
path: url,
@@ -180,7 +189,8 @@ _.extend( devicePrototype, {
oicReq.type = "update";
// FIXME: Check if this is the right way of doing it.
- oicReq.res = myUtils.payloadToObject( request.payload.values );
+ oicReq.res = request.payload ?
+ myUtils.payloadToObject( request.payload.values ) : {};
}
} else if ( request.method === iotivity.OCMethod.OC_REST_DELETE ) {
oicReq.type = "delete";
diff --git a/package.json b/package.json
index f6b52cc..fe495c0 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "iotivity-node",
- "version": "1.1.0-3",
+ "version": "1.1.0-4",
"description": "IoTivity bindings",
"repository": "https://github.com/otcshare/iotivity-node/",
"license": "Apache-2.0",
@@ -11,7 +11,7 @@
"iot"
],
"scripts": {
- "ci": "TESTING=true npm install && grunt testsuite testdist --ci",
+ "ci": "TESTING=true npm install && grunt testsuite ocf-suite testdist --ci",
"ci-basic": "TESTING=true npm install && grunt testsuite testdist --suites='Load Library.js' --ci",
"ci-lint": "./dist.sh -r && grunt lint",
"test": "grunt test"
diff --git a/tests/assert-to-console.js b/tests/assert-to-console.js
index 2792037..85e36da 100644
--- a/tests/assert-to-console.js
+++ b/tests/assert-to-console.js
@@ -28,6 +28,13 @@ module.exports = {
} ) );
},
+ info: function( message ) {
+ console.log( JSON.stringify( {
+ info: true,
+ message: message
+ } ) );
+ },
+
die: function( message ) {
console.log( JSON.stringify( { teardown: true, message: message, isError: true } ) );
process.exit( 1 );
diff --git a/tests/suite.js b/tests/suite.js
index bccd7fb..cd824d3 100644
--- a/tests/suite.js
+++ b/tests/suite.js
@@ -91,6 +91,9 @@ function spawnOne( assert, options ) {
if ( jsonObject.assertionCount ) {
options.reportAssertions( jsonObject.assertionCount + 2 );
+ } else if ( jsonObject.info ) {
+ console.log( "\x1b[46;30mi\x1b[0m " + jsonObject.message );
+
// The child has requested a teardown.
} else if ( jsonObject.teardown ) {
options.teardown(
@@ -124,7 +127,7 @@ function runTestSuites( files ) {
_.each( files, function( item ) {
var clientPathIndex,
clientPaths = glob.sync( path.join( item, "client*.js" ) ),
- serverPath = path.join( item, "server.js" );
+ serverPaths = glob.sync( path.join( item, "server*.js" ) );
if ( fs.lstatSync( item ).isFile() ) {
getQUnit().test( path.basename( item ).replace( /\.js$/, "" ), function( assert ) {
@@ -156,12 +159,14 @@ function runTestSuites( files ) {
}
}
- if ( !( fs.lstatSync( serverPath ).isFile() ) ) {
- throw new Error( "Cannot find server at " + serverPath );
- }
+ serverPaths.forEach( function( serverPath ) {
+ if ( !( fs.lstatSync( serverPath ).isFile() ) ) {
+ throw new Error( "Cannot find server at " + serverPath );
+ }
+ } );
getQUnit().test( path.basename( item ), function( assert ) {
- var totalChildren = clientPaths.length + 1,
+ var totalChildren = clientPaths.length + serverPaths.length,
// Track the child processes involved in this test in this array
children = [],
@@ -214,26 +219,27 @@ function runTestSuites( files ) {
}
};
- // We run the server first, because the server has to be there before the clients
+ // We run the servers first, because the servers have to be there before the clients
// can run. OTOH, the clients may initiate the termination of the test via a non-error
// teardown request.
- children.push( spawnOne( assert, _.extend( {}, spawnOptions, {
- name: "Server",
- path: serverPath,
- onReady: function() {
- var clientIndex = 0;
- async.eachSeries( clientPaths, function startOneChild( item, callback ) {
- children.push( spawnOne( assert, _.extend( {}, spawnOptions, {
- name: "Client" +
- ( clientPaths.length > 1 ? " " + ( ++clientIndex ) : "" ),
- path: item } ) ) );
-
- // Spawn clients at least two seconds apart to avoid message uniqueness
- // issue in iotivity: https://jira.iotivity.org/browse/IOT-724
- setTimeout( callback, 2000 );
- } );
- }
- } ) ) );
+ async.each( serverPaths, function( serverPath, callback ) {
+ var serverIndex = 0;
+ children.push( spawnOne( assert, _.extend( {}, spawnOptions, {
+ name: "Server" + ( serverPaths.length > 1 ? " " + ( ++serverIndex ) : "" ),
+ path: serverPath,
+ onReady: callback
+ } ) ) );
+ }, function() {
+ var clientIndex = 0;
+ async.eachSeries( clientPaths, function startOneChild( item, callback ) {
+ children.push( spawnOne( assert, _.extend( {}, spawnOptions, {
+ name: "Client" +
+ ( clientPaths.length > 1 ? " " + ( ++clientIndex ) : "" ),
+ path: item } ) ) );
+
+ setTimeout( callback, 0 );
+ } );
+ } );
} );
} );
}
diff --git a/tests/tests/API Create Delete/client.js b/tests/tests/API Create Delete/client.js
index f2cba4c..958fe67 100644
--- a/tests/tests/API Create Delete/client.js
+++ b/tests/tests/API Create Delete/client.js
@@ -14,7 +14,6 @@
var remoteResource,
_ = require( "lodash" ),
- async = require( "async" ),
utils = require( "../../assert-to-console" ),
device = require( "../../../index" )( "client" ),
uuid = process.argv[ 2 ],
@@ -22,7 +21,7 @@ var remoteResource,
id: {
path: "/a/new-resource"
- // We will set deviceId once we know the id of the remote device
+ // We will set deviceId once we know the id of the remote device
},
discoverable: true,
properties: {
@@ -32,7 +31,7 @@ var remoteResource,
interfaces: [ "oic.if.baseline" ]
};
-console.log( JSON.stringify( { assertionCount: 5 } ) );
+console.log( JSON.stringify( { assertionCount: 7 } ) );
function findResourceByUrl( url, deviceId ) {
return Promise.all( [
@@ -51,61 +50,65 @@ function findResourceByUrl( url, deviceId ) {
} );
}
-async.series( [
- function findTestServer( callback ) {
- findResourceByUrl( "/a/" + uuid ).then(
- function( resource ) {
- remoteResource = resource;
+device.create( { id: {} } )
+ .then( function() {
+ utils.assert( "ok", false, "Client: Creating malformed resource has succeeded" );
+ }, function( error ) {
+ utils.assert( "strictEqual", ( "" + error ),
+ "Error: create: Invalid OicResourceInit",
+ "Client: Creating malformed resource has errored locally with the correct message" );
+ return findResourceByUrl( "/a/" + uuid );
+ } )
+ .then( function destinationDeviceFound( resource ) {
+ newRemoteResourceTemplate.id.deviceId = resource.id.deviceId;
+ return newRemoteResourceTemplate;
+ } )
+ .then( function createRemoteMalformedResource( template ) {
+ return new Promise( function( fulfill, reject ) {
+ var lowlevel = require( "bindings" )( "iotivity" );
+ var receptacle = {};
+ var result = lowlevel.OCDoResource( receptacle, lowlevel.OCMethod.OC_REST_POST,
+ "/a/crazy", device._devices[ template.id.deviceId ].address, null,
+ lowlevel.OCConnectivityType.CT_DEFAULT, lowlevel.OCQualityOfService.OC_HIGH_QOS,
+ function( handle, response ) {
+ utils.assert( "strictEqual", response.result,
+ lowlevel.OCStackResult.OC_STACK_ERROR,
+ "Client: Server sent OC_STACK_ERROR when creating invalid resource" );
+ fulfill( template );
+ return lowlevel.OCStackApplicationResult.OC_STACK_DELETE_TRANSACTION;
+ }, null, 0 );
- // We know the id of the test server, so set it on the resource template
- newRemoteResourceTemplate.id.deviceId = resource.id.deviceId;
- callback();
- }, callback );
+ if ( result !== lowlevel.OCStackResult.OC_STACK_OK ) {
+ reject( new Error( "Client: Request to create malformed resource failed" ) );
+ }
+ } );
+ } )
+ .then( function createRemoteResource( template ) {
+ return device.create( template );
+ } )
+ .then( function discoverNewlyCreatedResource( resource ) {
+ utils.assert( "ok", true, "Client: Creating remote resource has succeeded" );
+ remoteResource = resource;
+ return findResourceByUrl( "/a/new-resource", resource.deviceId );
+ } )
+ .then( function attemptToCreateDuplicateResource( resource ) {
+ utils.assert( "ok", true, "Client: Discovering remote resource has succeeded" );
+ utils.assert( "deepEqual", resource.id, remoteResource.id,
+ "Client: Discovered remote resource has the same id as the created resource" );
+ return device.create( newRemoteResourceTemplate );
+ } )
+ .then( function creatingDuplicateResourceHasSucceeded() {
+ return Promise.reject( new Error( "Server created duplicate resource" ) );
},
-
- function createRemoteResource( callback ) {
- device.create( newRemoteResourceTemplate ).then(
- function( resource ) {
-
- // Use the newly created remote resource instead of the initial resource that we
- // used for locating the test server.
- remoteResource = resource;
- utils.assert( "ok", true, "Client: Creating remote resource has succeeded" );
- callback();
- }, callback );
- },
-
- function findRemoteResource( callback ) {
- findResourceByUrl( "/a/new-resource", remoteResource.deviceId ).then(
- function( resource ) {
- utils.assert( "ok", true, "Client: Discovering remote resource has succeeded" );
- utils.assert( "deepEqual", resource.id, remoteResource.id,
- "Client: Discovered remote resource has the same id as the created resource" );
- callback();
- }, callback );
- },
-
- function createDuplicateRemoteResource( callback ) {
- device.create( newRemoteResourceTemplate ).then(
- function() {
- callback( new Error( "Server created duplicate resource" ) );
- }, function() {
- utils.assert( "ok", true,
- "Client: Creating a duplicate remote resource is not possible" );
- callback();
- } );
- },
-
- function deleteRemoteResource( callback ) {
- device.delete( remoteResource.id ).then(
- function() {
- utils.assert( "ok", true, "Client: Deleting the remote resource has succeeded" );
- console.log( JSON.stringify( { killPeer: true } ) );
- process.exit( 0 );
- }, callback );
- }
-], function( error ) {
- if ( error ) {
+ function deleteRemoteResource() {
+ utils.assert( "ok", true, "Client: Creating a duplicate remote resource is not possible" );
+ return device.delete( remoteResource.id );
+ } )
+ .then( function remoteResourceDeleted() {
+ utils.assert( "ok", true, "Client: Deleting the remote resource has succeeded" );
+ console.log( JSON.stringify( { killPeer: true } ) );
+ process.exit( 0 );
+ } )
+ .catch( function catchErrors( error ) {
utils.die( "Client: Error: " + error.message + " and result " + error.result );
- }
-} );
+ } );
diff --git a/tests/tests/API Create Delete/server.js b/tests/tests/API Create Delete/server.js
index e9c93ca..30ad484 100644
--- a/tests/tests/API Create Delete/server.js
+++ b/tests/tests/API Create Delete/server.js
@@ -12,98 +12,84 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-var resource,
- async = require( "async" ),
- utils = require( "../../assert-to-console" ),
+var utils = require( "../../assert-to-console" ),
device = require( "../../../index" )( "server" ),
uuid = process.argv[ 2 ];
console.log( JSON.stringify( { assertionCount: 5 } ) );
-async.series( [
- function register( callback ) {
- device.register( {
- id: {
- path: "/a/" + uuid
- },
- resourceTypes: [ "core.light" ],
- interfaces: [ "oic.if.baseline" ],
- discoverable: true,
- properties: {
- someProperty: "someValue"
- }
- } ).then( function( theResource ) {
- resource = theResource;
- callback();
- }, callback );
+device.register( {
+ id: {
+ path: "/a/" + uuid
},
+ resourceTypes: [ "core.light" ],
+ interfaces: [ "oic.if.baseline" ],
+ discoverable: true,
+ properties: {
+ someProperty: "someValue"
+ }
+} ).then( function( resource ) {
+ var done,
+ requestIndex = 0,
+ requestHandler = function( request ) {
+ switch ( requestIndex ) {
- function processRequests( callback ) {
- var done,
- requestIndex = 0,
- requestHandler = function( request ) {
- switch ( requestIndex ) {
-
- // Both the initial "createrequest" event and the subsequent duplicate
- // "createrequest" event are handled by the same code. The expected behaviour is
- // asserted by the client.
- case 0:
- case 1:
- utils.assert( "strictEqual", request.type, "createrequest",
- "Server: " + ( requestIndex === 0 ? "First" : "Second" ) +
- " request is 'create'" );
- utils.assert( "deepEqual", request.res, {
- discoverable: true,
- id: {
- deviceId: device.device.uuid,
- path: "/a/new-resource"
- },
- resourceTypes: [ "core.light" ],
- interfaces: [ "oic.if.baseline" ],
- properties: {
- someKey: "someValue"
- }
- }, "Server: Resource signature is as expected" );
- device.register( request.res ).then(
- function( theResource ) {
- resource = theResource;
- request.sendResponse( null ).catch( done );
- },
- function( error ) {
- request.sendError( error ).catch( done );
- } );
- break;
+ // Both the initial "createrequest" event and the subsequent duplicate
+ // "createrequest" event are handled by the same code. The expected behaviour is
+ // asserted by the client.
+ case 0:
+ case 1:
+ utils.assert( "strictEqual", request.type, "createrequest",
+ "Server: " + ( requestIndex === 0 ? "First" : "Second" ) +
+ " request is 'create'" );
+ utils.assert( "deepEqual", request.res, {
+ discoverable: true,
+ id: {
+ deviceId: device.device.uuid,
+ path: "/a/new-resource"
+ },
+ resourceTypes: [ "core.light" ],
+ interfaces: [ "oic.if.baseline" ],
+ properties: {
+ someKey: "someValue"
+ }
+ }, "Server: Resource signature is as expected" );
+ device.register( request.res ).then(
+ function( theResource ) {
+ resource = theResource;
+ request.sendResponse( null ).catch( done );
+ },
+ function( error ) {
+ request.sendError( error ).catch( done );
+ } );
+ break;
- case 2:
- utils.assert( "strictEqual", request.type, "deleterequest",
- "Server: Third request is 'delete'" );
- device.unregister( resource ).then(
- function() {
- request.sendResponse( null ).then( done, done );
- },
- function( error ) {
- request.sendError( error ).then( done, done );
- } );
- break;
+ case 2:
+ utils.assert( "strictEqual", request.type, "deleterequest",
+ "Server: Third request is 'delete'" );
+ device.unregister( resource ).then(
+ function() {
+ request.sendResponse( null ).then( done, done );
+ },
+ function( error ) {
+ request.sendError( error ).then( done, done );
+ } );
+ break;
- default:
- done( new Error( "Unexpected request" ) );
- break;
- }
- requestIndex++;
- };
- done = function( error ) {
- device.removeEventListener( "createrequest", requestHandler );
- device.removeEventListener( "deleterequest", requestHandler );
- callback( error );
+ default:
+ done( new Error( "Unexpected request" ) );
+ break;
+ }
+ requestIndex++;
};
- device.addEventListener( "createrequest", requestHandler );
- device.addEventListener( "deleterequest", requestHandler );
- console.log( JSON.stringify( { ready: true } ) );
- }
-
-], function( error ) {
- if ( error ) {
- utils.die( "Client: Error " + error.message + " and result " + error.result );
- }
+ done = function( error ) {
+ device.removeEventListener( "createrequest", requestHandler );
+ device.removeEventListener( "deleterequest", requestHandler );
+ return Promise.reject( error );
+ };
+ device.addEventListener( "createrequest", requestHandler );
+ device.addEventListener( "deleterequest", requestHandler );
+ console.log( JSON.stringify( { ready: true } ) );
+}, function( error ) {
+ utils.die( "Server: Error: " + error.message + " and result " + error.result );
} );
diff --git a/tests/tests/API Device Information/client.js b/tests/tests/API Device Information/client.js
index 28c933b..b618ac8 100644
--- a/tests/tests/API Device Information/client.js
+++ b/tests/tests/API Device Information/client.js
@@ -16,8 +16,25 @@ var _ = require( "lodash" );
var utils = require( "../../assert-to-console" );
var oic = require( "../../../index" )( "client" );
var uuid = process.argv[ 2 ];
+var expectedDeviceInfo = {
-console.log( JSON.stringify( { assertionCount: 2 } ) );
+ // The uuid field will be set to the deviceId when we know the deviceId
+ name: "API Device Info Server " + uuid,
+ coreSpecVersion: "core.1.0.0"
+};
+var expectedPlatformInfo = {
+ id: "platform " + uuid,
+ manufacturerName: "Random",
+ manufactureDate: 1299752880000,
+ manufacturerUrl: "http://example.com/",
+ model: "model " + uuid,
+ osVersion: "osVersion " + uuid,
+ platformVersion: "platformVersion " + uuid,
+ firmwareVersion: "firmwareVersion " + uuid,
+ supportUrl: "supportUrl:" + uuid
+};
+
+console.log( JSON.stringify( { assertionCount: 4 } ) );
new Promise( function findTheDeviceId( fulfill, reject ) {
var teardown;
@@ -29,37 +46,74 @@ new Promise( function findTheDeviceId( fulfill, reject ) {
if ( error ) {
reject( error );
} else {
+
+ // Complete the expectedDeviceInfo structure
+ expectedDeviceInfo.uuid = deviceId;
fulfill( deviceId );
}
};
oic.addEventListener( "resourcefound", resourcefound );
oic.findResources( { resourcePath: "/a/" + uuid } ).catch( teardown );
-} ).then( function( deviceId ) {
+} ).then( function getDeviceInfo( deviceId ) {
return oic.getDeviceInfo( deviceId ).then( function assertDeviceInfo( deviceInfo ) {
- utils.assert( "deepEqual", deviceInfo, {
- uuid: deviceId,
- name: "API Device Info Server " + uuid,
- coreSpecVersion: "core.1.0.0"
- }, "Client: The retrieved device information is as expected" );
+ utils.assert( "deepEqual", deviceInfo, expectedDeviceInfo,
+ "Client: The retrieved device information is as expected" );
return deviceId;
} );
-} ).then( function( deviceId ) {
+} ).then( function getPlatformInfo( deviceId ) {
return oic.getPlatformInfo( deviceId ).then( function assertPlatformInfo( platformInfo ) {
// Convert the manufactureDate to a timestamp for unambiguous comparison
utils.assert( "deepEqual", _.extend( platformInfo, {
manufactureDate: new Date( platformInfo.manufactureDate ).getTime()
- } ), {
- id: "platform " + uuid,
- manufacturerName: "Random",
- manufactureDate: 1299752880000,
- manufacturerUrl: "http://example.com/",
- model: "model " + uuid,
- osVersion: "osVersion " + uuid,
- platformVersion: "platformVersion " + uuid,
- firmwareVersion: "firmwareVersion " + uuid,
- supportUrl: "supportUrl:" + uuid
- }, "Client: The retrieved platform information is as expected" );
+ } ), expectedPlatformInfo, "Client: The retrieved platform information is as expected" );
+ } );
+} ).then( function findDevices() {
+ return new Promise( function( fulfill, reject ) {
+ var devicefound,
+ teardown = function( error, remoteDevice ) {
+ oic.removeEventListener( "devicefound", devicefound );
+ if ( error ) {
+ reject( error );
+ } else {
+ fulfill( remoteDevice );
+ }
+ };
+ devicefound = function( event ) {
+ if ( event.device.uuid === expectedDeviceInfo.uuid ) {
+ teardown( null, event.device );
+ }
+ };
+ oic.addEventListener( "devicefound", devicefound );
+ oic.findDevices().catch( teardown );
+ } ).then( function assertDiscoveredDeviceInfo( deviceInfo ) {
+ utils.assert( "deepEqual", deviceInfo, expectedDeviceInfo,
+ "Client: The discovered device information is as expected" );
+ } );
+} ).then( function findPlatforms() {
+ return new Promise( function( fulfill, reject ) {
+ var platformfound,
+ teardown = function( error, remotePlatform ) {
+ oic.removeEventListener( "platformfound", platformfound );
+ if ( error ) {
+ reject( error );
+ } else {
+ fulfill( remotePlatform );
+ }
+ };
+ platformfound = function( event ) {
+ if ( event.platform.id === expectedPlatformInfo.id ) {
+ teardown( null, event.platform );
+ }
+ };
+ oic.addEventListener( "platformfound", platformfound );
+ oic.findPlatforms().catch( teardown );
+ } ).then( function assertDiscoveredPlatformInfo( platformInfo ) {
+
+ // Convert the manufactureDate to a timestamp for unambiguous comparison
+ utils.assert( "deepEqual", _.extend( platformInfo, {
+ manufactureDate: new Date( platformInfo.manufactureDate ).getTime()
+ } ), expectedPlatformInfo, "Client: The discovered platform information is as expected" );
} );
} ).then(
function testIsDone() {
diff --git a/tests/tests/API Device Rediscovery.js b/tests/tests/API Device Rediscovery.js
index 21dd323..2193a0f 100644
--- a/tests/tests/API Device Rediscovery.js
+++ b/tests/tests/API Device Rediscovery.js
@@ -16,8 +16,6 @@ var async = require( "async" );
var testUtils = require( "../assert-to-console" );
var uuid = process.argv[ 2 ];
-testUtils.assert( "ok", true, "Client: configured successfully" );
-
function runAsClient() {
var device = require( "../../index" )( "client" );
var currentServer, theResource;
@@ -60,8 +58,8 @@ function runAsClient() {
callback();
}
- // Forward assertions and teardown requests from server
- if ( jsonObject.assert || jsonObject.teardown ) {
+ // Forward assertions, teardown, and info requests from server
+ if ( jsonObject.assertion || jsonObject.teardown || jsonObject.info ) {
console.log( JSON.stringify( jsonObject ) );
}
} );
@@ -111,9 +109,14 @@ function runAsClient() {
currentServer.kill( "SIGINT" );
};
- console.log( JSON.stringify( { assertionCount: 9 } ) );
+ console.log( JSON.stringify( { assertionCount: 19 } ) );
+
+ testUtils.assert( "ok", true, "Client: configured successfully" );
async.series( [
+ function( callback ) {
+ device.subscribe().then( callback, callback );
+ },
launchServer,
discoverResource,
waitForDelete,
diff --git a/tests/tests/API Presence/client.js b/tests/tests/API Presence/client.js
index 9579fd6..5b449d2 100644
--- a/tests/tests/API Presence/client.js
+++ b/tests/tests/API Presence/client.js
@@ -12,113 +12,238 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-var theResource,
- _ = require( "lodash" ),
- async = require( "async" ),
- utils = require( "../../assert-to-console" ),
+var utils = require( "../../assert-to-console" ),
device = require( "../../../index" )( "client" ),
uuid = process.argv[ 2 ];
-console.log( JSON.stringify( { assertionCount: 5 } ) );
+console.log( JSON.stringify( { assertionCount: 8 } ) );
-function discoverTheResource() {
- var eventHandler,
- removeListener = function() {
- if ( eventHandler ) {
- device.removeEventListener( "resourcefound", eventHandler );
+// Tell the resource's server to perform an operation (either disablePresence or enablePresence)
+// or, if op is undefined, then do not communicate with the server. Either way, wait five seconds
+// for the ensuing events (or lack thereof).
+function countEvents( resource, op ) {
+ return new Promise( function( fulfill, reject ) {
+ var events = [];
+
+ function devicefound( event ) {
+ if ( event.device.uuid === resource.id.deviceId ) {
+ events.push( JSON.stringify( {
+ event: event.type,
+ source: { deviceId: event.device.uuid }
+ } ) );
}
- };
-
- return Promise.all( [
- new Promise( function( fulfill ) {
- eventHandler = function( event ) {
- var index,
- count = 0,
- url = "/a/" + uuid;
-
- if ( event.resource.id.path === url ) {
- utils.assert( "ok", true, "Client: Resource found" );
-
- for ( index in device._resources ) {
- if ( device._resources[ index ].id.path === url ) {
- count++;
- }
- }
+ }
+
+ function resourcefound( event ) {
+ if ( event.resource.id.path === resource.id.path &&
+ event.resource.id.deviceId === resource.id.deviceId ) {
+ events.push( JSON.stringify( {
+ event: event.type,
+ source: event.resource.id
+ } ) );
+ }
+ }
+
+ function deleteListener( event ) {
+ events.push( JSON.stringify( {
+ event: event.type,
+ source: resource.id
+ } ) );
+ }
+
+ function devicechange( event ) {
+ if ( event.device.uuid === resource.id.deviceId ) {
+ events.push( JSON.stringify( {
+ event: event.type,
+ changeType: event.changeType,
+ source: { deviceId: event.device.uuid }
+ } ) );
+ }
+ }
+
+ function waitForEvents() {
+
+ // Collect events for 5 more seconds before fulfilling the promise
+ setTimeout( function() {
+ doListeners( "remove" );
+ fulfill( { resource: resource, events: events.sort() } );
+ }, 2000 );
+ }
+
+ function doListeners( listenerOp ) {
+ if ( op === "enablePresence" ) {
+ device[ listenerOp + "EventListener" ]( "devicefound", devicefound );
+ device[ listenerOp + "EventListener" ]( "resourcefound", resourcefound );
+ } else {
+ resource[ listenerOp + "EventListener" ]( "delete", deleteListener );
+ }
+ device[ listenerOp + "EventListener" ]( "devicechange", devicechange );
+ }
+
+ doListeners( "add" );
+
+ if ( op ) {
+
+ // Issue an update() to trigger the desired presence op
+ resource.properties.op = op;
+ device.update( resource ).then( waitForEvents, reject );
+ } else {
+ waitForEvents();
+ }
+ } );
+}
+
+function discoverResources() {
+ return new Promise( function( fulfill, reject ) {
+ var resourcefound,
+ resources = [],
+ teardown = function( error ) {
+ device.removeEventListener( "resourcefound", resourcefound );
+ if ( error ) {
+ reject( error );
+ } else {
- utils.assert( "strictEqual", count, 1,
- "Client: Resource present exactly once among resources" );
- fulfill( event.resource );
+ // Sort the resources to make sure they always appear in the same order
+ fulfill( resources.sort( function( leftResource, rightResource ) {
+ return leftResource.id.deviceId.localeCompare( rightResource.id.deviceId );
+ } ) );
+ }
+ };
+ resourcefound = function resourcefound( event ) {
+ if ( event.resource.id.path === "/a/" + uuid ) {
+ resources.push( event.resource );
+ if ( resources.length >= 2 ) {
+ teardown();
+ }
}
};
- device.addEventListener( "resourcefound", eventHandler );
- } ),
- device.findResources().then(
- function() {
- utils.assert( "ok", true, "Client: device.findResources() successful" );
- } )
- ] ).then( function( results ) {
- removeListener();
- return results[ 0 ];
- }, removeListener );
+ device.addEventListener( "resourcefound", resourcefound );
+ device.findResources( { resourcePath: "/a/" + uuid } ).catch( teardown );
+ } );
}
-async.series( [
- function resourceDiscovery( callback ) {
- discoverTheResource().then(
- function( resource ) {
- theResource = resource;
- utils.assert( "strictEqual", !!device._handles[ resource.id.deviceId ], true,
- "Client: A presence handle is saved for the resource upon discovery" );
- callback( null );
- },
- function( error ) {
- callback( _.extend( error, { step: "first discovery" } ) );
- } );
- },
-
- function checkPresenceResponse( callback ) {
- var sequence = [],
- handler,
- cleanup = function( error ) {
- theResource.removeEventListener( "delete", handler );
- device.addEventListener( "resourcefound", handler );
- utils.assert( "deepEqual", sequence, [
- "delete",
- "resourcefound:" + theResource.id.deviceId + ":" + theResource.id.path
- ], "Client: presence response sequence is as expected" );
- callback( error );
- };
+function performStep( resourceOp, expectation, message ) {
+ return function( resources ) {
- handler = function( event ) {
- if ( event.type === "resourcefound" ) {
- if ( event.resource.id.deviceId === theResource.id.deviceId &&
- event.resource.id.path === theResource.id.path ) {
- sequence.push( event.type + ":" + theResource.id.deviceId + ":" +
- theResource.id.path );
- }
- } else {
- sequence.push( event.type );
- }
- if ( sequence.length >= 2 ) {
- cleanup();
- }
- };
-
- theResource.addEventListener( "delete", handler );
- device.addEventListener( "resourcefound", handler );
-
- // Start the presence cycling
- theResource.properties.op = "cyclePresence";
- device.update( theResource ).catch( cleanup );
- }
-
-], function( error ) {
- if ( error ) {
- utils.die( "Client: " + error.step + " failed with " + error + " and result " +
- error.result );
- } else {
- console.log( JSON.stringify( { killPeer: true } ) );
- process.exit( 0 );
- }
-} );
+ return Promise.all( [
+ countEvents( resources[ 0 ], resourceOp ),
+ countEvents( resources[ 1 ], resourceOp )
+ ] ).then( function( result ) {
+ utils.assert( "deepEqual",
+ [ result[ 0 ].events, result[ 1 ].events ], expectation( resources ), message );
+
+ return discoverResources();
+ } );
+ };
+}
+
+function performSubscription( operation, resourceIndex ) {
+ return function issueSubscribeOperation( resources ) {
+ return Promise.all( [
+ Promise.resolve( resources ),
+ device[ operation ].apply( device,
+ ( resourceIndex === undefined ? [] : [ resources[ resourceIndex ].id.deviceId ] ) )
+ ] )
+ .then( function passOnResources( result ) {
+ return Promise.resolve( result[ 0 ] );
+ } );
+ };
+}
+
+discoverResources()
+ .then( performStep( "disablePresence",
+ function expectationForDisablePresenceWhileUnsubscribedFromAll() {
+ return [ [], [] ];
+ }, "Client: No events upon disablePresence() while not subscribed" ) )
+ .then( performStep( "enablePresence",
+ function expectationForEnablePresenceWhileUnsubscribedFromAll() {
+ return [ [], [] ];
+ }, "Client: No events upon enablePresence() while not subscribed" ) )
+ .then( performSubscription( "subscribe" ) )
+ .then( performStep( "disablePresence",
+ function expectationForDisablePresenceWhileSubscribedToAll( resources ) {
+ return [
+ [
+ JSON.stringify( { event: "devicechange", changeType: "changed",
+ source: { deviceId: resources[ 0 ].id.deviceId }
+ } ),
+ JSON.stringify( { event: "devicechange", changeType: "deleted",
+ source: { deviceId: resources[ 0 ].id.deviceId }
+ } ),
+ JSON.stringify( { event: "delete", source: resources[ 0 ].id } )
+ ].sort(),
+ [
+ JSON.stringify( { event: "devicechange", changeType: "changed",
+ source: { deviceId: resources[ 1 ].id.deviceId }
+ } ),
+ JSON.stringify( { event: "devicechange", changeType: "deleted",
+ source: { deviceId: resources[ 1 ].id.deviceId }
+ } ),
+ JSON.stringify( { event: "delete", source: resources[ 1 ].id } )
+ ].sort()
+ ];
+ }, "Client: Events upon disablePresence() of both servers while subscribed are as " +
+ "expected" ) )
+ .then( performStep( "enablePresence",
+ function expectationForEnablePresenceWhileSubscribedToAll( resources ) {
+ return [
+ [
+ JSON.stringify( { event: "devicefound",
+ source: { deviceId: resources[ 0 ].id.deviceId }
+ } ),
+ JSON.stringify( { event: "devicechange", changeType: "added",
+ source: { deviceId: resources[ 0 ].id.deviceId }
+ } ),
+ JSON.stringify( { event: "resourcefound", source: resources[ 0 ].id } )
+ ].sort(),
+ [
+ JSON.stringify( { event: "devicefound",
+ source: { deviceId: resources[ 1 ].id.deviceId }
+ } ),
+ JSON.stringify( { event: "devicechange", changeType: "added",
+ source: { deviceId: resources[ 1 ].id.deviceId }
+ } ),
+ JSON.stringify( { event: "resourcefound", source: resources[ 1 ].id } )
+ ].sort()
+ ];
+ }, "Client: Events upon enablePresence() of both servers while subscribed are as " +
+ "expected" ) )
+ .then( performSubscription( "unsubscribe", 0 ) )
+ .then( performStep( "disablePresence",
+ function expectationForDisablePresenceWithOneUnsubscribed( resources ) {
+ return [
+ [],
+ [
+ JSON.stringify( { event: "devicechange", changeType: "deleted",
+ source: { deviceId: resources[ 1 ].id.deviceId }
+ } ),
+ JSON.stringify( { event: "delete", source: resources[ 1 ].id } )
+ ].sort()
+ ];
+ }, "Client: Events upon disablePresence() with one server not subscribed are as " +
+ "expected" ) )
+ .then( performStep( "enablePresence",
+ function expectationForEnablePresenceWithOneUnsubscribed( resources ) {
+ return [
+ [],
+ [
+ JSON.stringify( { event: "devicefound",
+ source: { deviceId: resources[ 1 ].id.deviceId }
+ } ),
+ JSON.stringify( { event: "devicechange", changeType: "added",
+ source: { deviceId: resources[ 1 ].id.deviceId }
+ } ),
+ JSON.stringify( { event: "resourcefound", source: resources[ 1 ].id } )
+ ].sort()
+ ];
+ }, "Client: Events upon enablePresence() with one server not subscribed are as " +
+ "expected" ) )
+ .then(
+ function() {
+ console.log( JSON.stringify( { killPeer: true } ) );
+ process.exit( 0 );
+ },
+ function( error ) {
+ utils.die( ( "" + error ) );
+ } );
diff --git a/tests/tests/API Presence/server.js b/tests/tests/API Presence/common-server.js
index e93cf67..04db1b2 100644
--- a/tests/tests/API Presence/server.js
+++ b/tests/tests/API Presence/common-server.js
@@ -12,45 +12,32 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+module.exports = function( serverIndex ) {
+
var theResource,
requestCount = 0,
utils = require( "../../assert-to-console" ),
uuid = process.argv[ 2 ],
device = require( "../../../index" )( "server" );
-console.log( JSON.stringify( { assertionCount: 5 } ) );
+console.log( JSON.stringify( { assertionCount: 8 } ) );
-utils.assert( "ok", true, "Server: device configured successfully" );
+utils.assert( "ok", true, "Server " + serverIndex + ": device configured successfully" );
device.addEventListener( "updaterequest", function( request ) {
requestCount++;
- if ( request.res.op === "cyclePresence" ) {
- setTimeout( function() {
- device.disablePresence().then(
- function() {
- utils.assert( "ok", true,
- "Server: device.disablePresence() successful" );
- },
- function( error ) {
- utils.assert( "ok", false,
- "Server: device.disablePresence() failed with: " + error );
- } );
- setTimeout( function() {
- device.enablePresence().then(
- function() {
- utils.assert( "ok", true,
- "Server: device.enablePresence() successful" );
- },
- function( error ) {
- utils.assert( "ok", false,
- "Server: device.enablePresence() failed with: " + error );
- } );
- }, 5000 );
- }, 5000 );
+ function sendResponse( error ) {
+ utils.assert( "strictEqual", ( error ? ( "" + error ) : "" ), "", "Server " +
+ serverIndex + ": " + request.res.op + "()" );
+ request[ error ? "sendError" : "sendResponse" ]( error || null )
+ .catch( function( error ) {
+ utils.die( "Server " + serverIndex + ": Failed to deliver update response: " +
+ ( "" + error ) + ", and result " + error.result );
+ } );
}
- request.sendResponse( null );
+ device[ request.res.op ]().then( sendResponse, sendResponse );
} );
device.enablePresence().then(
@@ -65,30 +52,34 @@ device.enablePresence().then(
} ).then(
function( resource ) {
theResource = resource;
- utils.assert( "ok", true, "Server: device.register() successful" );
+ utils.assert( "ok", true, "Server " + serverIndex +
+ ": device.register() successful" );
// Signal to the test suite that we're ready for the client
console.log( JSON.stringify( { ready: true } ) );
},
function( error ) {
utils.assert( "ok", false,
- "Server: device.register() failed with: " + error );
+ "Server " + serverIndex + ": device.register() failed with: " + error );
} );
}, function( error ) {
utils.assert( "ok", false,
- "Server: device.enablePresence() failed with: " + error );
+ "Server " + serverIndex + ": device.enablePresence() failed with: " + error );
} );
// Cleanup on SIGINT
process.on( "SIGINT", function() {
device.unregister( theResource ).then(
function() {
- utils.assert( "ok", true, "Server: device.unregister() successful" );
+ utils.assert( "ok", true, "Server " + serverIndex +
+ ": device.unregister() successful" );
process.exit( 0 );
},
function( error ) {
utils.assert( "ok", false,
- "Server: device.unregister() failed with: " + error );
+ "Server " + serverIndex + ": device.unregister() failed with: " + error );
process.exit( 0 );
} );
} );
+
+};
diff --git a/tests/tests/API Presence/server1.js b/tests/tests/API Presence/server1.js
new file mode 100644
index 0000000..56c2919
--- /dev/null
+++ b/tests/tests/API Presence/server1.js
@@ -0,0 +1 @@
+require( "./common-server" )( 1 );
diff --git a/tests/tests/API Presence/server2.js b/tests/tests/API Presence/server2.js
new file mode 100644
index 0000000..c1c3a87
--- /dev/null
+++ b/tests/tests/API Presence/server2.js
@@ -0,0 +1 @@
+require( "./common-server" )( 2 );