Running Tests
The library includes a set of unit tests, as well as scripts to collect performance measurements.
Running Unit Tests
The library includes unit tests (Mocha) and can be run with the following command:
~$ npm test
The test will check the core module:
Pubsub
Code
CodeEngine
Dispatcher
Scheduler
Each unit test can also be run individually using the command mocha <test file>
:
~$ mocha require-test.js
ThingsJS library itself
Test file(s): test/require-test.js
- Require test:
require('things-js')
- No error should be thrown
Code
Test file:test/Code-test.js
- No error should be thrown
- Output equivalency:
Code.run
- Tests the output equivalency of instrumented
things-js
code versus raw code
- Tests the output equivalency of instrumented
Code Engine
Test file: test/CodeEngine-test.js
- Initialization:
new CodeEngine()
:- Code Engine instance starts up and emits a
'ready'
signal
- Code Engine instance starts up and emits a
- ID assignment:
CodeEngine.id
:- A unique ID is assigned to an initialized
Code Engine
object
- A unique ID is assigned to an initialized
- Pubsub assignment:
CodeEngine.pubsub
- A
pubsub
object is assigned to an initializedCode Engine
- A
- Runs code:
CodeEngine.run_code
- Runs a new code instance on the engine when the proper arguments are given
- Dies gracefully:
CodeEngine.kill
Code Engine
exits and throws no errorsPubsub
Test file:test/Pubsub-test.js
- Initialization:
new Pubsub()
- Pubsub instance starts up and emits a
ready
signal
- Pubsub instance starts up and emits a
- ID assignment:
Pubsub.id
- A unique ID is assigned to an initialized
Pubsub
object
- A unique ID is assigned to an initialized
- Subscription works:
Pubsub.subscribe
- Handles single and multiple topic subscriptions
- Receives messages for a topic it is subscribed to
- Receives message in order
- Unsubscribe works:
Pubsub.unsubscribe
- No longer receives messages for the topic it has unsubscribed from
- Publishes to subscribed node:
Pubsub.publish
- Is able to publish data for a given topic to all node listening to the topic
- Dies gracefully on kill call:
Pubsub.kill
Pubsub
exits and throws no errors
Dispatcher
Test file: test/Dispatcher-test.js
- Initialization:
new Dispatcher()
Dispatcher
instance starts up and emits aready
signal
- ID assignment:
Dispatcher.id
- Dispatcher is assigned a unique ID
- Pubsub assignment:
Dispatcher.pubsub
- Dispatcher is assigned a
pubsub
object
- Dispatcher is assigned a
- Correct
Code Engine
view:Dispatcher.Engines
- Dispatcher detects the correct number of
code engine
instances in the network
- Dispatcher detects the correct number of
- Correct
Code Engine
statuses:Dispatcher.Engines
- Dispatcher detects the correct status of
idle
code engine
instances
- Dispatcher detects the correct status of
- Run code on a
Code Engine
:Dispatcher.runCode, Dispatcher.engines, Dispatcher.programs
Code Engine
returns a reply once code begins to execute- Dispatcher detects engines running code to have status
busy
- Dispatcher detects new
program
running - Dispatcher receives any
console output
by the new code instance
- Migration between
Code Engines
:Dispatcher.moveCode
,Dispatcher.engines
Code Engine
returns a reply once code migratesCode Engine
that migrates its code instance changes status toidle
Code Engine
that executes migrated snapshot changes status tobusy
- Migrated code executes from where it left off
- Process control:
Dispatcher.pauseCode, Dispatcher.resumeCode
- A
Code Engine
can receive consecutivepause
andresume
commands - Replies are received for each
pause
orresume
command sent
- A
Scheduler
Test file: test/Scheduler-test.js
- First fit scheduling algorithm:
Scheduler.algorithms['first_fit']
- Passes the base case scenario of 0 tasks and 0 devices
- Throws an error when not enough memory exists for the given tasks and devices
- Returns a correct mapping when enough memory exists for the given tasks and devices
- Computing actions to go from one mapping to another:
Scheduler.computeActions
- Computes the correct
run
commands to initialize new code instances - Computes the correct
kill
commands to stop existing code instances - Computes the correct
migrate
command to move code instances
- Computes the correct
- Initialization:
new Scheduler()
:Scheduler
instance starts up and emits aready
signal
- ID assignment:
Scheduler.id
:- Scheduler is assigned a unique ID
- Scheduler logs itself booting as its first event:
Scheduler.history
- The past history is non-empty
- The first event log is the Scheduler booting
- Ignores rogue processes:
Scheduler._assess
:- Does not return an error when assessing the current state of the network
- Ignores any processes without
code engine
owners
- Correct network view:
Scheduler._assess
:- Detects when a
Code Engine
has died - Does not fail when nodes leave the network
- Detects when a
- Handles an application request with no available devices:
Scheduler.behaviours['run_application']
- Request times out
- Able to schedule an empty application (no components):
Scheduler.behaviours['run_application']
- Sends back a reply over
pubsub
indicating success
- Sends back a reply over
- Able to schedule an application with components on a running device:
Scheduler.behaviours['run_application']
- Sends back a reply over
pubsub
indicating successs
- Sends back a reply over
- Utilizes available
Code Engines
that join network by migrating code instances:Scheduler._assess
Filesystem API
Test file: test/Filesystem-REST-test.js
- Writes a new file:
gfs.writeFile
- Execution causes no error
- Read an existing file:
gfs.readFile
- File name and contents match the file to be expected
- Read a non-existent file:
gfs.ReadFile
- Error is thrown
- Write an existing file:
gfs.writeFile
- Content is overwritten
- Append to a new file:
gfs.appendFile
- New file is created with the append content
- Append to an existing file:
gfs.appendFile
- The existing content is not overwritten but appended to for as long as
appendFile()
is called
- The existing content is not overwritten but appended to for as long as
- Delete a non-existent file:
gfs.deleteFile
- An error is thrown
- Delete an existing file:
gfs.deleteFile
- The file no longer exists and cannot be read
Filesystem REST API
Test file: test/Filesystem-test.js
GET requests
:- Get the
root
directory - Get a file from the
root
directory - Get a folder from the
root
directory - Get a file from a subdirectory
- Get the
POST requests:
:- Create a file in the
root
directory - Create a folder in the
root
directory - Create a file in a subdirectory
- Create a file in the
DELETE requests:
:- Delete a file from the
root
directory - Delete a non-empty folder
- Delete a file from the
Running Performance Measurement Scripts
There are scripts included that can be used to collect performance measurements. For example, we can measure the time taken to instrument input code or time taken to capture a snapshot.
~$ npm run test-script <script_name> <sample_count> <test_name>
<script_name>
can be one of the following:
instrument
snapshot
restore
execute
<sample_count>
must be a natural number greater than zero.
<test_name>
is the test session name; any output generated by the test will be prefixed with this string.
Measuring Instrumentation
This script will measure the time taken to instrument a given program, and the code size change.
~$ npm run test-script instrument 100 test-01
Measuring Snapshot
This script will measure the time taken to capture a snapshot, and the snapshot size.
~$ npm run test-script snapshot 100 test-01
Measuring Restoration
This script will measure the time taken to restore a program from a given snapshot.
~$ npm run test-script restore 100 test-01
Measuring Execution
This script will collect runtime statistics such as memory and CPU usage.
~$ npm run test-script execute 3 test-01